######################################################################## # Many of the tests in this script might seem silly. That is okay. # # See interactivetest.hss for tests which require user input. # # Any time you are preparing to fix an engine bug, consider adding a # test that demonstrates the bug FIRST before you fix the bug. include, hstests.hss include, autotest.hsi include, "autotest-plotstr.hsi" # Save slots used: # 1 for save/load tests # 2 for save/load tests # 3 for export globals tests # 4 test expanding strings from save slots # 5 empty, for testing failure of expanding strings from empty save slots # 31 save slot: persistent # 400 used for testing higher number save slots ######################################################################## define constant(1, default wait) define constant(10, timer:checkpoint) define constant(30, timer:set args test) global variable(100, menu item script global) global variable(101, timer global) global variable(102, timer global will become) global variable(103, battle script sequence) global variable(104, menu close global) global variable(105, embedding global) global variable(106, export global 1) global variable(107, export global 2) global variable(108, expecting death) global variable(109, died) global variable(110, map autorun triggered) global variable(111, eachstep triggered) global variable(112, textbox script triggered) global variable(113, npc script triggered) global variable(114, testing script triggers) global variable(115, savegame map) global variable(116, savegame slice) global variable(117, savegame npc) global variable(118, called resetgame) global variable(119, hook loadgame) global variable(120, timer checkpoint interval) global variable(121, saveglobal) global variable(122, observed hero count) global variable(123, move hero count) global variable(124, timer expect arg 1) global variable(125, timer expect arg 2) global variable(126, autorun counter) global variable(50000, the global with the biggest id) # 200-500 used in hstests.hss # 1000-1063 used in zonetests define constant(1000, array:zones) # 5000-6000 used in hstests.hss #string 0 = error messages #string 1-9 = temporary misc #string 10 = error message arg #string 11 = asserts #string 95-99 = temp misc define constant(10, err arg string) define constant(11, assert expression string) # see notes at the top of this file for what save slots get used define constant(31, save slot: persistent) define constant(400, save slot: high) ######################################################################## # Set on starting map 0 plotscript, mapautorun, begin # Test that the map autorun script on the starting map happens before # the newgame script map autorun triggered += 1 end # TODO: What would be nice is to reset the game between each test suite, # making use of the ability to pass args to a new/load game script, # so that we don't have to worry about things interfering with each other plotscript, new game, arg1, arg2, arg3, begin trace($1="newgame script") tracevalue(arg1, arg2, arg3) suspend player seed random(4444) # This stuff is for "test reset game" import globals(save slot: persistent, @called resetgame, @called resetgame) if (called resetgame || arg1) then ( after resetgame(arg1, arg2, arg3) run tests 2 # continue tests ) else ( run tests 1 # start tests ) end plotscript, load game, slot, arg1, arg2, arg3, begin trace($1="loadgame script") tracevalue(slot, arg1, arg2, arg3, hook loadgame) suspend player seed random(4444) if (hook loadgame) then ( after loadgame(arg1, arg2, arg3) run tests 3 # continue tests ) else ( # This means we weren't expecting this script to be called crash ) end # testing starts here script, run tests 1, begin assert(map autorun triggered == 1) interpreter tests extra array tests global npc tests scheduling tests plotstr tests string load tests platform tests save slot tests slice tests master palette tests maptile tests wallchecking tests zone tests map state tests inventory tests hero tests death tests trigger tests door tests map autorun tests npc tests menu tests textbox tests shop tests script trigger tests attack tests enemy tests battle tests timer tests pathfinding tests vehicle tests obsolete map data tests run game tests resetgame tests # Resets the game crash end script, start checkpoint timer, interval, begin timer checkpoint interval := interval do start checkpoint interval end script, do start checkpoint interval , begin _checkpoint set timer(timer:checkpoint, 0, timer checkpoint interval, @do start checkpoint interval) end script, stop checkpoint timer, begin stop timer(timer:checkpoint) end # follows "resetgame tests" script, run tests 2, begin savegame tests # Saves and loads the game crash end # follows "savegame tests" script, run tests 3, begin # more tests here, potentially passed tests end script, passed tests, begin $0="TESTS SUCCEEDED" trace(0) gameover end ######################################################################## script, w, ticks=default wait, begin wait(ticks) _checkpoint end # Returns a string of the script call stack in given string id script, callstack, string, begin variable(depth, id) depth := 1 $ string = "" while (true) do ( id := getcallingscriptid(depth) if (id == 0) then (break) if (depth == 1) then ( getscriptname(string, id) ) else ( getscriptname(95, id) stringsprintf(string, $96="%s -> %s", 95, string) ) depth += 1 ) return(string) end script, crash, begin if(string length(10) >> 0) then( $0+"(" concatenate strings(0, err arg string) $0+")" ) show text box(1) $1="crash() called from " callstack(2) 1 $+ 2 trace(1) $1="in testsuite: " 1 $+ 0 trace(1) w(25) # script error(0) game over end script, assert failure, begin $1="ASSERT FAILURE:" trace(1) trace(assert expression string) crash end ######################################################################## # Test the interaction of different script fibres, including pausing, # waiting, and back-compat blocking. # This makes use of timers, but the real timer tests are elsewhere. # (Timer tests happen after these tests!) # Triggering scripts is also tested in "script trigger tests" and # a number of other tests for npcs, menus, battles, etc. script, scheduling tests, begin $0="scheduling tests" tick counter slice := create container set slice velocity x(tick counter slice, 1, 1000) blocking tests assert(check bug 430 enabled == false) variable (oldval) oldval := read general(178) # genBits2 write general(178, oldval, or, (2 ^ 1)) # Enable bug 430 assert(check bug 430 enabled == true) write general(178, oldval) free slice(tick counter slice) end script, ticknumber, begin return(slice x(tick counter slice)) end ######################################################################## script, blocker script, begin # assert(ticker 0 == expected ticker 0) ticker 1 += 1 wait ticker 1 += 1 end script, blocker script 2, begin assert(ticker 1 == expected ticker 1) ticker 2 += 1 wait ticker 2 += 1 assert(ticker 1 == expected ticker 1) end script, eachtick script, begin ticker 0 += 1 set timer(3, 0, 1, @eachtick script) end script, blocking tests, begin $0="blocking tests" variable (start tick) start tick := ticknumber ticker 0 := 0 ticker 1 := 0 # globals ticker 2 := 0 # Start an each-tick script on a timer that should not interfer with anything else # (since it has a higher timer ID) set timer(3, 0, 1, @eachtick script) wait(2) assert(ticknumber == start tick + 2) # Trigger another script and check that this one is blocked # (This is also a test for bug 430, because of eachtick script) expected ticker 0 := 0 set timer(1, 0, 1, @blocker script) assert(ticknumber == start tick + 2) # This script will continue running until the next tick assert(ticker 1 == 0) wait(1) assert(ticker 1 == 2) assert(ticknumber == start tick + 4) # Trigger two scripts to run at once; one blocks the other (test for bug 430) expected ticker 1 := 2 set timer(1, 0, 1, @blocker script) set timer(2, 0, 1, @blocker script 2) # Runs first assert(ticknumber == start tick + 4) wait assert(ticknumber == start tick + 7) # one tick wait each plus the wait above assert(ticker 1 == 4) assert(ticker 2 == 2) # Check eachtick ran every tick #trace value(ticknumber, start tick, ticker 0, ticker 1, ticker 2) assert(ticker 0 == ticknumber -- start tick) stop timer(3) end ######################################################################## script, bug 430 tester, begin variable (start tick) start tick := ticknumber wait # should be skipped if (ticknumber == start tick) then ( # was skipped ticker 1 += 1 ) end # blocking tests is completely broken if bug 430 is enabled, so here is a separate test, # Returns whether bug 430 is enabled script, check bug 430 enabled, begin $0="check bug 430 enabled" variable (start tick) start tick := ticknumber ticker 1 := 0 # globals ticker 2 := 0 # Trigger two scripts to run at once; one blocks the other (test for bug 430) expected ticker 1 := 0 set timer(1, 0, 1, @bug 430 tester) set timer(2, 0, 1, @blocker script 2) # Runs first wait assert(ticker 2 == 2) if(ticknumber == start tick + 2 && ticker 1 == 1) then ( # wait in blocker script 2 plus the wait above return (true) ) else if(ticknumber == start tick + 3 && ticker 1 == 0) then ( return (false) ) else ( # Other combinations should be impossible crash ) end ######################################################################## script, inventory tests, begin $0="Inventory tests" # This tests only the inventory and commands to manipulate it. # Far more item tests including some equipment/inventory stuff is in 'equipment tests' # Precondition: empty inventory assert(get inventory size == 30) variable(slot, lastslot) for (slot, 0, get inventory size -- 1) do ( assert(item in slot(slot) == -1) assert(item count in slot(slot) == 0) ) # Simple stuff get item(item:Boots, 3) assert(inventory(item:Boots) == 3) assert(item in slot(0) == item:Boots) assert(item count in slot(0) == 3) get item(item:Boots, 99) assert(inventory(item:Boots) == 102) delete item(item:Boots) assert(inventory(item:Boots) == 101) delete item(item:Boots, 101) assert(inventory(item:Boots) == 0) # test item slot commands assert(item in slot(0) == -1) assert(item count in slot(0) == 0) assert(item in slot(1) == -1) assert(item count in slot(1) == 0) set item in slot(1, item:Boots) # used on empty slot, adds one item assert(item in slot(1) == item:Boots) assert(item count in slot(1) == 1) assert(inventory(item:Boots) == 1) lastslot := get inventory size -- 1 set item in slot(lastslot, item:Boots) assert(item in slot(lastslot) == item:Boots) set item count in slot(lastslot, 99) assert(inventory(item:Boots) == 100) # check deleteitem deletes in right order delete item(item:Boots, 5) assert(inventory(item:Boots) == 95) assert(item count in slot(1) == 0) assert(item count in slot(lastslot) == 95) # transmog a stack of items set item in slot(lastslot, item:SteelSho) assert(item in slot(lastslot) == item:SteelSho) assert(item count in slot(lastslot) == 95) assert(inventory(item:SteelSho) == 95) assert(inventory(item:Boots) == 0) # test the two additional ways of deleting items set item in slot(lastslot, -1) assert(item count in slot(lastslot) == 0) set item in slot(2, item:Boots) set item count in slot(2, 0) assert(item in slot(2) == -1) # leave nothing for (slot, 0, get inventory size -- 1) do ( assert(item in slot(slot) == -1) ) # test max stack sizes assert(get item maximum stack size(item:Boots) == 99) assert(get item maximum stack size(item:Bulky) == 3) get item(item:Bulky, 6) assert(inventory(item:Bulky) == 6) assert(item count in slot(0) == 3) assert(item count in slot(1) == 3) set item in slot(2, item:Boots) set item count in slot(2, 40) # changing type should cause stacks to overflow if too large set item in slot(2, item:Bulky) assert(inventory(item:Bulky) == 46) assert(item count in slot(2) == 3) delete item(item:Bulky, 46) # Max out the inventory (was set to 10 rows, 30 slots) assert(get item(item:spamknif, 99) == true) # yes there is room for 99 of these assert(get item(item:spamknif, 3000) == false) # No, there will not be room for these assert(inventory(item:spamknif) == 2970) variable(who) who := find hero(hero:Freki) assert(force equip(who, slot:weapon, item:spamknif) == true) # Force Freki to use one of the spamknifes from the inventory assert(inventory(item:spamknif) == 2969) assert(force equip(who, slot:weapon, item:overflow) == true) # The previously equipped spamknife will go back to the inventory assert(inventory(item:spamknif) == 2970) assert(force equip(who, slot:weapon, item:overflow) == false) # There is no free slot for the currently equipped overflow sword that has to be removed to make room for the new overflow sword # Delete the items we filled the inventory with (some later tests expect an empty inventory) delete item(item:spamknif, inventory(item:spamknif)) assert(inventory(item:spamknif) == 0) # Force equip should also fail for an invalid item number, but we can't check that without throwing an error #assert(force equip(who, slot:weapon, 99999999) == false) # Force equip should also fail for an invalid hero party slot, but we can't check that without throwing an error #assert(force equip(-5, slot:weapon, item:overflow) == false) # Remove the overflow sword and delete it unequip(who, slot:weapon) assert(inventory(item:overflow) == 1) delete item(item:overflow, 1) assert(inventory(item:overflow) == 0) end ######################################################################## script, hero tests, begin hero party management tests # The party now contains heroes 0, 1, 2, 3 in that order hero appearance tests hero caterpillar tests suspend walkabouts tests hero exp and levels tests hero stat growth test hero stat tests equipment tests hero misc tests hero names embed tests # The party should continue to be 0, 1, 2, 3 end ## hero commands left to test ## spell lists #can learn spell (hero,attack) #forget spell (hero,attack) #knows spell (hero,attack) #read spell (hero,list,slot) #teach spell (hero,attack) #write spell (hero,list,slot,attack) ## Equipment #set default weapon (hero,item) ## Stats #get level MP (who, mp level slot, type) #set level MP (who, mp level slot, new value) ## Misc #hero base elemental resist as int (who, element) #hero total elemental resist as int (who, element) #set hero base elemental resist (who, element, percent) plotscript, add hero hook, slot, who, begin assert(hero by slot(slot) == who) observed hero count += 1 end plotscript, remove hero hook, slot, who, begin assert(hero by slot(slot) == -1) observed hero count -= 1 end plotscript, move hero hook, slot, old slot, loading, begin # This script only does something on three specific calls, see below, if (testing script triggers) then ( move hero count += 1 assert(loading == false) ) if (testing script triggers == 1) then ( # Swapping slots 2 and 3, both occupied assert(slot == 2 || slot == 3) assert(slot + old slot == 5) ) if (testing script triggers == 2) then ( # Swapping hero into an empty slot assert(slot == 20) assert(old slot >= 4) assert(hero by slot(slot) == hero:Kitt) assert(hero by slot(old slot) == -1) ) end script, hero party management tests, begin $0="hero party management tests" # Preconditions assert(room in active party == 3) variable(h0x, h0y, h0speed, h0d) h0x := hero x(0) h0y := hero y(0) h0speed := get hero speed(0) h0d := hero direction(0) assert(observed hero count == 1) add hero(hero:Helga) add hero(hero:Olaf) add hero(hero:Frumpy) assert(room in active party == 0) assert(observed hero count == 4) assert(hero by rank(0) == hero:Freki) assert(hero by rank(1) == hero:Helga) assert(hero by rank(2) == hero:Olaf) assert(hero by rank(3) == hero:Frumpy) # with a full party, hero by rank and hero by slot should return the same values assert(hero by rank(0) == hero by slot(0)) assert(hero by rank(1) == hero by slot(1)) assert(hero by rank(2) == hero by slot(2)) assert(hero by rank(3) == hero by slot(3)) # now delete the leader delete hero(hero:Freki) assert(observed hero count == 3) assert(room in active party == 1) assert(hero by rank(0) == 1) assert(hero by rank(1) == 2) assert(hero by rank(2) == 3) assert(hero by rank(3) == -1) assert(hero by slot(0) == -1) assert(hero by slot(1) == 1) assert(hero by slot(2) == 2) assert(hero by slot(3) == 3) # Test some commands which should work by rank, not party slot # (TODO: incomplete) assert(hero x(0) == h0x) assert(hero y(0) == h0y) assert(hero z(0) == 0) assert(get hero speed(0) == h0speed) assert(hero direction(0) == h0d) # add a different leader add hero(hero:Styrge) assert(hero by slot(0) == hero:Styrge) # test find hero assert(find hero(hero:Freki) == -1) assert(find hero(hero:Helga) == 1) assert(find hero(hero:Olaf) == 2) assert(find hero(hero:Frumpy) == 3) assert(find hero(hero:Styrge) == 0) # test rank in caterpillar assert(rank in caterpillar(hero:Freki) == -1) assert(rank in caterpillar(hero:Helga) == 1) assert(rank in caterpillar(hero:Olaf) == 2) assert(rank in caterpillar(hero:Frumpy) == 3) assert(rank in caterpillar(hero:Styrge) == 0) # Add a hero to the reserve add hero(hero:Kitt) assert(room in active party == 0) variable(slot) slot := find hero(hero:Kitt) assert(slot >= 4) assert(rank in caterpillar(hero:Kitt) == -1) assert(hero by slot(slot) == hero:Kitt) # this command needs to work on reserve slots assert(hero by rank(4) == -1) # this command should not work on reserve slots # names $1="Styrge" get hero name(2, 0) assert(string compare(1, 2)) $1="Helga" get hero name(2, 1) assert(string compare(1, 2)) $1="Olaf" get hero name(2, 2) assert(string compare(1, 2)) $1="Frumpy" get hero name(2, 3) assert(string compare(1, 2)) $1="Kitt" get hero name(2, find hero(hero:Kitt)) assert(string compare(1, 2)) $1="Kittzilla" set hero name(1, find hero(hero:Kitt)) get hero name(2, find hero(hero:Kitt)) assert(string compare(1, 2)) # Swapping swap out hero(hero:Helga) assert(hero by slot(1) == -1) assert(find hero(hero:Helga) >= 4) swap in hero(hero:Kitt) assert(hero by slot(1) == hero:Kitt) assert(find hero(hero:Kitt) >> 0 && find hero(hero:Kitt) <= 3) swap by position(0, 3) # swap Styrge and Frumpy assert(hero by slot(0) == hero:Frumpy) assert(hero by slot(3) == hero:Styrge) swap by position(3, find hero(hero:Helga)) # swap Styrge and Helga assert(hero by slot(3) == hero:Helga) assert(find hero(hero:Styrge) >= 4) swap by name(hero:Kitt, hero:Styrge) assert(hero by slot(1) == hero:Styrge) assert(find hero(hero:Kitt) >= 4) swap out hero(hero by slot(0)) # swap out frumpy assert(hero by slot(0) == -1) # restore numeric order add hero(hero:Freki) # Freki assert(hero by slot(0) == hero:Freki) swap by position(1, find hero(hero:Helga)) assert(hero by slot(1) == hero:Helga) swap by position(2, find hero(hero:Olaf)) assert(hero by slot(2) == hero:Olaf) swap by position(3, find hero(hero:Frumpy)) assert(hero by slot(3) == hero:Frumpy) # add another copy of Freki, should go to reserve add hero(hero:Freki) slot := find hero(hero:Freki) assert(slot == 0) delete hero by slot(slot) slot := find hero(hero:Freki) assert(slot >= 4) # add back leader Freki assert(hero by slot(0) == -1) add hero(hero:Freki) assert(hero by slot(0) == hero:Freki) # Delete the reserve Freki assert(slot >= 4) assert(hero by slot(slot) == hero:Freki) assert(delete hero by slot(slot) == 0) assert(hero by slot(slot) == -1) # original Freki should be untouched, and not in the reserve assert(find hero(hero:Freki) >= 0) assert(find hero(hero:Freki) <= 3) w # Test "move hero hook" testing script triggers := 1 # Tell the hook script to pay attention assert(move hero count == 0) # Swapping two occupied slots swap by position (2, 3) assert(move hero count == 2) testing script triggers := 2 # Swap occupied and empty swap by position(find hero(hero:Kitt), 20) assert(move hero count == 3) testing script triggers := false swap by position (2, 3) end script, hero appearance tests, begin # preconditions assert(hero by slot(3) == hero:Frumpy) # ensure we're using party slots, not hero ids or caterpillar position swap by position(2, 3) swap by position(1, 10) # 2 = hero:Frumpy assert(get hero picture(2, inside battle) == 8) assert(get hero picture(2, outside battle) == 9) assert(get hero picture(2, hero portrait) == 2) assert(get hero palette(2, inside battle) == -1) assert(get hero palette(2, outside battle) == -1) assert(get hero palette(2, hero portrait) == 2) set hero picture(2, 0, inside battle) set hero picture(2, 4, outside battle) set hero picture(2, 7, hero portrait) set hero palette(2, 37, inside battle) set hero palette(2, 1, outside battle) set hero palette(2, -1, hero portrait) show text box(17) # test portrait by party slot w(3) advance text box assert(get hero picture(2, inside battle) == 0) assert(get hero picture(2, outside battle) == 4) assert(get hero picture(2, hero portrait) == 7) assert(get hero palette(2, inside battle) == 37) assert(get hero palette(2, outside battle) == 1) assert(get hero palette(2, hero portrait) == -1) reset hero picture(2, inside battle) reset hero picture(2, outside battle) reset hero picture(2, hero portrait) reset hero palette(2, inside battle) reset hero palette(2, outside battle) reset hero palette(2, hero portrait) assert(get hero picture(2, inside battle) == 8) assert(get hero picture(2, outside battle) == 9) assert(get hero picture(2, hero portrait) == 2) assert(get hero palette(2, inside battle) == -1) assert(get hero palette(2, outside battle) == -1) assert(get hero palette(2, hero portrait) == 2) # Restore state swap by position(2, 3) swap by position(1, 10) end script, hero names embed tests, begin $0="hero names embed tests" variable(h0, h1, h2, h3) # remember the old party order h0 := hero by slot(0) h1 := hero by slot(1) h2 := hero by slot(2) h3 := hero by slot(3) swap by position(0, find hero(hero:Olaf)) swap out hero(hero by slot(1)) swap out hero(hero by slot(2)) swap out hero(hero by slot(3)) $1="Olaf,,," $2="Olaf,,," $3="Freki,Helga,Olaf,Frumpy,Styrge" hero names embed helper swap by position(2, find hero(hero:Olaf)) $1="Olaf,,," $2=",,Olaf," hero names embed helper swap by position(0, find hero(hero:Frumpy)) $1="Frumpy,Olaf,," $2="Frumpy,,Olaf," hero names embed helper swap by position(1, find hero(hero:Olaf)) swap by position(3, find hero(hero:Frumpy)) $1="Olaf,Frumpy,," $2=",Olaf,,Frumpy" hero names embed helper add hero(hero:Olaf) # slot 1 $4="Nolaf" set hero name(4, 1) $1="Olaf,Nolaf,Frumpy," $2="Olaf,Nolaf,,Frumpy" hero names embed helper swap by position(0, find hero(hero:Frumpy)) $1="Frumpy,Nolaf,Olaf," $2="Frumpy,Nolaf,,Olaf" $3="Freki,Helga,Nolaf,Frumpy,Styrge" hero names embed helper show no value, w advance textbox swap by position(0, 1) # Nolaf to front delete hero(hero:Olaf) # Test embedtext from save slots # Control test, no change to hero name save in slot(4), w expand string($1="${H1}", 4) show string(1), w assert(string compare(1, $2="Helga")) # Hero name by hero id # Helga should be in the reserve when this happens, but we don't care exactly which reserve slot variable(helgaslot) helgaslot := find hero(hero:Helga) assert(helgaslot > 3) # Change Helga's name set hero name($3="Hagar", helgaslot) save in slot(4), w expand string($1="${H1}", 4) show string(1), w assert(string compare(1, $2="Hagar")) # Restore hero name, check again set hero name($3="Helga", helgaslot) assert(string compare(get hero name(1, helgaslot), $2="Helga")) expand string($1="${H1}", 4) show string(1), w assert(string compare(1, $2="Hagar")) # Hero name by party slot # Put helga in slot 2 swap by position(find hero(hero:Helga), 2) assert(find hero(hero:Helga) == 2) # Rename her and resave set hero name($3="Humpty", 2) save in slot(4), w expand string($1="${P2}", 4) show string(1), w assert(string compare(1, $2="Humpty")) # Restore name, check again set hero name($3="Helga", 2) assert(string compare(get hero name(1, 2), $2="Helga")) expand string($1="${P2}", 4) show string(1), w assert(string compare(1, $2="Humpty")) # Hero name by caterpillar slot # Put helga in slot 3 swap by position(find hero(hero:Helga), 3) assert(find hero(hero:Helga) == 3) # Make slot 2 empty show string(get hero name(1, 2)), w swap out hero(hero:Olaf) assert(hero by slot(2) == -1) # Make sure slot 0 is empty assert(hero by slot(0) == -1) # Helga should be rank 1 (2nd) in the caterpillar assert(rank in caterpillar(hero:Helga) == 1) # Rename Helga and save set hero name($1="Hissy", 3) save in slot(4), w expand string($1="${C1}", 4) show string(1), w assert(string compare(1, $2="Hissy")) # Restore name, check again set hero name($3="Helga", 3) assert(string compare(get hero name(1, 3), $2="Helga")) expand string($1="${C1}", 4) show string(1), w assert(string compare(1, $2="Hissy")) show no value # Check strings being expanded from an empty slot # Make sure slot 5 is really empty delete save(5) assert(string compare($1="${H1}", expand string($2="${H1}", 5))) assert(string compare($1="${P0}", expand string($2="${P0}", 5))) assert(string compare($1="${C0}", expand string($2="${C0}", 5))) assert(string compare($1="${H1},${H2},${H3}", expand string($2="${H1},${H2},${H3}", 5))) # Although not hero related, lets throw some tests for ${V} and ${S} in here too. #saveglobal is global id 121 saveglobal := 54321 show string(expand string($1="Var=${V121}")), w assert(string compare(1, $2="Var=54321")) # Save the global to save slot 4, check it save in slot(4), w saveglobal := 1 show string(expand string($1="Var=${V121}", 4)), w assert(string compare(1, $2="Var=54321")) # Replace it with another value saveglobal := 100001 save in slot(4), w saveglobal := 1 show string(expand string($1="Var=${V121}", 4)), w assert(string compare(1, $2="Var=100001")) assert(saveglobal == 1) # Check empty slot 5 show string(expand string($1="Var=${V121}", 5)), w assert(string compare(1, $2="Var=${V121}")) # Test strings $255="A very fine mess" save in slot(4), w $255="Cleaned up" show string(expand string($2="${S255}", 4)), w assert(string compare(255, $1="Cleaned up")) assert(string compare(2, $1="A very fine mess")) # A different string $2="Is better than sloppy order" save in slot(4), w $2="" show string(expand string($3="${S2}", 4)), w assert(string compare(3, $1="Is better than sloppy order")) # An empty save slot (5) show string(expand string($3="${S2}", 5)), w assert(string compare(3, $1="${S2}")) # Test a string in a high-numbered save slot $3="Not Over 9000!!" save in slot(save slot: high) $3="Cleaned up" show string(expand string($2="String=${S3}", save slot: high)) assert(string compare(2, $1="String=Not Over 9000!!")) # restore old party order swap by position(0, find hero(h0)) swap by position(1, find hero(h1)) swap by position(2, find hero(h2)) swap by position(3, find hero(h3)) show box names advance textbox end script, hero names embed helper, begin show string(2) $4="${C0},${C1},${C2},${C3}" expand string(4) $5="${P0},${P1},${P2},${P3}" expand string(5) $6="${H65},${H1},${H2},${H3},${H4}" expand string(6) assert(string compare(1, 4)) assert(string compare(2, 5)) assert(string compare(3, 6)) show box names end script, show box names, begin show text box(9) w(10) show text box(10) w(10) show text box(11) w(10) end ## Party management #lock hero (who) #unlock hero (who) script, hero caterpillar tests, begin variable(i) $0="hero caterpillar tests" $err arg string="leader, cater=ON" do hero cater tests(me) suspend caterpillar for(i, 0, 3) do( $err arg string="hero " append number(err arg string, i) $err arg string=", cater=OFF" do hero cater tests(i) ) resume caterpillar $err arg string="" end script, do hero cater tests, who, begin variable(d) walk hero to x (who, 8), w wait for hero(who) assert(hero x(who) == 8) walk hero to y(who, 10), w wait for hero(who) assert(hero y(who) == 10) camera follows hero(who) walk hero to x(who, 9), w wait for hero(who) assert(hero x(who) == 9) walk hero to y(who, 8), w wait for hero(who) assert(hero y(who) == 8) camera follows hero(0) walk hero to x (who, 6), w wait for hero(who) assert(hero x(who) == 6) set hero position(who, 7, 7), w assert(hero x(who) == 7) assert(hero y(who) == 7) walk hero(who, south, 2) wait for hero(who), w assert(hero y(who) == 9) assert(hero direction(who) == south) assert(forward x(who) == 7) assert(forward y(who) == 10) walk hero(who, south, -1) # negative distance means walk backwards wait for hero(who), w assert(hero y(who) == 8) assert(hero direction(who) == south) walk hero(who, south, 1) wait for hero(who), w assert(hero y(who) == 9) assert(hero direction(who) == south) walk hero(who, west, 2) wait for hero(who), w assert(hero x(who) == 5) assert(hero direction(who) == west) assert(forward x(who) == 4) assert(forward y(who) == 9) walk hero(who, north, 1) wait for hero(who), w assert(hero y(who) == 8) assert(hero direction(who) == north) walk hero(who, east, 1) wait for hero(who), w assert(hero x(who) == 6) assert(hero direction(who) == east) # spin for(d, north, west) do( set hero direction(who, d), w assert(hero direction(who) == d) ) # moonwalk in a circle walk hero(who, south, 1) set hero direction(who, north) wait for hero(who), w assert(hero direction(who) == north) assert(hero x(who) == 6) assert(hero y(who) == 9) walk hero(who, west, 1) set hero direction(who, east) wait for hero(who), w assert(hero direction(who) == east) assert(hero x(who) == 5) assert(hero y(who) == 9) walk hero(who, north, 1) set hero direction(who, south) wait for hero(who), w assert(hero direction(who) == south) assert(hero x(who) == 5) assert(hero y(who) == 8) walk hero(who, east, 1) set hero direction(who, west) wait for hero(who), w assert(hero direction(who) == west) assert(hero x(who) == 6) assert(hero y(who) == 8) # moonwalk in a circle using negative distance walk hero(who, north, -1) wait for hero(who), w assert(hero direction(who) == north) assert(hero x(who) == 6) assert(hero y(who) == 9) walk hero(who, east, -1) wait for hero(who), w assert(hero direction(who) == east) assert(hero x(who) == 5) assert(hero y(who) == 9) walk hero(who, south, -1) wait for hero(who), w assert(hero direction(who) == south) assert(hero x(who) == 5) assert(hero y(who) == 8) walk hero(who, west, -1) wait for hero(who), w assert(hero direction(who) == west) assert(hero x(who) == 6) assert(hero y(who) == 8) # walk into wall walk hero(who, north, 3) # should only make it 2 tiles wait for hero(who) assert(hero Y(who) == 6) # check walls expect hero walls(who, true, false, false, false) walk hero(who, west, 1) wait for hero(who), w walk hero(who, north, 1) wait for hero(who), w expect hero walls(who, false, true, false, false) walk hero(who, north, 2) wait for hero(who), w walk hero(who, east, 1) wait for hero(who), w expect hero walls(who, false, false, true, false) walk hero(who, east, 5) wait for hero(who), w expect hero walls(who, true, true, false, false) # speed test time! set hero speed(who, 2) assert(get hero speed(who) == 2) walk hero(who, south, 1) w(9), assert(hero is walking(who)) w(1), assert(hero is walking(who) == false) set hero speed(who, 4) assert(get hero speed(who) == 4) walk hero(who, south, 1) w(4), assert(hero is walking(who)) w(1), assert(hero is walking(who) == false) set hero speed(who, 5) assert(get hero speed(who) == 5) walk hero(who, south, 1) w(3), assert(hero is walking(who)) w(1), assert(hero is walking(who) == false) set hero speed(who, 10) assert(get hero speed(who) == 10) walk hero(who, south, 1) w(1), assert(hero is walking(who)) w(1), assert(hero is walking(who) == false) set hero speed(who, 20) assert(get hero speed(who) == 20) walk hero(who, south, 1) w(1), assert(hero is walking(who) == false) walk hero(who, west, 1), w walk hero(who, north, 2), w(2) assert(hero x(who) == 10) assert(hero y(who) == 6) set hero speed(who) assert(get hero speed(who) == 4) walk hero(who, west, 3) wait for hero(who) #FIXME: should probably test misaligned walking too # frame variable(fr) for(fr, 1, 8) do( set hero frame(who, (fr ,mod, 2)) assert(hero frame(who) == (fr ,mod, 2)) w(fr) ) #pixel pos variable(px, py) px := hero pixel x(who) assert(px == 140) py := hero pixel y(who) assert(py == 120) variable(ix, iy) for(iy, 0, 5) do( for(ix, 0, 5) do( put hero(who, 105 + ix, 105 + iy) assert(hero pixel x(who) == 105 + ix) assert(hero pixel y(who) == 105 + iy) w ) ) # Test one-wall walls set hero position(who, 5, 9) walk hero(who, south, 1) # normal walk onto a tile with aone-way wall wait for hero(who) assert(hero y(who) == 10) walk hero(who, east, 1) # walk off, where there is no wall wait for hero(who) assert(hero x(who) == 6) walk hero(who, west, 2) # walking between tiles with one-way walls in other directions wait for hero(who) assert(hero x(who) == 4) walk hero(who, west, 1) # attempt to walk through a normal wall while on a oneway wall wait for hero(who) assert(hero x(who) == 4) walk hero(who, north, 1) # walk through one-way wall (allowed) wait for hero(who) assert(hero y(who) == 9) walk hero(who, south, 1) # try to walk disallowed direction wait for hero(who) assert(hero y(who) == 9) walk hero(who, east, 1) wait for hero(who) walk hero(who, south, 2) # walk through one-way wall wait for hero(who) assert(hero y(who) == 11) walk hero(who, north, 1) # try to walk disallowed direction wait for hero(who) assert(hero y(who) == 11) put hero(who, px, py), w end script, suspend walkabouts tests, begin $0="suspend walkabouts tests" # There's an awful lot of things that could be tested, including NPC and hero movement. # Mostly just test heroes. trace(0) trace value (herox, heroy) # Check initial conditions assert(get hero speed(me) == 4) assert(hero is walking(me) == false) variable (initial x) initial x := hero x(me) walk hero(me, left, 1) wait(1) variable (px) px := hero pixel x(me) suspend walkabouts assert(walkabouts are suspended) # Check movement paused wait(2) assert(px == hero pixel x(me)) assert(hero is walking(me)) resume walkabouts assert(walkabouts are suspended == false) # Check moving wait(1) assert(px <> hero pixel x(me)) # Cancel move walk hero(me, left, 0) wait(1) assert(hero is walking(me) == false) suspend walkabouts # Can still move the hero while suspended assert(hero x(me) <> initial x) set hero position(me, initial x, hero y(me)) assert(hero x(me) == initial x) wait(1) # Walk commands don't take effect while suspended assert(hero direction(me) == left) walk hero(me, right, 1) assert(hero direction(me) == right) # walkhero changes the direction directly wait(1) assert(hero pixel x(me) == initial x * 20) # But do once resumed resume walkabouts wait(1) assert(hero pixel x(me) <> initial x * 20) # Cleanup set hero position(me, initial x, hero y(me)) walk hero(me, left, 0) end script, expect hero walls, who, wn, we, ws, ww, begin assert(check hero wall(who, north) == wn) assert(check hero wall(who, east) == we) assert(check hero wall(who, south) == ws) assert(check hero wall(who, west) == ww) end ## caterpillar #suspend hero walls #resume hero walls #set hero frame (who, frame) #set hero speed (who, speed) #set hero z (who, z) script, hero exp and levels tests, begin variable(i) $0="hero exp and levels tests" # check default level curve assert(experience to level(1) == 30) assert(experience to level(2) == 71) assert(experience to level(3) == 125) assert(experience to level(4) == 195) assert(experience to level(5) == 284) assert(experience to level(6) == 396) assert(experience to level(7) == 535) assert(experience to level(8) == 707) assert(experience to level(9) == 918) assert(experience to level(10) == 1176) assert(experience to level(11) == 1491) assert(experience to level(12) == 1874) assert(experience to level(13) == 2339) assert(experience to level(14) == 2902) assert(experience to level(15) == 3583) assert(experience to level(99) == 50183838) # check Freki's level curve (should be same as default) variable(freki slot) freki slot := find hero(hero:freki) assert(freki slot >= 0) assert(experience to level(1, freki slot) == 30) assert(experience to level(2, freki slot) == 71) assert(experience to level(3, freki slot) == 125) assert(experience to level(4, freki slot) == 195) assert(experience to level(5, freki slot) == 284) assert(experience to level(6, freki slot) == 396) assert(experience to level(7, freki slot) == 535) assert(experience to level(8, freki slot) == 707) assert(experience to level(9, freki slot) == 918) assert(experience to level(10, freki slot) == 1176) assert(experience to level(11, freki slot) == 1491) assert(experience to level(12, freki slot) == 1874) assert(experience to level(13, freki slot) == 2339) assert(experience to level(14, freki slot) == 2902) assert(experience to level(15, freki slot) == 3583) assert(experience to level(99, freki slot) == 50183838) # check Bram's level curve (should faster than default) add hero(hero:Bram) variable(bram slot) bram slot := find hero(hero:Bram) assert(bram slot >= 0) assert(experience to level(1, bram slot) == 30) assert(experience to level(2, bram slot) == 70) assert(experience to level(3, bram slot) == 121) assert(experience to level(4, bram slot) == 185) assert(experience to level(5, bram slot) == 264) assert(experience to level(6, bram slot) == 360) assert(experience to level(7, bram slot) == 475) assert(experience to level(8, bram slot) == 612) assert(experience to level(9, bram slot) == 775) assert(experience to level(10, bram slot) == 967) assert(experience to level(11, bram slot) == 1193) assert(experience to level(12, bram slot) == 1458) assert(experience to level(13, bram slot) == 1768) assert(experience to level(14, bram slot) == 2130) assert(experience to level(15, bram slot) == 2551) assert(experience to level(99, bram slot) == 36587434) delete hero(hero:Bram) assert(get hero level(0) == 0) # level setting for(i, 1, 99) do( set hero level(0, i) assert(get hero level(0) == i) assert(total experience(0) == experience to level(i)) ) # level-based spell learning/forgetting set hero level(0, 9) assert(knows spell (0, 2) == false) # Freki should not have L.10 Wolf at level 9 set hero level(0, 10) assert(knows spell (0, 2) == true) # Freki should learn L.10 Wolf at level 10 set hero level(0, 0, false) assert(get hero level(0) == 0) assert(hero levelled(0) == -10) assert(get hero stat(0, 12, 1) == -10) # Undocumented use of stat 12 assert(knows spell (0, 2) == true) # Freki should not forget L.10 Wolf because of how we deleveled update level up learning(0, true) assert(knows spell (0, 2) == false) # Now Freki should forget L.10 Wolf. # experience assert(experience to next level(0) == 30) give experience(0, 5) assert(hero levelled(0) == 0) assert(experience to next level(0) == 25) give experience(0, 25) assert(get hero level(0) == 1) assert(hero levelled(0) == 1) assert(experience to next level(0) == 41) give experience(0, -1) # de-level should not work with this command, but experience will go down assert(get hero level(0) == 1) assert(hero levelled(0) == 0) assert(experience to next level(0) == 42) give experience(0, 42) assert(get hero level(0) == 2) assert(hero levelled(0) == 1) set hero level(0, 9) give experience(0, experience to next level(0)) assert(get hero level(0) == 10) assert(hero levelled(0) == 1) assert(spells learned(0, get count) == 1) assert(spells learned(0, 0) == 2) assert(spells learnt(0, 0) == 1) #deprecated, but why not test it? give experience(0, 1) # should not cause levelling assert(hero levelled == 0) assert(spells learned(0, get count) == 0) # split experience set hero level(0, 0, true) give experience(party, 30) assert(hero levelled(0) == 0) assert(total experience(0) == 8) assert(total experience(1) == 8) assert(total experience(2) == 8) assert(total experience(3) == 8) give experience(party, 88) assert(get hero level(0) == 1) assert(get hero level(1) == 1) assert(get hero level(2) == 1) assert(get hero level(3) == 1) assert(total experience(find hero(hero:Kitt)) == 0) # outside of the party # experience for dead heroes set hero level(0, 0, true) set hero level(1, 0, true) set hero level(2, 0, true) set hero level(3, 0, true) set hero stat(2, 0, 0) # kill Olaf set dead heroes gain experience (false) give experience(party, 120) assert(total experience(0) == 40) assert(total experience(1) == 40) assert(total experience(2) == 0) assert(total experience(3) == 40) set hero level(0, 0, true) set hero level(1, 0, true) set hero level(2, 0, true) set hero level(3, 0, true) set dead heroes gain experience (true) give experience(party, 120) assert(total experience(0) == 30) assert(total experience(1) == 30) assert(total experience(2) == 30) assert(total experience(3) == 30) set hero level(0, 0, true) set hero level(1, 0, true) set hero level(2, 0, true) set hero level(3, 0, true) set hero stat(2, 0, get hero stat(2, 0, maximum stat)) # revive Olaf # level caps set hero level(0, 1) assert(get level cap == 99) set level cap(2) assert(get level cap == 2) give experience(0, 41) assert(get hero level(0) == 2) assert(total experience(0) == 71) assert(experience to next level(0) == 54) # being at the level cap does not alter the exp to next level give experience(0, 10000000) assert(hero levelled(0) == 0) assert(get hero level(0) == 2) set level cap(99) # Hero level range check tags assert(hero by slot(0) == hero:Freki) set hero level(0, 0, true), w assert(check tag(tag: freki level 0) == true) assert(check tag(tag: freki level teens) == false) set hero level(0, 12, true), w assert(check tag(tag: freki level 0) == false) assert(check tag(tag: freki level teens) == false) set hero level(0, 13, true), w assert(check tag(tag: freki level 0) == false) assert(check tag(tag: freki level teens) == true) set hero level(0, 19, true), w assert(check tag(tag: freki level 0) == false) assert(check tag(tag: freki level teens) == true) set hero level(0, 20, true), w assert(check tag(tag: freki level 0) == false) assert(check tag(tag: freki level teens) == false) # Undocumented stat 12 stuff assert(hero levelled(0) == 1) # Precondition assert(get hero level(0) == 20) # Precondition assert(get hero stat(0, 12, 1) == 1) # == hero levelled assert(get hero stat(0, 12, 0) == 20) # == getherolevel variable(temp) temp := get hero stat(0, 0, maximum stat) set hero stat(0, 12, 42, 0) # Set hero level = 42 (but don't change stats) assert(get hero level(0) == 42) assert(get hero stat(0, 12, 0) == 42) assert(get hero stat(0, 0, maximum stat) == temp) # Doesn't change stats assert(hero levelled(0) == 1) # Doesn't change "hero levelled" set hero stat(0, 12, 3, 1) # Set "hero levelled" = 3 assert(hero levelled(0) == 3) set hero stat(0, 12, 20, 0) # Reset # reset set hero level(0, 0, true) set hero level(1, 0, true) set hero level(2, 0, true) set hero level(3, 0, true) end script, hero stat growth test, begin $0="hero stat growth test" show string(0), w # This tests the crappy default stat growth curve, which we will hopefully replace someday set hero level(0 ,0) assert(get hero stat(0, 0, maximum stat) == 10) set hero level(0 ,1) assert(get hero stat(0, 0, maximum stat) == 13) set hero level(0 ,2) assert(get hero stat(0, 0, maximum stat) == 16) # set hero level(0 ,3) # assert(get hero stat(0, 0, maximum stat) == 19) # set hero level(0 ,4) # assert(get hero stat(0, 0, maximum stat) == 23) # set hero level(0 ,5) # assert(get hero stat(0, 0, maximum stat) == 26) # set hero level(0 ,6) # assert(get hero stat(0, 0, maximum stat) == 30) # set hero level(0 ,7) # assert(get hero stat(0, 0, maximum stat) == 34) # set hero level(0 ,8) # assert(get hero stat(0, 0, maximum stat) == 38) # set hero level(0 ,9) # assert(get hero stat(0, 0, maximum stat) == 42) set hero level(0 ,10) assert(get hero stat(0, 0, maximum stat) == 46) assert(get level mp(0, 0) == 5) assert(get level mp(0, 1) == 3) assert(get level mp(0, 2) == 2) assert(get level mp(0, 3) == 1) assert(get level mp(0, 4) == 0) assert(get level mp(0, 5) == 0) assert(get level mp(0, 6) == 0) assert(get level mp(0, 7) == 0) # set hero level(0 ,11) # assert(get hero stat(0, 0, maximum stat) == 50) # set hero level(0 ,12) # assert(get hero stat(0, 0, maximum stat) == 55) # set hero level(0 ,13) # assert(get hero stat(0, 0, maximum stat) == 60) # set hero level(0 ,14) # assert(get hero stat(0, 0, maximum stat) == 64) # set hero level(0 ,15) # assert(get hero stat(0, 0, maximum stat) == 69) # set hero level(0 ,16) # assert(get hero stat(0, 0, maximum stat) == 75) # set hero level(0 ,17) # assert(get hero stat(0, 0, maximum stat) == 80) # set hero level(0 ,18) # assert(get hero stat(0, 0, maximum stat) == 85) # set hero level(0 ,19) # assert(get hero stat(0, 0, maximum stat) == 91) set hero level(0 ,20) assert(get hero stat(0, 0, maximum stat) == 96) # set hero level(0 ,21) # assert(get hero stat(0, 0, maximum stat) == 102) # set hero level(0 ,22) # assert(get hero stat(0, 0, maximum stat) == 108) # set hero level(0 ,23) # assert(get hero stat(0, 0, maximum stat) == 114) # set hero level(0 ,24) # assert(get hero stat(0, 0, maximum stat) == 121) # set hero level(0 ,25) # assert(get hero stat(0, 0, maximum stat) == 127) # set hero level(0 ,26) # assert(get hero stat(0, 0, maximum stat) == 134) # set hero level(0 ,27) # assert(get hero stat(0, 0, maximum stat) == 140) # set hero level(0 ,28) # assert(get hero stat(0, 0, maximum stat) == 147) # set hero level(0 ,29) # assert(get hero stat(0, 0, maximum stat) == 154) set hero level(0 ,30) assert(get hero stat(0, 0, maximum stat) == 161) # set hero level(0 ,31) # assert(get hero stat(0, 0, maximum stat) == 168) # set hero level(0 ,32) # assert(get hero stat(0, 0, maximum stat) == 176) # set hero level(0 ,33) # assert(get hero stat(0, 0, maximum stat) == 183) # set hero level(0 ,34) # assert(get hero stat(0, 0, maximum stat) == 191) # set hero level(0 ,35) # assert(get hero stat(0, 0, maximum stat) == 199) # set hero level(0 ,36) # assert(get hero stat(0, 0, maximum stat) == 207) # set hero level(0 ,37) # assert(get hero stat(0, 0, maximum stat) == 215) # set hero level(0 ,38) # assert(get hero stat(0, 0, maximum stat) == 223) # set hero level(0 ,39) # assert(get hero stat(0, 0, maximum stat) == 232) set hero level(0 ,40) assert(get hero stat(0, 0, maximum stat) == 240) # set hero level(0 ,41) # assert(get hero stat(0, 0, maximum stat) == 249) # set hero level(0 ,42) # assert(get hero stat(0, 0, maximum stat) == 258) # set hero level(0 ,43) # assert(get hero stat(0, 0, maximum stat) == 267) # set hero level(0 ,44) # assert(get hero stat(0, 0, maximum stat) == 276) # set hero level(0 ,45) # assert(get hero stat(0, 0, maximum stat) == 285) # set hero level(0 ,46) # assert(get hero stat(0, 0, maximum stat) == 295) # set hero level(0 ,47) # assert(get hero stat(0, 0, maximum stat) == 304) # set hero level(0 ,48) # assert(get hero stat(0, 0, maximum stat) == 314) # set hero level(0 ,49) # assert(get hero stat(0, 0, maximum stat) == 324) set hero level(0 ,50) assert(get hero stat(0, 0, maximum stat) == 334) # set hero level(0 ,51) # assert(get hero stat(0, 0, maximum stat) == 344) # set hero level(0 ,52) # assert(get hero stat(0, 0, maximum stat) == 354) # set hero level(0 ,53) # assert(get hero stat(0, 0, maximum stat) == 365) # set hero level(0 ,54) # assert(get hero stat(0, 0, maximum stat) == 375) # set hero level(0 ,55) # assert(get hero stat(0, 0, maximum stat) == 386) # set hero level(0 ,56) # assert(get hero stat(0, 0, maximum stat) == 397) # set hero level(0 ,57) # assert(get hero stat(0, 0, maximum stat) == 408) # set hero level(0 ,58) # assert(get hero stat(0, 0, maximum stat) == 419) # set hero level(0 ,59) # assert(get hero stat(0, 0, maximum stat) == 430) set hero level(0 ,60) assert(get hero stat(0, 0, maximum stat) == 442) # set hero level(0 ,61) # assert(get hero stat(0, 0, maximum stat) == 453) # set hero level(0 ,62) # assert(get hero stat(0, 0, maximum stat) == 465) # set hero level(0 ,63) # assert(get hero stat(0, 0, maximum stat) == 477) # set hero level(0 ,64) # assert(get hero stat(0, 0, maximum stat) == 489) # set hero level(0 ,65) # assert(get hero stat(0, 0, maximum stat) == 501) # set hero level(0 ,66) # assert(get hero stat(0, 0, maximum stat) == 513) # set hero level(0 ,67) # assert(get hero stat(0, 0, maximum stat) == 526) # set hero level(0 ,68) # assert(get hero stat(0, 0, maximum stat) == 538) # set hero level(0 ,69) # assert(get hero stat(0, 0, maximum stat) == 551) set hero level(0 ,70) assert(get hero stat(0, 0, maximum stat) == 564) # set hero level(0 ,71) # assert(get hero stat(0, 0, maximum stat) == 577) # set hero level(0 ,72) # assert(get hero stat(0, 0, maximum stat) == 590) # set hero level(0 ,73) # assert(get hero stat(0, 0, maximum stat) == 604) # set hero level(0 ,74) # assert(get hero stat(0, 0, maximum stat) == 617) # set hero level(0 ,75) # assert(get hero stat(0, 0, maximum stat) == 631) # set hero level(0 ,76) # assert(get hero stat(0, 0, maximum stat) == 644) # set hero level(0 ,77) # assert(get hero stat(0, 0, maximum stat) == 658) # set hero level(0 ,78) # assert(get hero stat(0, 0, maximum stat) == 672) # set hero level(0 ,79) # assert(get hero stat(0, 0, maximum stat) == 686) set hero level(0 ,80) assert(get hero stat(0, 0, maximum stat) == 701) # set hero level(0 ,81) # assert(get hero stat(0, 0, maximum stat) == 715) # set hero level(0 ,82) # assert(get hero stat(0, 0, maximum stat) == 730) # set hero level(0 ,83) # assert(get hero stat(0, 0, maximum stat) == 745) # set hero level(0 ,84) # assert(get hero stat(0, 0, maximum stat) == 759) # set hero level(0 ,85) # assert(get hero stat(0, 0, maximum stat) == 774) # set hero level(0 ,86) # assert(get hero stat(0, 0, maximum stat) == 790) # set hero level(0 ,87) # assert(get hero stat(0, 0, maximum stat) == 805) # set hero level(0 ,88) # assert(get hero stat(0, 0, maximum stat) == 820) # set hero level(0 ,89) # assert(get hero stat(0, 0, maximum stat) == 836) set hero level(0 ,90) assert(get hero stat(0, 0, maximum stat) == 852) # set hero level(0 ,91) # assert(get hero stat(0, 0, maximum stat) == 868) # set hero level(0 ,92) # assert(get hero stat(0, 0, maximum stat) == 884) # set hero level(0 ,93) # assert(get hero stat(0, 0, maximum stat) == 900) # set hero level(0 ,94) # assert(get hero stat(0, 0, maximum stat) == 916) # set hero level(0 ,95) # assert(get hero stat(0, 0, maximum stat) == 933) # set hero level(0 ,96) # assert(get hero stat(0, 0, maximum stat) == 949) # set hero level(0 ,97) # assert(get hero stat(0, 0, maximum stat) == 966) set hero level(0 ,98) assert(get hero stat(0, 0, maximum stat) == 983) set hero level(0 ,99) assert(get hero stat(0, 0, maximum stat) == 1000) set hero level(0, 0, true) show no value, w end script, restore hp and mp, begin variable(i) for (i, 0, 40) do ( if (hero by slot(i) <> -1) then ( set hero stat(i, stat:hp, get hero stat(i, stat:hp, maximum stat), current stat) set hero stat(i, stat:mp, get hero stat(i, stat:mp, maximum stat), current stat) ) ) end script, hero stat tests, begin $0="hero stat tests" # test hero stat commands, stat capping, equip stat bonuses, current vs. max quirks, negative stats, level mp # check initial condition assert(hero by slot(1) == hero:Helga) assert(get hero level(1) == 0) assert(get hero stat(1, stat:hp, current stat) == 10) assert(get hero stat(1, stat:mp, current stat) == 20) assert(get hero stat(1, stat:atk, current stat) == 10) assert(get hero stat(1, stat:spd, current stat) == 30) assert(get hero stat(1, stat:hp, base stat) == 10) assert(get hero stat(1, stat:mp, base stat) == 20) assert(get hero stat(1, stat:atk, base stat) == 9) # default weapon has +1 atk assert(get hero stat(1, stat:spd, base stat) == 30) # test stat caps assert(get hero stat cap(stat:hp) == 0) assert(get hero stat cap(stat:mp) == 0) assert(get hero stat cap(stat:atk) == 100) #in-editor assert(get hero stat cap(stat:spd) == 0) # set capped hero stat set capped hero stat(1, stat:atk, 110, current stat) assert(get hero stat(1, stat:atk, current stat) == 100) assert(get hero stat(1, stat:atk, base stat) == 9) set capped hero stat(1, stat:atk, 120, maximum stat) assert(get hero stat(1, stat:atk, maximum stat) == 100) assert(get hero stat(1, stat:atk, base stat) == 99) set capped hero stat(1, stat:hp, 999999, current stat) assert(get hero stat(1, stat:hp, current stat) == 999999) set hero stat(1, stat:hp, 10, current stat) #back to original # set hero stat should not be affected by caps set hero stat(1, stat:atk, 110, current stat) assert(get hero stat(1, stat:atk, current stat) == 110) # modify caps set hero stat cap(stat:hp, 5) set hero stat cap(stat:mp, 12) assert(get hero stat cap(stat:hp) == 5) assert(get hero stat(1, stat:hp, current stat) == 5) assert(get hero stat(1, stat:hp, maximum stat) == 5) assert(get hero stat(1, stat:hp, base stat) == 10) assert(get hero stat(1, stat:mp, current stat) == 12) assert(get hero stat(1, stat:mp, maximum stat) == 12) assert(get hero stat(1, stat:mp, base stat) == 20) # set base stat set hero stat(1, stat:atk, 20, base stat) assert(get hero stat(1, stat:atk, maximum stat) == 21) #+1 from weapon set hero stat(1, stat:atk, 200, base stat) assert(get hero stat(1, stat:atk, base stat) == 200) assert(get hero stat(1, stat:atk, maximum stat) == 100) #capped set hero stat(1, stat:atk, 10, current stat) #back to original set hero stat(1, stat:atk, 10, maximum stat) #original max stat but not base stat! assert(get hero stat(1, stat:atk, base stat) == 110) set hero stat(1, stat:atk, 9, base stat) #back to original max and base assert(get hero stat(1, stat:atk, maximum stat) == 10) set hero stat cap(stat:atk, 0) set hero stat cap(stat:spd, 10) assert(get hero stat cap(stat:atk) == 0) assert(get hero stat cap(stat:spd) == 10) assert(get hero stat(1, stat:atk, current stat) == 10) assert(get hero stat(1, stat:atk, maximum stat) == 10) assert(get hero stat(1, stat:spd, current stat) == 10) assert(get hero stat(1, stat:spd, maximum stat) == 10) set hero stat cap(stat:hp, 0) set hero stat cap(stat:mp, 0) set hero stat cap(stat:spd, 0) # gain hero stat assert(gain hero stat(1, stat:atk, 3) == 13) #return new maximum assert(get hero stat(1, stat:atk, base stat) == 12) assert(get hero stat(1, stat:atk, maximum stat) == 13) assert(get hero stat(1, stat:atk, current stat) == 13) # Only max and current should respect caps set hero stat cap(stat:atk, 11) assert(gain hero stat(1, stat:atk, 4) == 11) #return new maximum assert(get hero stat(1, stat:atk, base stat) == 16) assert(get hero stat(1, stat:atk, maximum stat) == 11) assert(get hero stat(1, stat:atk, current stat) == 11) gain hero stat(1, stat:atk, -7) #back to original set hero stat cap(stat:atk, 0) # Test proportional increase assert(get hero stat(1, stat:hp, current stat) == 5) # HP: 5/10 assert(gain hero stat(1, stat:hp, 6) == 16) assert(get hero stat(1, stat:hp, current stat) == 8) # HP: 8/16 assert(get hero stat(1, stat:hp, maximum stat) == 16) assert(gain hero stat(1, stat:hp, -7) == 9) # Test reset-to-max assert(gain hero stat(1, stat:hp, 1, true) == 10) assert(get hero stat(1, stat:hp, current stat) == 10) # HP: 10/10 assert(get hero stat(1, stat:hp, maximum stat) == 10) # test original stats restored assert(get hero stat(1, stat:hp, maximum stat) == 10) assert(get hero stat(1, stat:hp, base stat) == 10) assert(get hero stat(1, stat:mp, maximum stat) == 20) assert(get hero stat(1, stat:mp, base stat) == 20) assert(get hero stat(1, stat:atk, current stat) == 10) assert(get hero stat(1, stat:atk, maximum stat) == 10) assert(get hero stat(1, stat:atk, base stat) == 9) assert(get hero stat(1, stat:spd, current stat) == 30) assert(get hero stat(1, stat:spd, maximum stat) == 30) assert(get hero stat(1, stat:spd, base stat) == 30) # but not current values for hp, mp assert(get hero stat(1, stat:hp, current stat) == 10) assert(get hero stat(1, stat:mp, current stat) == 12) # Simple test of hero level mp # The hero is level 0. Other levels are tested in "hero stat growth test" assert(get level mp(1, 0, current stat) == 1) assert(get level mp(1, 0, maximum stat) == 1) variable(level) for(level, 1, 7) do ( assert(get level mp(1, level, current stat) == 0) assert(get level mp(1, level, maximum stat) == 0) ) set level mp(1, 7, 99) assert(get level mp(1, 7) == 99) set level mp(1, 7, 0) # Check for valid level MP spell lists assert(hero uses level mp(find hero(hero:Frumpy)) == true) # has valid Level MP spell list assert(hero uses level mp(find hero(hero:Olaf)) == false) # has Level MP spell list, but it is not in the battle menu assert(hero uses level mp(find hero(hero:Freki)) == false) # has other spell list, but not level mp based assert(hero uses level mp(find hero(hero:Helga)) == false) # Has no spell lists # test heal party assert(get hero stat(1, stat:hp, current stat) == 10) # Precondition assert(read preference bit(4) == false) # Precondition: "Inns don't revive dead heroes" off set level mp(1, 0, 0) heal party assert(get hero stat(1, stat:mp, current stat) == 20) assert(get level mp(1, 0, current stat) == 1) set hero stat(1, stat:hp, 0, current stat) heal party(false) # Don't revive heroes assert(get hero stat(1, stat:hp, current stat) == 0) heal party(true) # Revive heroes assert(get hero stat(1, stat:hp, current stat) == 10) set hero stat(1, stat:hp, 0, current stat) heal party # Default: Revive heroes, because of the above pref bit assert(get hero stat(1, stat:hp, current stat) == 10) end script, equipment tests, begin $0="equipment tests" # This is a continuation of 'hero stat tests', see that for preconditions # additional precondition: empty inventory, as well as: assert(find hero(hero:Freki) == 0) assert(find hero(hero:Helga) == 1) # test equipment commands get item(item:Boots) assert(inventory(item:Boots) == 1) assert(equip where(1, item:Boots) == slot:legs) # equippable by all heroes assert(check equipment(1, slot:legs) == -1) assert(check equipment(1, slot:weapon) == item:DefltWep) # default weapon (not properly tested) assert(get default weapon(1) == item:DefltWep) force equip(1, slot:legs, item:Boots) # stat bonuses: spd+2 hp+3 assert(check equipment(1, slot:legs) == item:Boots) assert(inventory(item:Boots) == 0) # test stat bonuses assert(get hero stat(1, stat:hp, current stat) == 10) assert(get hero stat(1, stat:hp, maximum stat) == 13) # only affects max values of hp, mp assert(get hero stat(1, stat:hp, base stat) == 10) assert(get hero stat(1, stat:spd, current stat) == 32) assert(get hero stat(1, stat:spd, maximum stat) == 32) # resets current value to max for other stats assert(get hero stat(1, stat:spd, base stat) == 30) assert(inventory(item:SteelSho) == 0) assert(equip where(0, item:SteelSho) == slot:legs) #equippable only by Freki assert(equip where(1, item:SteelSho) == false) # Note that force equip can be used to equip an item not in the inventory, # in that case you get a free copy. # Force equip also allows equipping in wrong slot force equip(1, slot:weapon, item:SteelSho) # stat bonuses: atk+4 (default weapon has atk+1) assert(check equipment(1, slot:weapon) == item:SteelSho) assert(check equipment(1, slot:legs) == item:Boots) assert(inventory(item:DefltWep) == 0) # shouldn't gain a default weapon assert(get hero stat(1, stat:atk, current stat) == 13) assert(get hero stat(1, stat:atk, maximum stat) == 13) # test unequipping unequip(1, slot:legs) assert(inventory(item:Boots) == 1) assert(check equipment(1, slot:legs) == -1) set hero stat(1, stat:hp, 29, current stat) # check effects of unequipping... even on stats set hero stat(1, stat:spd, 29, current stat) # which the equipment doesn't have bonuses on unequip(1, slot:weapon) assert(inventory(item:SteelSho) == 1) assert(check equipment(1, slot:weapon) == item:DefltWep) assert(get hero stat(1, stat:hp, current stat) == 10) # current values of hp,mp should be capped to max assert(get hero stat(1, stat:hp, maximum stat) == 10) assert(get hero stat(1, stat:spd, current stat) == 30) assert(get hero stat(1, stat:spd, maximum stat) == 30) # resets current value to max for other stats # TODO: test equip elemental resists # test for Bug 743 - Unequipping items with stat bonuses that exceed stat limits is broken assert(get hero stat(1, stat:hp, current stat) == 10) # preconditions (max same) assert(get hero stat(1, stat:spd, current stat) == 30) set hero stat cap(stat:hp, 5) set hero stat cap(stat:spd, 5) force equip(1, slot:legs, item:Boots) assert(get hero stat(1, stat:hp, current stat) == 5) # cap hp, mp to the cap assert(get hero stat(1, stat:hp, maximum stat) == 5) assert(get hero stat(1, stat:spd, current stat) == 5) # resets current value to max for other stats assert(get hero stat(1, stat:spd, maximum stat) == 5) unequip(1, slot:legs) assert(get hero stat(1, stat:hp, current stat) == 5) assert(get hero stat(1, stat:hp, maximum stat) == 5) assert(get hero stat(1, stat:spd, current stat) == 5) assert(get hero stat(1, stat:spd, maximum stat) == 5) set hero stat cap(stat:hp, 0) set hero stat cap(stat:spd, 0) assert(get hero stat(1, stat:hp, current stat) == 5) # only affects max values of hp, mp assert(get hero stat(1, stat:hp, maximum stat) == 10) assert(get hero stat(1, stat:spd, current stat) == 30) # resets current value to max for other stats assert(get hero stat(1, stat:spd, maximum stat) == 30) # ...fix that up restore hp and mp assert(get hero stat(1, stat:hp, current stat) == 10) # cleanup delete item(item:SteelSho) delete item(item:Boots) end script, hero misc tests, begin $0="hero misc tests" show string(0), w variable(n) assert(hero by slot(0) == hero:Freki) assert(get hero hand x(0, hand:attack A) == 22) assert(get hero hand y(0, hand:attack A) == 11) assert(get hero hand x(0, hand:attack B) == 2) assert(get hero hand y(0, hand:attack B) == 17) set hero hand x(0, hand:attack A, 50) set hero hand y(0, hand:attack A, -25) set hero hand x(0, hand:attack B, -10) set hero hand y(0, hand:attack B, 1) assert(get hero hand x(0, hand:attack A) == 50) assert(get hero hand y(0, hand:attack A) == -25) assert(get hero hand x(0, hand:attack B) == -10) assert(get hero hand y(0, hand:attack B) == 1) set hero hand x(0, hand:attack A, get default hero hand x(0, hand:attack A)) set hero hand y(0, hand:attack A, get default hero hand y(0, hand:attack A)) set hero hand x(0, hand:attack B, get default hero hand x(0, hand:attack B)) set hero hand y(0, hand:attack B, get default hero hand y(0, hand:attack B)) assert(get hero hand x(0, hand:attack A) == 22) assert(get hero hand y(0, hand:attack A) == 11) assert(get hero hand x(0, hand:attack B) == 2) assert(get hero hand y(0, hand:attack B) == 17) end ######################################################################## script, npc tests, begin npc reference tests npc movement tests(1) npc movement tests(NPC reference(1)) npc id tests npc extra tests npc tag and onetime tests use npc script tests npc slice tests alter npc tests end script, npc movement tests, n, begin $0="npc movement tests" $err arg string="n=", append number(err arg string, n) show string(0) # assert starting pos assert(npc X(n) == 3) assert(npc Y(n) == 5) # walk in a circle, testing speed set npc speed(n, 4) walk npc(n, south, 1), w wait for npc(n),w assert(npc Y(n) == 6) set NPC speed(n, 5) walk npc(n, east, 1), w wait for npc(n),w assert(npc X(n) == 4) set NPC speed(n, 10) walk npc(n, north, 1), w wait for npc(n),w assert(npc Y(n) == 5) set NPC speed(n, 2) walk npc(n, west, 1), w wait for npc(n),w assert(npc X(n) == 3) set npc speed(n, 4) # Walk backwards walk npc(n, east, -1) wait for npc(n), w assert(npc X(n) == 2) assert(npc direction(n) == east) walk npc(n, south, -1) wait for npc(n), w assert(npc Y(n) == 4) assert(npc direction(n) == south) set npc position(n, 3, 5) show no value $err arg string="" end script, npc reference tests, begin variable(n) assert(npc copy count(1) == 1) assert(npc copy count(2) == 0) assert(npc copy count(4) == 3) n := npc reference(1) assert(n < 0) assert(get npc id(n) == 1) assert(npc copy number(n) == 0) n := NPC reference(4, 1) assert(get npc id(n) == 4) assert(npc copy number(n) == 1) assert(npc x(n) == 2) assert(npc y(n) == 13) n := NPC reference(4, 3) assert(n == 0) # Doesn't exist # Passing an ID should return the ID, copy 0 assert(get npc id(1) == 1) assert(npc copy number(1) == 0) assert(get npc id(2) == -1) # Doesn't exist assert(npc copy number(2) == -1) # Doesn't exist assert(get npc id(3) == 3) assert(npc copy number(3) == 0) # Invalid IDs or references assert(get npc id(9000) == -1) assert(get npc id(-9000) == -1) assert(npc copy number(9000) == -1) assert(npc copy number(-9000) == -1) assert(npc copy count(9000) == 0) assert(npc copy count(-9000) == 0) end script, npc id tests, begin variable(n) n := NPC reference(1) set npc direction(n, south), w assert(get npc id(n) == 1) change npc id(n, 2), w(4) assert(get npc id(n) == 2) change npc id(n, 1), w end script, npc extra tests, begin $0="npc extra tests" variable(npc) npc := npc reference(4) assert(extra length(npc) == 3) assert(npc extra(npc, 0) == 0) assert(npc extra(npc, 2) == 0) set npc extra(npc, -1, 999) assert(npc extra(npc, 2) == 999) assert(get extra(npc, 2) == 999) append extra(npc, 1000) assert(get extra(npc, 3) == 1000) end script, npc tag and onetime tests, begin $0="npc onetime tests" variable(hx, hy, n) # Moving the hero in this script is a little silly, # but ensures that the onetime NPCs are on-screen # ...which they probably would have been anyway hx := hero x(me) hy := hero y(me) walk hero to y(me, 12) wait for hero(me), w walk hero to x(me, 4) wait for hero(me), w assert(check onetime(3) == false) assert(check onetime(4) == false) use npc(3), w assert(check onetime(3) == true) assert(check onetime(4) == false) use npc(4), w assert(check onetime(3) == true) assert(check onetime(4) == true) w n := npc reference(4) assert(n == false) set onetime(4, false), w n := npc reference(4) assert(n <> false) assert(check tag(10000) == false) w n := npc reference(5) assert(n <> false) set tag(10000, true) w n := npc reference(5) assert(n == false) assert(check tag(10000) == true) walk hero to x(me, hx) wait for hero(me), w walk hero to y(me, hy) wait for hero(me), w end plotscript, another npc script, begin assert(ticknumber == 2) npc script triggered := 1 end script, use npc script tests, begin $0="use npc script tests" # Tests the implicit wait behaviour of usenpc tick counter slice := create container set slice velocity x(tick counter slice, 1, 1000) assert(ticknumber == 0) create npc(8) # Does nothing use npc(8) assert(ticknumber == 1) delete npc(8) # Both this script and anothernpcscript should wait create npc(7) # Calls anothernpcscript npc script triggered := 0 use npc(7) assert(npc script triggered == 1) assert(ticknumber == 2) delete npc(7) free slice(tick counter slice) end script, npc slice tests, begin $0="npc slice tests" # Test positioning of NPC slices. # Note that there's lots of tests for npc slices elsewhere too, # in particular sprite slice spritesets are tested in "global npc tests". variable(n1, sl1) n1 := create npc(9, 10, 8, west) # Check that walkabout slice positions are set immediately sl1 := get npc slice(n1) assert(slice x(sl1) == 10 * 20 && slice y(sl1) == 8 * 20) set NPC position(n1, 3, 4) assert(slice x(sl1) == 3 * 20 && slice y(sl1) == 4 * 20) put NPC(n1, 31, 41) assert(slice x(sl1) == 31 && slice y(sl1) == 41) delete npc(n1) end script, alter npc tests, begin $0="alter npc tests" assert(current map == 0) w # make a pair of NPCs to use in these tests variable(n1, n2) n1 := create npc(9, 5, 5, west),w n2 := create npc(9, 11, 5, east),w # First do picture and palette changes on the first instance # and verify the changes on the second instance assert(get npc sprite id(n2) == 13) assert(get npc sprite palette(n2) == -1) alter npc(n1, NPCStat:picture, 12),w assert(get npc sprite id(n2) == 12) alter npc(n1, NPCStat:palette, 5),w assert(get npc sprite palette(n2) == 5) alter npc(n1, NPCStat:picture, 13),w alter npc(n1, NPCStat:palette, -1),w # Test move speed variable(ox, oy) assert(read npc(n1, NPCStat:move speed) == 0) alter npc(n1, NPCStat:move speed, 1), w ox := npc pixel x(n2) oy := npc pixel y(n2) walk npc(n2, east, 1) w(10) assert(npc pixel x(n2) == ox + 10) w(10) assert(npc pixel x(n2) == ox + 20) alter npc(n1, NPCStat:move speed, 2),w walk npc(n2, south, 1) w(1) assert(npc pixel y(n2) == oy + 2) wait for npc(n2) assert(npc pixel y(n2) == oy + 20) alter npc(n1, NPCStat:move speed, 4),w walk npc(n2, west, 1) w(1) assert(npc pixel x(n2) == ox + 16) wait for npc(n2) alter npc(n1, NPCStat:move speed, 5),w walk npc(n2, north, 1) w(1) assert(npc pixel y(n2) == oy + 15) wait for npc(n2) # Has gone in a complete circle assert(npc pixel x(n2) == ox) assert(npc pixel y(n2) == oy) # How about diagonal next? alter npc(n1, NPCStat:move speed, 10),w walk npc(n2, right, 1) walk npc(n2, down, 1) w(1) assert(npc pixel x(n2) == ox + 10) assert(npc pixel y(n2) == oy + 10) w(1) assert(npc pixel x(n2) == ox + 20) assert(npc pixel y(n2) == oy + 20) alter npc(n1, NPCStat:move speed, 20) walk npc(n2, left, 1) walk npc(n2, up, 1) w(1) assert(npc pixel x(n2) == ox + 0) assert(npc pixel y(n2) == oy + 0) alter npc(n1, NPCStat:move speed, 0) # Now test textbox display assert(read npc(n1, NPCStat:display text) == 0) alter npc(n1, NPCStat:display text, 18),w assert(current text box == -1) use npc(n2), w assert(current text box == 18) advance text box, w assert(current text box == -1) # Test touch and step-on activation # We will use a text box for this test alter npc(n1, NPCStat:display text, 18),w assert(current text box == -1) assert(read npc(n1, NPCstat:activation) == NPCactivation:use) # Move the hero adjacent to the npc walk hero to x(me, npc x(n2)),w wait for hero(me),w assert(hero x(me) == npc x(n2)) assert(hero y(me) == npc y(n2) + 1) assert(current text box == -1) # Try to walking into the npc, nothing should happen walk hero(me, north, 1), w wait for hero(me),w assert(hero y(me) == npc y(n2) + 1) assert(current text box == -1) # Make the NPC touch-activated alter npc(n1, NPCstat:activation, NPCactivation:touch), w # Nothing should happen yet assert(current text box == -1) # Walk the hero into the NPC walk hero(me, north, 1), w wait for hero(me), w assert(current text box == 18) advance text box assert(current text box == -1) # Walk the hero next to the NPC (does nothing because the NPC isn't moving) walk hero(me, right, 1), w wait for hero(me), w assert(current text box == -1) walk hero(me, left, 1), w wait for hero(me), w assert(current text box == -1) # Now move the npc. Even when moving away into a tile one space diagonal, activation should happen alter npc(n1, NPCStat:move speed, 5) walk npc(n2, right, 1), w wait for npc(n2), w assert(current text box == 18) advance text box assert(current text box == -1) # Move one tile further. This should not activate walk npc(n2, right, 1), w wait for npc(n2), w assert(current text box == -1) # Walk back one tile, into the diagonal space. Will activate walk npc(n2, left, 1), w wait for npc(n2), w assert(current text box == 18) advance text box, w assert(current text box == -1) # Walk back one more space, into the adjacent tile, will activate again walk npc(n2, left, 1), w wait for npc(n2), w assert(current text box == 18) advance text box, w assert(current text box == -1) # Now try step-on activation alter npc(n1, NPCstat:activation, NPCactivation:stepon),w walk hero(me, north, 1), w assert(current text box == -1) wait for hero(me), w assert(current text box == 18) advance text box, w assert(current text box == -1) # Move the heroes around walk hero(me, north, 1), w wait for hero(me),w assert(current text box == -1) walk hero(me, right, 1), w wait for hero(me),w walk hero(me, down, 2), w wait for hero(me),w walk hero(me, left, 1), w wait for hero(me),w assert(hero x(me) == npc x(n2)) assert(hero y(me) == npc y(n2) + 1) # Move the step-on npc under the hero (for better or for worse, this does not activate) assert(current text box == -1) walk npc(n2, down, 1), w assert(current text box == -1) wait for npc(n2), w assert(current text box == -1) # Walk the npc back and reset its activation walk npc(n2, up, 1), w wait for npc(n2), w assert(current text box == -1) alter npc(n1, NPCstat:activation, NPCactivation:use), w assert(read npc(n1, NPCstat:activation) == NPCactivation:use) alter npc(n1, NPCstat:display text, 0) #_cancel runfast #wait for key #_runfast # Test getting items assert(read npc(n1, NPCstat:give item) == 0) alter npc(n1, NPCstat:give item, item:Whatsit + 1), w assert(read npc(n1, NPCstat:give item) == item:Whatsit + 1) # There should not be any Whatsits in your inventory yet assert(inventory(item:Whatsit) == 0) # Now get one use npc(n2),w assert(inventory(item:Whatsit) == 1) # Get a few more use npc(n2),w use npc(n2),w use npc(n2),w assert(inventory(item:Whatsit) == 4) # Now clear the get item alter npc(n1, NPCstat:give item, 0), w # This should not give the item use npc(n2),w assert(inventory(item:Whatsit) == 4) ### still needs tests: # NPCstat:move type # NPCstat:when activated # NPCstat:pushability # NPCstat:script # NPCstat:script argument # NPCstat:vehicle # NPCstat:default movement zone # NPCstat:default avoidance zone # NPCstat:ignore passmap while (npc copy count(9) > 0) do( delete npc(9),w ) end script, get npc sprite id, ref, begin variable(sl, spr) sl := get npc slice(ref) spr := lookup slice(sl:walkabout sprite component, sl) assert(get sprite type(spr) == spritetype:walkabout) exit returning(get sprite set number(spr)) end script, get npc sprite palette, ref, begin variable(sl, spr) sl := get npc slice(ref) spr := lookup slice(sl:walkabout sprite component, sl) assert(get sprite type(spr) == spritetype:walkabout) exit returning(get sprite palette(spr)) end ######################################################################## script, death tests, begin $0="death tests" # See also "test harm tile death" below assert (get death script == 0) set death script(@on death script) assert (get death script == @on death script) assert(died == 0) expecting death := false # Setting hero stats to 0 should NOT trigger death variable (i) for (i, 0, 3) do ( set hero stat(i, stat:hp, 0) ) wait assert(died == 0) # Death by oob attack set hero stat(0, stat:hp, 10) # Only hero 0 alive expecting death := true map cure(atk:suicide, 0) assert(get hero stat(0, stat:hp) == 0) # Death script should not be triggered until next tick assert(died == 0) wait(1) assert(died == 1) # Re-trigger even if already dead map cure(atk:suicide, 0) assert(died == 1) wait(1) assert(died == 2) # Death shouldn't be triggered if one hero has 0 max hp variable(old maxhp) oldmaxhp := get hero stat(1, stat:hp, maximum stat) set hero stat(1, stat:hp, 0, maximum stat) map cure(atk:suicide, 0) wait(1) assert(died == 2) set hero stat(1, stat:hp, old maxhp, maximum stat) # Death shouldn't be triggered if heroes are healed before the next tick map cure(atk:suicide, 0) set hero stat(0, stat:hp, 1) wait(1) assert(died == 2) #FIXME: test death by battle expecting death := false died := 0 restore hp and mp end script, on death script, begin $9="on death script" trace(9) if (expecting death) then ( died += 1 exit script ) crash end script, test harm tile death, start hp, begin # Called from "harm tile tests" expecting death := true set harm tile damage(100) walk hero(me, right, 1) wait for hero(me) # The step finishes, causing death, then it will be processed the same tick, spawning the script assert(died == 1) assert hurt pattern(7, start hp, start hp, start hp, start hp, start hp) expecting death := false died := 0 end script, global npc tests, begin $0="global npc tests" variable(oldmap, oldx, oldy) oldmap := current map oldx := hero x(me) oldy := hero y(me) teleport to map(8, 3, 3) w(1) variable(n1, n2) n2 := create npc(0, 10, 8, south, pool:global) n1 := create npc(0, 9, 8, south, pool:local) w(1) # Global NPC 0 and local NPC 0 will have different sprites assert(get sprite set number(get npc sprite(n1)) == 11) # sheep assert(get sprite set number(get npc sprite(n2)) == 15) # chewie # By ref works on both set npc direction(n1, left) set npc direction(n2, left) w(1) assert(npc direction(n1) == left) assert(npc direction(n2) == left) # by ID does the local ID set npc direction(0, right) w(1) assert(npc direction(n1) == right) assert(npc direction(n2) == left) teleport to map(9, 3, 2) w(1) n1 := npc reference(2) assert(n1 <> 0) assert(npc x(n1) == 4) assert(npc y(n1) == 3) assert(get sprite set number(get npc sprite(n1)) == 13) w(1) n1 := global npc reference(2) assert(n1 == global npc(2)) # test alias assert(n1 <> 0) assert(npc x(n1) == 11) assert(npc y(n1) == 3) assert(get sprite set number(get npc sprite(n1)) == 17) w(1) n1 := global npc reference(2, 2) assert(n1 <> 0) assert(npc x(n1) == 11) assert(npc y(n1) == 5) assert(get sprite set number(get npc sprite(n1)) == 17) w(1) n2 := create npc(2, 11, 6, left, pool:global) assert(npc copy count(2) == 3) assert(npc copy count(2, pool:global) == 4) w(1) # Change NPC ID and pool change npc id(n2, 1) # should still be global assert(get sprite set number(get npc sprite(n2)) == 16) # triangle w(1) # become local change npc id(n2, 0, pool:local) assert(get sprite set number(get npc sprite(n2)) == 13) # pup w(1) # back to a global again change npc id(n2, 0, pool:global) assert(get sprite set number(get npc sprite(n2)) == 15) # chewie w(1) # Test change npc id with ID numbers # Change the first local 2 into 3 change npc id(2, 3) assert(npc y(2) == 4) w(1) # change the first global 2 into 1 change npc id(2, 1, pool:global) assert(get sprite set number(get npc sprite(global npc reference(1))) == 16) # chewie w(1) # many NPC commands will only refer to local NPCs by ID, you need a refernce to worj with globals # No NPC should move, because local ID 1 does not exist, even though global ID 1 exists walk npc(1, left, 1) wait for npc(1) # Global NPC id 1 will not have moved assert(get npc id(npc at spot(11, 3)) == 1) w(1) # Test read npc assert(read npc(2, npcstat:picture, pool:local) == 13) # pup n1 := npc reference(2) assert(read npc(n1, npcstat:picture) == 13) # pup w(1) assert(read npc(2, npcstat:picture, pool:global) == 17) # snake n1 := global npc reference(2) assert(read npc(n1, npcstat:picture) == 17) # snake w(1) n2 := create global npc(0, 7, 5) assert(n2 < 0) assert(get sprite set number(get npc sprite(n2)) == 15) # chewie walk npc(n2, south, 1) wait for npc(n2) assert(npc y(n2) == 6) w(1) # Test alter NPC assert(read npc(0, npcstat:movespeed, pool:global) == 4) alter npc(0, npcstat:movespeed, 10, pool:global) assert(read npc(0, npcstat:movespeed, pool:global) == 10) w(1) # Alterations made to global NPC persist between map changes teleport to map(8, 3, 3) w(1) assert(npc copy count(0, pool:global) == 0) assert(read npc(0, npcstat:movespeed, pool:global) == 10) n1 := create global npc(0, 4, 4, south) assert(read npc(0, npcstat:movespeed, pool:global) == 10) walk npc(n1, south,1) wait for npc(n1) w(1) # And a local npc with the same ID is unaffected n2 := create npc(0, 5, 4, left) assert(read npc(0, npcstat:movespeed, pool:local) == 4) assert(read npc(n2, npcstat:movespeed) == 4) w(1) # change it into a global npc assert(get npc pool(n2) == pool:local) change npc id(n2, 0, pool:global) assert(get npc pool(n2) == pool:global) assert(read npc(n2, npcstat:movespeed) == 10) w(1) teleport to map(oldmap, oldx, oldy) end ######################################################################## script, trigger tests, begin harm tile tests end script, assert hurt pattern, test no, start hp, h0, h1, h2, h3, begin # h# is the amount of damage by which hero # in the walkabout party should be hurt # h# is ignored if # > number of heroes in walkabout party $0="assert hurt pattern, test no. " append number(0, test no) subscript, consider rank, rank, harm amount, begin if (hero by rank(rank) == -1) then (exit script) # This will break if there are duplicate heroes, a well known flaw variable(slot) slot := find hero(hero by rank(rank)) tracevalue(rank, slot, get hero stat(slot, stat:hp), start hp -- harm amount) assert(get hero stat(slot, stat:hp) == start hp -- harm amount) set hero stat(slot, stat:hp, start hp) end consider rank(0, h0) consider rank(1, h1) consider rank(2, h2) consider rank(3, h3) end script, harm tile tests, begin $0="harm tile tests" # Test harmtile damage walk hero to x(me, 7) walk hero to y(me, 6) wait for hero(me) variable(start hp) start hp := get hero stat(0, stat:hp, current stat) write map block(6, 6, 140, 1) write pass block(6, 6, harmtile) subscript, test damage patterns, begin set caterpillar mode(on) # Turn off "Harm tiles harm non-caterpillar party" (actually the current setting in the .rpg) write general(177, read general(177), and, (-1, xor, 2^12)) # hero is now at tile 7,6, caterpillar is enabled and not suspended, and all heroes have 'start hp' hp # Test damage to individual heroes, caterpillar enabled # also testing crossing over harmtile without stopping walk hero(me, left, 1) wait for hero(me), _checkpoint # check for flash assert hurt pattern(0, start hp, true, false, false, false) walk hero(me, left, 2) wait for hero(me), _checkpoint assert hurt pattern(1, start hp, false, true, true, false) # Test damage to individual heroes, caterpillar suspended suspend caterpillar walk hero(1, right, 1) # step onto harm tile walk hero(2, down, 1) # step off harm tile walk hero(3, left, 1) # step onto harm tile wait for hero(2) assert hurt pattern(2, start hp, false, true, false, true) # Test damage to whole party, caterpillar disabled # (Leader walks back and forth over the tile) set caterpillar mode(off) walk hero(me, right, 3) # two tiles onto harm, then one off wait for hero(me) # "Harm tiles harm non-caterpillar party" is off assert hurt pattern(3, start hp, true, false, false, false) # Turn on "Harm tiles harm non-caterpillar party" write general(177, read general(177), or, 2^12) walk hero(me, left, 2) wait for hero(me) assert hurt pattern(4, start hp, true, true, true, true) # Check this makes no difference resume caterpillar walk hero(me, right, 2) wait for hero(me) assert hurt pattern(5, start hp, true, true, true, true) end # Test with four heroes in the party trace($99="First harmtile test run") test damage patterns # Move hero back to 7,6 set hero position(me, 11, 6) walk hero to x(me, 7) wait for hero(me) # Test party and walkabout slots not matching trace($99="Second harmtile test run") swap by position(0, 20) # remove leader test damage patterns # Test damage value set harm tile damage(2) walk hero(me, left, 2) wait for hero(me) assert hurt pattern(6, start hp, 2, 2, 2, 2) # Test death test harm tile death(start hp) # Cleanup write map block(6, 6, 0, 1) write pass block(6, 6, 0) swap by position(0, 20) # return leader set caterpillar mode(on) set harm tile damage(1) # restore # Caterpiller on and resumed, all heroes have 'start hp' hp end ######################################################################## script, menu tests, begin $0="menu tests" variable(i, m, mi, main, m1) m := create menu, w if(get menu id(m) <> -1) then($0="get menu id should have reported -1 for script-generated menu", crash) mi := add menu item(m), w $1="Puppies" set menu item caption(mi, 1), w mi := add menu item(m), w $1="Kittens" set menu item caption(mi, 1), w mi := add menu item(m), w $1="Walruses" set menu item caption(mi, 1), w mi := add menu item(m), w $1="Octopus" set menu item caption(mi, 1), w mi := add menu item(m), w $1="Plip" set menu item caption(mi, 1), w main := open menu, w bring menu forward(bottom menu), w for(i, align:left, align:right) do( set menu anchor x(m, i), w if(get menu anchor x(m) <> i) then($0="menu anchor x", crash) set menu anchor y(m, i), w if(get menu anchor y(m) <> i) then($0="menu anchor y", crash) ) $1="Puppies" mi := find menu item caption(m, 1) if(mi == 0) then($0="failed to find first menu item by caption", crash) get menu item caption(mi, 2) if(not(string compare(1, 2))) then($0="found menu item that was not puppies", crash) $1="Walruses" mi := find menu item caption(m, 1) get menu item caption(mi, 2) if(not(string compare(1, 2))) then($0="found menu item that was not walruses", crash) mi := find menu item caption(m, 1, mi) #search for Walruses starting after Walruses if(mi <> 0) then($0="find menu item walrus should have failed", crash) mi := add menu item(m) $1="Kittens" set menu item caption(mi, 1), w variable(kitten1, kitten2, walruses) #search for Kitten from top $1="Kittens" kitten1 := find menu item caption(m, 1) #search for Kitten starting after Walruses $2="Walruses" walruses := find menu item caption(m, 2) kitten2 := find menu item caption(m, 1, walruses) if(kitten1 == 0) then($0="failed to find first kitten", crash) if(kitten2 == 0) then($0="failed to find second kitten", crash) get menu item caption(kitten1, 2) if(not(string compare(1, 2))) then($0="found menu item that was not kitten(1)", crash) get menu item caption(kitten2, 2) if(not(string compare(1, 2))) then($0="found menu item that was not kitten(2)", crash) if(kitten1 == kitten2) then($0="found the same kitten twice", crash) mi := add menu item(m), w delete menu item(mi), w $1="Kittens" mi := find menu item caption(m, 1) delete menu item(mi), w mi := first menu item(m) delete menu item(mi), w if(find menu ID(1) <> 0) then($0="found unopened menu", crash) open menu(1),w m1 := find menu ID(1) if(get menu ID(m1) <> 1) then($0="get menu ID mismatch vs find menu", crash) if(m1 == 0) then($0="failed to find opened menu", crash) close menu(m1),w for(i, -3, 12) do( set menu border(main, i), w ) if(get menu border(main) <> 12) then($0="readback of menu border thickness failed", crash) for(i, 0, 10) do( set menu boxstyle(main, i) ) if(get menu boxstyle(main) <> 10) then($0="readback of menu boxstyle failed", crash) for(i, 0, 15) do( set menu max chars(main, i), w if(get menu max chars(main) <> i) then($0="readback of menu max chars failed", crash) ) set menu max chars(main, 0), w for(i, 10, 30) do( set menu min chars(main, i), w if(get menu min chars(main) <> i) then($0="readback of menu min chars failed", crash) ) set menu min chars(main, 0), w for(i, -50, 50, 10) do( set menu offset x(main, i), w if(get menu offset x(main) <> i) then($0="readback of menu offset x failed", crash) set menu offset y(main, i), w if(get menu offset y(main) <> i) then($0="readback of menu offset y failed", crash) ) set menu offset x(main, 0) set menu offset y(main, 0) for(i, align:left, align:right) do( set menu anchor x(main, i), w if(get menu anchor x(main) <> i) then($0="readback of menu anchor x failed", crash) set menu anchor y(main, i), w if(get menu anchor y(main) <> i) then($0="readback of menu anchor y failed", crash) ) set menu anchor x(main, 0), w set menu anchor y(main, 0), w for(i, align:left, align:right) do( set menu text align(main, i), w if(get menu text align(main) <> i) then($0="readback of menu text align failed", crash) ) set menu text align(main, 0), w set menu bit(main, 0, true), w #Transparent box if(get menu bit(main, 0) <> true) then($0="failed to set menu bit", crash) set menu bit(main, 0, false), w #Opaque box if(get menu bit(main, 0) <> false) then($0="failed to unset menu bit", crash) variable(save rows) save rows := get menu max rows(main) set menu max rows(main, 3), w set menu bit(main, 1, true), w #never show scrollbar set menu bit(main, 1, false), w #show scrollbar set menu max rows(main, save rows) set menu bit(main, 4, true), w #no box set menu bit(main, 4, false), w #show box set menu bit(main, 8, true), w #Advance text box when menu closes show text box(6), w if(not(menu is open(main))) then($0="why isn't the main menu open?", crash) close menu(top menu), w close menu(main), w if(current text box <> -1) then($0="menu bit failed to close textbox", crash) if(menu is open(main)) then($0="why is the main menu still open?", crash) variable(antelope, buffalo, catbus, duiker) m := create menu, w antelope := add menu item(m), $0="Antelope", set menu item caption(antelope, 0), w buffalo := add menu item(m), $0="Buffalo", set menu item caption(buffalo, 0), w catbus := add menu item(m), $0="Catbus", set menu item caption(catbus, 0), w duiker := add menu item(m), $0="Duiker", set menu item caption(duiker, 0), w # Test disabled, hidden, and unselectable items set menu item bit(catbus, menuitembit:hide when disabled, true), w assert(get menu item bit(catbus, menuitembit:hide when disabled) == true) # Disable and hide now set menu item type(catbus, menutype:label) set menu item subtype(catbus, menulabel:disabled) # (menu item disabled/visible/selectable should reflect changes immediately without needing a wait) assert(menu item disabled(catbus) == true) assert(menu item visible(catbus) == false) assert(menu item selectable(catbus) == false) w # Don't hide set menu item bit(catbus, menuitembit:hide when disabled, false) assert(menu item disabled(catbus) == true) assert(menu item visible(catbus) == true) assert(menu item selectable(catbus) == true) w # Un-selectable set menu item subtype(catbus, menulabel:unselectable) assert(menu item disabled(catbus) == false) assert(menu item visible(catbus) == true) assert(menu item selectable(catbus) == false) w # Re-enable set menu item subtype(catbus, menulabel:selectable) assert(menu item disabled(catbus) == false) assert(menu item visible(catbus) == true) assert(menu item selectable(catbus) == true) w # Test unselectable menu items are unselectable # (Note: you can select hidden items! Keeping for backcompat?) select menu item(catbus), w assert(selected menu item(m) == catbus) set menu item subtype(catbus, menulabel:unselectable) w # Requires a wait to take effect assert(selected menu item(m) == duiker) # The next selectable item assert(select menu item(antelope) == true) assert(select menu item(catbus) == false) # Unselectable, fails assert(selected menu item(m) == antelope) set menu item subtype(catbus, menulabel:selectable) select menu item(catbus) assert(selected menu item(m) == catbus) set menu item settag(catbus, 3) if(get menu item settag(catbus) <> 3) then($0="menu item settag readback failed", crash) if(check tag(3)) then($0="tag 3 shouldn't be on yet (menu)", crash) use menu item(catbus), w if(not(check tag(3))) then($0="tag 3 shouldn't be on now (menu)", crash) set menu item settag(catbus, 0) set menu item togtag(catbus, 3) use menu item(catbus), w if(check tag(3)) then($0="tag 3 should be toggled off (menu)", crash) use menu item(catbus), w if(check tag(3)==OFF) then($0="tag 3 should be toggled on (menu)", crash) use menu item(catbus), w if(check tag(3)) then($0="tag 3 should be toggled off again (menu)", crash) set menu item togtag(catbus, 0) for(i, 1, 2) do( set menu item tag(catbus, -3, i), w if(get menu item tag(catbus, i) <> -3) then($0="menu item tag readback failed", crash) set tag(3, ON), w set menu item tag(catbus, 3, i), w set tag(3, OFF), w set menu item tag(catbus, 0, i), w ) $0="menu item tag req", show string(0) for(i, 1, 2) do( set tag(3, OFF) if(get menu item tag(catbus, i) <> 0) then($0="menu item tag req should be zero:", append number(0, i), crash) set menu item tag(catbus, 3, i), w if(get menu item tag(catbus, i) <> 3) then($0="menu item tag req readback fail:", append number(0, i), crash) set tag(3, ON), w set menu item tag(catbus, -3, i), w set tag(3, OFF), w set menu item tag(catbus, 0, i), w ) show no value # menu item extra data assert(extra length(buffalo) == 3) for(i, 0, 2) do( assert(get menu item extra(buffalo, i) == 0) set menu item extra(buffalo, i, i*10) assert(get menu item extra(buffalo, i) == i*10) assert(get extra(buffalo, i) == i*10) set extra(buffalo, i, 20) assert(get menu item extra(buffalo) == 20) ) resize extra(buffalo, 0) assert(extra length(buffalo) == 0) append extra(buffalo, 42) assert(extra length(buffalo) == 1) assert(get menu item extra(buffalo, -1) == 42) # Check negative indices set menu item type(catbus, menutype: special) $0="" # use default caption set menu item caption(catbus, 0), w for (i, 0, 13) do( set menu item subtype(catbus, i), w ) $0="Catbus, not Save" set menu item caption(catbus, 0), w $0="" set menu item caption(catbus, 0), w set menu item type(catbus, menutype:menu), w for(i, 0, 2) do( set menu item subtype(catbus, i), w if(get menu item subtype(catbus) <> i) then($0="menu item subtype readback failed", crash) ) $0="Catbus" set menu item caption(catbus, 0), w # open another menu set menu item type(catbus, menutype:menu) set menu item subtype(catbus, 2) use menu item(catbus), w if(get menu id(top menu) <> 2) then($0="Failed to open a menu from a menu", crash) close menu(top menu), w # open a text box set menu item type(catbus, menutype:textbox) set menu item subtype(catbus, 7) use menu item(catbus), w if(current textbox <> 7) then($0="Failed to open a text box from a menu", crash) advance text box, w if(get menu item type(catbus) <> menutype:textbox) then($0="menu item type readback failed", crash) set menu item type(catbus, menutype:label) # run a script from a menu set menu item type(catbus, menutype:script) set menu item subtype(catbus, @on menu item use) use menu item(catbus), w if(menu item script global <> 99) then($0="Failed to run a script from a menu", crash) variable(oldcol) oldcol := get menu textcolor(m) for(i, 0, 10) do( set menu textcolor(m, i * 13), w if(get menu textcolor(m) <> i * 13) then($0="menu textcolor readback failed", crash) ) set menu textcolor(m, oldcol) # iterate all menu items variable(count) count := 0 mi := first menu item(m) while(mi) do( count += 1 mi := next menu item(mi) ) if(count <> 4) then($0="iterated wrong number of menu items (all)") # now hide catbus set menu item bit(catbus, menu item bit:hide when disabled, true) set menu item tag(catbus, 3) set tag(3, OFF) # iterate all except hidden count := 0 mi := first menu item(m) while(mi) do( count += 1 mi := next menu item(mi, true) ) if(count <> 3) then($0="iterated wrong number of menu items (vis only)") # iterate all including hidden count := 0 mi := first menu item(m) while(mi) do( count += 1 mi := next menu item(mi, false) ) if(count <> 4) then($0="iterated wrong number of menu items (vis only)") # close when selected set menu item bit(antelope, 1, true) use menu item(antelope), w if(top menu == m) then($0="Even Toed Ungulate menu failed to close", crash) menu close global := 0 m := create menu, w mi := add menu item(m) $1="close me" set menu item caption(mi, 1), w set menu item bit(mi, menu item bit:close menu when selected, true) set menu on close script(m, @on close menu test) if(get menu on close script(m) <> @on close menu test) then($0="menu close script readback failed", crash) use menu item(mi), w if(menu close global <> 99) then($0="menu close script didn't run", crash) # Test background highlight w(2) m := create menu, w mi := add menu item(m), w set menu item caption(mi, $1="Lorem"), w mi := add menu item(m), w set menu item caption(mi, $1="Ipsum"), w mi := add menu item(m), w set menu item caption(mi, $1="Dolor"), w mi := add menu item(m), w set menu item caption(mi, $1="Sit"), w mi := add menu item(m), w set menu item caption(mi, $1="Amet"), w assert(get menu bit(m, menubit:highlight selection background) == false) set menu bit(m, menubit:highlight selection background), w assert(get menu bit(m, menubit:highlight selection background) == true) mi := first menu item(m) while(mi) do( mi := next menu item(mi, true) if(mi) then(select menu item(mi)) w(2) ) w close menu(m),w # Test closing a textbox by closing the menu m := create menu, w mi := add menu item(m), w set menu item caption(mi, $1="Twas brillig"), w mi := add menu item(m), w set menu item caption(mi, $1="And the slithy toaves..."), w assert(get menu bit(m, menubit:advance text box when menu closes) == false) show text box(2),w # first, closing the menu should not close the textbox by default close menu(m),w assert(current text box == 2) advance text box, w assert(current text box == -1) # Now do it again... m := create menu, w mi := add menu item(m), w set menu item caption(mi, $1="Did gyre and gimbal"), w mi := add menu item(m), w set menu item caption(mi, $1="In the wabe..."), w assert(get menu bit(m, menubit:advance text box when menu closes) == false) set menu bit(m, menubit:advance text box when menu closes), w assert(get menu bit(m, menubit:advance text box when menu closes) == true) show text box(2),w # This time, closing the menu should close the textbox too close menu(m),w assert(current text box ==-1) # Test edgecases for menus without selectable menu item. # Open a menu where the first item is unselectable: m := open menu(3) assert(menu item slot(selected menu item(m)) == 1) w close menu(m) # Empty menu m := create menu() assert(selected menu item == 0) close menu(m) # Open a menu with no selectable items m := open menu(4) assert(selected menu item == 0) w close menu(m) menu scroll tests end script, on menu item use, begin menu item script global := 99 end script, on close menu test, begin menu close global := 99 end script, scroll through menu, menu, numitems, begin variable (i, j, mi) # test scrolling for (i, 1, 3, 2) do ( set menu max rows(menu, i) # forwards mi := first menu item(menu) while (mi) do ( select menu item(mi), w mi := next menu item(mi) ) # backwards mi := menu item by slot(menu, numitems -- 1) while (mi) do ( select menu item(mi), w mi := previous menu item(mi) ) # randomly for (j, 0, 4) do ( select menu item(menu item by slot(menu, random(0, numitems -- 1))), w ) ) set menu max rows(menu, 0) end script, menu scroll tests, begin $0="menu scroll tests" variable (main, mi, next, i, numitems, numvisitems) main := open menu, w set menu bit(main, 1, false), w #show scrollbar allow minimap(true), w #make sure there are no hidden items allow save anywhere(true), w # test menu item iteration mi := first menu item(main) while (mi) do ( get menuitemcaption(mi, err arg string) #trace(err arg string) appendnumber(err arg string, numitems) assert (menu item slot(mi) == numitems) assert (menu item by slot(main, numitems, true) == mi) assert (menu item by slot(main, numitems, false) == mi) assert (menu item true slot(mi) == numitems) assert (menu item by true slot(main, numitems) == mi) next := next menu item(mi, false) if (next) then ( assert (mi == previous menu item(next)) ) set menu item extra(mi, 0, numitems) numitems += 1 mi := next ) assert(numitems == 9) $err arg string="" # menu item iteration edge cases assert (previous menu item(first menu item(main)) == 0) assert (next menu item(menu item by slot(main, numitems -- 1)) == 0) assert (menu item by slot(main, numitems, true) == 0) assert (menu item by slot(main, numitems, false) == 0) assert (menu item by slot(main, -1) == 0) assert (menu item by true slot(main, -1) == 0) assert (menu item by true slot(main, numitems) == 0) # test "select menu item" mi := first menu item(main) while (mi) do ( select menu item(mi), w assert (selected menu item(main) == mi) mi := next menu item(mi) ) # test scrolling scroll through menu(main, numitems) # check hidden items sorted to end: # 1. hide three items set tag(3, OFF) variable(hidden1, hidden2, hidden3) hidden3 := menu item by slot(main, 4) set menu item bit(hidden3, menu item bit: hide when disabled, true) set menu item tag(hidden3, 3, 1) hidden1 := menu item by slot(main, 0) set menu item bit(hidden1, menu item bit: hide when disabled, true) set menu item tag(hidden1, 3, 1) hidden2 := menu item by slot(main, 3) set menu item bit(hidden2, menu item bit: hide when disabled, true) set menu item tag(hidden2, 3, 1) w # 2. add new item mi := add menu item(main) set menu item extra(mi, 0, numitems) numitems += 1 $1="a new item!" set menu item caption(mi, 1), w assert (numitems == menu item count(main)) numvisitems := numitems -- 3 assert (numvisitems == visible menu item count(main)) # 3. check visible items in correct order variable (temp, extra, mi2) temp := -1 for (i, 0, num items -- 3 -- 1) do ( mi := menu item by slot(main, i, true) mi2 := menu item by slot(main, i, false) assert (mi == mi2) extra := get menu item extra(mi2, 0) if (extra <= temp) then ($0="menu items not in increasing order", crash) temp := extra ) # 4. check hidden items at end in correct order if (menu item by slot(main, numitems -- 3, true) <> 0) then ($0="menuitembyslot returned hidden item", crash) if (menu item by slot(main, numitems -- 3, false) <> hidden1) then ($0="hidden items out of order", crash) if (menu item by slot(main, numitems -- 2, false) <> hidden2) then ($0="hidden items out of order", crash) if (menu item by slot(main, numitems -- 1, false) <> hidden3) then ($0="hidden items out of order", crash) assert (menu item by slot(main, numitems, false) == 0) # 5. check "true order" hasn't changed for (i, 0, num items -- 1) do ( mi := menu item by true slot(main, i) assert (menu item true slot(mi) == i) assert (get menu item extra(mi, 0) == i) ) # 6. test "last menu item" assert (last menu item(main) == menu item by slot(main, numvisitems -- 1)) assert (last menu item(main, false) == menu item by slot(main, numitems -- 1, false)) assert (next menu item(last menu item(main)) == 0) # Don't return first invisible item assert (next menu item(last menu item(main), false) <> 0) # Do return first invisible item # test scrolling again scroll through menu(main, numitems -- 3) # check unhidden menu items sorted back correctly set tag(3, ON), w for (i, 0, numitems -- 1) do ( mi := menu item by slot(main, i, false) assert (mi == menu item by slot(main, i, true)) assert (get menu item extra(mi, 0) == i) ) close menu(main) end #next menu(menu handle) #open menu (ID, allow duplicate) #parent menu(menu item handle) #previous menu(menu handle) #swap menu items(handle1, handle2) #wait for menu (menu handle) ######################################################################## script, textbox tests, begin $0="textbox tests" show text box(2), w advance text box, w show text box(3), w advance text box, w if(current textbox <> 4) then(crash) advance text box, w show text box(3), w advance text box, w if(current textbox <> 5) then(crash) advance text box, w if(current textbox <> -1) then(crash) w show text box(12), w, advance text box, w show text box(13), w, advance text box, w variable (oldval) oldval := read general(178) # genBits2 assert((oldval, and, 2^2) == 2^2) # "showtextbox happens immediately" is on # check showtextbox isn't delayed show text box(14) assert(checktag(tag:textbox 14 happened) == true) # no wait required before advance advance textbox wait assert(current textbox == -1) settag(tag:textbox 14 happened, false) # check both showtextboxes work show text box(14) show text box(15) assert(checktag(tag:textbox 14 happened) == true) assert(checktag(tag:textbox 15 happened) == true) advance textbox settag(tag:textbox 14 happened, false) settag(tag:textbox 15 happened, false) write general(178, oldval -- 2^2) # "showtextbox happens immediately" OFF # Check currenttextbox still updates immediately show text box(2) assert(current textbox == 2) # wait required before advance wait advance text box assert(current textbox == -1) # check showtextbox is delayed show text box(14) assert(checktag(tag:textbox 14 happened) == false) wait assert(checktag(tag:textbox 14 happened) == true) advance textbox settag(tag:textbox 14 happened, false) # check only last showtextbox works show text box(14) show text box(15) wait assert(checktag(tag:textbox 14 happened) == false) assert(checktag(tag:textbox 15 happened) == true) advance textbox write general(178, oldval) end ######################################################################## script, shop tests, begin # Ain't much here yet... $0="shop tests" assert(is shop buy menu empty(shop:Boring Shop) == false) assert(is shop hire menu empty(shop:Boring Shop) == false) assert(is shop buy menu empty(shop:Empty Shop) == true) assert(is shop hire menu empty(shop:Empty Shop) == true) w end ######################################################################## script, battle tests, begin # there are some other battle tests in with the timer tests $0="battle tests" set battle countdown (182) assert(get battle countdown == 182) show value(random(1, 999999999)), w force random battle(1) show value(random(1, 999999999)), w # this should remain deterministic after battle show no value, w # attack forces run set tag(tag:Victory, true) assert(fight formation(1) == false), w # sets victory tag assert(check tag(tag:Victory) == false) # attack forces victory assert(fight formation(2) == true), w assert(check tag(tag:Victory) == true) # attack forces exit assert(fight formation(3) == false), w # test tag check: battle should not trigger force random battle(2) assert(last formation <> 4) w(5) fight formation(5) w(5) # Test attacks setting tags. set tag(4, false) set tag(5, false) fight formation(6) w(5) # The first time should NOT set tag 5 because tag 4 was not set assert(not(check tag(5))), w set tag(4, true) fight formation(6) w(5) # The second time SHOULD set tag 5 because tag 4 WAS set assert(check tag(5)), w set tag(6, false) fight formation(7) w(5) # The third one should always turn the tag on because the tag check is 0. assert(check tag(6)), w test battle formation modification test hero counterattacks end # Formation set should be 1-2 script, force random battle, formation set = 1, begin variable(s, x, y, d) if (formation set == 0) then (formation set := 1) # If run with runscriptbyid # save hero state s := get hero speed(me) x := hero x(me) y := hero y(me) d := hero direction(me) # get the hero ready for forcing the battle set hero speed(0, 20) # Formations 1-3 are at x = 14-16 set hero position(0, 13 + formation set, 9), w # force the battle walk hero(0, north, 1), w # restore hero state set hero speed(me, s) set hero position(me, x, y) set hero direction(me, d) w # Countdown should be in range 40-160 (test this here so we see multiple battles assert(get battle countdown >= 40 && get battle countdown <= 160) end script, force non random battle, begin w fight formation(0) w(2) end plotscript, after battle, begin $0="After battle script:" battle script sequence += 1 append number(0, battle script sequence) trace(0) show string at(0, 0, 0) w hide string(0) end plotscript, instead of battle, form, begin $0="Instead of battle script(" append number(0, form) $0+")" trace(0) w fight formation(form) $0=" finish instead of battle" trace(0) end script, test battle formation modification, begin # Test formation editing commands # use reward gold to keep track of how many enemies are present in each formation variable(orig gold, gold) orig gold := party money gold := party money fight formation(8), w assert(party money == gold + 1) # Add an enemy to the formation add enemy to formation (8, 8, 100, 90, 1) assert(formation slot enemy(8, 1) == 8) assert(formation slot x(8, 1) == 100) assert(formation slot y(8, 1) == 90) gold := party money fight formation(8), w assert(party money == gold + 2) # Fill the empty slots in the formation variable(i) for(i, 2, 7) do( add enemy to formation (8, 8, 100, 90 + i * 20, 1) ) gold := party money fight formation(8), w assert(party money == gold + 8) # remove several enemies, including the original enemy in slot 0 delete enemy from formation (8, 0) delete enemy from formation (8, 1) delete enemy from formation (8, 2) delete enemy from formation (8, 3) w assert(formation slot enemy(8, 0) == -1) assert(formation slot enemy(8, 1) == -1) assert(formation slot enemy(8, 2) == -1) assert(formation slot enemy(8, 3) == -1) gold := party money fight formation(8), w assert(party money == gold + 4) # Test finding an enemy w # an enemy that will not be found assert(find enemy in formation (8, 1, 0) == -1) assert(find enemy in formation (8, 1, get count) == 0) # an enemy that is actually present assert(find enemy in formation (8, 8, 0) == 4) assert(find enemy in formation (8, 8, 3) == 7) assert(find enemy in formation (8, 8, get count) == 4) # Test background changing assert(get formation background(8) == 1) set formation background(8, 0) assert(get formation background(8) == 0) # Test writing song id number show value(get formation song(8)) assert(get formation song(8) == song:silence) set formation song(8, song:same as map) assert(get formation song(8) == song:same as map) set formation song(8, song:tin glazed and noncommutative) assert(get formation song(8) == song:tin glazed and noncommutative) # actually run the battle. We can't test the visuals, but -autosnap will see it fight formation(8), w # Test resetting the formation to default reset formation(8), w assert(get formation background(8) == 1) assert(get formation song(8) == song:silence) assert(find enemy in formation (8, 8, 0) == 0) assert(find enemy in formation (8, 8, get count) == 1) assert(formation slot enemy(8, 0) == 8) assert(formation slot x(8, 0) == 131) assert(formation slot y(8, 0) == 169) show string(string sprintf(2, $1="%dx%d", formation slot x(8, 0), formation slot y(8, 0))), w fight formation(8), w # Test modifying and resetting individual formation slots # Assert original values assert(formation slot enemy(9, 0) == 0) assert(formation slot x(9, 0) == 18) assert(formation slot y(9, 0) == 201) assert(formation slot enemy(9, 1) == -1) assert(formation slot x(9, 1) == 0) assert(formation slot y(9, 1) == 0) # Make changes delete enemy from formation(9, 0) add enemy to formation(9, 8, 50, 190, 1) assert(formation slot enemy(9, 0) == -1) # deleted enemies do not have their x,y deleted, but width/height are no longer included assert(formation slot x(9, 0) == 1) assert(formation slot y(9, 0) == 167) assert(formation slot enemy(9, 1) == 8) assert(formation slot x(9, 1) == 50) assert(formation slot y(9, 1) == 190) fight formation(9) # Reset single slots reset formation slot(9, 0) assert(formation slot enemy(9, 0) == 0) assert(formation slot x(9, 0) == 18) assert(formation slot y(9, 0) == 201) assert(formation slot enemy(9, 1) == 8) assert(formation slot x(9, 1) == 50) assert(formation slot y(9, 1) == 190) reset formation slot(9, 1) assert(formation slot enemy(9, 0) == 0) assert(formation slot x(9, 0) == 18) assert(formation slot y(9, 0) == 201) assert(formation slot enemy(9, 1) == -1) assert(formation slot x(9, 1) == 0) assert(formation slot y(9, 1) == 0) # restore original money set money(orig gold) show no value end script, test hero counterattacks, begin assert(hero by slot(0) == hero:Freki) assert(get hero stat(0, stat:HP, current stat) == 10), w fight formation(10), w # Freki's HP should be 4 points lower, -1 for the ice attack, and -3 for her self-counterattack assert(get hero stat(0, stat:HP, current stat) == 6), w # Heal up set hero stat(0, stat:HP, 10, current stat), w end ######################################################################## script, attack tests, begin $0="attack tests" # Reading attack name $1="SelfStab" read attack name(2, atk:SelfStab) assert(string compare(1,2)) $2="" read attack name(2, 0 + 1) assert(string compare(1,2)) $2="" get attack name(2, 0 -- 1) assert(string compare(1,2)) # Reading attack caption $1="Attack self" get attack caption(2, 0 + 1) assert(string compare(1,2)) assert(get attack extra(atk:SelfStab, 0) == 42) assert(get attack extra(atk:SelfStab, 2) == 99) assert(get attack extra(atk:ForceRun) == 0) assert(get attack extra(10 + 1, 2) == 1) # Currently the last attack end ######################################################################## script, enemy tests, begin $0="enemy tests" assert(enemy elemental resist as int(enemy:Data Read Test, 2) == 0) assert(enemy elemental resist as int(enemy:Data Read Test, 15) == -5) # Name get enemy name(enemy:Data Read Test, 1) $2 = "Data read test" assert(string compare(1, 2)) $2 = "Data read test2" set enemy name(enemy:Data Read Test, 2) get enemy name(enemy:Data Read Test, 1) assert(string compare(1, 2)) reset enemy name(enemy:Data Read Test) get enemy name(enemy:Data Read Test, 1) $2 = "Data read test" assert(string compare(1, 2)) # Length limit is currently 16, so check at least first 16 chars saved $2 = "SeenGoldDuckUponHigh" set enemy name(enemy:Data Read Test, 2) get enemy name(enemy:Data Read Test, 1) trim string(1, 1, 16) trim string(2, 1, 16) assert(string compare(1, 2)) # Stats assert(get enemy stat(enemy:Data Read Test, stat:HP) == 13) set enemy stat(enemy:Data Read Test, stat:Hits, 6) assert(get enemy stat(enemy:Data Read Test, stat:Hits) == 6) reset enemy stat(enemy:Data Read Test, stat:Hits) assert(get enemy stat(enemy:Data Read Test, stat:HP) == 13) # Other set enemy appearance(enemy:Data Read Test, Enemy:PictureSize, EnemySize:large) assert(get enemy appearance(enemy:Data Read Test, Enemy:PictureSize) == EnemySize:large) assert(get enemy appearance(enemy:Data Read Test, Enemy:Palette) == -1) assert(read enemy data(enemy:Data Read Test, Enemy:Item) == 1) write enemy data(enemy:Data Read Test, Enemy:RareItem, 2) assert(read enemy data(enemy:Data Read Test, Enemy:RareItem) == 2) assert(read enemy data(enemy:Data Read Test, Enemy:ItemPercent) == 50) write enemy data(enemy:Data Read Test, Enemy:ItemPercent, 24) assert(read enemy data(enemy:Data Read Test, Enemy:ItemPercent) == 24) reset enemy data(enemy:Data Read Test, Enemy:ItemPercent) assert(read enemy data(enemy:Data Read Test, Enemy:ItemPercent) == 50) end ######################################################################## script, timer tests, begin variable(i) allocate timers(32) set timer(31, 10, 1, @timer test 1, 1) show string at(1, 10, 10) w, w, w, w if(read timer(31) <> 6) then($0="read timer failed", crash) w(6), w timer global := 100 set timer(31, 0, 1, @looping timer test, 1) w(11) # Test "set timer args" timer global := 0 set timer(timer:set args test, 0, 1, @timer args test) set timer args(timer:set args test, -999, 100) timer expect arg 1 := -999 timer expect arg 2 := 100 wait assert(timer global == 1) # Args should be preserved when run again set timer(timer:set args test, 0) wait assert(timer global == 2) # Changing the trigger to different script wipes the args set timer(timer:set args test, 0, 1, @timer args test 2) timer expect arg 1 := timer:set args test # Default args timer expect arg 2 := 0 wait assert(timer global == 12) # Passing no args set timer args(timer:set args test) set timer(timer:set args test, 0) timer expect arg 1 := 0 timer expect arg 2 := 0 wait assert(timer global == 22) # Throw an error if no script triggered # set timer(timer:set args test, 0, 1, 0) # set timer args(timer:set args test, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) $err arg string="random" test timer and battle interactions(@force random battle) $err arg string="fight formation" test timer and battle interactions(@force non random battle) $err arg string="" $0="timer interactions with after battle", trace(0) teleport to map(1, 10, 8), w # after battle battle script sequence := 0 test timer and battle interactions(@force random battle) if(battle script sequence <> 3) then($0="(ab) afterbattle should have happened 3 times", crash) $0="timer interactions with instead of battle", trace(0) teleport to map(2, 10, 8), w # instead of battle battle script sequence := 0 test timer and battle interactions(@force random battle) if(battle script sequence <> 0) then($0="(ib) afterbattle should have happened 0 times", crash) $0="timer interactions with after battle + instead of battle", trace(0) teleport to map(3, 10, 8), w # after battle + instead of battle battle script sequence := 0 test timer and battle interactions(@force random battle) if(battle script sequence <> 3) then($0="(ab+ib) afterbattle should have happened 3 times", crash) teleport to map(0, 10, 8), w hide string(1) end script, test timer and battle interactions, trigger battle, begin #regular timer timer global := 0 timer global will become := 1000 set timer(31, 10, 1, @battle timer test, -1, 0) run script by id(trigger battle) if(read timer(31) <= 0) then($0="timer should still be running", crash) if(timer global <> 0) then($0="timer global should still be zero", crash) w(10) if(read timer(31) >> 0) then($0="timer should be done by now", crash) if(timer global <> timer global will become) then($0="timer global didn't change", crash) #timerflag:battle timer global := 0 timer global will become := 2000 set timer(31, 10, 1, @battle timer test, 1, timerflag:battle) run script by id(trigger battle) if(read timer(31) >> 0) then($0="(bat) timer should be done by now", crash) if(timer global <> timer global will become) then($0="(bat) timer global didn't change", crash) #timerflag:battle ,or, timerflag:critical timer global := 0 timer global will become := 3000 set timer(31, 10, 1, @battle timer test, 1, (timerflag:battle,or,timerflag:critical)) set timer(30, 20, 1, @battle timer test, 0, (timerflag:battle)) run script by id(trigger battle) if(read timer(31) >> 0) then($0="(bat+crit) timer should be done by now", crash) if(timer global <> timer global will become) then($0="(bat+crit) timer global didn't change", crash) timer global will become := 4000 if(read timer(30) <= 0) then($0="(bat+crit) second timer should still be running", crash) w(12) if(read timer(30) >> 0) then($0="(bat+crit) second timer should be done by now", crash) if(timer global <> timer global will become) then($0="(bat+crit) timer global didn't change on second timer", crash) timer global := 0 w end script, timer test 1, begin show value(12345) w end script, looping timer test, begin if(timer global <= 0) then(timer global := 0, exit script) set timer(31, 0, 1, @looping timer test, 1) timer global -= 10 show value(timer global) _checkpoint end script, timer args test, arg1, arg2, arg3 = 42, begin assert(arg1 == timer expect arg 1) assert(arg2 == timer expect arg 2) #assert(arg3 == 42) # arg3 defaults to 0 instead of 42 due to an engine bug assert(arg3 == 0) timer global += 1 end script, timer args test 2, arg1, arg2, begin assert(arg1 == timer expect arg 1) assert(arg2 == timer expect arg 2) timer global += 10 end script, battle timer test, begin timer global := timer global will become $0="battle timer test:" append number(0, timer global) trace(0) show string(0), w end ######################################################################## script, pathfinding tests, begin $0="Pathfinding tests" variable(oldx, oldy, oldmap) oldx := herox(me) oldy := heroy(me) oldmap := current map teleport to map(map:Pathland, 8, 7) start checkpoint timer(4) variable(ref) ref := create NPC(0, 17, 3) pathfind npc to(ref, hero x(me), hero y(me), 1) wait for npc(ref) assert(dist from leader(ref) == 1) pathfind npc to(ref, 8, 3) wait for npc(ref) assert(npc x(ref) == 8) assert(npc y(ref) == 3) # With pacing NPCs in the way create npc(1, 8, 10, right) create npc(1, 8, 11, right) create npc(1, 8, 12, right) create npc(1, 8, 13, right) camera follows npc(ref) pathfind npc to(ref, 10, 16) wait for npc(ref) assert(npc x(ref) == 10) assert(npc y(ref) == 16) # To an unreachable zone destination pathfind npc to(ref, 15, 5, 2) wait for npc(ref) assert(dist from pos(ref, 15, 5) == 2) # Cancelling a walk pathfind npc to(ref, 14, 9) wait for npc(ref) assert(is at pos(ref, 14, 9)) pathfind npc to(ref, 17, 11) wait(10) cancel npc walk(ref) assert(is at pos(ref, 15, 8)) # also test cancelling a regular walk npc walk npc(ref, left, 4) wait(10) cancel npc walk(ref) assert(is at pos(ref, 13, 8)) # cancel a walk mid-step walk npc(ref, right, 3) wait(1) cancel npc walk(ref) assert(npc is walking(ref) == true) # this doesn't become false until the current step finishes wait for npc(ref) assert(npc is walking(ref) == false) assert(is at pos(ref, 14, 8)) # Test obstructed wait with no timeout create npc(2, 9, 8) pathfind npc to(ref, 9, 7) wait(7 * 5) # wait the time it takes to walk 7 steps (will get stuck after 6 steps) assert(is at pos(ref, 10, 8)) walk npc(2, south, 1) wait for npc(ref) assert(is at pos(ref, 9, 7)) # Test npc following npc walk npc(ref, south, 1) wait for npc(1) assert(is at pos(ref, 9, 7)) pathfind npc to(ref, 16, 18) wait(10) npc chases npc(2, ref, false, 10) wait for npc(ref) assert(is at pos(ref, 16, 18)) wait for npc(2) assert(dist from pos(2, 16, 18) == 1) # Test that npc is walking supports pathfinding pathfind npc to(2, 13, 16) variable(i) # The whole path (4 tiles at 4 ticks each) should return true for(i, 1, 4 * 4) do( w assert(npc is walking(2)) ) # And then when the path finishes, return false w assert(npc is walking(2) == false) # Test NPC giving up an unreachable path after stillticks pathfind npc to(2, 14, 14, 12) # the first two steps should go fine w(4) w(4) assert(is at pos(2, 14, 15)) assert(npc is walking(2)) # wait 12 more ticks, should not have moved w(12) assert(is at pos(2, 14, 15)) assert(npc is walking(2)) # wait 1 more tick, and the npc should have given up walking w(1) assert(npc is walking(2)==false) # now add some barrier npcs variable(n0, n1, n2, n3) n0 := create npc(3, 15, 15, left) n1 := create npc(3, 14, 16, up) n2 := create npc(3, 13, 15, right) set npc speed(3, 10) # make the npc walk, make one of the barrier npcs move at the last moment pathfind npc to(2, 17, 15, 7) # try for 7 ticks # should fail for 6 ticks for(i, 1, 6) do( w(1) assert(npc is walking(2)) assert(is at pos(2, 14, 15)) assert(npc pixel x(2) == 14 * 20) ) # abruptly remove a barrier npc destroy npc(n0) w(1) # NPC 2 should start walking assert(npc is walking(2)==true) assert(npc pixel x(2) == 14 * 20 + 5) wait for npc(2) assert(is at pos(2, 17, 15)) # clean up the other barrier npcs destroy npc(n1) destroy npc(n2) # Now test some hero pathfinding camera follows hero subscript, hero tile, rank, x, y, begin exit returning(hero x(rank) == x && hero y(rank) == y) end #show string(string sprintf(1, $2="x=%d y=%d", hero x(me), hero y(me))) assert(hero tile(me, 8, 7)) pathfind hero to(me, 13, 5) wait(5) assert(hero tile(me, 9, 7)) wait for hero(me) # should reach dest assert(hero tile(me, 13, 5)) # caterpillar should follow assert(hero tile(1, 12, 5)) assert(hero tile(2, 12, 6)) assert(hero tile(3, 11, 6)) # now path each hero individually suspend caterpillar pathfind hero to(0, 8, 7) pathfind hero to(1, 9, 7) pathfind hero to(2, 8, 8) pathfind hero to(3, 9, 8) wait for hero(0) wait for hero(1) wait for hero(2) wait for hero(3) assert(hero tile(0, 8, 7)) assert(hero tile(1, 9, 7)) assert(hero tile(2, 8, 8)) assert(hero tile(3, 9, 8)) # Test hero-on-hero collision (or lack thereof) pathfind hero to(0, 12, 7) pathfind hero to(1, 11, 7) wait for hero(0) wait for hero(1) # Hero 1 should walk right through hero 0 pathfind hero to(1, 13, 7) wait(5) assert(hero tile(1, hero x(0), hero y(0))) wait(5) # should still be walking for one more tick assert(hero is walking(1)) wait(1) # okay, should be done now assert(hero is walking(1)==false) # now lets test hero-on-npc collisions # get hero 1 out of the way walk hero(1, right, 1) # create some NPC barriers n0 := create npc(3, 12, 6, down) n1 := create npc(3, 13, 7, left) n2 := create npc(3, 12, 8, up) n3 := create npc(3, 11, 7, right) w(1) # Hero pathing should fail pathfind hero to(0, 12, 10) wait(1) # the hero is still trying to walk assert(hero is walking(0)) # the hero has not moved assert(hero tile(0, 12, 7)) cancel hero walk(0) assert(hero is walking(0) == false) # now move one NPC out of the way set npc speed(3, 10) pathfind npc to(n0, 14, 6) wait(2) # Now the hero can move pathfind hero to(0, 12, 10) wait(5) assert(hero is walking(0)) assert(hero tile(0, 12, 6)) wait for hero(0) # move the npcs over to block hero 1 pathfind npc to(n2, 15, 7) pathfind npc to(n3, 14, 8) # npc 1 is blocked, but non-leader heroes # are currently allowed to pass through npcs pathfind hero to(1, 16, 7) wait(1) assert(hero is walking(1) == true) wait for hero(1) assert(hero tile(1, 16, 7)) # send an NPC running, and have the hero chase it pathfind npc to(n1, 5, 5) hero chases npc(0, n1, true) wait for npc(n1) # hero should still be catching up assert(hero is walking(0)) wait for hero(0) assert(hero is walking(0) == false) # now path the hero somewhere it can't reach, it should get close pathfind hero to(0, 8, 5) wait(5) assert(hero is walking(0)) wait(5) assert(hero tile(0, 7, 4)) w # send the hero on a long path, but cancel after a step and a half pathfind hero to(0, 8, 12) w(7) cancel hero walk(0) # hero should still finish the current step assert(hero is walking(0)), w assert(hero is walking(0)), w wait for hero(0) assert(hero is walking(0) == false) assert(hero tile(0, 6, 5)) # now to test stop-after-stillticks # put hero hero back in the NPC cage set hero speed(me, 10) pathfind hero to(0, 14, 7) wait(2) # move the npc back to block pathfind npc to(n1, 13, 7) wait for hero(0) wait for npc(n1) # The hero should try to walk for 10 ticks and give up pathfind hero to(0, 13, 8, 10) for(i, 1, 10) do( show value(i) w(1) assert(hero is walking(0)) assert(hero tile(0, 14, 7)) ) w(1) # the hero should have given up by now assert(hero is walking(0) == false) assert(hero tile(0, 14, 7)) # Now more tests of hero chasing npc hero chases npc(0, n1) w(2) # should not have moved yet, but trying to move assert(hero tile(0, 14, 7)) assert(hero is walking(0)) pathfind npc to(n1, 12, 7) wait for npc(n1) #the hero is slower, so they should still be walking assert(hero tile(0, 13, 7)) assert(hero is walking(0)) # the hero does not give up w(10) assert(hero tile(0, 13, 7)) assert(hero is walking(0)) # make the NPC move again walk npc(n1, south, 1) wait(5) assert(hero tile(0, 12, 7)) assert(hero is walking(0)) # cancel chase cancel hero walk(0) assert(hero is walking(0)==false) # move the npc again pathfind npc to(n1, 10, 8) wait for npc(n1) assert(is at pos(n1, 10, 8)) # make the hero chase the npc, but stop when reached hero chases npc(0, n1, true) wait for hero(0) assert(hero tile(0, 11, 8)) assert(hero is walking(0)==false) # make the npc move again, hero will not follow walk npc(n1, south, 1) wait for npc(n1) assert(hero tile(0, 11, 8)) assert(hero is walking(0)==false) # get rid of the barrier npcs w destroy npc(n0) destroy npc(n1) destroy npc(n2) destroy npc(n3) w # Now test doors and teleporting when pathfinding resume caterpillar set hero speed(0, 4) pathfind hero to(0, 15, 5) wait for hero(0) assert(hero tile(0, 15, 5)) # path the hero into the door pathfind hero to(0, 17, 5) for(i, 1, 9) do( w assert(current map == 5) ) # In one more tick, we should be through the door w assert(current map == 7) assert(hero is walking(0)==false) assert(hero tile(0, 3, 4)) # walk through a door that leads to the current map pathfind hero to(0, 8, 2) wait for hero(0) assert(hero tile(0, 8, 2)) # ready start to go through the door pathfind hero to(0, 14, 2) assert(hero pixel x(0) == 8 * 20) assert(hero pixel y(0) == 2 * 20) w(4) # almost through the door assert(hero pixel x(0) == 8 * 20 + 16) assert(hero pixel y(0) == 2 * 20) w(1) # through the door, should keep walking since the dest is still on this map assert(hero pixel x(0) == 9 * 20) assert(hero pixel y(0) == 6 * 20) wait for hero(0) assert(hero tile(0, 14, 2)) assert(hero tile(1, 13, 2)) assert(hero tile(2, 13, 3)) assert(hero tile(3, 12, 3)) # now test teleporting while pathing pathfind hero to(0, 14, 8) #wait for one step w(5) assert(hero tile(0, 14, 3)) teleport to map(5, 4, 4) assert(hero is walking(0)==false) assert(hero tile(0, 4, 4)) # Test again, but partway through a step # go back to the other map teleport to map(7, 14, 2) w pathfind hero to(0, 14, 8) #wait for a fraction of a step w(2) assert(hero pixel y(0) == 2 * 20 + 8) # now teleport mid-step teleport to map(5, 4, 4) wait for hero(0) assert(hero pixel x(0) == 4 * 20) # Uh oh! the hero is misaligned with the grid # (which sucks, but is sort of expected, and is consistent with teleporting while doing a "walk hero") assert(hero pixel y(0) == 4 * 20 + 3 * 4) w # fix alignment put hero(0, 4 * 20, 5 * 20) w # Do it again with "walk hero" instead of "pathfind hero to" # go back to the other map teleport to map(7, 14, 2) w walk hero(0, south, 1) #wait for a fraction of a step w(2) assert(hero pixel y(0) == 2 * 20 + 8) # now teleport mid-step teleport to map(5, 4, 4) wait for hero(0) assert(hero pixel x(0) == 4 * 20) # Yep, hero is misaligned again, just like with "pathfind hero to" assert(hero pixel y(0) == 4 * 20 + 3 * 4) w # fix alignment again put hero(0, 4 * 20, 5 * 20) w # test that "wait for all" respects pathfinding suspend caterpillar pathfind hero to(2, 4, 3) pathfind hero to(3, 4, 2) n0 := create npc(3, 5, 2) n1 := create npc(3, 5, 4) set npc speed(3, 10) w pathfind hero to(0, 2, 6) pathfind hero to(1, 1, 6) pathfind hero to(2, 2, 7) pathfind hero to(3, 1, 7) pathfind npc to(n0, 7, 8) pathfind npc to(n1, 8, 8) wait for all assert(hero tile(0, 2, 6)) assert(hero tile(1, 1, 6)) assert(hero tile(2, 2, 7)) assert(hero tile(3, 1, 7)) assert(is at pos(n0, 7, 8)) assert(is at pos(n1, 8, 8)) # now a path the hero should finish first pathfind hero to(0, 2, 5) pathfind npc to(n1, 4, 4) wait for all assert(hero tile(0, 2, 5)) assert(is at pos(n1, 4, 4)) # now a path the npc should finish first pathfind hero to(0, 8, 8) pathfind npc to(n1, 5, 4) wait for all assert(hero tile(0, 8, 8)) assert(is at pos(n1, 5, 4)) resume caterpillar # clean up stop checkpoint timer show no value # Pathfinding to extra data set hero position(me, 12, 6) # Hero pos doesn't matter, just moving the camera variable(sl, result) sl := create map overlay container result := pathfind into extra as hero(sl, 13, 6, 14, 3) assert(result==true) visualize path from extra(sl) assert(get extra(sl, 0) == 13) assert(get extra(sl, 1) == 6) assert(get extra(sl, extra length(sl)--2) == 14) assert(get extra(sl, extra length(sl)--1) == 3) assert(extra length(sl) == 5 * 2) w # With maxsearch, give up at a range of 2 result := pathfind into extra as hero(sl, 13, 6, 14, 3, 2) assert(result==false) visualize path from extra(sl) assert(extra length(sl) == 2 * 2) w # To an unreachable spot result := pathfind into extra as hero(sl, 13, 6, 19, 0) assert(result==false) visualize path from extra(sl) w # Ignore the first step result := pathfind into extra as hero(sl, 13, 6, 14, 3, 0, false, true) assert(result==true) visualize path from extra(sl) assert(get extra(sl, extra length(sl)--2) == 14) assert(get extra(sl, extra length(sl)--1) == 3) assert(extra length(sl) == 4 * 2) w # Append a second path to the first result := pathfind into extra as hero(sl, 13, 6, 14, 3, 0, false, false) assert(result==true) result := pathfind into extra as hero(sl, 14, 3, 15, 2, 0, true, true) assert(result==true) visualize path from extra(sl) assert(extra length(sl) == 7 * 2) w # Append a first path while preserving default 3 extra values free slice(sl) sl := create map overlay container result := pathfind into extra as hero(sl, 13, 6, 14, 3, 0, true, false) assert(extra length(sl) == 3 + 5*2) assert(get extra(sl, 0) == 0) assert(get extra(sl, 1) == 0) assert(get extra(sl, 2) == 0) assert(get extra(sl, 3) == 13) assert(get extra(sl, 4) == 6) visualize path from extra(sl, 3) w # Test NPC pathing (current NPC positions don't matter really) n0 := create npc(0, 10, 6) # Cannot walk across grass n1 := create npc(1, 10, 8) # Can walk across grass # Across the grass result := pathfind into extra as npc(sl, n1, 12, 5, 14, 3) assert(result == true) assert(extra length(sl) == 5 * 2) assert(get extra(sl, 0) == 12) assert(get extra(sl, 1) == 5) assert(get extra(sl, 8) == 14) assert(get extra(sl, 9) == 3) visualize path from extra(sl) w # Around the grass result := pathfind into extra as npc(sl, n0, 12, 5, 14, 3) assert(result == true) assert(extra length(sl) == 9 * 2) assert(get extra(sl, 0) == 12) assert(get extra(sl, 1) == 5) assert(get extra(sl, extra length(sl)--2) == 14) assert(get extra(sl, extra length(sl)--1) == 3) visualize path from extra(sl) w # around the grass and the hero set hero position(me, 11, 3) result := pathfind into extra as npc(sl, n0, 12, 5, 14, 3) assert(extra length(sl) == 11 * 2) visualize path from extra(sl) w # Can't reach the middle of the grass result := pathfind into extra as npc(sl, n0, 12, 5, 13, 4) assert(result == false) assert(extra length(sl) == 1 * 2) visualize path from extra(sl) w # Zero-length path (start excluded) result := pathfind into extra as npc(sl, n0, 12, 5, 13, 4, 0, false, true) assert(result == false) assert(extra length(sl) == 0 * 2) visualize path from extra(sl) w # Append 3 waypoints (full circle) pathfind into extra as npc(sl, n0, 12, 5, 14, 3, 0, false, true) pathfind into extra as npc(sl, n0, 14, 3, 15, 7, 0, true, true) pathfind into extra as npc(sl, n0, 15, 7, 12, 5, 0, true, true) assert(extra length(sl) == 24 * 2) visualize path from extra(sl) w # Cleanup free slice(sl) teleport to map(oldmap, oldx, oldy) end script, create map overlay container, begin variable(sl) sl := create container(0, 0) set parent(sl, lookup slice(sl:map overlay)) fill parent(sl, true) exit(sl) end script, visualize path from extra, sl, offset=0, begin free slice children(sl) variable(i, x, y, box) for(i, 0, ((extra length(sl) -- offset) / 2) -- 1) do( x := get extra(sl, offset + i * 2) y := get extra(sl, offset + i * 2 + 1) box := create rect(18, 18) set parent(box, sl) set rect bgcol(box, 1) set rect opacity(box, 25) set slice x(box, x * 20 + 1) set slice y(box, y * 20 + 1) ) end script, is at pos, ref, x, y, begin exit returning(npc x (ref) == x && npc y (ref) == y) end script, dist from leader, ref, begin exit returning(dist from pos(ref, hero x(me), hero y(me))) end script, dist from pos, ref, x, y, begin variable(dx, dy) dx := abs(npc x(ref) -- x) dy := abs(npc y(ref) -- y) exit returning(dx + dy) end ######################################################################## script, vehicle tests, begin $0="Vehicle tests" variable(oldx, oldy, oldmap) oldx := herox(me) oldy := heroy(me) oldmap := current map teleport to map(map:Vehicle Test Map, 4, 4) w assert(current vehicle id == -1) assert(current vehicle npc == false) # First test mount with usenpc. Does not need to be adjacent, but does check mount passability rules assert(check tag(tag:boat tag) == false) assert(hero x(me) == 4) assert(hero y(me) == 4) w # This should fail to mount because we are not on a "B" tile use npc(1) assert(current vehicle npc == false) w(20) assert(check tag(tag:boat tag) == false) assert(hero x(me) == 4) assert(hero y(me) == 4) walk hero(me, left, 2) wait for hero(me), w assert(hero x(me) == 2) assert(hero y(me) == 4) # This should work because although we are not adjacent, we are standing on a B tile use npc(1) assert(current vehicle npc == NPC reference(1)) assert(current vehicle id == 0) w(20) assert(check tag(tag:boat tag) == true) assert(hero x(me) == 2) assert(hero y(me) == 2) # Now move the hero, and the vehicle npc should move also walk hero(me, left, 2) wait for hero(me), w walk hero(me, up, 2) wait for hero(me), w assert(hero x(me) == 0) assert(hero y(me) == 0) assert(npc x(1) == 0) assert(npc y(1) == 0) # Moving the NPC while it is being used as a vehicle should not move it # (although the sprite will be shifted slightly as it tries to move) walk npc(1, right, 5) wait for npc(1), w assert(npc x(1) == 0) assert(npc y(1) == 0) # Move back to the shore walk hero(me, right, 3) wait for hero(me), w walk hero(me, down, 2) wait for hero(me), w show string(string sprintf(1, $2="x=%d y=%d", hero x(me), hero y(me))) assert(hero x(me) == 3) assert(hero y(me) == 1) set hero direction(me, right), w # Dismount into the ocean. should not step forward assert(current vehicle npc) dismount vehicle assert(not(current vehicle npc)) w(20) assert(hero x(me) == 3) assert(hero y(me) == 1) assert(check tag(tag:boat tag) == false) # Mounting from here should fail use npc(1) assert(not(current vehicle npc)) w(20) assert(hero x(me) == 3) assert(hero y(me) == 1) assert(check tag(tag:boat tag) == false) # Force mounting, which ignores checking the tile we are standing on force mount vehicle(1) assert(current vehicle npc) w(20) assert(hero x(me) == 3) assert(hero y(me) == 1) assert(check tag(tag:boat tag) == true) walk hero(me, right, 1) wait for hero(me), w assert(hero x(me) == 4) assert(hero y(me) == 1) assert(npc x(1) == 4) assert(npc y(1) == 1) # Dismount, does not step ahead (which sucks, and was a mistake, but is rather too old to fix cleanly) dismount vehicle, w(20) assert(not(current vehicle npc)) assert(check tag(tag:boat tag) == false) assert(hero x(me) == 4) assert(hero y(me) == 1) assert(npc x(1) == 4) assert(npc y(1) == 1) w set hero position(me, 4, 2) variable(n) n := create global npc(3, 2, 5) hero chases npc(me, n, true, 10) wait for hero(me) set hero direction(me, left) use npc(n) w(20) walk hero(me, right, 5) w wait for hero(me) gracefully dismount vehicle w(20) walk hero(me, right, 1) w wait for hero(me) walk hero(me, south, 4) w teleport to map(oldmap, oldx, oldy) end ######################################################################## script, door tests, begin $0="Door tests" # preconditions assert(herox(me) == 6) assert(heroy(me) == 6) # querying doors assert(get door x(0) == 8) assert(get door y(0) == 5) assert(door at spot(8, 5) == 0) assert(door at spot(0, 0) == -1) # lots of doors at 0,0 with exists=NO assert(door at spot(1, 1) == 1) # door 1 has no enabled doorlinks assert(get door destination id(0) == 1) assert(get door destination map(0) == 4) assert(door exists(0)) assert(get door destination id(1) == -1) assert(get door destination map(1) == -1) assert(door exists(1)) assert(door exists(10) == false) # using doors walk hero to x(0, 8) wait for hero walk hero to y(0, 5) wait for hero assert(current map == map:door test map) assert(hero x(0) == 4) assert(hero y(0) == 4) walk hero(0, south, 1) wait for hero use door(1) # shouldn't work, disabled by tag assert(hero x(0) == 4) # not triggered walk hero(0, north, 1) wait for hero assert(hero x(0) == 4) # not triggered set tag(tag:door req tag, on) wait assert(hero x(0) == 4) # not triggered by enabling a door underneath you suspend doors walk hero(0, south, 1) wait for hero walk hero(0, north, 1) wait for hero assert(hero x(0) == 4) # not triggered resume doors wait assert(hero x(0) == 4) # door underneath hero not triggered walk hero(0, south, 1) wait for hero walk hero(0, north, 1) wait for hero assert(hero x(0) == 11) # triggered assert(hero y(0) == 4) show string($99="screen should be faded out") use door(0) # door underneath hero _checkpoint assert(current map == map:test town) show string($99="screen should not be faded out") use door(4, false) _checkpoint show no value assert(hero x(0) == 2) assert(hero y(0) == 6) walk hero to x(0, 6) wait for hero # querying doors on other maps assert(current map == map:test town) assert(door exists(0, map:Door test map)) assert(not(door exists(2, map:Door test map))) assert(get door x(0, map:Door test map) == 11) assert(get door y(0, map:Door test map) == 4) assert(check tag(7) == on) assert(get door destination id(0, map:Door test map) == 0) assert(get door destination map(0, map:Door test map) == map:Test Town) assert(get door destination id(1, map:Door test map) == 0) assert(get door destination map(1, map:Door test map) == map:Door test map) set tag(7, off) assert(get door destination id(1, map:Door test map) == -1) assert(get door destination map(1, map:Door test map) == -1) set tag(7, on) assert(get door destination id(2, map:Door test map) == -1) assert(get door destination map(2, map:Door test map) == -1) end ######################################################################## # Not defined as a plotscript on purpose script, each-step script, x, y, dir, begin $0="each-step script", trace assert(x == hero x) assert(y == hero y) assert(dir == hero direction) # First to run assert(npc script triggered == 0) assert(map autorun triggered == 0) eachstep triggered += 1 end plotscript, npc triggered script, arg, npc, begin $0="npc triggered script", trace assert(arg == -1) assert(npc == npc reference(6)) # Second to run assert(eachstep triggered == 1) assert(map autorun triggered == 0) npc script triggered += 1 # Should happen immediately (also tested in textbox tests) # and trigger two scripts advance textbox # Fifth to run assert(current textbox == -1) assert(textbox script triggered == 1) assert(map autorun triggered == 1) npc script triggered += 1 end plotscript, textbox advance script, begin $0="textbox advance script", trace assert(current map == 4) assert(hero direction == left) # These should be the correct position on the new map assert(hero x == 11) assert(hero y == 4) # Third to run assert(current textbox == -1) assert(npc script triggered == 1) assert(eachstep triggered == 1) assert(map autorun triggered == 0) textbox script triggered += 1 end # Set on map 4 plotscript, map 4 autorun, arg, begin if (testing script triggers == false) then (exit script) $0="map 4 autorun", trace assert(arg == 42) assert(current map == 4) # Fourth to run assert(textbox script triggered == 1) map autorun triggered += 1 # also set in "map autorun" end script, concurrent script trigger tests, begin $0="concurrent script trigger tests" # Test multiple triggers happening the same tick, and that they all happen in the right order. # This tests both scripts triggered inside and outside the interpreter # Take a step, triggering each-step script and stepping on an NPC, which runs a script, # and opens a textbox, which is advanced, running a script and activating a door which # goes to a map with an autorun script # Preconditions assert(current map == 0) set hero position(0, 6, 6) npc script triggered := 0 map autorun triggered := 0 set eachstep script (@each-step script) testing script triggers := true tick counter slice := create container set slice velocity x(tick counter slice, 1, 1000) assert(ticknumber == 0) assert(get hero speed(me) == 4) create npc(6, hero x -- 1, hero y) walk hero(me, left, 1) wait for hero #Events happen in this order, and scripts should run in reverse #order of triggering: # (step completes) # (npc triggered, opens textbox) # npc script triggered # each step triggered # (interpreter entered) # (npc script calls advancetextbox) # (textbox loaded) # (preparemap called) # autorun triggered # after textbox script triggered # (this script resumes, waitfornpc finishing) #So scripts run in order (all in the same tick): # | each step # | npc # | | autorun # | | textbox # | npc finishes # this script # Sixth to run assert(npc script triggered == 2) assert(textbox script triggered == 1) assert(map autorun triggered == 1) assert(ticknumber == 5) # reset testing script triggers := false free slice(tick counter slice) teleport to map (0, 6, 6) assert(current map == 0) # Incremented by the other map autorun script assert(map autorun triggered == 2) end ######################################################################## script, another eachstep script, begin eachstep triggered += 1 wait for hero end script, yet another eachstep script, begin eachstep triggered += 1 subscript, waiting, begin wait for hero end waiting end script, script double trigger tests, begin $0="script trigger tests" # precondition assert(hero x == 6 && hero y == 6) # Test double triggering of scripts set eachstep script(@another eachstep script) variable (oldval) oldval := read general(101) # genBits assert((oldval, and, 2^10) == 0) # "permit double triggering scripts" is off eachstep triggered := 0 walk hero(me, left, 3) wait for hero # eachstep should only trigger once assert(eachstep triggered == 1) write general(101, oldval + 2^10) # turn on "permit double triggering scripts" eachstep triggered := 0 walk hero(me, right, 3) wait for hero assert(eachstep triggered == 3) set eachstep script(@yet another eachstep script) eachstep triggered := 0 walk hero(me, left, 3) wait for hero assert(eachstep triggered == 3) write general(101, oldval) # turn off "permit double triggering scripts" eachstep triggered := 0 walk hero(me, right, 3) wait for hero # eachstep should should trigger three times because the "waiting" # subscript foils the double trigger check assert(eachstep triggered == 3) set eachstep script(0) assert(hero x == 6 && hero y == 6) end script, script trigger tests, begin concurrent script trigger tests script double trigger tests end ######################################################################## script, slice tests, begin $0="Slice tests" # Highly unfinished! slice extra tests slice collection tests slice tree tests slice search tests slice collision tests slice correspondence tests slice blending tests slice sort tests slice clone tests slice handle tests rect slice tests text slice tests panel slice tests ellipse slice tests end ######################################################################## script, slice handle tests, begin $0="Slice handle tests" # slice is valid variable (sl) assert(slice is valid(0) == false) sl := create container assert(slice is valid(sl)) free slice(sl) assert(slice is valid(sl) == false) assert(slice is valid(sprite layer)) assert(slice is valid(-1) == false) assert(slice is valid(4242) == false) # Check that slice handles aren't reused quickly # (The actual minimum number of frees before a handle is reused # is currently 256*4000 = 1,024,000) variable(i, sl2) sl := create container free slice(sl) for (i, 0, 5100) do ( sl2 := create container assert(sl2 <> sl) free slice(sl2) ) end ######################################################################## script, rect slice tests, begin $0="Rect Slice tests" variable (sl) # Test rect transparency sl := create rect(100, 100), w assert(slice is rect(sl)) assert(slice type(sl) == slicetype:rect) assert(get rect trans(sl) == trans:solid) assert(get rect fuzziness(sl) == 100) set rect trans(sl, trans:hollow), w assert(get rect trans(sl) == trans:hollow) assert(get rect fuzziness(sl) == 0) set rect fuzziness(sl, 25), w assert(get rect trans(sl) == trans:fuzzy) assert(get rect fuzziness(sl) == 25) assert(get rect opacity(sl) == 25) # It's an alias set rect fuzziness(sl, 0), w assert(get rect trans(sl) == trans:hollow) assert(get rect fuzziness(sl) == 0) set rect fuzziness(sl, 100), w assert(get rect trans(sl) == trans:solid) assert(get rect fuzziness(sl) == 100) set rect opacity(sl, 70), w assert(get rect trans(sl) == trans:blend) assert(get rect opacity(sl) == 70) assert(get rect fuzziness(sl) == 70) # Alias set rect trans(sl, trans:solid) assert(get rect opacity(sl) == 100) set rect trans(sl, trans:hollow) assert(get rect opacity(sl) == 0) set rect trans(sl, trans:fuzzy), w assert(get rect trans(sl) == trans:fuzzy) # Using setrecttrans preserves previous opacity to 70% assert(get rect fuzziness(sl) == 70) set rect trans(sl, trans:blend), w assert(get rect opacity(sl) == 70) assert(get rect fuzzy zoom(sl) == 1) # Default value set rect fuzzy zoom(sl, 1000) assert(get rect fuzzy zoom(sl) == 1000) assert(get rect stationary pattern(sl) == false) # Default value set rect stationary pattern(sl, true) assert(get rect stationary pattern(sl) == true) free slice(sl) # Test rect box border and raw box border sl := create rect(99, 99), w set slice x(sl, 10) set slice y(sl, 10) assert(get rect style(sl) == 0) assert(get rect border(sl) == 0) assert(get rect raw border(sl) == border:line) set rect raw border(sl, 0), w assert(get rect style(sl) == -1) assert(get rect border(sl) == border:raw) assert(get rect raw border(sl) == 0) set rect border(sl, border:none), w assert(get rect border(sl) == border:none) assert(get rect raw border(sl) == border:none) set rect border(sl, border:line), w assert(get rect border(sl) == border:line) assert(get rect raw border(sl) == border:line) set rect raw border(sl, 1), w assert(get rect border(sl) == border:raw) assert(get rect raw border(sl) == 1) set rect border(sl, 13), w assert(get rect border(sl) == 13) assert(get rect raw border(sl) == 1) set rect raw border(sl, border:none), w assert(get rect border(sl) == border:none) assert(get rect raw border(sl) == border:none) set rect raw border(sl, border:line), w assert(get rect border(sl) == border:line) assert(get rect raw border(sl) == border:line) free slice(sl) # Test rect style sl := create rect(120, 120, 14), w assert(get rect style(sl) == 14) # Testing that the style is as in the box style editor assert(get rect border(sl) == 14) assert(get rect raw border(sl) == 1) assert(get rect fg col(sl) == 252) assert(get rect bg col(sl) == 65) set rect fg col(sl, 42), w assert(get rect style(sl) == -1) set rect style(sl, 0), w assert(get rect style(sl) == 0) assert(get rect border(sl) == 0) assert(get rect raw border(sl) == border:line) assert(get rect fg col(sl) == 249) assert(get rect bg col(sl) == 33) free slice(sl) end ######################################################################## script, text slice tests, begin $0="Text Slice tests" # Check initial state as expected variable(sl) sl := create text assert(slice is text(sl)) assert(slice type(sl) == slicetype:text) get slice text(2, sl) assert(string length(2) == 0) assert(get wrap(sl) == false) assert(get outline(sl) == false) assert(get text color(sl) == 0) # which means uiText assert(get text bg(sl) == 0) $2="fee Fi\n \n foe" set slice text(sl, 2) get slice text(3, sl) assert(string compare(2, 3)) # Non-wrapping slices have fixed size tracevalue(slice width(sl),slice height(sl)) assert(slice width(sl) == 48) assert(slice height(sl) == 30) # 3 lines #set slice width(sl, 10) # throws an error #set slice height(sl, 10) # throws an error assert(slice width(sl) == 48) assert(slice height(sl) == 30) # 3 lines # Wrapping slices recompute height based on width set wrap(sl, true) assert(get wrap(sl) == true) assert(slice width(sl) == 48) # shouldn't have changed set slice width(sl, 35) set wrap(sl, true) # Modifying text slice data calls UpdateTextSlice (wait also would) assert(slice width(sl) == 35) assert(slice height(sl) == 40) set slice height(sl, 10) # Doesn't show an error but will be overridden when updated set wrap(sl, true) # Modifying text slice data calls UpdateTextSlice (wait also would) assert(slice height(sl) == 40) set wrap(sl, false) assert(slice width(sl) == 48) # Recomputes immediately # TODO: test recomputing of embed codes $2="" $3="" free slice(sl) end ######################################################################## script, panel slice tests, begin $0="Panel Slice tests" variable(sl) sl := create panel assert(slice is panel(sl)) assert(slice type(sl) == slicetype:panel) # Check initial state as expected assert(get panel primary index(sl) == 0) assert(get panel percent as int(sl) == 50) assert(get panel padding(sl) == 0) assert(get panel is vertical(sl) == false) assert(get panel pixels(sl) == 0) set panel primary index(sl, 1) assert(get panel primary index(sl) == 1) set panel percent(sl, 40) assert(get panel percent as int(sl) == 40) set panel padding(sl, 4) assert(get panel padding(sl) == 4) set panel pixels(sl, 5) assert(get panel pixels(sl) == 5) set panel is vertical(sl, true) assert(get panel is vertical(sl) == true) # TODO: check children are sized correctly free slice(sl) end ######################################################################## script, ellipse slice tests, begin $0="Ellipse Slice tests" variable(sl) sl := create ellipse(10, 10, 15, 0), w assert(slice is ellipse(sl)) assert(get ellipse border col(sl) == 15) set ellipse border col(sl, 14), w assert(get ellipse border col(sl) == 14) assert(get ellipse fill col(sl) == 0) set ellipse fill col(sl, 1), w assert(get ellipse fill col(sl) == 1) set slice x(sl, 20), w set slice y(sl, 20), w set slice width(sl, 30), w set slice height(sl, 30), w free slice(sl) end ######################################################################## script, slice clone tests, begin # Test cloneslice variable(sl, sl2, ch, ch2) sl := get npc slice(next npc reference) set slice extra(sl, extra 2, 42) sl2 := clone slice(sl) assert(next sibling(sl2) == sl) # Check that things got copied assert(slice screen x(sl) == slice screen x(sl2)) assert(slice screen y(sl) == slice screen y(sl2)) assert(get slice extra(sl2, extra 2) == 42) assert(child count(sl) == child count(sl2)) ch := first child(sl) ch2 := first child(sl2) assert(get slice lookup(ch) < 0) assert(get slice lookup(ch2) == 0) # special lookup codes removed free slice(sl2) sl := sprite layer assert(slice type(sl) == slicetype:special) sl2 := clone slice(sl) assert(slice is container(sl2)) # Special slices turn into containers assert(previous sibling(sl) == sl2) assert(child count(sl) == child count(sl2)) free slice(sl2) sl2 := clone slice(sl, false) # recurse == false assert(child count(sl2) == 0) # Didn't recurse free slice(sl2) # More detailed checks that extra arrays are copied sl := create container sl2 := clone slice(sl) # Copy a default NULL array assert(extra length(sl2) == 3) free slice(sl2) resize extra(sl, 0) # Copy a length 0 array sl2 := clone slice(sl) assert(extra length(sl2) == 0) free slice(sl2) resize extra(sl, 50000) set slice extra(sl, -1, 12345) sl2 := clone slice(sl) # Copy a large array assert(extra length(sl2) == 50000) assert(get slice extra(sl2, 49999) == 12345) # Check the array, not its pointer, is copied set slice extra(sl2, 49998, 16) assert(get slice extra(sl2, 49998) == 16) assert(get slice extra(sl, 49998) == 0) free slice(sl2) free slice(sl) end ######################################################################## # Example from plotdict script, remove extra, obj, value, begin variable (idx) while (true) do ( idx := find extra (obj, value) if (idx == -1) then (exit) # All deleted delete extra (obj, idx) # Normally we'd do "idx += 1" to look for the next occurrence of 'value' # after idx, but because we deleted it we don't need to. ) end ######################################################################## # The following sets of testcases for slice & generic extra data commands # were accidentally duplicated. Most also get tested in many other places. script, slice extra tests, begin variable(sl) sl := create line assert(extra length(sl) == 3) set slice extra(sl, 0, 100) set slice extra(sl, 2, 102) assert(get slice extra(sl, 2) == 102) assert(get slice extra(sl, -1) == 102) assert(get slice extra(sl, -3) == 100) assert(get extra(sl, 2) == 102) assert(get extra(sl, -1) == 102) assert(get extra(sl, -3) == 100) set extra(sl, 1, 4242) assert(get slice extra(sl, 1) == 4242) assert(get extra(sl, -2) == 4242) resize extra(sl, 1) assert(extra length(sl) == 1) assert(get slice extra(sl, -1) == 100) resize extra(sl, 3) assert(get slice extra(sl, -1) == 0) append extra(sl, 103) assert(extra length(sl) == 4) assert(get slice extra(sl, 3) == 103) resize extra(sl, 999999) set slice extra(sl, 999998, 998) assert(get slice extra(sl, -1) == 998) append extra(sl, 999) set slice extra(sl, 999999, 999) assert(get slice extra(sl, -1000000) == 100) free slice(sl) # Also, loading slice extra data is tested in "slice collection tests" # And clone extra data is tested in "slice clone tests" end script, extra array tests, begin $0="extra array tests" variable(xt) show string($1="Make a testing slice and use its extra data") xt := create container(0,0) show string($1="Test the 3 default elements") assert(extra length(xt)==3) assert(get extra(xt,0)==0) assert(get extra(xt,1)==0) assert(get extra(xt,2)==0) show string($1="Test setting data") set extra(xt, 0, 100) set extra(xt, 1, 101) set extra(xt, 2, 102) assert(get extra(xt,0)==100) assert(get extra(xt,1)==101) assert(get extra(xt,2)==102) show string($1="Test end-based indexing") assert(get extra(xt,-1)==102) assert(get extra(xt,-2)==101) assert(get extra(xt,-3)==100) show string($1="Test appending") assert(append extra(xt, 999)==4) assert(get extra(xt,3)==999) show string($1="Test resizing") resize extra(xt,6) assert(extra length(xt)==6) assert(get extra(xt,0)==100) assert(get extra(xt,3)==999) assert(get extra(xt,4)==0) assert(get extra(xt,5)==0) resize extra(xt,3) assert(extra length(xt)==3) assert(get extra(xt,-1)==102) resize extra(xt,0) assert(extra length(xt)==0) resize extra(xt,2) assert(get extra(xt,0)==0) assert(get extra(xt,1)==0) show string($1="Test extra insertion") set extra(xt,0,100) set extra(xt,1,200) assert(extra length(xt)==2) insert extra(xt,1,111) assert(extra length(xt)==3) assert(get extra(xt,0)==100) assert(get extra(xt,1)==111) assert(get extra(xt,2)==200) insert extra(xt,0,99) assert(get extra(xt,0)==99) assert(get extra(xt,1)==100) assert(get extra(xt,2)==111) assert(get extra(xt,3)==200) show string($1="Test extra insertion from negative index") insert extra(xt,-1,222) assert(get extra(xt,0)==99) assert(get extra(xt,1)==100) assert(get extra(xt,2)==111) assert(get extra(xt,3)==222) assert(get extra(xt,4)==200) append extra(xt,100) show string($1="Test find extra") assert(find extra(xt,1)==-1) assert(find extra(xt,111)==2) assert(find extra(xt,111,2)==2) assert(find extra(xt,111,3)==-1) assert(find extra(xt,100)==1) assert(find extra(xt,100,2)==5) assert(find extra(xt,100,-1)==5) assert(find extra(xt,100,-4)==5) assert(find extra(xt,100,-5)==1) assert(find extra(xt,100,-40)==1) assert(find extra(xt,100,99)==-1) show string($1="Test deletion") assert(delete extra(xt,3)==222) assert(delete extra(xt,1)==100) assert(delete extra(xt,-1)==100) assert(get extra(xt,0)==99) assert(get extra(xt,1)==111) assert(get extra(xt,2)==200) assert(extra length(xt)==3) show string($1="Test delete range") append extra(xt,333) append extra(xt,444) assert(extra length(xt)==5) assert(delete extra range(xt, 1, 4) == 111) assert(extra length(xt)==2) assert(get extra(xt,0)==99) assert(get extra(xt,1)==444) show string($1="Delete empty range (nothing happens)") delete extra range(xt, 1, 1) assert(extra length(xt)==2) assert(get extra(xt,0)==99) assert(get extra(xt,1)==444) delete extra range(xt, 0, 0) assert(extra length(xt)==2) delete extra range(xt, 2, 2) # [length, length) assert(extra length(xt)==2) delete extra range(xt, 3, 3) # out of bounds is OK assert(extra length(xt)==2) assert(get extra(xt,0)==99) assert(get extra(xt,1)==444) show string($1="Delete range from end") append extra(xt,555) append extra(xt,666) assert(extra length(xt)==4) assert(delete extra range(xt, -3, -1) == 444) assert(extra length(xt)==2) assert(get extra(xt,0)==99) assert(get extra(xt,1)==666) show string($1="Delete range up to end") assert(delete extra range(xt, 1, 2) == 666) assert(extra length(xt)==1) assert(get extra(xt,0)==99) show string($1="Test remove extra") append extra(xt, 100) append extra(xt, 99) remove extra(xt, 100) assert(extra length(xt)==2) assert(get extra(xt,0)==99) assert(get extra(xt,1)==99) remove extra(xt, 99) assert(extra length(xt)==0) show no value free slice(xt) end ######################################################################## # Tests loading slices from file. # A lot more could be added to this script, slice collection tests, begin $0="slice collection tests" variable(sl, sl2) # This is a slice collection with the root slice edited sl := load slice collection(0) w assert(sl) assert(parent slice(sl) == sprite layer) # default parent # Test "check parentage" assert(check parentage(sl, sprite layer) == true) assert(check parentage(sl, sl) == false) # Defined behaviour assert(check parentage(sprite layer, sl) == false) assert(check parentage(sl, lookup slice(sl:root)) == true) sl2 := create rect assert(check parentage(sl, sl2) == false) assert(check parentage(sl2, sl) == false) free slice(sl2) assert(slice is rect(sl) == true) assert(is filling parent(sl) == false) assert(slice x(sl) == -10) assert(slice y(sl) == -20) assert(slice width(sl) == 100) assert(slice height(sl) == 50) assert(get slice visible(sl) == true) assert(extra length(sl) == 3) assert(get slice extra(sl, 0) == 10) assert(get slice extra(sl, 1) == 20) assert(get slice extra(sl, 2) == 30) assert(get top padding(sl) == 1) assert(get right padding(sl) == 2) assert(get bottom padding(sl) == 3) assert(get left padding(sl) == 4) assert(get slice clipping(sl) == true) assert(get slice lookup(sl) == sli:my root slice) assert(get rect fuzziness(sl) == 48) assert(get rect fg col(sl) == 101) assert(get rect bg col(sl) == 71) assert(get rect style(sl) == -1) assert(get horiz align(sl) == edge:right) assert(get vert align(sl) == edge:middle) assert(get horiz anchor(sl) == edge:right) assert(get vert anchor(sl) == edge:top) assert(slice screen x(sl) == 310) # position of the anchor point assert(slice screen y(sl) == 80) sl2 := lookup slice(sli:some lookup) assert(sl2) assert(parent slice(sl2) == sl) assert(slice is sprite(sl2) == true) assert(get sprite type(sl2) == spritetype:walkabout) assert(get spriteset number(sl2) == 6) assert(get sprite palette(sl2) == 1) assert(get sprite frame(sl2) == 4) assert(get sprite trans(sl2) == false) assert(sprite is horiz flipped(sl2) == true) assert(sprite is vert flipped(sl2) == false) assert(sprite is dissolving(sl2) == false) assert(slice width(sl2) == 20) assert(slice height(sl2) == 20) assert(slice x(sl2) == 10) assert(slice y(sl2) == -4) assert(slice screen x(sl2) == 224) assert(slice screen y(sl2) == 77) sl2 := lookup slice(sli:no extras) assert(extra length(sl2) == 0) sl2 := lookup slice(sli:default extras) assert(extra length(sl2) == 3) assert(get slice extra(sl2, 0) == 0) assert(get slice extra(sl2, 1) == 0) assert(get slice extra(sl2, 2) == 0) sl2 := lookup slice(sli:one extra) # Check the length is saved although no non-default values assert(extra length(sl2) == 1) assert(get slice extra(sl2, 0) == 0) sl2 := lookup slice(sli:lotsa extras) assert(extra length(sl2) == 10000) # This tests the encoding of runs of identical values as "repeat" nodes # A "repeat" before any "int" defaults to 0 assert(get slice extra(sl2, 0) == 0) assert(get slice extra(sl2, 2) == 0) assert(get slice extra(sl2, 3) == -98) assert(get slice extra(sl2, 4) == -98) assert(get slice extra(sl2, 5) == -98) assert(get slice extra(sl2, 6) == 0) assert(get slice extra(sl2, 9997) == 0) # A run which goes to the end of the array is a special case assert(get slice extra(sl2, 9998) == -99) assert(get slice extra(sl2, 9999) == -99) free slice(sl) # A simpler collection sl := load slice collection(1) assert(sl) assert(get slice visible(sl) == false) # edited assert(is filling parent(sl) == true) # the default free slice(sl) # Test loading slice extra from obsolete file format sl := load slice collection(1300) assert(extra length(sl) == 3) assert(get slice extra(sl, 0) == 0) assert(get slice extra(sl, 1) == 0) assert(get slice extra(sl, 2) == 0) sl2 := first child(sl) assert(extra length(sl) == 3) assert(get slice extra(sl2, 0) == 10) assert(get slice extra(sl2, 1) == 20) assert(get slice extra(sl2, 2) == 30) free slice(sl) # Loading a missing collection in range 0-32767 returns 0 without error sl := load slice collection(32700) assert(sl == 0) end ######################################################################## script, slice search tests, begin $0="Slice search tests" variable(sl, sl1, sl11, sl111, sl1111, sl1112, sl2) # Build a tree. X1 is the first child of X, X2 is the second child sl1 := create container sl11 := create container, set parent(sl11, sl1) sl111 := create container, set parent(sl111, sl11) set slice lookup(sl111, 14) sl1111 := create container, set parent(sl1111, sl111) sl1112 := create container, set parent(sl1112, sl111) set slice lookup(sl1112, 14) sl2 := create container set slice lookup(sl2, 14) # next slice in tree sl := 0 sl := next slice in tree(sl, sl1), assert(sl == sl1) sl := next slice in tree(sl, sl1), assert(sl == sl11) assert(next slice in tree(sl, sl1, false) == 0) # sl11 has no sibling sl := next slice in tree(sl, sl1), assert(sl == sl111) sl := next slice in tree(sl, sl1), assert(sl == sl1111) assert(next slice in tree(sl, sl1, false) == sl1112) sl := next slice in tree(sl, sl1), assert(sl == sl1112) assert(next slice in tree(sl, sl1, false) == 0) sl := next slice in tree(sl, sl1), assert(sl == 0) # Iterate whole tree sl := next slice in tree(sl1112), assert(sl == sl2) # Starting at the root slice sl := next slice in tree(0, 0), assert(sl == lookup slice(sl:root)) sl := next slice in tree(sl, 0, false), assert(sl == 0) # lookup next slice assert(lookup next slice(sl:walkabout sprite component) == lookup slice(sl:walkabout sprite component)) sl := 0 assert(lookup slice(14, sl1) == sl111) sl := lookup next slice(14, sl, sl1), assert(sl == sl111) sl := lookup next slice(14, sl, sl1), assert(sl == sl1112) sl := lookup next slice(14, sl, sl1), assert(sl == 0) sl := lookup next slice(14, sl1112), assert(sl == sl2) sl := lookup next slice(14, sl1111, sl1), assert(sl == sl1112) # Starting from a slice without lookup 14 sl := 0 while (true) do ( sl := lookup next slice(sl:walkabout sprite component, sl) if (sl == 0) then (break) assert(get sprite type(sl) == spritetype:walkabout) ) free slice(sl1) free slice(sl2) end ######################################################################## # Unfinished tests of findcollidingslice and sliceatpixel script, slice collision tests, begin $0="Slice collision tests" variable(parent, sl1, sl2, sl3, sl4, sl5, temp) parent := create select # f put slice (parent, 100, 200) sl1 := create container(0, 0) set parent(sl1, parent) assert(slice at pixel(parent, 0, 0) == 0) # can't be hit assert(slice at pixel(parent, 0, 0, get count) == 0) sl2 := create container(10, 10) set parent(sl2, parent) sl3 := create container(1, 1) set parent(sl3, parent) put slice(sl3, 4, 5) sl4 := create container(1, 1) set parent(sl4, sl3) wait(1) # update select visibility ### Testing slice at pixel # Unfinished: no testing of slices with negative width/height. # Only sl1 should be visible... assert(get slice visible(sl1) == true) assert(get slice visible(sl2) == false) assert(get slice visible(sl3) == false) assert(get slice visible(sl4) == true) assert(slice at pixel(parent, 100, 200) == sl2) # topleft corner assert(slice at pixel(parent, 105, 205) == sl2) # inside assert(slice at pixel(parent, 100, 210) == 0) # on the edge of sl2 assert(slice at pixel(parent, 110, 200) == 0) # on the edge of sl2 assert(slice at pixel(parent, 100, 200, get count) == 1) # sliceatpixel and findcollidingslice must return slices from bottommost to topmost assert(slice at pixel(parent, 104, 205, get count) == 3) assert(slice at pixel(parent, 104, 205, 0) == sl2) assert(slice at pixel(parent, 104, 205, 1) == sl3) assert(slice at pixel(parent, 104, 205, 2) == sl4) assert(slice at pixel(parent, 104, 205, 3) == 0) # now without descending: assert(slice at pixel(parent, 104, 205, get count, false) == 2) assert(slice at pixel(parent, 104, 205, 0, false) == sl2) assert(slice at pixel(parent, 104, 205, 1, false) == sl3) assert(slice at pixel(parent, 104, 205, 2, false) == 0) # test visibleonly assert(slice at pixel(parent, 104, 205, get count, true, true) == 0) # visibility of the 'parent' slice doesn't affect anything (sl3 is not visible) assert(slice at pixel(sl3, 104, 205, get count, true, true) == 1) # change visible slice to sl3 set select slice index(parent, 2) wait(1) # update select visibility assert(get slice visible(sl3) == true) # now sl3 and sl4 should be hit assert(slice at pixel(parent, 104, 205, get count, true, true) == 2) assert(slice at pixel(parent, 104, 205, 0, true, true) == sl3) assert(slice at pixel(parent, 104, 205, 1, true, true) == sl4) # ##Testing find colliding slice # Unfinished: don't yet check all the cases of an AABB collision test. # (Easier to test manually with collisiontest.rpg) # Also, no testing of slices with negative width/height. sl5 := load walkabout sprite(0) assert(slice height(sl5) == 20) put slice(sl5, 100, 190) # sliceatpixel and findcollidingslice must return slices from bottommost to topmost #tracevalue(parent, sl1, sl2, sl3, sl4, sl5) assert(find colliding slice(parent, sl5, get count) == 3) # Don't find sl1 which is zero size (FIXME: which I think probably isn't correct, since "slice contains" would say yes) #assert(find colliding slice(parent, sl5, 0) == sl1) assert(find colliding slice(parent, sl5, 0) == sl2) assert(find colliding slice(parent, sl5, 1) == sl3) assert(find colliding slice(parent, sl5, 2) == sl4) assert(find colliding slice(parent, sl5, 3) == 0) # Test non-overlap of adjacent slices set parent(sl5, parent) put slice(sl5, 0, 5 -- 20) # positioned so sl3 on bottom edge # Create another couple slices adjacent to sl5 on the top and left temp := create container(1, 1) set parent(temp, parent) put slice(temp, -1, 0) # 1 left of sl5 temp := create container(1, 1) set parent(temp, parent) put slice(temp, 0, 5 -- 20 -- 1) # 1 up from sl5 assert(find colliding slice(parent, sl5, 0) == sl2) # doesn't find sl1, zero size assert(find colliding slice(parent, sl5, 1) == 0) # should not find sl3, nor sl5, nor the temp slices # Now overlap by one put slice(sl5, 0, 5 -- 20 + 1) # now sl3 overlaps assert(find colliding slice(parent, sl5, 0) == sl2) # doesn't find sl1, zero size assert(find colliding slice(parent, sl5, 1) == sl3) assert(find colliding slice(parent, sl5, 2) == sl4) assert(find colliding slice(parent, sl5, 3) == 0) # should not find sl5 # try using a 0x0 slice to search for overlaps - should find nothing. # (FIXME: This is probably not correct; as an infinitesimal point it overlaps sl2 and sl5. assert(find colliding slice(parent, sl1, 0) == 0) # Test no descending # Don't find sl1 which is zero size (FIXME: which I think probably isn't correct, since "slice contains" would say yes) assert(find colliding slice(parent, sl5, 0, false) == sl2) assert(find colliding slice(parent, sl5, 1, false) == sl3) assert(find colliding slice(parent, sl5, 2, false) == 0) # shouldn't find sl4 assert(find colliding slice(parent, sl5, get count, false) == 2) # Test visible only (only sl3 is visible) assert(find colliding slice(parent, sl5, get count, true, true) == 2) assert(find colliding slice(parent, sl5, 0, true, true) == sl3) assert(find colliding slice(parent, sl5, 1, true, true) == sl4) assert(find colliding slice(parent, sl5, 2, true, true) == 0) # Do two 0x0 slices at the same point overlap? (they shouldn't) free slice children(parent) temp := create container(0, 0) set parent(temp, parent) temp := create container(0, 0) set parent(temp, parent) assert(find colliding slice(parent, temp, get count) == 0) free slice(parent) end ######################################################################## # Return index of a child of the root slice. script, root child index, lookup code, begin variable(sl) clear string(err arg string) append number(err arg string, lookup code) sl := lookup slice(lookup code) assert(sl) assert(parent slice(sl) == lookup slice(sl:root)) return(slice child indeX(sl)) end # Unfinished, only inspects children of root. # But see also "slice correspondence tests" for NPC & hero slices script, slice tree tests, begin $0="Slice tree tests" assert(sprite layer == lookup slice(sl:script layer)) # Ensure the major slice layers appear in a fixed order. # Probably OK for new slices to get added between these (anyone making assumptions about the n-th # child here may be too much pain to support) variable (scr, map, textbox, backdrop, strings) map := root child index(sl:map root) backdrop := root child index(sl:backdrop) scr := root child index(sl:script layer) textbox := root child index(sl:textbox layer) strings := root child index(sl:string layer) assert(map < backdrop) assert(backdrop < scr) assert(scr < textbox) assert(textbox < strings) end ######################################################################## # This tests commands for converting between slices and hero/npc references script, slice correspondence tests, begin #### Test NPC slices variable(npc, sl, sl2, walkabout layer) walkabout layer := lookup slice(sl:npc layer) npc := next npc reference while (npc) do ( sl := get npc slice(npc) assert(sl) assert(npc reference from slice(sl) == npc) # Should be the top-level walkabout container assert(slice parent(sl) == walkabout layer) assert(slice x(sl) == npc pixel x(npc)) assert(slice y(sl) == npc pixel y(npc)) sl2 := lookup slice(sl:walkabout sprite component, sl) assert(sl2) assert(sl2 == get npc sprite(npc)) assert(npc reference from slice(sl2) == 0) npc := next npc reference(npc) ) sl := get hero slice(0) assert(sl && npc reference from slice(sl) == 0) #### Test hero slices # Preconditions assert(hero by slot(0) == hero:Freki) assert(hero by slot(1) == -1) assert(hero by slot(2) == -1) assert(hero by slot(3) == -1) sl := get hero slice(0) assert(sl) assert(slice parent(sl) == lookup slice(sl:hero layer)) assert(slice x(sl) == hero pixel x(0)) assert(slice y(sl) == hero pixel y(0)) assert(sl == get hero slice by slot(0)) assert(hero rank from slice(sl) == 0) assert(hero slot from slice(sl) == 0) sl2 := lookup slice(sl:walkabout sprite component, sl) assert(sl2) assert(sl2 == get hero sprite(0)) # Defaults to -1 assert(hero rank from slice(get npc slice(next npc reference)) == -1) assert(hero slot from slice(get npc slice(next npc reference)) == -1) # Check it returns rank, not party slot swap by position(0, 1) sl := get hero slice(0) assert(sl) assert(hero rank from slice(sl) == 0) assert(hero slot from slice(sl) == 1) # Compare with 'by slot' assert(get hero slice by slot(0) == 0) assert(get hero slice by slot(1) == sl) # Test rank > 0 add hero(hero:Bram) sl := get hero slice(1) assert(sl) assert(get hero slice by slot(1) == sl) assert(hero rank from slice(sl) == 1) assert(hero slot from slice(sl) == 1) # Hero slices belonging to non-existent heroes shouldn't exist, but before # gorgonzola they did exist. (Already tested "get hero slice by slot") assert(get hero slice(2) == 0) # Restore delete hero(hero:Bram) swap by position(0, 1) end ######################################################################## script, slice blending tests, begin variable(sl, collection) sl := load hero sprite(0, 1) assert(get blending enabled(sl) == false) assert(get opacity(sl) == 100) assert(get blend mode(sl) == blend:normal) # Test opacity set opacity(sl, 50), w assert(get opacity(sl) == 50) assert(get blending enabled(sl) == true) # And blend mode set blend mode(sl, blend:multiply), w assert(get blend mode(sl) == blend:multiply) set blending enabled(sl, false), w assert(get blending enabled(sl) == false) assert(get opacity(sl) == 100) assert(get blend mode(sl) == blend:normal) # Check blending settings preserved when blending is disabled set blending enabled(sl, true), w assert(get opacity(sl) == 50) assert(get blend mode(sl) == blend:multiply) free slice(sl) sl := create container # Allowed on non-blendable slices assert(get blending enabled(sl) == false) assert(get opacity(sl) == 100) assert(get blend mode(sl) == blend:normal) free slice(sl) # Check Map slice blending sl := lookup slice(sl:map layer 1) set opacity(sl, 30), w assert(get opacity(sl) == 30) set opacity(sl, 100) # Check blending settings loaded from file correctly collection := load slice collection(2) sl := first sprite child(collection) assert(get opacity(sl) == 45) assert(get blend mode(sl) == blend:add) free slice(collection) end ######################################################################## script, slice sort tests, begin # Very incomplete variable(parent, ch1, ch2, ch3, ch4) parent := create container(100,100) ch1 := create container(10,10) set parent(ch1, parent) ch2 := create container(10,10) set parent(ch2, parent) ch3 := create container(10,20) set parent(ch3, parent) ch4 := create container(10,100) set parent(ch4, parent) #### get/set child autosort # Preconditions assert(current map == 0) assert(read gmap(16) == 0) # Map 0 is set to heroes over NPCs. assert(get child autosort(lookup slice(sl:walkabout layer)) == autosort:off) write gmap(16, 2) # Layering: NPCs and heroes together assert(read gmap(16) == 2) # This might actually change in future when more sophisticated layering is implemented assert(get child autosort(lookup slice(sl:walkabout layer)) == autosort:Y) write gmap(16, 0) # Reset to heroes over NPCs set child autosort(parent, autosort:bottomY) assert(get child autosort(parent) == autosort:bottomY) # Test bottomY sorting set slice edge y(ch1, edge:bottom, 100) assert(slice y(ch1) == 100 -- 10) set vert anchor(ch2, edge:middle) set slice edge y(ch2, edge:bottom, 50) assert(slice y(ch2) == 50 -- 10/2) set vert anchor(ch3, edge:bottom) set slice edge y(ch3, edge:bottom, 80) assert(slice y(ch3) == 80) set vert anchor(ch4, edge:middle) set vert align(ch4, edge:bottom) set slice edge y(ch4, edge:bottom, 50) wait(1) assert(slice child(parent, 0) == ch2) assert(slice child(parent, 1) == ch4) assert(slice child(parent, 2) == ch3) assert(slice child(parent, 3) == ch1) free slice(parent) end ######################################################################## define constant(1, zone:center) define constant(2, zone:wholegrass) define constant(3, zone:rightedge) define constant(4, zone:leftedge) define constant(5, zone:everything) define constant(6, zone:topedge) define constant(7, zone:bottomedge) # Equivalent to readzone, but checks it agrees with zoneatspot script, check zone, id, x, y, begin variable(i, what) for (i, 0, zone at spot(x, y, getcount) -- 1) do ( what := zone at spot(x, y, i) assert(what > 0) if (what == id) then ( assert(read zone(id, x, y) == true) exit returning (true) ) ) assert(read zone(id, x, y) == false) return(false) end script, zone tests, begin $0="zone tests" # Could do with more tests of the internal complexities of ZoneMap # Read some existing tiles (green plot of grass) assert(zone at spot(17, 15, getcount) == 3) assert(check zone(1, 17, 15) == 1) assert(check zone(2, 17, 15) == 1) assert(check zone(3, 17, 15) == 0) assert(check zone(5, 17, 15) == 1) assert(zone at spot(16, 16, getcount) == 4) assert(check zone(1, 16, 16) == 0) assert(check zone(2, 16, 16) == 1) assert(check zone(3, 16, 16) == 0) assert(check zone(4, 16, 16) == 1) assert(check zone(5, 16, 16) == 1) assert(check zone(7, 16, 16) == 1) # zone number of tiles assert(zone number of tiles(1) == 1) assert(zone number of tiles(2) == 9) assert(zone number of tiles(5) == 800) assert(zone number of tiles(8) == 0) assert(zone number of tiles(9988) == 0) # Zone name get zone name (95, zone:leftedge) $96="left edge" assert(string compare(95, 96)) # Test zone handles (except extra data) variable (zone5) zone5 := get zone(5) assert(zone5 && zone5 <> 5) clearstring(95) get zone name (95, get zone(zone:leftedge)) assert(string compare(95, 96)) assert(read zone(zone5, 16, 16) == 1) write zone(zone5, 16, 16, 0) assert(read zone(5, 16, 16) == 0) write zone(5, 16, 16, 1) assert(zone number of tiles(zone5) == 800) # Zone extra #assert(get zone(zone 5) == zone 5) # Meant to be idempotent assert(get zone extra(5, 0) == 10) assert(get zone extra(5, 1) == -2147483648) assert(get zone extra(5, 2) == 2147483647) assert(get zone extra(5, -1) == 2147483647) assert(get extra(zone5, -1) == 2147483647) set extra(zone5, -2, 32) assert(get zone extra(5, 1) == 32) assert(extra length(zone5) == 3) # A zone with zero length extra variable (zone10) zone10 := get zone(10) assert(extra length(zone10) == 0) append extra(zone10, 2) assert(extra length(zone10) == 1) assert(get extra(zone10, -1) == 2) # A zone with more extra assert(extra length(get zone(11)) == 13) assert(get zone extra(11, 12) == 999) # A nonexistent zone get zone name (95, 9000) assert(string length(95) == 0) assert(get zone extra(9000, 0) == 0) assert(get zone extra(9000, 1) == 0) set zone extra (9000, 2, 32) assert(get zone extra(9000, 2) == 32) # Erasing a zone shouldn't affect others on the same tile write zone(1, 17, 15, 0) assert(check zone(1, 17, 15) == 0) assert(check zone(2, 17, 15) == 1) assert(check zone(5, 17, 15) == 1) assert(zone at spot(17, 15, getcount) == 2) write zone(1, 17, 15, 1) assert(check zone(1, 17, 15) == 1) # Write a zone that doesn't exist at all yet write zone(1003, 16, 14, 1) assert(check zone(1003, 16, 14) == 1) variable(idx, idx2, count) # Already 3 zones here; place 15 zones on one tile (the max) count := 3 for (idx, 1001, 1012) do ( write zone(idx, 17, 15, 1) assert(check zone(idx, 17, 15) == 1) count += 1 assert(zone at spot(17, 15, getcount) == count) ) # Remove them again in FIFO order for (idx, 1001, 1012) do ( write zone(idx, 17, 15, 0) assert(check zone(idx, 17, 15) == 0) count -= 1 assert(zone at spot(17, 15, getcount) == count) for (idx2, idx + 1, 1012) do ( assert(check zone(idx2, 17, 15) == 1) ) ) # Check other zones within the same 4x4 block are undisturbed assert(zone at spot(16, 14, getcount) == 5) assert(check zone(1, 16, 14) == 0) assert(check zone(2, 16, 14) == 1) assert(check zone(3, 16, 14) == 0) assert(check zone(4, 16, 14) == 1) assert(check zone(5, 16, 14) == 1) assert(check zone(6, 16, 14) == 1) assert(check zone(1003, 16, 14) == 1) zones randomized test end script, zones randomized test, begin # Do a randomised test of writing and reading zones in a 8x8 area # (four 4x4 blocks) at top-left corner of the map). # The idea is that the zonemap code has a lot of edge cases as a result of # implementation details which are a huge pain to write tests for, and those # tests wouldn't be worth much if the implementation changes. # Unfortunately these tests still aren't worth much; changing the various magic # constants results in them not hitting the edge cases anyway! :( # We check which zones we expect to see in 64 globals which act as bitvectors, # the i-th bit set if that tile should be in zone i. show value(1) wait # Returns true or false. id >= 1 subscript, readarray, id, x, y, begin return((read global(array:zones + x + y * 8) / 2^(id--1)), and, 1) end # id >= 1 subscript, writearray, id, x, y, value, begin variable(bits) bits := read global(array:zones + x + y * 8) bits := bits, and, (-1 -- 2^(id--1)) if (value) then (bits += 2^(id--1)) write global(array:zones + x + y * 8, bits) end # Num zones at tile subscript, array count zones, x, y, begin return(bitcount(read global(array:zones + x + y * 8))) end # Check the whole area is as it should be subscript, test area, begin variable(x, y, id) for (x, 0, 7) do ( for (y, 0, 7) do ( for (id, 1, 31) do ( assert(readarray(id, x, y) == read zone(id, x, y)) ) assert(zone at spot(x, y, getcount) == array count zones(x, y)) ) ) end # Initial state variable(x,y) for (x, 0, 7) do ( for (y, 0, 7) do (writearray(zone:everything, x, y, 1)) ) #test area # Stick to 15 zones in the whole area, so no overflow occurs variable(it, id, newvalue) for (it, 0, 300) do ( show value(it) x := random(0,4) y := random(0,5) id := random(1,15) newvalue := random(0,1000) 0 assert(readarray(id, x, y) == readzone(id, x, y)) writezone(id, x, y, newvalue) writearray(id, x, y, newvalue) if (it, mod, 5 == 0) then (test area) ) show value(2) wait # Now start using more zones, so that overcrowding occurs # But don't use more than 15 per tile for (it, 0, 5000) do ( show value(10000+it) x := random(0,7) y := random(0,3) id := random(1,31) newvalue := random(0,2) > 0 if (newvalue == 1 && array count zones(x, y) == 15) then (continue) assert(readarray(id, x, y) == readzone(id, x, y)) writezone(id, x, y, newvalue) writearray(id, x, y, newvalue) if (it, mod, 200 == 0) then (test area) ) shownovalue wait end ######################################################################## script, master palette tests, begin $0="master palette tests" # Test reading master palette assert(get color(0) == RGB(255, 0, 255)) assert(get color(6) == RGB(155, 83, 3)) assert(extract color(get color(19), color:red) == 37) assert(extract color(get color(19), color:green) == 139) assert(extract color(get color(19), color:blue) == 255) # Test searching master palette assert(find color(255, 0, 255) == 0) assert(find color(0, 0, 0, 255) == 255) assert(find color(0, 0, 0, 256) == 0) assert(find color(255, 0, 255, 1) == 185) assert(find color(255, 0, 255, 185) == 185) assert(find color(255, 0, 255, 186) == 200) assert(find color(180, 180, 0) == 103) assert(find color(512, 100, -512) == 168) # Test get/set ui color assert(get ui color(ui:background) == 240) assert(get ui color(0) == 0) assert(get ui color(255) == 255) assert(get ui color(ui:MPBarFlash) == 37) # Currently last UI col open menu(0) set ui color(ui:menu item, 99) assert(get ui color(ui:menu item) == 99) w close menu(top menu) set ui color(ui:menu item, ui:background) assert(get ui color(ui:menu item) == 240) set ui color(ui:menu item, 250) # Reset # Test "get ui color" autotoggling variable(c1, c2, tog1, tog2, ) c1 := get ui color(ui:selected item) c2 := get ui color(ui:selected item 2) assert(c1 == 14) assert(c2 == 15) tog1 := get ui color(ui:selected item 2, true) wait(1) assert(get ui color(ui:selected item 2) == c2) tog2 := get ui color(ui:selected item 2, true) # Currently it changes every tick assert((tog1 == c1 && tog2 == c2) || (tog1 == c2 && tog2 == c1)) # Test box styles assert(get box style color(14) == 65) assert(get box style edge color(14) == 252) assert(get box style border(14) == 1) assert(get box style color(0) == 33) # Lots of other color commands could be tested end ######################################################################## script, maptile tests, begin $0="maptile tests" # Test some tilemap, passmap, foemap commands # (Zones are tested separately in 'zone tests', and wallchecking # in 'wallchecking tests' and 'do hero cater tests') # Read some tiles on the left edge of the house (of map 0) assert(read map block(6, 5, 0) == 112) assert(read map block(6, 4, 0) == 96) assert(read map block(6, 4, 1) == 0) assert(read map block(6, 4, 2) == 58) assert(read pass block(6, 5) == westwall + southwall) # Oneway doors assert(read zone(zone:OneWayExit, 4, 10) == 1) assert(read pass block(4, 10) == southwall + northwall) assert(read pass block(5, 10) == southwall) # Write tilemap write map block(3, 2, 255, 1) assert(read map block(3, 2, 1) == 255) reset map state(mapstate:tilemap) assert(read map block(3, 2, 1) == 0) # Write passmap write pass block(3, 2, 255) assert(read pass block(3, 2) == 255) write pass block(3, 2, 0) # Read/write wall bit write pass block(3, 2, east wall + harm tile) assert(read wall bit(3, 2, east wall) == true) assert(read wall bit(3, 2, west wall) == false) write wall bit(3, 2, east wall, false) write wall bit(3, 2, vehicle a, true) assert(read pass block(3, 2) == vehicle a + harm tile) write pass block(3, 2, 0) # Read foemap assert(read foe map(14, 5) == 0) assert(read foe map(14, 6) == 1) assert(read foe map(-1, 0) == 0) # Test tile animations # An animated tile (water assert(read map block(3, 3, 0) == 192) # animation start tile (tile number, layer) assert(animation start tile (192, 0) == 144) # get tile animation offset (animation pattern, layer) # Second animation pattern isn't used, so this should be zero assert(get tile animation offset(1, 0) == 0) assert(currentdisplaytile(208) == 0) # first tile of the range assert(currentdisplaytile(192) >= 144 && currentdisplaytile(192) <= 147) assert(currentdisplaytile(192) == 144 + get tile animation offset(0, 0)) #set tile animation offset (animation pattern, offset, layer) not tested # These checks will have to change if any layers are added to map 0 or 1 variable(rem map, rem x, rem y) rem map := current map rem x := hero x(me) rem y := hero y(me) teleport to map(0, 0, 0) w assert(last layer id == 2) assert(layer id under walkabouts == 1) w teleport to map(1, 0, 0) w assert(last layer id == 1) assert(layer id under walkabouts == 0) w # go back to the map we were on before teleport to map(rem map, rem x, rem y) end ######################################################################## script, map state tests, begin $0="map state tests" # Preconditions assert(current map == 0) # These tests are quite incomplete: # No tests of NPC definition or map settings # No tests of saving/loading data when leaving/entering map # No tests that load/saving/deleting one state doesn't change the others # The RELOAD NPC instance file format is used only by map state, so it should be fully tested somewhere variable(mapstate:tilepass) mapstate:tilepass := mapstate:tilemap + mapstate:passmap # Test maptiles and walls, saving to a custom save state file assert(read map block(7, 2, 2) == 42) # Precondition assert(read pass block(7, 2) == northwall) # Precondition write map block(7, 2, 11, 2) write pass block(7, 2, eastwall) assert(read map block(7, 2, 2) == 11) assert(read pass block(7, 2) == eastwall) # "load map state" with a custom ID should do nothing if the file doesn't exist load map state(mapstate:tilepass, 1) assert(read map block(7, 2, 2) == 11) assert(read pass block(7, 2) == eastwall) # save/reset/load save map state(mapstate:tilepass, 1) reset map state(mapstate:tilepass) assert(read map block(7, 2, 2) == 42) assert(read pass block(7, 2) == northwall) load map state(mapstate:tilemap, 1) # Half-hearted test of loading tiles and walls separately assert(read map block(7, 2, 2) == 11) assert(read pass block(7, 2) == northwall) load map state(mapstate:passmap, 1) assert(read pass block(7, 2) == eastwall) write map block(7, 2, 12, 2) write pass block(7, 2, westwall) # delete state delete map state(mapstate:tilepass, 1) # "load map state" with a custom ID should again do nothing load map state(mapstate:tilepass, 1) assert(read map block(7, 2, 2) == 12) assert(read pass block(7, 2) == westwall) # "load map state" with no ID should default to resetting load map state(mapstate:tilepass) assert(read map block(7, 2, 2) == 42) assert(read pass block(7, 2) == northwall) # Test zones assert(read zone(6, 16, 14) == true) # Precondition assert(read zone(6, 15, 14) == false) # Precondition assert(get zone extra(5, 0) == 10) # Precondition write zone(6, 16, 14, false) write zone(6, 15, 14, true) set zone extra(5, 0, 11) write zone(765, 10, 20, true) # an unused zone assert(read zone(6, 16, 14) == false) assert(read zone(6, 15, 14) == true) assert(read zone(765, 10, 20) == true) # "load map state" with a custom ID should do nothing if the file doesn't exist load map state(mapstate:zonemap, 1) assert(read zone(6, 16, 14) == false) assert(read zone(6, 15, 14) == true) assert(read zone(765, 10, 20) == true) assert(get zone extra(5, 0) == 11) # save/reset/load save map state(mapstate:zonemap, 1) reset map state(mapstate:zonemap) assert(read zone(6, 16, 14) == true) assert(read zone(6, 15, 14) == false) assert(read zone(765, 10, 20) == false) assert(get zone extra(5, 0) == 10) load map state(mapstate:zonemap, 1) assert(read zone(6, 16, 14) == false) assert(read zone(6, 15, 14) == true) assert(read zone(765, 10, 20) == true) assert(get zone extra(5, 0) == 11) write zone(6, 16, 14, true) write zone(6, 15, 14, true) set zone extra(5, 0, 12) # delete state delete map state(mapstate:zonemap, 1) # "load map state" with a custom ID should again do nothing load map state(mapstate:zonemap, 1) assert(read zone(6, 16, 14) == true) assert(read zone(6, 15, 14) == true) assert(read zone(765, 10, 20) == true) assert(get zone extra(5, 0) == 12) # "load map state" with no ID should default to resetting load map state(mapstate:zonemap) assert(read zone(6, 16, 14) == true) assert(read zone(6, 15, 14) == false) assert(read zone(765, 10, 20) == false) assert(get zone extra(5, 0) == 10) # Test NPC instances variable(npc, newnpc) npc := npc reference(4, 2) assert(npc x(npc) == 3 && npc y(npc) == 13) # Precondition assert(npc copy count(7) == 0) # Precondition set npc position(npc, 5, 6) newnpc := create NPC(7, 1, 2) assert(npc copy count(7) == 1) # "load map state" with a custom ID should do nothing if the file doesn't exist load map state(mapstate:npcs, 1) assert(npc x(npc) == 5 && npc y(npc) == 6) assert(npc x(newnpc) == 1 && npc y(newnpc) == 2) # save/load/reset save map state(mapstate:npcs, 1) # Check deleted NPCs are restored, with same npcref delete NPC(npc) assert(get NPC id(npc) == -1) # Check the npcref is invalid reset map state(mapstate:npcs) # NPC references should stay valid if it's the same NPC assert(npc x(npc) == 3 && npc y(npc) == 13) # Check created NPCs are deleted assert(npc copy count(7) == 0) assert(get NPC id(newnpc) == -1) load map state(mapstate:npcs, 1) assert(npc x(npc) == 5 && npc y(npc) == 6) # Check created NPCs are loaded assert(npc copy count(7) == 1) assert(npc x(newnpc) == 1 && npc y(newnpc) == 2) # delete state delete map state(mapstate:npcs, 1) # "load map state" with a custom ID should again do nothing load map state(mapstate:npcs, 1) assert(npc x(npc) == 5 && npc y(npc) == 6) assert(npc x(newnpc) == 1 && npc y(newnpc) == 2) # "load map state" with no ID should default to resetting load map state(mapstate:npcs) assert(npc x(npc) == 3 && npc y(npc) == 13) assert(get NPC id(newnpc) == -1) end ######################################################################## script, obsolete map data tests, begin $0="obsolete map data tests" variable(rem map, rem x, rem y) rem map := current map rem x := hero x(me) rem y := hero y(me) teleport to map(map: Obsolete map data DO NOT EDIT, 0, 0) # This map should not be opened in Custom, so that the map lumps aren't loaded and re-saved. # Copied from zone tests # Other zone data commands get zone name (95, 5) $96="extra data test" assert(string compare(95, 96)) assert(extra length(get zone(5)) == 3) assert(get zone extra(5, 0) == 10) assert(get zone extra(5, 1) == -2147483648) assert(get zone extra(5, 2) == 2147483647) # A nonexistent zone get zone name (95, 9000) assert(string length(95) == 0) assert(extra length(get zone(9000)) == 3) assert(get zone extra(9000, 0) == 0) assert(get zone extra(9000, 1) == 0) assert(get zone extra(9000, 2) == 0) teleport to map(rem map, rem x, rem y) end ######################################################################## script, wallchecking tests, begin $0="wallchecking tests" # This tests moveslicewithwallchecking and checkwallcollisionx/y commands. # This follows previous tests of basic hero movement ("do hero cater tests") # which check hero movement obeys walls. variable(tilex, tiley) tilex := 6 tiley := 15 # The tile at tilex,tiley is called the center tile # hitbox size variable(hitboxw, hitboxh) variable(hitbox) hitbox := create container set parent(hitbox, lookupslice(sl:map layer 0)) subscript, set hitbox size, wide, high, begin hitboxw := wide hitboxh := high set slice size(hitbox, wide, high) end subscript, testcollide, testnum, x, y, xgo, ygo, expectedx, expectedy, expectedret = -1, begin #show value(testnum) #tracevalue(testnum, x, y, xgo, ygo, expectedx, expectedy) variable(resultx, resulty, ret) # if (false) then ( # resultx := check wall collision x(x, y, hitboxw, hitboxh, xgo, ygo) # resulty := check wall collision y(x, y, hitboxw, hitboxh, xgo, ygo) # ) else ( putslice(hitbox, x, y) ret := moveslicewithwallchecking(hitbox, xgo, ygo) resultx := slicex(hitbox) -- x resulty := slicey(hitbox) -- y # ) #tracevalue(resultx, resulty) if (expectedret <> -1) then ( #trace(string sprintf(98, $99="ret: %b wanted %b", ret, expectedret)) assert(ret == expectedret) ) assert(resultx == expectedx) assert(resulty == expectedy) end # Test 45 degree and near-45 degree collisions with one of the 4 corners of the center tile # cornerx and cornery are 0 or 1 for left/up or right/bottom, # and also possibly the far corner if there are no walls at the first one. subscript, test corner, cornerx, cornery, cornermask, begin #tracevalue(cornerx, cornery) # The four walls of the center tile variable(wall up, wall left, wall right, wall down) wall left := (readpassblock(tilex, tiley), and, westwall) || (readpassblock(tilex--1, tiley), and, eastwall) wall right := (readpassblock(tilex, tiley), and, eastwall) || (readpassblock(tilex+1, tiley), and, westwall) wall up := (readpassblock(tilex, tiley ), and, northwall) || (readpassblock(tilex, tiley--1), and, southwall) wall down := (readpassblock(tilex, tiley ), and, southwall) || (readpassblock(tilex, tiley+1 ), and, northwall) # whether there is a wall on the vertical or horizontal side of # the center tile adjacent to the corner variable(vside, hside, far vside, far hside) if (cornerx == 0) then ( vside := wall left far vside := wall right ) else ( vside := wall right far vside := wall left ) if (cornery == 0) then ( hside := wall up far hside := wall down ) else ( hside := wall down far hside := wall up ) # position in pixels of the corner variable(x, y) x := (tilex + cornerx) * 20 y := (tiley + cornery) * 20 # Convert to position of (top-left of) collider when at the corner if (cornerx == 0) then (x -= hitboxw) if (cornery == 0) then (y -= hitboxh) # Distance moved by collider before reaching corner variable(strikedist) strikedist := 22 # should be >= 2 # 1 or -1, the sign of xgo/ygo of the collider variable(xgodir, ygodir) if (cornerx == 0) then (xgodir := 1) else (xgodir := -1) if (cornery == 0) then (ygodir := 1) else (ygodir := -1) # xgo/ygo are 10 pixels beyond the corner variable(xgo, ygo) xgo := xgodir * 2 * strikedist #(10 + strikedist) ygo := ygodir * 2 * strikedist #(10 + strikedist) # start position of collider (top-left), in pixels variable(startx, starty) startx := x -- xgodir * strikedist starty := y -- ygodir * strikedist if (vside || hside) then ( # Hit the corner at 45 degree testcollide(1, startx, starty, xgo, ygo, xgodir * strikedist, ygodir * strikedist, cornermask) # Move parallel to the corner, so we miss it by 1 pixel, but still at 45 degree testcollide(2, startx + xgodir, starty, xgo, ygo, xgodir * strikedist, ygodir * strikedist) testcollide(3, startx, starty + ygodir, xgo, ygo, xgodir * strikedist, ygodir * strikedist) # Try angles slightly above and below 45 degree but which still hit the corner at the same time testcollide(4, startx+1, starty, xgo -- 2, ygo, xgodir * strikedist -- 1, ygodir * strikedist, cornermask) testcollide(5, startx, starty+1, xgo, ygo -- 2, xgodir * strikedist, ygodir * strikedist -- 1, cornermask) testcollide(6, startx--1, starty, xgo + 2, ygo, xgodir * strikedist + 1, ygodir * strikedist, cornermask) testcollide(7, startx, starty--1, xgo, ygo + 2, xgodir * strikedist, ygodir * strikedist + 1, cornermask) # Hit the corner in one axis a fraction of a pixel before the other testcollide(8, startx+1, starty, xgo -- 1, ygo, xgodir * strikedist -- 1, ygodir * strikedist, cornermask) testcollide(9, startx--1, starty, xgo + 1, ygo, xgodir * strikedist + 1, ygodir * strikedist, cornermask) testcollide(10, startx, starty -- 1, xgo, ygo + 1, xgodir * strikedist, ygodir * strikedist + 1, cornermask) testcollide(11, startx, starty + 1, xgo, ygo -- 1, xgodir * strikedist, ygodir * strikedist -- 1, cornermask) ) else if (strikedist > 20 && (far vside || farhside)) then ( # Pass through the corner, but hit a wall on the opposite side variable(expectx, expecty, expect ret) expectx := xgodir * (strikedist+20) expecty := ygodir * (strikedist+20) # Hit the inside corner at 45 degree if (far vside) then ( if (cornerx == 0) then (expectret += eastwall) else (expectret += westwall) ) if (far hside) then ( if (cornery == 0) then (expectret += southwall) else (expectret += northwall) ) testcollide(20, startx, starty, xgo, ygo, expectx, expecty, expectret) # Hit the inside corner in one axis a fraction of a pixel before the other testcollide(21, startx + 1, starty, xgo -- 1, ygo, expectx -- 1, expecty) testcollide(22, startx -- 1, starty, xgo + 1, ygo, expectx + 1, expecty) testcollide(23, startx, starty + 1, xgo, ygo -- 1, expectx, expecty -- 1) testcollide(24, startx, starty -- 1, xgo, ygo + 1, expectx, expecty + 1) ) else ( # Hit no walls testcollide(30, startx, starty, xgo, ygo, xgo, ygo, 0) ) end # Some simple manually written test configurations # The tile at tilex,tiley has walls on all sides variable(x,y) x := tilex*20 y := tiley*20 set hitbox size(10, 10) testcollide(100, x--20, y--5, 20, 0, 10, 0, eastwall) # hit wall from west testcollide(101, x+40, y--5, -40, 0, -20, 0, westwall) # ...east testcollide(102, x--5, y--15, 0, 20, 0, 5, southwall) # ...north testcollide(103, x--5, y+35, 0, -20, 0, -15, northwall) # ...south # Diagonal movement that hits the corner in one axis a fraction of a pixel # before the other axis variable(hitx, hity) # position of the the corner to hit hitx := tilex*20 + 20 hity := tiley*20 + 20 testcollide(110, hitx + 2, hity + 3, -5, -6, -2, -3, northwest corner) testcollide(111, hitx + 3, hity + 2, -6, -5, -3, -2, northwest corner) hitx := tilex*20 -- hitboxw testcollide(112, hitx -- 3, hity + 2, 6, -5, 3, -2, northeast corner) testcollide(113, hitx -- 2, hity + 3, 5, -6, 2, -3, northeast corner) hity := tiley*20 -- hitboxh testcollide(114, hitx -- 3, hity -- 2, 6, 5, 3, 2, southeast corner) testcollide(115, hitx -- 2, hity -- 3, 5, 6, 2, 3, southeast corner) # Test collision with the ends and center of a large hitbox (only x axis) set hitbox size(1, 58) variable(dx) # the sign of xgo (whether hit from left or right) variable(expected) # expected return value (wall mask) for(dx, 1, -1, -2) do ( x := tilex*20 if (dx == 1) then (x -= 10 + hitboxw) else (x += 10 + 20) if (dx == 1) then (expected := eastwall) else (expected := westwall) testcollide(104, x, y--19,dx*30, 9, dx*10, 3, expected) # hitting with the center of the hitbox testcollide(105, x, y+9, dx*30, 30, dx*10, 10, expected) # just hit the corner w/ the top by 1 pixel testcollide(106, x, y+10, dx*30, 30, dx*30, 30, 0) # just miss testcollide(107, x, y--47,dx*30, -30, dx*10, -10, expected) # just hit the corner w/ the bottom by 1 pixel testcollide(108, x, y--58,dx*30, -30, dx*30, -30, 0) # just miss ) # Test hitting diagonal hits of corners of various wall configurations walls. # We test multiple hitbox sizes, but we're not actually doing a serious test of that for (hitboxw, 10, 30, 10) do ( for (hitboxh, 10, 30, 10) do ( set hitbox size(hitboxw, hitboxh) variable(tt, walls) tt := microseconds # Try all possible walls on the center tile for (walls, 15, 0, -1) do ( #tracevalue(hitboxw, hitboxh, walls, 1) write pass block(tilex, tiley, walls) testcorner(0,0, southeast corner) testcorner(0,1, northeast corner) testcorner(1,0, southwest corner) testcorner(1,1, northwest corner) ) # Test walls in the same places, but on the adjacent 4 tiles instead for (walls, 15, 0, -1) do ( #tracevalue(hitboxw, hitboxh, walls, 2) write pass block(tilex+1, tiley, walls,and,westwall) write pass block(tilex, tiley+1, walls,and,northwall) write pass block(tilex--1, tiley, walls,and,eastwall) write pass block(tilex, tiley--1, walls,and,southwall) testcorner(0,0, southeast corner) testcorner(0,1, northeast corner) testcorner(1,0, southwest corner) testcorner(1,1, northwest corner) ) ) ) tracevalue(microseconds--tt) show no value free slice(hitbox) end ######################################################################## script, platform tests, begin $0="platform tests" # Blackbox, but we test reading from autotest_config.ini assert(read environment(1, $2="nonexistent") == 0) assert(string equal(1, $3="")) # Test case insensitivity assert(read environment(1, $2="Has_Value_42") == 42) assert(string equal(1, $3="42")) # Blackbox only. These don't do or return anything. assert(xbox request account picker == 0) assert(ps5 start story == 0) assert(ps5 end story == 0) # Steam or blackbox. # achievements.rpgdir can be used to test this actually works, here we just # test it throws no error. set rich presence($1="status") set rich presence($1="status", $2="substitution") end ######################################################################## script, save slot tests, begin $0="save slot tests" # This includes tests for commands which operate on save slots, # but doesn't test load saves itself; that's in "savegame tests" delete save(1) assert(save slot used(1) == false) # import/export globals export global 1 := -123456789 export globals(1, @export global 1, @export global 1) export global 1 := -2 assert(import globals(1, @export global 1) == -123456789) assert(export global 1 == -2) import globals(1, @export global 1, @export global 2) assert(export global 1 == -123456789) assert(export global 2 == 0) export global 2 := -100 export globals(1, @export global 2, @export global 2) save in slot(2) export global 1 := 0 export global 2 := 0 export globals(1, @export global 1, @export global 2) export global 1 := 100 export global 2 := 100 import globals(1, @export global 1, @export global 2) assert(export global 1 == 0) assert(export global 2 == 0) # high global id numbers # (Export this to slot 3 so I can inspect autotest.saves/2.rsav) the global with the biggest id := 123 assert(the global with the biggest id == 123) assert(@the global with the biggest id == 50000) export globals(3, @the global with the biggest id, @the global with the biggest id) write global(@the global with the biggest id, 321) assert(import globals(3, @the global with the biggest id) == 123) assert(the global with the biggest id == 321) # exporting globals should not cause a slot to appear as used assert(save slot used(1) == false) assert(save slot used(2) == true) # testing saveinslot earlier import globals(2, @export global 1, @export global 2) assert(export global 1 == -123456789) assert(export global 2 == -100) # different form export global 1 := -3 assert(import globals(2, @export global 1, @export global 1) == 0) assert(export global 1 == -123456789) # loading from nonexistent save... must zero out the globals without throwing an error import globals(28, @export global 1, @export global 2) assert(export global 1 == 0) assert(export global 2 == 0) # Check interaction between deletesave and importglobals # Originally, deletesave was documented as preserving stored global variables, so they could # still be read with importglobals. Since switching to RSAV in Zenzizenzic (2011), deletesave # now deletes the globals too! # Using rpgbatch, it seems no games are affected by the change (see svn commit message) export global 1 := 100 export global 2 := 100 export globals(1, @export global 1, @export global 2) delete save(1) # Slot 1 only had global variables delete save(2) # Slot 2 had a full save assert(save slot used(1) == false) assert(save slot used(2) == false) export global 1 := -1 export global 2 := -2 import globals(1, @export global 1, @export global 2) # Pre-Zenzizenzic behaviour #assert(export global 1 == -123456789) #assert(export global 2 == -100) # New behaviour assert(export global 1 == 0) assert(export global 2 == 0) import globals(2, @export global 1, @export global 2) # Pre-Zenzizenzic behaviour #assert(export global 1 == -123456789) #assert(export global 2 == -100) # New behaviour assert(export global 1 == 0) assert(export global 2 == 0) end ######################################################################## script, string load tests, begin $0="string load tests" # attack string tests in "attack tests" # Stat names assert(get stat name(1, stat:hp) == true) assert(string compare(1, $2="HP")) get stat name(1, stat:hits) # Last stat assert(string compare(1, $2="Hits")) get stat name(1, 15) # Last register assert(string compare(1, $2="mute register")) assert(get stat name(1, 16) == false) # Out of bounds assert(string compare(1, $2="")) embedding global := -1234 $3=" " # Test the obsolete stringfromtextbox: 4th arg ignored stringfromtextbox(1, 8, 2) $2="." assert(string compare(1, 2)) stringfromtextbox(1, 8, 3) $2="" assert(string compare(1, 2)) stringfromtextbox(1, 8, 4) $2="##" assert(string compare(1, 2)) stringfromtextbox(1, 8, 5, false) $2=",-1234. , " assert(string compare(1, 2)) stringfromtextbox(1, 8, 5, true) assert(string compare(1, 2)) # textbox line textboxline(1, 8, 2) $2="." assert(string compare(1, 2)) textboxline(1, 8, 3) $2="" assert(string compare(1, 2)) textboxline(1, 8, 4) $2=" ## " assert(string compare(1, 2)) textboxline(1, 8, 4, true, false) assert(string compare(1, 2)) textboxline(1, 8, 4, true, true) $2="##" assert(string compare(1, 2)) textboxline(1, 8, 5, false) $2=" ,${V105}. ,${S3} " assert(string compare(1, 2)) textboxline(1, 8, 5, true) $2=" ,-1234. , " assert(string compare(1, 2)) textboxline(1, 8, 5, true, true) $2=",-1234. , " assert(string compare(1, 2)) # textbox text $2=" This box is for testing textboxline\n and stringfromtextbox\n.\n\n ## \n ,-1234. , " textboxtext(1, 8, true, false) assert(string compare(1, 2)) # get scancode name tests $1="Left Bracket" get scancode name(2, key:leftbracket) assert(string compare(1,2)) $1="[" get scancode name(2, key:leftbracket, false) assert(string compare(1,2)) $1="Flee" # Previously "Run" get scancode name(2, flee key) # AKA run key assert(string compare(1,2)) $1="Menu" get scancode name(2, menu key) assert(string compare(1,2)) $1="Cancel" get scancode name(2, cancel key) assert(string compare(1,2)) $1="None" get scancode name(2, 0) assert(string compare(1,2)) $1="Space" get scancode name(2, key:space, false) assert(string compare(1,2)) $1="Left Shift" get scancode name(2, key:left shift, false) assert(string compare(1,2)) $1="Gamepad B" get scancode name(2, joy:B, false) tracevalue(joy:B) trace(2) assert(string compare(1,2)) $1="Gamepad Up" get scancode name(2, joy:up, false) assert(string compare(1,2)) $1="Gamepad Button 16" get scancode name(2, joy:button16, false) assert(string compare(1,2)) # get slice lookup name tests $1="" get slice lookup name(2, 0, true) assert(string compare(1,2)) get slice lookup name(2, 0, false) assert(string compare(1,2)) $1="map layer0" get slice lookup name(2, sl:maplayer0) assert(string compare(1,2)) $1="Some lookup" get slice lookup name(2, sli:Some lookup) assert(string compare(1,2)) $1="Lookup-40" get slice lookup name(2, -40) assert(string compare(1,2)) $1="" get slice lookup name(2, -40, false) assert(string compare(1,2)) end ######################################################################## script, savegame tests, begin $0="saving/loading tests 1" trace(0) # Partially split into savegame tests 2 # Preconditions assert(current map == 0) delete save(1) assert(save slot used(1) == false) savegame map := current map savegame slice := create rect(15, 20) put slice(savegame slice, 40, 40) savegame npc := createnpc(0, 5, 5) # Saving of string state # There are no commands to return the current colour and style # of a string (and I don't see any need to add them), so that can only be # tested by screenshot. $5 = "This is a green string" show string at(5, 100, 100) string color(5, 72) # Should default to 'string:outline' style $6 = "This is flat yellow-on-purple" show string at(6, 100, 110) string style(6, string:flat) string color(6, 14, 194) # yellow, purple $7="This should be plain style" show string at(7, 100, 120) w hook loadgame := 1 save in slot(1) hook loadgame := 0 assert(save slot used(1) == true) # Change state a bit to check it's actually reset and loaded hide string(5) $5 = "" $6 = "Now it's plain" string color(6) $7 = "Now it's yellow-on-purple" string style(7, string:flat) string color(7, 14, 194) # yellow, purple show string at(8, 0, 0) teleport to map(1) savegame map := -1 free slice(savegame slice) w load from slot(1) # continues from "after loadgame" $0="Loadgame didn't happen!" crash end script, after loadgame, arg1, arg2, arg3, begin if (hook loadgame == 1) then ( assert(arg1 == 0) assert(arg2 == 0) assert(arg3 == 0) savegame tests 2 crash ) else ( assert(arg1 == 30) assert(arg2 == -100) assert(arg3 == 0) hook loadgame := 0 ) end # This is called from the loadgame plotscript script, savegame tests 2, begin $0="saving/loading tests 2" trace(0) # Check globals, map number, slices loaded (game is set to save slices) assert(savegame map == current map) assert(slicewidth(savegame slice) == 15) # Check NPCs were NOT saved assert(get npc id(savegame npc) == -1) # doesn't exist # Check strings were loaded assert(string is visible(5)) assert(string is visible(6)) assert(string is visible(8) == false) $8 = "This is a green string" assert(string compare(5, 8)) assert(string x(6) == 100) assert(string y(6) == 110) # Can't test colour, style w # Clean up hide string(5) hide string(6) hide string(7) free slice(savegame slice) # Now try passing args to the loadgame script hook loadgame := 2 save in slot(1) hook loadgame := 0 load from slot(1, 30, -100) # continues from "after loadgame" $0="Loadgame didn't happen!" crash end ######################################################################## script, reset game tests, begin $0="reset game tests" trace(0) # Test with no args called resetgame := true export globals(save slot: persistent, @called resetgame, @called resetgame) reset game $0="resetgame didn't happen!" crash end # Called with default args plotscript, after resetgame, arg1, arg2, arg3, begin $0="reset game tests" trace(0) called resetgame := false export globals(save slot: persistent, @called resetgame, @called resetgame) if (arg1 == 0) then ( # after resetting with no args assert(arg2 == 0) assert(arg3 == 0) reset game(10, 20) $0="resetgame didn't happen!" crash ) else ( # after resetting with args assert(arg1 == 10) assert(arg2 == 20) assert(arg3 == 0) # exit the script and continue to next tests ) end ######################################################################## script, map autorun tests, begin $0="map autorun tests" autorun counter := 0 variable(last map, last x, last y) last map := current map last x := hero x(me) last y := hero y(me) w teleport to map(map:map with an autorun, 1, 1) assert(autorun counter == 1) # teleporting to a map you are already on does still run the autorun teleport to map(map:map with an autorun, 2, 2) assert(autorun counter == 2) # force a re-run of the autorun run script by id(get current map autorun script, get current map autorun script argument) assert(autorun counter == 3) w teleport to map(last map, last x, last y) end plotscript, simple autorun, arg, begin assert(arg == 444) autorun counter += 1 end ######################################################################## # rungame is not actually tested! script, run game tests, begin $0="run game tests" assert(check game exists($1="") == false) assert(check game exists($1="test.rpg")) assert(check game exists($1="foo.rpg") == false) #assert(check game exists($1="test.rpgdir")) assert(check game exists($1="autotest.rpgdir")) assert(check game exists($1="autotest.hss") == false) assert(check game exists($1="../testgame") == false) assert(check game exists($1="../testgame/autotest.rpgdir")) assert(check game exists($1="..\\testgame/Autotest.rpgdir")) assert(check game exists($1="../testgame\\autotest.rpgdir")) assert(check game exists($1="autotest.rpg") == false) #assert(check game exists($1="./testgame\\test.rpgDIR")) # Also check .rpg if given .rpgdir (not implemented) assert(check game exists($1=".//..\\TESTGAME\\autotest.RPGdir")) #assert(check game exists($1="../vikings/vikings.rpg")) # Also check .rpgdir if given .rpg (not implemented) # Should trailing slashes be allowed? #assert(check game exists($1="../vikings/vikings.rpgdir/") == false) #assert(check game exists($1="..\\vikings\\vikings.rpgdir\\") == false) end ######################################################################## #### COMMANDS THAT STILL NEED TESTS #### (Actually, this list is out of date) #### (Note: interactive commands should be in interactivetest.hss, and #### fundamental HS tests are in hstests.hss) #allow minimap (setting) #allow save anywhere (setting) #append ascii (ID, char) #ascii from string (ID, position) #autosave #camera follows hero (who) #camera follows NPC (who) #camera pixel X #camera pixel Y #cancel map name display #center slice (handle) #change tileset (tileset, layer) #check NPC wall (who, direction) #check parentage (handle, parent handle) #clamp slice (handle1, handle2) #clone sprite (handle) #concatenate strings (dest, source) #copy string (dest, source) #create container (width, height) #create ellipse (width, height, border color, fill color) #create grid (width, height, rows, columns) #create NPC (ID,X,Y,direction) #days of play #delete char (ID, position) #delete save (slot) #destroy NPC (reference) #dismount vehicle #draw NPCs above heroes (setting) #expand string(ID) #extended scancodes enabled #extract color(color, component) #fade screen in #fade screen out (red,green,blue) #fill parent (handle, true_or_false) #first child(handle) #first container child(handle) #first rect child(handle) #first sprite child(handle) #focus camera (x,y,speed) #for(counter,start,finish,step) do(commands) #formation probability (formation set, formation) #formation set frequency (formation set) #free slice children (handle) #free sprite (handle) #game over #get ambient music #get color(index) #get damage cap #get each step script #get ellipse border col (handle) #get ellipse fill col (handle) #get foot offset #get global string (ID, global) #get grid columns (handle) #get grid rows (handle) #get instead of battle script #get item name (ID, item) #get load script #get map edge mode #get map name (ID, map) #get map tileset #get money (amount) #get NPC ignores walls (who) #get NPC moves (who) #get NPC obstructs (who) #get NPC usable (who) #get on keypress script #get outline(handle) #get slice velocity x (handle) #get slice velocity y (handle) #get song name (ID, song) #get sort order (handle) #get text bg(handle) #get text color(handle) #get victory music #get wrap(handle) #globals to string(ID, starting global, length) #greyscale palette (first, last) #grid is shown (handle) #hide battle health meter (state) #hide battle ready meter (state) #horiz flip sprite (handle, flip) #hours of play #is filling parent (handle) #last child(handle) #last formation #last save slot #layer tileset (layer) #leader #load attack sprite (num, palette) #load backdrop sprite (num) #load border sprite (num, palette) #load hero sprite (num, palette) #load large enemy sprite (num, palette) #load medium enemy sprite (num, palette) #load palette (palette number) #load portrait sprite (num, palette) #load small enemy sprite (num, palette) #load tileset (tileset, layer) #load walkabout sprite (num, palette) #load weapon sprite (num, palette) #lose money (amount) #map cure (attack, target, attacker) #map height (map) #map width (map) #milliseconds #minutes of play #move slice above (handle, above what handle) #move slice below (handle, below what handle) #move slice by (handle, relative x, relative y, ticks) #move slice to (handle, x, y, ticks) #next container sibling(handle) #next rect sibling(handle) #next sibling(handle) #next sprite sibling(handle) #NPC at pixel (x, y, number) #NPC at spot (x, y, number) #NPC direction (who) #NPC frame (who) #number from string (ID, default) #order menu #outside battle cure #overhead tile #pan camera (direction,distance,pixelstep) #party money #pay money (amount) #place sprite #previous sibling(handle) #put camera (x,y) #put npc (who,x,y) #put slice (handle, X, Y) #put slice screen (handle, x, y) #put sprite (handle, x, y) #random formation (formation set) #read color (index, element) #realign slice (handle, horiz align, vert align, horiz anchor, vert anchor) #replace attack sprite (handle, num, palette) #replace backdrop sprite (handle, num) #replace border sprite (handle, num, palette) #replace char (ID, position, char) #replace hero sprite (handle, num, palette) #replace large enemy sprite (handle, num, palette) #replace medium enemy sprite (handle, num, palette) #replace portrait sprite (handle, num, palette) #replace small enemy sprite (handle, num, palette) #replace walkabout sprite (handle, num, palette) #replace weapon sprite (handle, num, palette) #reset palette #resume map music #resume NPCs #resume NPC walls #resume obstruction #resume overlay #resume random enemies #resume random enemys #resume timers #RGB(red, green, blue) #run game #run script by ID (id, argument1, argument2, argument3...) #save menu (reallysave) #search string (ID1, ID2, start) #seconds of play #seed random (new seed) #set battle wait mode (state) #set bottom padding (handle, pixels) #set color(index, value) #set damage cap (cap) #set days of play (days) #set debug keys disable (state) #set each step script (id) #set ellipse border col (handle, color) #set ellipse fill col (handle, color) #set foot offset (offset) #set grid columns (handle, columns) #set grid rows (handle, rows) #set harm tile damage (amount) #set harm tile flash (color) #set horiz align (handle, edge) #set horiz anchor (handle, edge) #set hours of play (hours) #set inn no revive mode (state) #set instead of battle script (id) #set inventory size (new size) #set left padding (handle, pixels) #set load script (id) #set map edge mode (mode, default tile) #set minutes of play (min) #set money (amount) #set no HP level up restore (state) #set no MP level up restore (state) #set NPC direction (who, direction) #set NPC frame (who, frame) #set NPC ignores walls (who, value) #set NPC moves (who, value) #set NPC obstructs (who, value) #set NPC speed (who, speed) #set NPC usable (who, value) #set on keypress script (id) #set outline(handle, outline) #set padding (handle, pixels) #set parent (handle, parent handle) #set rect bgcol (handle, color) #set rect fgcol (handle, color) #set right padding (handle, pixels) #set seconds of play (sec) #set slice clipping (handle, clip) #set slice edge x (handle, edge, value) #set slice height (handle, height) #set slice lookup (handle, code) #set slice screen x (handle, x) #set slice screen y (handle, y) #set slice velocity (handle, horiz pixels per tick, vert pixels per tick, ticks) #set slice velocity x (handle, pixels per tick, ticks) #set slice velocity y (handle, pixels per tick, ticks) #set slice visible (handle, vis) #set slice width (handle, width) #set slice x (handle, X) #set slice y (handle, Y) #set sort order (handle, order) #set sprite frame (handle, num) #set sprite palette (handle, num) #set sprite trans (handle, drawtransparent) #set sprite visible #set tag (tag,value) #set text bg(handle, color) #set text color(handle, color) #set tile animation offset (animation pattern, offset, layer) #set timer (id, count, speed, trigger, string, flags) #set top padding (handle, pixels) #set vert align (handle, edge) #set vert anchor (handle, edge) #set wrap(handle, wrap) #show backdrop (number) #show grid (handle, shown) #show map #show no value #show value (number) #slice collide (handle1, handle2) #slice collide point (handle, x, y) #slice contains (handle1, handle2) #slice edge x (handle, edge) #slice edge y (handle, edge) #slice is ellipse (handle) #slice is grid (handle) #slice is moving (handle) #slice is valid (id) #slice to back (handle) #slice to front (handle) #sort children (handle, wipe) #sprite frame count (handle) #sqrt (number) #stop slice (handle) #stop timer (id) #string to globals (ID, starting global, length) #suspend catapillar #suspend map music #suspend NPCs #suspend NPC walls #suspend obstruction #suspend overlay #suspend random enemies #suspend random enemys #suspend timers #system day #system hour #system minute #system month #system second #system year #tweak palette (red, green, blue, first, last) #update palette #use item (item) #use item in slot(slot) #vert flip sprite (handle, flip) #wait for all #wait for camera #wait for NPC (who) #wait for slice (handle) #walk NPC (who, direction, distance) #walk NPC to X (who, X) #walk NPC to Y (who, Y) #write color (index, element, value) #Y sort children (handle)