The Dink Network

Help with complicated script

October 10th 2014, 05:10 AM
custom_coco.gif
CocoMonkey
Bard He/Him United States
Please Cindy, say the whole name each time. 
I'm trying to make a DMOD with a turn-based battle system. The combat seems to work fine until I win the fight - then, it refuses to end. I can't quite figure out why.

I apologize in advance if my code is a nightmare. I try.

//Here it is, my turn-based battle system. It's heavily based on Dragon Warrior's.
//Thanks to Ryan8bit for taking apart the assembly code and figuring out how DW's system works.

void start
{

if (&arg1 == 1)
{
//Fighting The Slime
&mystr = 5
&mydef = 2
&myhp = 3
&myexp = 1
&mygold = 2
}

runaway()
fight()
}

void fight
{
command:
choice_start
title_start
Command?
title_end
"Attack"
"Magic"
"Item"
"Run"
choice_end

if (&result == 1)
{
strike()
}

if (&result == 2)
{
spell()
}

if (&result == 3)
{
herb()
}

if (&result == 4)
{
dinkrun()
}

enemy:
//Enemy's turn
say_stop_xy("`%The monster attacks!", 10, 370)
//First, let's see how enemy's attack compares to Dink's defense.

if (&mystr > &defense)
{
&jug = &mystr
&jugb = &defense
&jugb / 2
&jug -= &jugb
&jug / 2
//&jug is now the top of enemy's attack range.
&jugc = &jug
&jugc / 2
//&jugc is now the bottom of enemy's attack range.
&jug -= &jugc
&jug += 1
//gotta add 1 to get the right range because of how random() works
&jugb = random(&jug, &jugc)
}
else
{
&jug = &mystr
&jug += 4
&jug / 6
&jug += 1
&jugb = random(&jug, 0)
}

if (&jugb > 0)
{
&life -= &jugb
say_stop_xy("`%Dink takes &jugb damage.", 10, 370)
}
else
{
say_stop_xy("`%Dink is unharmed.", 10, 370)
}

if (&life > 0)
{
goto command;
}
else
{
//You lose. Let's make sure nothing else happens before the death cutscene
wait(10000)
}
}

void runaway
{
//let's give the enemy a chance to run away if Dink is too strong for them
&jug = &strength
&jug / 2

if (&jug > &mystr)
{
//Dink is over twice as strong as the monster, they should try to run
&jugb = random(4, 1)

 if (&jugb == 4)
 {
 //successful run attempt by the monster
 say_stop_xy("`%The monster ran away!", 10, 370)
 kill_this_task
 }
return
}

void strike
{
//Dink tries to hit the monster
say_stop_xy("`%Dink attacks!", 10, 370)
//First, there's a 1/64 chance the monster dodges
&jug = random(64, 1)

if (&jug == 64)
{
//tough luck, you missed
say_stop_xy("`%But the monster dodges the attack!", 10, 370)
goto enemy;
}

//Is the attack a critical hit? Let's find out.
&jug = random(32, 1)

if (&jug == 32)
{
//Crit! This bypasses the enemy's defense.
&jug = &strength
&jugb = &strength
&jugb / 2
//&jug is top of attack's range, &jugb is bottom. So:
&jug -= &jugb
&jug += 1
//gotta add 1 to get the right range because of how random() works
&dmg = random(&jug, &jugb)
//&dmg is the value of Dink's attack.
say_stop_xy("`%Critical hit!", 10, 370)
goto pow;
}

//Now we determine the strength of Dink's attack if not a crit
&jug = &strength

&jug -= &mydef
&jug / 2

//now &jug is half of (Dink's attack - Enemy defense). This is the TOP possible damage.
if (&jug == 1)
{
//If top possible damage is 1, bottom needs to be 0.
//Otherwise the range would be 1 to 1.
&jugc = 0
goto calc;
}

&jugc = &jug
&jugc / 2

//&jugc is a QUARTER of (Dink's attack - Enemy defense). This is the BOTTOM.

&jug -= &jugc
&jug += 1

&dmg = random(&jug, &jugc)

if (&dmg <= 0)
{
//Uh oh, Dink's attack ain't doing squat. Let's give him one more chance to hit the thing.
&jug = random(2, 1)
 if (&jug == 2)
 {
 //Dink wins the coinflip and damage becomes 1.
 &dmg = 1
 }
 else
 {
 //Bummer, no damage.
 &dmg = 0
 }
}

if (&dmg == 0)
{
say_stop_xy("`%The monster laughs at Dink's feeble attack.", 10, 370)
goto enemy;
}

pow:
say_stop_xy("`%Dink does &dmg damage.", 10, 370)
&myhp -= &dmg

if (&myhp <= 0)
{
//Monster is dead.
win()
kill_this_task
}
goto enemy;
}

void win
{
stopmidi
playsound(56, 44100, 0, 0, 0)
say_stop_xy("`%You have done well in defeating the monster.", 10, 370)
&myexp * &expmulti
&exp += &myexp
&gold += &mygold
say_stop_xy("`%You have gained &myexp experience points and &mygold gold.", 10, 370)
kill_this_task
}


I haven't implemented commands other than "attack" yet, but as long as you pick that over and over, combat works as intended until you win. The sound plays, it says you won, and it awards the exp and gold, but the enemy then proceeds to attack as if the winning never happened. I tried moving kill_this_task back into the strike procedure after win() is called, but it didn't help.
October 10th 2014, 05:31 AM
slimeg.gif
metatarasal
Bard He/Him Netherlands
I object 
I think each command is treated as a seperate script, so kill_this_task() only ends win(), not fight(). Placing it in strike() isn't going to do anything for the same reason, you need to kill fight() rather than strike() or win().

At least that's what I'm guessing right now.
October 10th 2014, 05:36 AM
spike.gif
Try adding a return at the end of win(). I know both kill_this_task and return seems redundant, but it's worked for me in the past; due reasons Meta explained, no doubt.

EDIT: Scratch that, return doesn't work any better.
October 10th 2014, 05:53 AM
custom_magicman.gif
magicman
Peasant They/Them Netherlands duck
Mmmm, pizza. 
This is true. kill_this_task() kills a script instance. Whenever you blah(), that code is run in a new script instance. I think the if (&myhp < 0) block would do great just after handling the choices in void fight.

For a similar reason, I see that in void strike, you goto enemy. I suggest using the simple return, here's why:

Script instance 1 encounters the line "strike()", creates script instance 2, and waits for it to get back.
Script instance 2 searches for "void strike", finds it, runs it.
... encounters the line "goto enemy".
... searches for "enemy:", finds it, runs it.
... encounters "goto command".
... searches for "command:", finds it, runs it.
... encounters the line "strike()", creates instance 3, and waits for it to get back.
Script instance 3 searches for "void strike", finds it, runs it.
etc.

I expect an epic combat of lots of rounds to crash the game, because you'll run into the script limit. With return, it'll be script instance 1 that wakes up, hopefully skips the other &result checks, and proceeds at enemy:. I say "hopefully", because &result may change if whatever choice you made results in another choice menu.
October 10th 2014, 07:57 AM
slimeg.gif
metatarasal
Bard He/Him Netherlands
I object 
just make a new variable called &oldresult and just use &oldresult = &result. Then you just use &oldresult so that whatever menu might have popped up in the mean time is irrelevant. This is nice having to prevent some nasty bugs that are hard to replicate. I think the local variable &oldresult would also be unique to that script instance.
October 10th 2014, 11:38 AM
wizardg.gif
Paul
Peasant He/Him United States
 
The Dink Engine just don't handle user-called procedures very well. It should be possible make this work right if you're very careful to kill them off after each use, but I think an easier solution would be (against all conventional programing advice) to rewrite it using only goto.
October 10th 2014, 05:09 PM
custom_coco.gif
CocoMonkey
Bard He/Him United States
Please Cindy, say the whole name each time. 
I rewrote it using only goto and it seems to have fixed the problem, thanks.