From bb781f21a1652a3667bd908c467e6d5618e368d7 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 8 Jan 2025 10:27:00 +0000 Subject: [PATCH] Arbitrary trainer scripts + map script/trigger softlock prevention (#5033) Script_RunImmediatelyUntilEffect runs a script until either a specified effect may occur or it reaches an end. All existing script commands and natives, and some specials, call Script_RequestEffects which allows us to analyze them. Any downstream script commands/natives/specials will be statically known not to call Script_RequestEffects and treated as if they have all effects. Manually tagging them with requests_effects=1 and calling Script_RequestEffects will make them analyzable. Using these, we're able to execute scripts until they either exit with no effect, or would possibly have an effect. This allows us to: 1. Not run on frame map scripts or triggers if they would have no effect. 2. Immediately run triggers if they only affect flags/vars. This removes the lag frames when biking into the Cycling Road, for example. 3. Migrate on load/on transition/on resume/on return to field/on dive warp scripts onto the global script context if they would block (approximated via SCREFF_HARDWARE). 4. Support arbitrary control flow in trainer scripts. The trainer does not see the player if the script has no effect, and the trainer will use whichever trainerbattle command is branched to. 5. Support arbitrary scripts in trainer scripts. cant_see and cant_see_if_* commands have been introduced so that scripts are able to do something when the player interacts with the trainer even if that trainer wouldn't see them. --- asm/macros/event.inc | 158 +++++--- constants/gba_constants.inc | 3 + data/maps/Route110/scripts.inc | 1 + data/script_cmd_table.inc | 469 ++++++++++++------------ data/scripts/trainer_script.inc | 6 + data/specials.inc | 13 +- include/event_scripts.h | 1 + include/script.h | 112 +++++- include/test/overworld_script.h | 11 + src/battle_ai_main.c | 2 + src/battle_main.c | 4 + src/difficulty.c | 10 + src/event_object_movement.c | 22 +- src/fake_rtc.c | 7 + src/field_control_avatar.c | 9 +- src/field_player_avatar.c | 2 + src/main.c | 4 +- src/overworld.c | 5 + src/scrcmd.c | 613 ++++++++++++++++++++++++++++++-- src/script.c | 150 +++++++- src/script_pokemon_util.c | 22 ++ src/trainer_see.c | 74 ++-- test/script.c | 116 ++++++ 23 files changed, 1475 insertions(+), 339 deletions(-) create mode 100644 test/script.c diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 2bb2027cb1..6c1547fafc 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -251,15 +251,26 @@ .endm @ Calls the native C function stored at func. - .macro callnative func:req + @ 'callnative's should set 'requests_effects=1' if the native + @ contains a call to 'Script_RequestEffects', which allows them + @ to be analyzed by 'RunScriptImmediatelyUntilEffect'. + .macro callnative func:req, requests_effects=0 .byte 0x23 + .if \requests_effects == 0 .4byte \func + .else + .4byte \func + ROM_SIZE + .endif .endm @ Replaces the script with the function stored at func. Execution returns to the bytecode script when func returns TRUE. - .macro gotonative func:req + .macro gotonative func:req, requests_effects=0 .byte 0x24 + .if \requests_effects == 0 .4byte \func + .else + .4byte \func + ROM_SIZE + .endif .endm @ Calls a function listed in the table in data/specials.inc. @@ -997,9 +1008,9 @@ @ Gives the player a Pokémon of the specified species and level, and allows to customize extra parameters. @ VAR_RESULT will be set to MON_GIVEN_TO_PARTY, MON_GIVEN_TO_PC, or MON_CANT_GIVE depending on the outcome. .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, ggMaxFactor, teraType - callnative ScrCmd_createmon + callnative ScrCmd_createmon, requests_effects=1 .byte 0 - .byte 6 @ PARTY_SIZE - assign to first empty slot + .byte PARTY_SIZE @ assign to first empty slot .set givemon_flags, 0 .2byte \species .2byte \level @@ -1057,7 +1068,7 @@ @ creates a mon for a given party and slot @ otherwise .macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, ggMaxFactor, teraType - callnative ScrCmd_createmon + callnative ScrCmd_createmon, requests_effects=1 .byte \side @ 0 - player, 1 - opponent .byte \slot @ 0-5 .set givemon_flags, 0 @@ -2103,7 +2114,7 @@ .endm .macro setdynamicaifunc func:req - callnative ScriptSetDynamicAiFunc + callnative ScriptSetDynamicAiFunc, requests_effects=1 .4byte \func .endm @@ -2112,7 +2123,7 @@ @ The rest of the arguments are the stat change values to each stat. @ For example, giving the first opponent +1 to atk and -2 to speed would be: settotemboost B_POSITION_OPPONENT_LEFT, 1, 0, -2 .macro settotemboost battler:req, atk=0,def=0,speed=0,spatk=0,spdef=0,acc=0,evas=0 - callnative ScriptSetTotemBoost + callnative ScriptSetTotemBoost, requests_effects=1 .2byte \battler .2byte \atk .2byte \def @@ -2183,21 +2194,21 @@ .macro ai_vs_ai_battle trainer1:req, trainer2:req setflag B_FLAG_AI_VS_AI_BATTLE setvar VAR_0x8004, \trainer1 - callnative CreateTrainerPartyForPlayer + callnative CreateTrainerPartyForPlayer, requests_effects=1 trainerbattle_no_intro \trainer2, NULL .endm @ Sets VAR_RESULT to TRUE if stat can be hyper trained, or to @ FALSE otherwise. .macro canhypertrain stat:req, slot:req - callnative CanHyperTrain + callnative CanHyperTrain, requests_effects=1 .byte \stat .2byte \slot .endm @ Hyper Trains a stat. .macro hypertrain stat:req, slot:req - callnative HyperTrain + callnative HyperTrain, requests_effects=1 .byte \stat .2byte \slot .endm @@ -2205,7 +2216,7 @@ @ Sets VAR_RESULT to TRUE if the Pokemon has the Gigantamax Factor, @ or to FALSE otherwise. .macro hasgigantamaxfactor slot:req - callnative HasGigantamaxFactor + callnative HasGigantamaxFactor, requests_effects=1 .2byte \slot .endm @@ -2213,7 +2224,7 @@ @ Fails for Melmetal (vanilla behavior). @ Sets VAR_RESULT to TRUE if it succeeds, and FALSE otherwise. .macro togglegigantamaxfactor slot:req - callnative ToggleGigantamaxFactor + callnative ToggleGigantamaxFactor, requests_effects=1 .2byte \slot .endm @@ -2251,27 +2262,27 @@ @ Inflicts \status1 to the Pokémon in \slot. @ If \slot is greater or equal than PARTY_SIZE, the status is inflicted on each of the Player's Pokémon. .macro setstatus1 status1:req, slot:req - callnative Script_SetStatus1 + callnative Script_SetStatus1, requests_effects=1 .2byte \status1 .2byte \slot .endm @ Sets VAR_RESULT to the Pokémon in \slot's Tera Type .macro checkteratype slot:req - callnative CheckTeraType + callnative CheckTeraType, requests_effects=1 .2byte \slot .endm @ Sets the Pokémon in \slot's Tera Type .macro setteratype type:req, slot:req - callnative SetTeraType + callnative SetTeraType, requests_effects=1 .byte \type .2byte \slot .endm @ Saves species and forms of Daycare Pokémon to specific vars. Saves the amount of Daycare mon to VAR_RESULT. .macro getdaycaregfx varSpecies1:req varSpecies2:req varForm1:req varForm2:req - callnative GetDaycareGraphics + callnative GetDaycareGraphics, requests_effects=1 .2byte \varSpecies1 .2byte \varSpecies2 .2byte \varForm1 @@ -2280,12 +2291,12 @@ @ Plays the cry of the first alive party member. .macro playfirstmoncry - callnative PlayFirstMonCry + callnative PlayFirstMonCry, requests_effects=1 .endm @ Buffers the nickname of the first alive party member. .macro bufferlivemonnickname out:req - callnative BufferFirstLiveMonNickname + callnative BufferFirstLiveMonNickname, requests_effects=1 .byte \out .endm @@ -2296,13 +2307,13 @@ @ Checks if Field move is being used by the current follower. .macro isfollowerfieldmoveuser var:req - callnative IsFollowerFieldMoveUser + callnative IsFollowerFieldMoveUser, requests_effects=1 .2byte \var .endm @ Saves the direction from where source object event would need to turn to to face the target into the specified var. .macro getdirectiontoface var:req, sourceId:req, targetId:req - callnative GetDirectionToFaceScript + callnative GetDirectionToFaceScript, requests_effects=1 .2byte \var .byte \sourceId .byte \targetId @@ -2311,50 +2322,50 @@ @ set the wild double battle flag @ can be used in conjunection with createmon to set up a wild battle with 2 player mons vs. 1 enemy mon .macro setwilddoubleflag - callnative ScriptSetDoubleBattleFlag + callnative ScriptSetDoubleBattleFlag, requests_effects=1 .endm @ When OW_USE_FAKE_RTC and OW_FLAG_PAUSE_TIME is assigned, this macro will stop the flow of time. .macro pausefakertc - callnative Script_PauseFakeRtc + callnative Script_PauseFakeRtc, requests_effects=1 .endm @ When OW_USE_FAKE_RTC and OW_FLAG_PAUSE_TIME is assigned, this macro will resume the flow of time. .macro resumefakertc - callnative Script_ResumeFakeRtc + callnative Script_ResumeFakeRtc, requests_effects=1 .endm @ When OW_USE_FAKE_RTC and OW_FLAG_PAUSE_TIME is assigned, this macro will resume the flow of time if paused, and stop the flow of time otherwise. .macro togglefakertc - callnative Script_ToggleFakeRtc + callnative Script_ToggleFakeRtc, requests_effects=1 .endm @ ============================ @ @ ITEM DESCRIPTION HEADER MACROS @ Used with OW_SHOW_ITEM_DESCRIPTIONS config .macro showitemdescription - callnative ScriptShowItemDescription + callnative ScriptShowItemDescription, requests_effects=1 .byte 0 .endm .macro showberrydescription - callnative ScriptShowItemDescription + callnative ScriptShowItemDescription, requests_effects=1 .byte 1 .endm .macro hideitemdescription - callnative ScriptHideItemDescription + callnative ScriptHideItemDescription, requests_effects=1 .endm @ Remove all of specified item from the player's bag and return the number of removed items to VAR_RESULT .macro removeallitem itemId:req - callnative ScrCmd_removeallitem + callnative ScrCmd_removeallitem, requests_effects=1 .2byte \itemId .endm @ Stores the position of the given object in destX and destY. Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will take the object's template position. .macro getobjectxy localId:req, posType:req, destX:req, destY:req - callnative ScrCmd_getobjectxy + callnative ScrCmd_getobjectxy, request_effects=1 .2byte \localId .2byte \posType .2byte \destX @@ -2362,7 +2373,7 @@ .endm .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION, destX:req, destY:req - callnative ScrCmd_getobjectxy + callnative ScrCmd_getobjectxy, request_effects=1 .2byte \localId .2byte \posType .2byte \destX @@ -2370,7 +2381,7 @@ .endm .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION, destX:req, destY:req - callnative ScrCmd_getobjectxy + callnative ScrCmd_getobjectxy, request_effects=1 .2byte \localId .2byte \posType .2byte \destX @@ -2379,7 +2390,7 @@ @ Return TRUE to dest if there is an object at the position x and y. .macro checkobjectat x:req, y:req, dest = VAR_RESULT - callnative ScrCmd_checkobjectat + callnative ScrCmd_checkobjectat, request_effects=1 .2byte \x .2byte \y .2byte \dest @@ -2387,28 +2398,28 @@ @ Returns the state of the Pokedex Seen Flag to VAR_RESULT for the Pokemon with speciesId .macro getseenmon species:req - callnative Scrcmd_getsetpokedexflag + callnative Scrcmd_getsetpokedexflag, request_effects=1 .2byte \species .2byte FLAG_GET_SEEN .endm @ Returns the state of the Pokedex Caught Flag to VAR_RESULT for the Pokemon with speciesId .macro getcaughtmon species:req - callnative Scrcmd_getsetpokedexflag + callnative Scrcmd_getsetpokedexflag, request_effects=1 .2byte \species .2byte FLAG_GET_CAUGHT .endm @ Sets the Pokedex Seen Flag for the Pokemon with speciesId .macro setseenmon species:req - callnative Scrcmd_getsetpokedexflag + callnative Scrcmd_getsetpokedexflag, request_effects=1 .2byte \species .2byte FLAG_SET_SEEN .endm @ Sets the Pokedex Caught Flag for the Pokemon with speciesId .macro setcaughtmon species:req - callnative Scrcmd_getsetpokedexflag + callnative Scrcmd_getsetpokedexflag, request_effects=1 .2byte \species .2byte FLAG_SET_CAUGHT .endm @@ -2418,10 +2429,10 @@ .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon waitstate - callnative Scrcmd_checkspecies_choose + callnative Scrcmd_checkspecies_choose, request_effects=1 .2byte \speciesId .else - callnative Scrcmd_checkspecies + callnative Scrcmd_checkspecies, request_effects=1 .2byte \speciesId .endif .endm @@ -2432,24 +2443,85 @@ @ Gets the facing direction of a given event object and stores it in the variable dest. .macro getobjectfacingdirection localId:req, dest:req - callnative Scrcmd_getobjectfacingdirection + callnative Scrcmd_getobjectfacingdirection, request_effects=1 .2byte \localId .2byte \dest .endm .macro increasedifficulty - callnative Script_IncreaseDifficulty + callnative Script_IncreaseDifficulty, requests_effects=1 .endm .macro decreasedifficulty - callnative Script_DecreaseDifficulty + callnative Script_DecreaseDifficulty, requests_effects=1 .endm .macro getdifficulty var:req - callnative Script_GetDifficulty + callnative Script_GetDifficulty, requests_effects=1 .endm .macro setdifficulty difficulty:req - callnative Script_SetDifficulty + callnative Script_SetDifficulty, requests_effects=1 .byte \difficulty .endm + + @ Makes the trainer unable to see the player if executed. + @ This is a no-op if the player interacts with the trainer. + .macro cant_see_if, condition:req + callnative Script_EndTrainerCanSeeIf, requests_effects=1 + .byte \condition + .endm + + .macro cant_see + cant_see_if_unset 0 @ flag 0 is always FALSE. + .endm + + .macro cant_see_if_unset, flag:req + checkflag \flag + cant_see_if FALSE + .endm + + .macro cant_see_if_set, flag:req + checkflag \flag + cant_see_if TRUE + .endm + + .macro cant_see_if_trainerflag_unset, trainer:req + checktrainerflag \trainer + cant_see_if FALSE + .endm + + .macro cant_see_if_trainerflag_set, trainer:req + checktrainerflag \trainer + cant_see_if TRUE + .endm + + .macro cant_see_if_lt, a:req, b:req + compare \a, \b + cant_see_if 0 + .endm + + .macro cant_see_if_eq, a:req, b:req + compare \a, \b + cant_see_if 1 + .endm + + .macro cant_see_if_gt, a:req, b:req + compare \a, \b + cant_see_if 2 + .endm + + .macro cant_see_if_le, a:req, b:req + compare \a, \b + cant_see_if 3 + .endm + + .macro cant_see_if_ge, a:req, b:req + compare \a, \b + cant_see_if 4 + .endm + + .macro cant_see_if_ne, a:req, b:req + compare \a, \b + cant_see_if 5 + .endm diff --git a/constants/gba_constants.inc b/constants/gba_constants.inc index 9f51b0f02b..febb1244d0 100644 --- a/constants/gba_constants.inc +++ b/constants/gba_constants.inc @@ -29,6 +29,9 @@ .set OAM, 0x7000000 + .set ROM, 0x8000000 + .set ROM_SIZE, 0x2000000 + .set SOUND_INFO_PTR, 0x3007FF0 .set INTR_CHECK, 0x3007FF8 .set INTR_VECTOR, 0x3007FFC diff --git a/data/maps/Route110/scripts.inc b/data/maps/Route110/scripts.inc index 8447604aca..7c494a586d 100644 --- a/data/maps/Route110/scripts.inc +++ b/data/maps/Route110/scripts.inc @@ -11,6 +11,7 @@ Route110_MapScripts:: Route110_OnResume: special UpdateCyclingRoadState + msgbox Route110_Text_WeCantTalkAboutAquaActivities, MSGBOX_SIGN end Route110_OnTransition: diff --git a/data/script_cmd_table.inc b/data/script_cmd_table.inc index 711b118cc6..61046e6cb4 100644 --- a/data/script_cmd_table.inc +++ b/data/script_cmd_table.inc @@ -1,234 +1,245 @@ +@ 'requests_effects' should be set to 1 if the command contains a call +@ to 'Script_RequestEffects', which allows it to be analyzed with +@ 'RunScriptImmediatelyUntilEffect'. +.macro cmd func:req, requests_effects=0 + .if \requests_effects == 0 + .4byte \func + .else + .4byte \func + ROM_SIZE + .endif +.endm + .align 2 gScriptCmdTable:: - .4byte ScrCmd_nop @ 0x00 - .4byte ScrCmd_nop1 @ 0x01 - .4byte ScrCmd_end @ 0x02 - .4byte ScrCmd_return @ 0x03 - .4byte ScrCmd_call @ 0x04 - .4byte ScrCmd_goto @ 0x05 - .4byte ScrCmd_goto_if @ 0x06 - .4byte ScrCmd_call_if @ 0x07 - .4byte ScrCmd_gotostd @ 0x08 - .4byte ScrCmd_callstd @ 0x09 - .4byte ScrCmd_gotostd_if @ 0x0a - .4byte ScrCmd_callstd_if @ 0x0b - .4byte ScrCmd_returnram @ 0x0c - .4byte ScrCmd_endram @ 0x0d - .4byte ScrCmd_setmysteryeventstatus @ 0x0e - .4byte ScrCmd_loadword @ 0x0f - .4byte ScrCmd_loadbyte @ 0x10 - .4byte ScrCmd_setptr @ 0x11 - .4byte ScrCmd_loadbytefromptr @ 0x12 - .4byte ScrCmd_setptrbyte @ 0x13 - .4byte ScrCmd_copylocal @ 0x14 - .4byte ScrCmd_copybyte @ 0x15 - .4byte ScrCmd_setvar @ 0x16 - .4byte ScrCmd_addvar @ 0x17 - .4byte ScrCmd_subvar @ 0x18 - .4byte ScrCmd_copyvar @ 0x19 - .4byte ScrCmd_setorcopyvar @ 0x1a - .4byte ScrCmd_compare_local_to_local @ 0x1b - .4byte ScrCmd_compare_local_to_value @ 0x1c - .4byte ScrCmd_compare_local_to_ptr @ 0x1d - .4byte ScrCmd_compare_ptr_to_local @ 0x1e - .4byte ScrCmd_compare_ptr_to_value @ 0x1f - .4byte ScrCmd_compare_ptr_to_ptr @ 0x20 - .4byte ScrCmd_compare_var_to_value @ 0x21 - .4byte ScrCmd_compare_var_to_var @ 0x22 - .4byte ScrCmd_callnative @ 0x23 - .4byte ScrCmd_gotonative @ 0x24 - .4byte ScrCmd_special @ 0x25 - .4byte ScrCmd_specialvar @ 0x26 - .4byte ScrCmd_waitstate @ 0x27 - .4byte ScrCmd_delay @ 0x28 - .4byte ScrCmd_setflag @ 0x29 - .4byte ScrCmd_clearflag @ 0x2a - .4byte ScrCmd_checkflag @ 0x2b - .4byte ScrCmd_initclock @ 0x2c - .4byte ScrCmd_dotimebasedevents @ 0x2d - .4byte ScrCmd_gettime @ 0x2e - .4byte ScrCmd_playse @ 0x2f - .4byte ScrCmd_waitse @ 0x30 - .4byte ScrCmd_playfanfare @ 0x31 - .4byte ScrCmd_waitfanfare @ 0x32 - .4byte ScrCmd_playbgm @ 0x33 - .4byte ScrCmd_savebgm @ 0x34 - .4byte ScrCmd_fadedefaultbgm @ 0x35 - .4byte ScrCmd_fadenewbgm @ 0x36 - .4byte ScrCmd_fadeoutbgm @ 0x37 - .4byte ScrCmd_fadeinbgm @ 0x38 - .4byte ScrCmd_warp @ 0x39 - .4byte ScrCmd_warpsilent @ 0x3a - .4byte ScrCmd_warpdoor @ 0x3b - .4byte ScrCmd_warphole @ 0x3c - .4byte ScrCmd_warpteleport @ 0x3d - .4byte ScrCmd_setwarp @ 0x3e - .4byte ScrCmd_setdynamicwarp @ 0x3f - .4byte ScrCmd_setdivewarp @ 0x40 - .4byte ScrCmd_setholewarp @ 0x41 - .4byte ScrCmd_getplayerxy @ 0x42 - .4byte ScrCmd_getpartysize @ 0x43 - .4byte ScrCmd_additem @ 0x44 - .4byte ScrCmd_removeitem @ 0x45 - .4byte ScrCmd_checkitemspace @ 0x46 - .4byte ScrCmd_checkitem @ 0x47 - .4byte ScrCmd_checkitemtype @ 0x48 - .4byte ScrCmd_addpcitem @ 0x49 - .4byte ScrCmd_checkpcitem @ 0x4a - .4byte ScrCmd_adddecoration @ 0x4b - .4byte ScrCmd_removedecoration @ 0x4c - .4byte ScrCmd_checkdecor @ 0x4d - .4byte ScrCmd_checkdecorspace @ 0x4e - .4byte ScrCmd_applymovement @ 0x4f - .4byte ScrCmd_applymovementat @ 0x50 - .4byte ScrCmd_waitmovement @ 0x51 - .4byte ScrCmd_waitmovementat @ 0x52 - .4byte ScrCmd_removeobject @ 0x53 - .4byte ScrCmd_removeobjectat @ 0x54 - .4byte ScrCmd_addobject @ 0x55 - .4byte ScrCmd_addobjectat @ 0x56 - .4byte ScrCmd_setobjectxy @ 0x57 - .4byte ScrCmd_showobjectat @ 0x58 - .4byte ScrCmd_hideobjectat @ 0x59 - .4byte ScrCmd_faceplayer @ 0x5a - .4byte ScrCmd_turnobject @ 0x5b - .4byte ScrCmd_trainerbattle @ 0x5c - .4byte ScrCmd_dotrainerbattle @ 0x5d - .4byte ScrCmd_gotopostbattlescript @ 0x5e - .4byte ScrCmd_gotobeatenscript @ 0x5f - .4byte ScrCmd_checktrainerflag @ 0x60 - .4byte ScrCmd_settrainerflag @ 0x61 - .4byte ScrCmd_cleartrainerflag @ 0x62 - .4byte ScrCmd_setobjectxyperm @ 0x63 - .4byte ScrCmd_copyobjectxytoperm @ 0x64 - .4byte ScrCmd_setobjectmovementtype @ 0x65 - .4byte ScrCmd_waitmessage @ 0x66 - .4byte ScrCmd_message @ 0x67 - .4byte ScrCmd_closemessage @ 0x68 - .4byte ScrCmd_lockall @ 0x69 - .4byte ScrCmd_lock @ 0x6a - .4byte ScrCmd_releaseall @ 0x6b - .4byte ScrCmd_release @ 0x6c - .4byte ScrCmd_waitbuttonpress @ 0x6d - .4byte ScrCmd_yesnobox @ 0x6e - .4byte ScrCmd_multichoice @ 0x6f - .4byte ScrCmd_multichoicedefault @ 0x70 - .4byte ScrCmd_multichoicegrid @ 0x71 - .4byte ScrCmd_drawbox @ 0x72 - .4byte ScrCmd_erasebox @ 0x73 - .4byte ScrCmd_drawboxtext @ 0x74 - .4byte ScrCmd_showmonpic @ 0x75 - .4byte ScrCmd_hidemonpic @ 0x76 - .4byte ScrCmd_showcontestpainting @ 0x77 - .4byte ScrCmd_braillemessage @ 0x78 - .4byte ScrCmd_nop1 @ 0x79 - .4byte ScrCmd_giveegg @ 0x7a - .4byte ScrCmd_setmonmove @ 0x7b - .4byte ScrCmd_checkpartymove @ 0x7c - .4byte ScrCmd_bufferspeciesname @ 0x7d - .4byte ScrCmd_bufferleadmonspeciesname @ 0x7e - .4byte ScrCmd_bufferpartymonnick @ 0x7f - .4byte ScrCmd_bufferitemname @ 0x80 - .4byte ScrCmd_bufferdecorationname @ 0x81 - .4byte ScrCmd_buffermovename @ 0x82 - .4byte ScrCmd_buffernumberstring @ 0x83 - .4byte ScrCmd_bufferstdstring @ 0x84 - .4byte ScrCmd_bufferstring @ 0x85 - .4byte ScrCmd_pokemart @ 0x86 - .4byte ScrCmd_pokemartdecoration @ 0x87 - .4byte ScrCmd_pokemartdecoration2 @ 0x88 - .4byte ScrCmd_playslotmachine @ 0x89 - .4byte ScrCmd_setberrytree @ 0x8a - .4byte ScrCmd_choosecontestmon @ 0x8b - .4byte ScrCmd_startcontest @ 0x8c - .4byte ScrCmd_showcontestresults @ 0x8d - .4byte ScrCmd_contestlinktransfer @ 0x8e - .4byte ScrCmd_random @ 0x8f - .4byte ScrCmd_addmoney @ 0x90 - .4byte ScrCmd_removemoney @ 0x91 - .4byte ScrCmd_checkmoney @ 0x92 - .4byte ScrCmd_showmoneybox @ 0x93 - .4byte ScrCmd_hidemoneybox @ 0x94 - .4byte ScrCmd_updatemoneybox @ 0x95 - .4byte ScrCmd_getpokenewsactive @ 0x96 - .4byte ScrCmd_fadescreen @ 0x97 - .4byte ScrCmd_fadescreenspeed @ 0x98 - .4byte ScrCmd_setflashlevel @ 0x99 - .4byte ScrCmd_animateflash @ 0x9a - .4byte ScrCmd_messageautoscroll @ 0x9b - .4byte ScrCmd_dofieldeffect @ 0x9c - .4byte ScrCmd_setfieldeffectargument @ 0x9d - .4byte ScrCmd_waitfieldeffect @ 0x9e - .4byte ScrCmd_setrespawn @ 0x9f - .4byte ScrCmd_checkplayergender @ 0xa0 - .4byte ScrCmd_playmoncry @ 0xa1 - .4byte ScrCmd_setmetatile @ 0xa2 - .4byte ScrCmd_resetweather @ 0xa3 - .4byte ScrCmd_setweather @ 0xa4 - .4byte ScrCmd_doweather @ 0xa5 - .4byte ScrCmd_setstepcallback @ 0xa6 - .4byte ScrCmd_setmaplayoutindex @ 0xa7 - .4byte ScrCmd_setobjectsubpriority @ 0xa8 - .4byte ScrCmd_resetobjectsubpriority @ 0xa9 - .4byte ScrCmd_createvobject @ 0xaa - .4byte ScrCmd_turnvobject @ 0xab - .4byte ScrCmd_opendoor @ 0xac - .4byte ScrCmd_closedoor @ 0xad - .4byte ScrCmd_waitdooranim @ 0xae - .4byte ScrCmd_setdooropen @ 0xaf - .4byte ScrCmd_setdoorclosed @ 0xb0 - .4byte ScrCmd_addelevmenuitem @ 0xb1 - .4byte ScrCmd_showelevmenu @ 0xb2 - .4byte ScrCmd_checkcoins @ 0xb3 - .4byte ScrCmd_addcoins @ 0xb4 - .4byte ScrCmd_removecoins @ 0xb5 - .4byte ScrCmd_setwildbattle @ 0xb6 - .4byte ScrCmd_dowildbattle @ 0xb7 - .4byte ScrCmd_setvaddress @ 0xb8 - .4byte ScrCmd_vgoto @ 0xb9 - .4byte ScrCmd_vcall @ 0xba - .4byte ScrCmd_vgoto_if @ 0xbb - .4byte ScrCmd_vcall_if @ 0xbc - .4byte ScrCmd_vmessage @ 0xbd - .4byte ScrCmd_vbuffermessage @ 0xbe - .4byte ScrCmd_vbufferstring @ 0xbf - .4byte ScrCmd_showcoinsbox @ 0xc0 - .4byte ScrCmd_hidecoinsbox @ 0xc1 - .4byte ScrCmd_updatecoinsbox @ 0xc2 - .4byte ScrCmd_incrementgamestat @ 0xc3 - .4byte ScrCmd_setescapewarp @ 0xc4 - .4byte ScrCmd_waitmoncry @ 0xc5 - .4byte ScrCmd_bufferboxname @ 0xc6 - .4byte ScrCmd_nop1 @ 0xc7 - .4byte ScrCmd_nop1 @ 0xc8 - .4byte ScrCmd_nop1 @ 0xc9 - .4byte ScrCmd_nop1 @ 0xca - .4byte ScrCmd_nop1 @ 0xcb - .4byte ScrCmd_nop1 @ 0xcc - .4byte ScrCmd_setmodernfatefulencounter @ 0xcd - .4byte ScrCmd_checkmodernfatefulencounter @ 0xce - .4byte ScrCmd_trywondercardscript @ 0xcf - .4byte ScrCmd_nop1 @ 0xd0 - .4byte ScrCmd_warpspinenter @ 0xd1 - .4byte ScrCmd_setmonmetlocation @ 0xd2 - .4byte ScrCmd_moverotatingtileobjects @ 0xd3 - .4byte ScrCmd_turnrotatingtileobjects @ 0xd4 - .4byte ScrCmd_initrotatingtilepuzzle @ 0xd5 - .4byte ScrCmd_freerotatingtilepuzzle @ 0xd6 - .4byte ScrCmd_warpmossdeepgym @ 0xd7 - .4byte ScrCmd_selectapproachingtrainer @ 0xd8 - .4byte ScrCmd_lockfortrainer @ 0xd9 - .4byte ScrCmd_closebraillemessage @ 0xda - .4byte ScrCmd_messageinstant @ 0xdb - .4byte ScrCmd_fadescreenswapbuffers @ 0xdc - .4byte ScrCmd_buffertrainerclassname @ 0xdd - .4byte ScrCmd_buffertrainername @ 0xde - .4byte ScrCmd_pokenavcall @ 0xdf - .4byte ScrCmd_warpwhitefade @ 0xe0 - .4byte ScrCmd_buffercontestname @ 0xe1 - .4byte ScrCmd_bufferitemnameplural @ 0xe2 - .4byte ScrCmd_dynmultichoice @ 0xe3 - .4byte ScrCmd_dynmultipush @ 0xe4 + cmd ScrCmd_nop, requests_effects=1 @ 0x00 + cmd ScrCmd_nop1, requests_effects=1 @ 0x01 + cmd ScrCmd_end, requests_effects=1 @ 0x02 + cmd ScrCmd_return, requests_effects=1 @ 0x03 + cmd ScrCmd_call, requests_effects=1 @ 0x04 + cmd ScrCmd_goto, requests_effects=1 @ 0x05 + cmd ScrCmd_goto_if, requests_effects=1 @ 0x06 + cmd ScrCmd_call_if, requests_effects=1 @ 0x07 + cmd ScrCmd_gotostd, requests_effects=1 @ 0x08 + cmd ScrCmd_callstd, requests_effects=1 @ 0x09 + cmd ScrCmd_gotostd_if, requests_effects=1 @ 0x0a + cmd ScrCmd_callstd_if, requests_effects=1 @ 0x0b + cmd ScrCmd_returnram, requests_effects=1 @ 0x0c + cmd ScrCmd_endram, requests_effects=1 @ 0x0d + cmd ScrCmd_setmysteryeventstatus, requests_effects=1 @ 0x0e + cmd ScrCmd_loadword, requests_effects=1 @ 0x0f + cmd ScrCmd_loadbyte, requests_effects=1 @ 0x10 + cmd ScrCmd_setptr, requests_effects=1 @ 0x11 + cmd ScrCmd_loadbytefromptr, requests_effects=1 @ 0x12 + cmd ScrCmd_setptrbyte, requests_effects=1 @ 0x13 + cmd ScrCmd_copylocal, requests_effects=1 @ 0x14 + cmd ScrCmd_copybyte, requests_effects=1 @ 0x15 + cmd ScrCmd_setvar, requests_effects=1 @ 0x16 + cmd ScrCmd_addvar, requests_effects=1 @ 0x17 + cmd ScrCmd_subvar, requests_effects=1 @ 0x18 + cmd ScrCmd_copyvar, requests_effects=1 @ 0x19 + cmd ScrCmd_setorcopyvar, requests_effects=1 @ 0x1a + cmd ScrCmd_compare_local_to_local, requests_effects=1 @ 0x1b + cmd ScrCmd_compare_local_to_value, requests_effects=1 @ 0x1c + cmd ScrCmd_compare_local_to_ptr, requests_effects=1 @ 0x1d + cmd ScrCmd_compare_ptr_to_local, requests_effects=1 @ 0x1e + cmd ScrCmd_compare_ptr_to_value, requests_effects=1 @ 0x1f + cmd ScrCmd_compare_ptr_to_ptr, requests_effects=1 @ 0x20 + cmd ScrCmd_compare_var_to_value, requests_effects=1 @ 0x21 + cmd ScrCmd_compare_var_to_var, requests_effects=1 @ 0x22 + cmd ScrCmd_callnative, requests_effects=1 @ 0x23 + cmd ScrCmd_gotonative, requests_effects=1 @ 0x24 + cmd ScrCmd_special, requests_effects=1 @ 0x25 + cmd ScrCmd_specialvar, requests_effects=1 @ 0x26 + cmd ScrCmd_waitstate, requests_effects=1 @ 0x27 + cmd ScrCmd_delay, requests_effects=1 @ 0x28 + cmd ScrCmd_setflag, requests_effects=1 @ 0x29 + cmd ScrCmd_clearflag, requests_effects=1 @ 0x2a + cmd ScrCmd_checkflag, requests_effects=1 @ 0x2b + cmd ScrCmd_initclock, requests_effects=1 @ 0x2c + cmd ScrCmd_dotimebasedevents, requests_effects=1 @ 0x2d + cmd ScrCmd_gettime, requests_effects=1 @ 0x2e + cmd ScrCmd_playse, requests_effects=1 @ 0x2f + cmd ScrCmd_waitse, requests_effects=1 @ 0x30 + cmd ScrCmd_playfanfare, requests_effects=1 @ 0x31 + cmd ScrCmd_waitfanfare, requests_effects=1 @ 0x32 + cmd ScrCmd_playbgm, requests_effects=1 @ 0x33 + cmd ScrCmd_savebgm, requests_effects=1 @ 0x34 + cmd ScrCmd_fadedefaultbgm, requests_effects=1 @ 0x35 + cmd ScrCmd_fadenewbgm, requests_effects=1 @ 0x36 + cmd ScrCmd_fadeoutbgm, requests_effects=1 @ 0x37 + cmd ScrCmd_fadeinbgm, requests_effects=1 @ 0x38 + cmd ScrCmd_warp, requests_effects=1 @ 0x39 + cmd ScrCmd_warpsilent, requests_effects=1 @ 0x3a + cmd ScrCmd_warpdoor, requests_effects=1 @ 0x3b + cmd ScrCmd_warphole, requests_effects=1 @ 0x3c + cmd ScrCmd_warpteleport, requests_effects=1 @ 0x3d + cmd ScrCmd_setwarp, requests_effects=1 @ 0x3e + cmd ScrCmd_setdynamicwarp, requests_effects=1 @ 0x3f + cmd ScrCmd_setdivewarp, requests_effects=1 @ 0x40 + cmd ScrCmd_setholewarp, requests_effects=1 @ 0x41 + cmd ScrCmd_getplayerxy, requests_effects=1 @ 0x42 + cmd ScrCmd_getpartysize, requests_effects=1 @ 0x43 + cmd ScrCmd_additem, requests_effects=1 @ 0x44 + cmd ScrCmd_removeitem, requests_effects=1 @ 0x45 + cmd ScrCmd_checkitemspace, requests_effects=1 @ 0x46 + cmd ScrCmd_checkitem, requests_effects=1 @ 0x47 + cmd ScrCmd_checkitemtype, requests_effects=1 @ 0x48 + cmd ScrCmd_addpcitem, requests_effects=1 @ 0x49 + cmd ScrCmd_checkpcitem, requests_effects=1 @ 0x4a + cmd ScrCmd_adddecoration, requests_effects=1 @ 0x4b + cmd ScrCmd_removedecoration, requests_effects=1 @ 0x4c + cmd ScrCmd_checkdecor, requests_effects=1 @ 0x4d + cmd ScrCmd_checkdecorspace, requests_effects=1 @ 0x4e + cmd ScrCmd_applymovement, requests_effects=1 @ 0x4f + cmd ScrCmd_applymovementat, requests_effects=1 @ 0x50 + cmd ScrCmd_waitmovement, requests_effects=1 @ 0x51 + cmd ScrCmd_waitmovementat, requests_effects=1 @ 0x52 + cmd ScrCmd_removeobject, requests_effects=1 @ 0x53 + cmd ScrCmd_removeobjectat, requests_effects=1 @ 0x54 + cmd ScrCmd_addobject, requests_effects=1 @ 0x55 + cmd ScrCmd_addobjectat, requests_effects=1 @ 0x56 + cmd ScrCmd_setobjectxy, requests_effects=1 @ 0x57 + cmd ScrCmd_showobjectat, requests_effects=1 @ 0x58 + cmd ScrCmd_hideobjectat, requests_effects=1 @ 0x59 + cmd ScrCmd_faceplayer, requests_effects=1 @ 0x5a + cmd ScrCmd_turnobject, requests_effects=1 @ 0x5b + cmd ScrCmd_trainerbattle, requests_effects=1 @ 0x5c + cmd ScrCmd_dotrainerbattle, requests_effects=1 @ 0x5d + cmd ScrCmd_gotopostbattlescript, requests_effects=1 @ 0x5e + cmd ScrCmd_gotobeatenscript, requests_effects=1 @ 0x5f + cmd ScrCmd_checktrainerflag, requests_effects=1 @ 0x60 + cmd ScrCmd_settrainerflag, requests_effects=1 @ 0x61 + cmd ScrCmd_cleartrainerflag, requests_effects=1 @ 0x62 + cmd ScrCmd_setobjectxyperm, requests_effects=1 @ 0x63 + cmd ScrCmd_copyobjectxytoperm, requests_effects=1 @ 0x64 + cmd ScrCmd_setobjectmovementtype, requests_effects=1 @ 0x65 + cmd ScrCmd_waitmessage, requests_effects=1 @ 0x66 + cmd ScrCmd_message, requests_effects=1 @ 0x67 + cmd ScrCmd_closemessage, requests_effects=1 @ 0x68 + cmd ScrCmd_lockall, requests_effects=1 @ 0x69 + cmd ScrCmd_lock, requests_effects=1 @ 0x6a + cmd ScrCmd_releaseall, requests_effects=1 @ 0x6b + cmd ScrCmd_release, requests_effects=1 @ 0x6c + cmd ScrCmd_waitbuttonpress, requests_effects=1 @ 0x6d + cmd ScrCmd_yesnobox, requests_effects=1 @ 0x6e + cmd ScrCmd_multichoice, requests_effects=1 @ 0x6f + cmd ScrCmd_multichoicedefault, requests_effects=1 @ 0x70 + cmd ScrCmd_multichoicegrid, requests_effects=1 @ 0x71 + cmd ScrCmd_drawbox, requests_effects=1 @ 0x72 + cmd ScrCmd_erasebox, requests_effects=1 @ 0x73 + cmd ScrCmd_drawboxtext, requests_effects=1 @ 0x74 + cmd ScrCmd_showmonpic, requests_effects=1 @ 0x75 + cmd ScrCmd_hidemonpic, requests_effects=1 @ 0x76 + cmd ScrCmd_showcontestpainting, requests_effects=1 @ 0x77 + cmd ScrCmd_braillemessage, requests_effects=1 @ 0x78 + cmd ScrCmd_nop1, requests_effects=1 @ 0x79 + cmd ScrCmd_giveegg, requests_effects=1 @ 0x7a + cmd ScrCmd_setmonmove, requests_effects=1 @ 0x7b + cmd ScrCmd_checkpartymove, requests_effects=1 @ 0x7c + cmd ScrCmd_bufferspeciesname, requests_effects=1 @ 0x7d + cmd ScrCmd_bufferleadmonspeciesname, requests_effects=1 @ 0x7e + cmd ScrCmd_bufferpartymonnick, requests_effects=1 @ 0x7f + cmd ScrCmd_bufferitemname, requests_effects=1 @ 0x80 + cmd ScrCmd_bufferdecorationname, requests_effects=1 @ 0x81 + cmd ScrCmd_buffermovename, requests_effects=1 @ 0x82 + cmd ScrCmd_buffernumberstring, requests_effects=1 @ 0x83 + cmd ScrCmd_bufferstdstring, requests_effects=1 @ 0x84 + cmd ScrCmd_bufferstring, requests_effects=1 @ 0x85 + cmd ScrCmd_pokemart, requests_effects=1 @ 0x86 + cmd ScrCmd_pokemartdecoration, requests_effects=1 @ 0x87 + cmd ScrCmd_pokemartdecoration2, requests_effects=1 @ 0x88 + cmd ScrCmd_playslotmachine, requests_effects=1 @ 0x89 + cmd ScrCmd_setberrytree, requests_effects=1 @ 0x8a + cmd ScrCmd_choosecontestmon, requests_effects=1 @ 0x8b + cmd ScrCmd_startcontest, requests_effects=1 @ 0x8c + cmd ScrCmd_showcontestresults, requests_effects=1 @ 0x8d + cmd ScrCmd_contestlinktransfer, requests_effects=1 @ 0x8e + cmd ScrCmd_random, requests_effects=1 @ 0x8f + cmd ScrCmd_addmoney, requests_effects=1 @ 0x90 + cmd ScrCmd_removemoney, requests_effects=1 @ 0x91 + cmd ScrCmd_checkmoney, requests_effects=1 @ 0x92 + cmd ScrCmd_showmoneybox, requests_effects=1 @ 0x93 + cmd ScrCmd_hidemoneybox, requests_effects=1 @ 0x94 + cmd ScrCmd_updatemoneybox, requests_effects=1 @ 0x95 + cmd ScrCmd_getpokenewsactive, requests_effects=1 @ 0x96 + cmd ScrCmd_fadescreen, requests_effects=1 @ 0x97 + cmd ScrCmd_fadescreenspeed, requests_effects=1 @ 0x98 + cmd ScrCmd_setflashlevel, requests_effects=1 @ 0x99 + cmd ScrCmd_animateflash, requests_effects=1 @ 0x9a + cmd ScrCmd_messageautoscroll, requests_effects=1 @ 0x9b + cmd ScrCmd_dofieldeffect, requests_effects=1 @ 0x9c + cmd ScrCmd_setfieldeffectargument, requests_effects=1 @ 0x9d + cmd ScrCmd_waitfieldeffect, requests_effects=1 @ 0x9e + cmd ScrCmd_setrespawn, requests_effects=1 @ 0x9f + cmd ScrCmd_checkplayergender, requests_effects=1 @ 0xa0 + cmd ScrCmd_playmoncry, requests_effects=1 @ 0xa1 + cmd ScrCmd_setmetatile, requests_effects=1 @ 0xa2 + cmd ScrCmd_resetweather, requests_effects=1 @ 0xa3 + cmd ScrCmd_setweather, requests_effects=1 @ 0xa4 + cmd ScrCmd_doweather, requests_effects=1 @ 0xa5 + cmd ScrCmd_setstepcallback, requests_effects=1 @ 0xa6 + cmd ScrCmd_setmaplayoutindex, requests_effects=1 @ 0xa7 + cmd ScrCmd_setobjectsubpriority, requests_effects=1 @ 0xa8 + cmd ScrCmd_resetobjectsubpriority, requests_effects=1 @ 0xa9 + cmd ScrCmd_createvobject, requests_effects=1 @ 0xaa + cmd ScrCmd_turnvobject, requests_effects=1 @ 0xab + cmd ScrCmd_opendoor, requests_effects=1 @ 0xac + cmd ScrCmd_closedoor, requests_effects=1 @ 0xad + cmd ScrCmd_waitdooranim, requests_effects=1 @ 0xae + cmd ScrCmd_setdooropen, requests_effects=1 @ 0xaf + cmd ScrCmd_setdoorclosed, requests_effects=1 @ 0xb0 + cmd ScrCmd_addelevmenuitem, requests_effects=1 @ 0xb1 + cmd ScrCmd_showelevmenu, requests_effects=1 @ 0xb2 + cmd ScrCmd_checkcoins, requests_effects=1 @ 0xb3 + cmd ScrCmd_addcoins, requests_effects=1 @ 0xb4 + cmd ScrCmd_removecoins, requests_effects=1 @ 0xb5 + cmd ScrCmd_setwildbattle, requests_effects=1 @ 0xb6 + cmd ScrCmd_dowildbattle, requests_effects=1 @ 0xb7 + cmd ScrCmd_setvaddress, requests_effects=1 @ 0xb8 + cmd ScrCmd_vgoto, requests_effects=1 @ 0xb9 + cmd ScrCmd_vcall, requests_effects=1 @ 0xba + cmd ScrCmd_vgoto_if, requests_effects=1 @ 0xbb + cmd ScrCmd_vcall_if, requests_effects=1 @ 0xbc + cmd ScrCmd_vmessage, requests_effects=1 @ 0xbd + cmd ScrCmd_vbuffermessage, requests_effects=1 @ 0xbe + cmd ScrCmd_vbufferstring, requests_effects=1 @ 0xbf + cmd ScrCmd_showcoinsbox, requests_effects=1 @ 0xc0 + cmd ScrCmd_hidecoinsbox, requests_effects=1 @ 0xc1 + cmd ScrCmd_updatecoinsbox, requests_effects=1 @ 0xc2 + cmd ScrCmd_incrementgamestat, requests_effects=1 @ 0xc3 + cmd ScrCmd_setescapewarp, requests_effects=1 @ 0xc4 + cmd ScrCmd_waitmoncry, requests_effects=1 @ 0xc5 + cmd ScrCmd_bufferboxname, requests_effects=1 @ 0xc6 + cmd ScrCmd_nop1, requests_effects=1 @ 0xc7 + cmd ScrCmd_nop1, requests_effects=1 @ 0xc8 + cmd ScrCmd_nop1, requests_effects=1 @ 0xc9 + cmd ScrCmd_nop1, requests_effects=1 @ 0xca + cmd ScrCmd_nop1, requests_effects=1 @ 0xcb + cmd ScrCmd_nop1, requests_effects=1 @ 0xcc + cmd ScrCmd_setmodernfatefulencounter, requests_effects=1 @ 0xcd + cmd ScrCmd_checkmodernfatefulencounter, requests_effects=1 @ 0xce + cmd ScrCmd_trywondercardscript, requests_effects=1 @ 0xcf + cmd ScrCmd_nop1, requests_effects=1 @ 0xd0 + cmd ScrCmd_warpspinenter, requests_effects=1 @ 0xd1 + cmd ScrCmd_setmonmetlocation, requests_effects=1 @ 0xd2 + cmd ScrCmd_moverotatingtileobjects, requests_effects=1 @ 0xd3 + cmd ScrCmd_turnrotatingtileobjects, requests_effects=1 @ 0xd4 + cmd ScrCmd_initrotatingtilepuzzle, requests_effects=1 @ 0xd5 + cmd ScrCmd_freerotatingtilepuzzle, requests_effects=1 @ 0xd6 + cmd ScrCmd_warpmossdeepgym, requests_effects=1 @ 0xd7 + cmd ScrCmd_selectapproachingtrainer, requests_effects=1 @ 0xd8 + cmd ScrCmd_lockfortrainer, requests_effects=1 @ 0xd9 + cmd ScrCmd_closebraillemessage, requests_effects=1 @ 0xda + cmd ScrCmd_messageinstant, requests_effects=1 @ 0xdb + cmd ScrCmd_fadescreenswapbuffers, requests_effects=1 @ 0xdc + cmd ScrCmd_buffertrainerclassname, requests_effects=1 @ 0xdd + cmd ScrCmd_buffertrainername, requests_effects=1 @ 0xde + cmd ScrCmd_pokenavcall, requests_effects=1 @ 0xdf + cmd ScrCmd_warpwhitefade, requests_effects=1 @ 0xe0 + cmd ScrCmd_buffercontestname, requests_effects=1 @ 0xe1 + cmd ScrCmd_bufferitemnameplural, requests_effects=1 @ 0xe2 + cmd ScrCmd_dynmultichoice, requests_effects=1 @ 0xe3 + cmd ScrCmd_dynmultipush, requests_effects=1 @ 0xe4 gScriptCmdTableEnd:: .4byte ScrCmd_nop diff --git a/data/scripts/trainer_script.inc b/data/scripts/trainer_script.inc index c0998aef8e..aad79334ab 100644 --- a/data/scripts/trainer_script.inc +++ b/data/scripts/trainer_script.inc @@ -20,3 +20,9 @@ EventScript_GotoTrainerScript:: gotobeatenscript releaseall end + +EventScript_ObjectApproachPlayer:: + lock + special DoTrainerApproach + waitstate + gotonative LoadTrainerObjectScript diff --git a/data/specials.inc b/data/specials.inc index 4d2bea3d33..9bf3734491 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -1,9 +1,16 @@ -.macro def_special ptr +@ 'requests_effects' should be set to 1 if the special contains a call +@ to 'Script_RequestEffects', which allows it to be analyzed with +@ 'RunScriptImmediatelyUntilEffect'. +.macro def_special ptr:req, requests_effects=0 .global SPECIAL_\ptr .set SPECIAL_\ptr, __special__ .set __special__, __special__ + 1 + .if \requests_effects == 0 .4byte \ptr - .endm + .else + .4byte \ptr + ROM_SIZE + .endif +.endm .set __special__, 0 .align 2 @@ -297,7 +304,7 @@ gSpecials:: def_special WaitWeather def_special BufferEReaderTrainerName def_special GetSlotMachineId - def_special GetPlayerFacingDirection + def_special GetPlayerFacingDirection, requests_effects=TRUE def_special FoundAbandonedShipRoom1Key def_special FoundAbandonedShipRoom2Key def_special FoundAbandonedShipRoom4Key diff --git a/include/event_scripts.h b/include/event_scripts.h index 133d9fa98b..782576a30a 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -36,6 +36,7 @@ extern const u8 EventScript_TryDoDoubleTrainerBattle[]; extern const u8 EventScript_TryDoNormalTrainerBattle[]; extern const u8 EventScript_TryDoDoubleRematchBattle[]; extern const u8 EventScript_TryDoRematchBattle[]; +extern const u8 EventScript_ObjectApproachPlayer[]; extern const u8 BerryTreeScript[]; diff --git a/include/script.h b/include/script.h index 1f3c4f7afb..68e0c9e20e 100644 --- a/include/script.h +++ b/include/script.h @@ -1,6 +1,8 @@ #ifndef GUARD_SCRIPT_H #define GUARD_SCRIPT_H +#include + struct ScriptContext; typedef bool8 (*ScrCmdFunc)(struct ScriptContext *); @@ -11,6 +13,7 @@ struct ScriptContext u8 stackDepth; u8 mode; u8 comparisonResult; + bool8 breakOnTrainerBattle; u8 (*nativePtr)(void); const u8 *scriptPtr; const u8 *stack[20]; @@ -39,12 +42,13 @@ void ScriptContext_Init(void); bool8 ScriptContext_IsEnabled(void); bool8 ScriptContext_RunScript(void); void ScriptContext_SetupScript(const u8 *ptr); +void ScriptContext_ContinueScript(struct ScriptContext *ctx); void ScriptContext_Stop(void); void ScriptContext_Enable(void); void RunScriptImmediately(const u8 *ptr); -u8 *MapHeaderGetScriptTable(u8 tag); +const u8 *MapHeaderGetScriptTable(u8 tag); void MapHeaderRunScriptType(u8 tag); -u8 *MapHeaderCheckScriptTable(u8 tag); +const u8 *MapHeaderCheckScriptTable(u8 tag); void RunOnLoadMapScript(void); void RunOnTransitionMapScript(void); void RunOnResumeMapScript(void); @@ -66,4 +70,108 @@ void SetMovingNpcId(u16 npcId); extern u8 gMsgIsSignPost; extern u8 gMsgBoxIsCancelable; +/* Script effects analysis. + * + * 'RunScriptImmediatelyUntilEffect' executes a script until it reaches + * the first command which calls 'Script_RequestEffects' with an + * effect in 'effects' in which case it returns 'TRUE' and stores the + * current state in 'ctx'; or until it reaches an 'end'/'return' in + * which case it returns 'FALSE'. + * + * 'Script_HasNoEffect' wraps 'RunScriptImmediatelyUntilEffect' and + * returns 'TRUE' if the script exits without an effect on the save or + * the hardware, or 'FALSE' if it would have an effect (the effect is + * not performed). + * + * Commands, natives, and specials which call 'Script_RequestEffects' + * must be explicitly tagged with 'requests_effects=1', and must call + * the function before any of those effects occur. An untagged function + * could cause any effect, so execution is stopped to be safe. If the + * code has no effects it must call 'Script_RequestEffects(SCREFF_V1)' + * to note that explicitly. + * + * Regular variables are in the save (so should use 'SCREFF_SAVE'), but + * special variables are not in the save, so 'Script_RequestWriteVar' is + * provided to only request the 'SCREFF_SAVE' effect for a non-special + * variable. + * + * The 'effects' parameter to 'RunScriptImmediatelyUntilEffect' and + * 'Script_RequestEffects' must be the bitwise or of an effects version + * (currently 'SCREFF_V1') and any number of effects. For example + * 'Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE)'. */ + +enum // effects +{ + SCREFF_SAVE = 1 << 0, // writes to the save. + SCREFF_HARDWARE = 1 << 1, // writes to a hardware register. + SCREFF_TRAINERBATTLE = 1 << 2, // 'trainerbattle' command. +}; + +enum // effects versions +{ + SCREFF_V1 = ~7, +}; + +extern struct ScriptEffectContext *gScriptEffectContext; + +bool32 RunScriptImmediatelyUntilEffect_Internal(u32 effects, const u8 *ptr, struct ScriptContext *ctx); +bool32 Script_HasNoEffect(const u8 *ptr); +void Script_GotoBreak_Internal(void); +void Script_RequestEffects_Internal(u32 effects); +void Script_RequestWriteVar_Internal(u32 varId); + +static inline bool32 Script_IsAnalyzingEffects(void) +{ + return gScriptEffectContext != NULL; +} + +#define RunScriptImmediatelyUntilEffect(effects, ptr, ctx) \ + ({ \ + _Static_assert((effects) & 0x80000000, "RunScriptImmediatelyUntilEffect requires an effects version"); \ + RunScriptImmediatelyUntilEffect_Internal(effects, ptr, ctx); \ + }) + +/* Optimize 'Script_RequestEffects' to a no-op if it would have no + * effect. 'Script_RequestEffects' must be called in all commands and + * natives/specials with 'request_effects=TRUE' even if it would have + * no effect to future-proof against new effects. */ +#define Script_RequestEffects(effects) \ + ({ \ + _Static_assert((effects) & 0x80000000, "Script_RequestEffects requires an effects version"); \ + if ((effects) != SCREFF_V1) \ + if (Script_IsAnalyzingEffects()) \ + Script_RequestEffects_Internal(effects); \ + }) + +/* Optimize 'Script_RequestWriteVar' to a no-op if it would have no + * effect. */ +#define Script_RequestWriteVar(varId) \ + ({ \ + if (Script_IsAnalyzingEffects()) \ + Script_RequestWriteVar_Internal(varId); \ + }) + +static inline void Script_CheckEffectInstrumentedSpecial(u32 specialId) +{ + typedef u16 (*SpecialFunc)(void); + extern const SpecialFunc gSpecials[]; + // In ROM mirror 1. + if (Script_IsAnalyzingEffects() && (((uintptr_t)gSpecials[specialId]) & 0xE000000) != 0xA000000) + Script_GotoBreak_Internal(); +} + +static inline void Script_CheckEffectInstrumentedGotoNative(bool8 (*func)(void)) +{ + // In ROM mirror 1. + if (Script_IsAnalyzingEffects() && (((uintptr_t)func) & 0xE000000) != 0xA000000) + Script_GotoBreak_Internal(); +} + +static inline void Script_CheckEffectInstrumentedCallNative(void (*func)(struct ScriptContext *)) +{ + // In ROM mirror 1. + if (Script_IsAnalyzingEffects() && (((uintptr_t)func) & 0xE000000) != 0xA000000) + Script_GotoBreak_Internal(); +} + #endif // GUARD_SCRIPT_H diff --git a/include/test/overworld_script.h b/include/test/overworld_script.h index e2f65930f5..9b85f82ac3 100644 --- a/include/test/overworld_script.h +++ b/include/test/overworld_script.h @@ -42,6 +42,17 @@ #define RUN_OVERWORLD_SCRIPT(...) RunScriptImmediately(OVERWORLD_SCRIPT(__VA_ARGS__)) +// Make important constants available. +// TODO: Find a better approach to this. +asm(".set FALSE, 0\n" + ".set TRUE, 1\n" + ".set PARTY_SIZE, " STR(PARTY_SIZE) "\n" + ".set VARS_START, " STR(VARS_START) "\n" + ".set VARS_END, " STR(VARS_END) "\n" + ".set SPECIAL_VARS_START, " STR(SPECIAL_VARS_START) "\n" + ".set SPECIAL_VARS_END, " STR(SPECIAL_VARS_END) "\n"); +asm(".include \"constants/gba_constants.inc\"\n"); + // Make overworld script macros available. asm(".include \"asm/macros/event.inc\"\n"); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 828b7b7c94..61e27ddb5a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5353,6 +5353,8 @@ static s32 AI_DynamicFunc(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) void ScriptSetDynamicAiFunc(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + AiScoreFunc func = (AiScoreFunc)ScriptReadWord(ctx); sDynamicAiFunc = func; } diff --git a/src/battle_main.c b/src/battle_main.c index 1938cef20c..ebc391983a 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -2060,6 +2060,8 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir void CreateTrainerPartyForPlayer(void) { + Script_RequestEffects(SCREFF_V1); + ZeroPlayerPartyMons(); gPartnerTrainerId = gSpecialVar_0x8004; CreateNPCTrainerPartyFromTrainer(gPlayerParty, GetTrainerStructFromId(gSpecialVar_0x8004), TRUE, BATTLE_TYPE_TRAINER); @@ -6135,6 +6137,8 @@ void ScriptSetTotemBoost(struct ScriptContext *ctx) u32 stat; u32 i; + Script_RequestEffects(SCREFF_V1); + for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) { stat = VarGet(ScriptReadHalfword(ctx)); diff --git a/src/difficulty.c b/src/difficulty.c index bf7caca313..470c7fb3fd 100644 --- a/src/difficulty.c +++ b/src/difficulty.c @@ -64,6 +64,9 @@ void Script_IncreaseDifficulty(void) if (currentDifficulty++ > DIFFICULTY_MAX) return; + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(B_VAR_DIFFICULTY); + SetCurrentDifficultyLevel(currentDifficulty); } @@ -79,11 +82,15 @@ void Script_DecreaseDifficulty(void) if (!currentDifficulty) return; + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(B_VAR_DIFFICULTY); + SetCurrentDifficultyLevel(--currentDifficulty); } void Script_GetDifficulty(void) { + Script_RequestEffects(SCREFF_V1); gSpecialVar_Result = GetCurrentDifficultyLevel(); } @@ -91,5 +98,8 @@ void Script_SetDifficulty(struct ScriptContext *ctx) { enum DifficultyLevel desiredDifficulty = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(B_VAR_DIFFICULTY); + SetCurrentDifficultyLevel(desiredDifficulty); } diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9392a81d86..581cd94620 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6012,9 +6012,15 @@ u8 GetDirectionToFace(s16 x, s16 y, s16 targetX, s16 targetY) // Uses the above, but script accessible, and uses localIds void GetDirectionToFaceScript(struct ScriptContext *ctx) { - u16 *var = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); u8 sourceId = GetObjectEventIdByLocalId(ScriptReadByte(ctx)); u8 targetId = GetObjectEventIdByLocalId(ScriptReadByte(ctx)); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + + u16 *var = GetVarPointer(varId); + if (var == NULL) return; if (sourceId >= OBJECT_EVENTS_COUNT || targetId >= OBJECT_EVENTS_COUNT) @@ -6030,7 +6036,12 @@ void GetDirectionToFaceScript(struct ScriptContext *ctx) // Intended to be called before the field effect itself void IsFollowerFieldMoveUser(struct ScriptContext *ctx) { - u16 *var = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + + u16 *var = GetVarPointer(varId); u16 userIndex = gFieldEffectArguments[0]; // field move user index struct Pokemon *follower = GetFirstLiveMon(); struct ObjectEvent *obj = GetFollowerObject(); @@ -10995,6 +11006,13 @@ void GetDaycareGraphics(struct ScriptContext *ctx) u8 form; u8 shiny; s32 i; + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varGfx[0]); + Script_RequestWriteVar(varGfx[1]); + Script_RequestWriteVar(varForm[0]); + Script_RequestWriteVar(varForm[1]); + for (i = 0; i < 2; i++) { GetMonInfo((struct Pokemon *) &gSaveBlock1Ptr->daycare.mons[i].mon, &specGfx, &form, &shiny); diff --git a/src/fake_rtc.c b/src/fake_rtc.c index f3f3b74c39..4dbf7837ba 100644 --- a/src/fake_rtc.c +++ b/src/fake_rtc.c @@ -5,6 +5,7 @@ #include "rtc.h" #include "fake_rtc.h" #include "event_data.h" +#include "script.h" struct Time *FakeRtc_GetCurrentTime(void) { @@ -90,15 +91,21 @@ STATIC_ASSERT((OW_FLAG_PAUSE_TIME == 0 || OW_USE_FAKE_RTC == TRUE), FakeRtcMustB void Script_PauseFakeRtc(void) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + FlagSet(OW_FLAG_PAUSE_TIME); } void Script_ResumeFakeRtc(void) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + FlagClear(OW_FLAG_PAUSE_TIME); } void Script_ToggleFakeRtc(void) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + FlagToggle(OW_FLAG_PAUSE_TIME); } diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index eabbc202c8..d87c7ca010 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -275,7 +275,7 @@ static u16 GetPlayerCurMetatileBehavior(int runningState) static bool8 TryStartInteractionScript(struct MapPosition *position, u16 metatileBehavior, u8 direction) { const u8 *script = GetInteractionScript(position, metatileBehavior, direction); - if (script == NULL) + if (script == NULL || Script_HasNoEffect(script)) return FALSE; // Don't play interaction sound for certain scripts. @@ -590,7 +590,12 @@ static bool8 TryStartCoordEventScript(struct MapPosition *position) if (script == NULL) return FALSE; - ScriptContext_SetupScript(script); + + struct ScriptContext ctx; + if (!RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_HARDWARE, script, &ctx)) + return FALSE; + + ScriptContext_ContinueScript(&ctx); return TRUE; } diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 9abb81a18e..2aed46627c 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -1236,6 +1236,8 @@ u8 player_get_pos_including_state_based_drift(s16 *x, s16 *y) u8 GetPlayerFacingDirection(void) { + Script_RequestEffects(SCREFF_V1); + return gObjectEvents[gPlayerAvatar.objectEventId].facingDirection; } diff --git a/src/main.c b/src/main.c index 39b4f7e7a7..c2758ee227 100644 --- a/src/main.c +++ b/src/main.c @@ -93,7 +93,9 @@ void AgbMain() { *(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup InitGpuRegManager(); - REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; + REG_WAITCNT = WAITCNT_PREFETCH_ENABLE + | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3 + | WAITCNT_WS1_S_1 | WAITCNT_WS1_N_3; InitKeys(); InitIntrHandlers(); m4aSoundInit(); diff --git a/src/overworld.c b/src/overworld.c index 443d10295b..05c04d0fba 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -3386,6 +3386,9 @@ static u8 ReformatItemDescription(u16 item, u8 *dest) void ScriptShowItemDescription(struct ScriptContext *ctx) { u8 headerType = ScriptReadByte(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + struct WindowTemplate template; u16 item = gSpecialVar_0x8006; u8 textY; @@ -3425,6 +3428,8 @@ void ScriptShowItemDescription(struct ScriptContext *ctx) void ScriptHideItemDescription(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + DestroyItemIconSprite(); if (!GetSetItemObtained(gSpecialVar_0x8006, FLAG_GET_ITEM_OBTAINED)) diff --git a/src/scrcmd.c b/src/scrcmd.c index 2471f3b595..54585aa503 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -101,16 +101,22 @@ static u8 *const sScriptStringVars[] = bool8 ScrCmd_nop(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + return FALSE; } bool8 ScrCmd_nop1(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + return FALSE; } bool8 ScrCmd_end(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + FlagClear(FLAG_SAFE_FOLLOWER_MOVEMENT); StopScript(ctx); return FALSE; @@ -120,6 +126,9 @@ bool8 ScrCmd_gotonative(struct ScriptContext *ctx) { bool8 (*addr)(void) = (bool8 (*)(void))ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + Script_CheckEffectInstrumentedGotoNative(addr); + SetupNativeScript(ctx, addr); return TRUE; } @@ -128,15 +137,24 @@ bool8 ScrCmd_special(struct ScriptContext *ctx) { u16 index = ScriptReadHalfword(ctx); + Script_RequestEffects(SCREFF_V1); + Script_CheckEffectInstrumentedSpecial(index); + gSpecials[index](); return FALSE; } bool8 ScrCmd_specialvar(struct ScriptContext *ctx) { - u16 *var = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 index = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); - *var = gSpecials[ScriptReadHalfword(ctx)](); + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + Script_CheckEffectInstrumentedSpecial(index); + + *ptr = gSpecials[index](); return FALSE; } @@ -144,12 +162,17 @@ bool8 ScrCmd_callnative(struct ScriptContext *ctx) { NativeFunc func = (NativeFunc)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + Script_CheckEffectInstrumentedCallNative(func); + func(ctx); return FALSE; } bool8 ScrCmd_waitstate(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ScriptContext_Stop(); return TRUE; } @@ -158,12 +181,16 @@ bool8 ScrCmd_goto(struct ScriptContext *ctx) { const u8 *ptr = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ScriptJump(ctx, ptr); return FALSE; } bool8 ScrCmd_return(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + ScriptReturn(ctx); return FALSE; } @@ -172,6 +199,8 @@ bool8 ScrCmd_call(struct ScriptContext *ctx) { const u8 *ptr = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ScriptCall(ctx, ptr); return FALSE; } @@ -181,6 +210,8 @@ bool8 ScrCmd_goto_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); const u8 *ptr = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) ScriptJump(ctx, ptr); return FALSE; @@ -191,6 +222,8 @@ bool8 ScrCmd_call_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); const u8 *ptr = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) ScriptCall(ctx, ptr); return FALSE; @@ -201,6 +234,8 @@ bool8 ScrCmd_setvaddress(struct ScriptContext *ctx) u32 addr1 = (u32)ctx->scriptPtr - 1; u32 addr2 = ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + sAddressOffset = addr2 - addr1; return FALSE; } @@ -209,6 +244,8 @@ bool8 ScrCmd_vgoto(struct ScriptContext *ctx) { u32 addr = ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ScriptJump(ctx, (u8 *)(addr - sAddressOffset)); return FALSE; } @@ -217,6 +254,8 @@ bool8 ScrCmd_vcall(struct ScriptContext *ctx) { u32 addr = ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ScriptCall(ctx, (u8 *)(addr - sAddressOffset)); return FALSE; } @@ -226,6 +265,8 @@ bool8 ScrCmd_vgoto_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); const u8 *ptr = (const u8 *)(ScriptReadWord(ctx) - sAddressOffset); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) ScriptJump(ctx, ptr); return FALSE; @@ -236,6 +277,8 @@ bool8 ScrCmd_vcall_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); const u8 *ptr = (const u8 *)(ScriptReadWord(ctx) - sAddressOffset); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) ScriptCall(ctx, ptr); return FALSE; @@ -246,6 +289,8 @@ bool8 ScrCmd_gotostd(struct ScriptContext *ctx) u8 index = ScriptReadByte(ctx); const u8 **ptr = &gStdScripts[index]; + Script_RequestEffects(SCREFF_V1); + if (ptr < gStdScripts_End) ScriptJump(ctx, *ptr); return FALSE; @@ -256,6 +301,8 @@ bool8 ScrCmd_callstd(struct ScriptContext *ctx) u8 index = ScriptReadByte(ctx); const u8 **ptr = &gStdScripts[index]; + Script_RequestEffects(SCREFF_V1); + if (ptr < gStdScripts_End) ScriptCall(ctx, *ptr); return FALSE; @@ -266,6 +313,8 @@ bool8 ScrCmd_gotostd_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); u8 index = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) { const u8 **ptr = &gStdScripts[index]; @@ -280,6 +329,8 @@ bool8 ScrCmd_callstd_if(struct ScriptContext *ctx) u8 condition = ScriptReadByte(ctx); u8 index = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + if (sScriptConditionTable[condition][ctx->comparisonResult] == 1) { const u8 **ptr = &gStdScripts[index]; @@ -291,12 +342,16 @@ bool8 ScrCmd_callstd_if(struct ScriptContext *ctx) bool8 ScrCmd_returnram(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + ScriptJump(ctx, gRamScriptRetAddr); return FALSE; } bool8 ScrCmd_endram(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + FlagClear(FLAG_SAFE_FOLLOWER_MOVEMENT); ClearRamScript(); StopScript(ctx); @@ -307,6 +362,8 @@ bool8 ScrCmd_setmysteryeventstatus(struct ScriptContext *ctx) { u8 status = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + SetMysteryEventScriptStatus(status); return FALSE; } @@ -315,6 +372,8 @@ bool8 ScrCmd_loadword(struct ScriptContext *ctx) { u8 index = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->data[index] = ScriptReadWord(ctx); return FALSE; } @@ -323,6 +382,8 @@ bool8 ScrCmd_loadbytefromptr(struct ScriptContext *ctx) { u8 index = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->data[index] = *(const u8 *)ScriptReadWord(ctx); return FALSE; } @@ -331,6 +392,9 @@ bool8 ScrCmd_setptr(struct ScriptContext *ctx) { u8 value = ScriptReadByte(ctx); + // TODO: Check if 'ptr' is within a save block? + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + *(u8 *)ScriptReadWord(ctx) = value; return FALSE; } @@ -339,6 +403,8 @@ bool8 ScrCmd_loadbyte(struct ScriptContext *ctx) { u8 index = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->data[index] = ScriptReadByte(ctx); return FALSE; } @@ -347,6 +413,9 @@ bool8 ScrCmd_setptrbyte(struct ScriptContext *ctx) { u8 index = ScriptReadByte(ctx); + // TODO: Check if 'ptr' is within a save block? + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + *(u8 *)ScriptReadWord(ctx) = ctx->data[index]; return FALSE; } @@ -356,6 +425,8 @@ bool8 ScrCmd_copylocal(struct ScriptContext *ctx) u8 destIndex = ScriptReadByte(ctx); u8 srcIndex = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->data[destIndex] = ctx->data[srcIndex]; return FALSE; } @@ -363,27 +434,46 @@ bool8 ScrCmd_copylocal(struct ScriptContext *ctx) bool8 ScrCmd_copybyte(struct ScriptContext *ctx) { u8 *ptr = (u8 *)ScriptReadWord(ctx); + + // TODO: Check if 'ptr' is within a save block? + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + *ptr = *(const u8 *)ScriptReadWord(ctx); return FALSE; } bool8 ScrCmd_setvar(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr = ScriptReadHalfword(ctx); return FALSE; } bool8 ScrCmd_copyvar(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr = *GetVarPointer(ScriptReadHalfword(ctx)); return FALSE; } bool8 ScrCmd_setorcopyvar(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr = VarGet(ScriptReadHalfword(ctx)); return FALSE; } @@ -402,6 +492,8 @@ bool8 ScrCmd_compare_local_to_local(struct ScriptContext *ctx) const u8 value1 = ctx->data[ScriptReadByte(ctx)]; const u8 value2 = ctx->data[ScriptReadByte(ctx)]; + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -411,6 +503,8 @@ bool8 ScrCmd_compare_local_to_value(struct ScriptContext *ctx) const u8 value1 = ctx->data[ScriptReadByte(ctx)]; const u8 value2 = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -420,6 +514,8 @@ bool8 ScrCmd_compare_local_to_ptr(struct ScriptContext *ctx) const u8 value1 = ctx->data[ScriptReadByte(ctx)]; const u8 value2 = *(const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -429,6 +525,8 @@ bool8 ScrCmd_compare_ptr_to_local(struct ScriptContext *ctx) const u8 value1 = *(const u8 *)ScriptReadWord(ctx); const u8 value2 = ctx->data[ScriptReadByte(ctx)]; + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -438,6 +536,8 @@ bool8 ScrCmd_compare_ptr_to_value(struct ScriptContext *ctx) const u8 value1 = *(const u8 *)ScriptReadWord(ctx); const u8 value2 = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -447,6 +547,8 @@ bool8 ScrCmd_compare_ptr_to_ptr(struct ScriptContext *ctx) const u8 value1 = *(const u8 *)ScriptReadWord(ctx); const u8 value2 = *(const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -456,6 +558,8 @@ bool8 ScrCmd_compare_var_to_value(struct ScriptContext *ctx) const u16 value1 = *GetVarPointer(ScriptReadHalfword(ctx)); const u16 value2 = ScriptReadHalfword(ctx); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(value1, value2); return FALSE; } @@ -465,6 +569,8 @@ bool8 ScrCmd_compare_var_to_var(struct ScriptContext *ctx) const u16 *ptr1 = GetVarPointer(ScriptReadHalfword(ctx)); const u16 *ptr2 = GetVarPointer(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = Compare(*ptr1, *ptr2); return FALSE; } @@ -474,14 +580,24 @@ bool8 ScrCmd_compare_var_to_var(struct ScriptContext *ctx) // in the contest scripts to `subvar VAR_*, 1`, else contests will break. bool8 ScrCmd_addvar(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr += ScriptReadHalfword(ctx); return FALSE; } bool8 ScrCmd_subvar(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr -= VarGet(ScriptReadHalfword(ctx)); return FALSE; } @@ -490,6 +606,8 @@ bool8 ScrCmd_random(struct ScriptContext *ctx) { u16 max = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = Random() % max; return FALSE; } @@ -499,6 +617,8 @@ bool8 ScrCmd_additem(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u32 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = AddBagItem(itemId, quantity); return FALSE; } @@ -508,6 +628,8 @@ bool8 ScrCmd_removeitem(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u32 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = RemoveBagItem(itemId, quantity); return FALSE; } @@ -517,6 +639,8 @@ bool8 ScrCmd_checkitemspace(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u32 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CheckBagHasSpace(itemId, quantity); return FALSE; } @@ -526,6 +650,8 @@ bool8 ScrCmd_checkitem(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u32 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CheckBagHasItem(itemId, quantity); return FALSE; } @@ -534,6 +660,8 @@ bool8 ScrCmd_checkitemtype(struct ScriptContext *ctx) { u16 itemId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = GetPocketByItemId(itemId); return FALSE; } @@ -543,6 +671,8 @@ bool8 ScrCmd_addpcitem(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u16 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = AddPCItem(itemId, quantity); return FALSE; } @@ -552,6 +682,8 @@ bool8 ScrCmd_checkpcitem(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u16 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CheckPCHasItem(itemId, quantity); return FALSE; } @@ -560,6 +692,8 @@ bool8 ScrCmd_adddecoration(struct ScriptContext *ctx) { u32 decorId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = DecorationAdd(decorId); return FALSE; } @@ -568,6 +702,8 @@ bool8 ScrCmd_removedecoration(struct ScriptContext *ctx) { u32 decorId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = DecorationRemove(decorId); return FALSE; } @@ -576,6 +712,8 @@ bool8 ScrCmd_checkdecorspace(struct ScriptContext *ctx) { u32 decorId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = DecorationCheckSpace(decorId); return FALSE; } @@ -584,44 +722,68 @@ bool8 ScrCmd_checkdecor(struct ScriptContext *ctx) { u32 decorId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CheckHasDecoration(decorId); return FALSE; } bool8 ScrCmd_setflag(struct ScriptContext *ctx) { - FlagSet(ScriptReadHalfword(ctx)); + u32 flagId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + + FlagSet(flagId); return FALSE; } bool8 ScrCmd_clearflag(struct ScriptContext *ctx) { - FlagClear(ScriptReadHalfword(ctx)); + u32 flagId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + + FlagClear(flagId); return FALSE; } bool8 ScrCmd_checkflag(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = FlagGet(ScriptReadHalfword(ctx)); return FALSE; } bool8 ScrCmd_incrementgamestat(struct ScriptContext *ctx) { - IncrementGameStat(ScriptReadByte(ctx)); + u32 statId = ScriptReadByte(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + + IncrementGameStat(statId); return FALSE; } bool8 ScrCmd_animateflash(struct ScriptContext *ctx) { - AnimateFlash(ScriptReadByte(ctx)); + u32 level = ScriptReadByte(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + AnimateFlash(level); ScriptContext_Stop(); return TRUE; } bool8 ScrCmd_setflashlevel(struct ScriptContext *ctx) { - SetFlashLevel(VarGet(ScriptReadHalfword(ctx))); + u32 level = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + + SetFlashLevel(level); return FALSE; } @@ -635,7 +797,11 @@ static bool8 IsPaletteNotActive(void) bool8 ScrCmd_fadescreen(struct ScriptContext *ctx) { - FadeScreen(ScriptReadByte(ctx), 0); + u32 mode = ScriptReadByte(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + FadeScreen(mode, 0); SetupNativeScript(ctx, IsPaletteNotActive); return TRUE; } @@ -645,6 +811,8 @@ bool8 ScrCmd_fadescreenspeed(struct ScriptContext *ctx) u8 mode = ScriptReadByte(ctx); u8 speed = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + FadeScreen(mode, speed); SetupNativeScript(ctx, IsPaletteNotActive); return TRUE; @@ -654,6 +822,8 @@ bool8 ScrCmd_fadescreenswapbuffers(struct ScriptContext *ctx) { u8 mode = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + switch (mode) { case FADE_TO_BLACK: @@ -683,7 +853,11 @@ static bool8 RunPauseTimer(void) bool8 ScrCmd_delay(struct ScriptContext *ctx) { - sPauseCounter = ScriptReadHalfword(ctx); + u32 frames = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + sPauseCounter = frames; SetupNativeScript(ctx, RunPauseTimer); return TRUE; } @@ -693,18 +867,24 @@ bool8 ScrCmd_initclock(struct ScriptContext *ctx) u8 hour = VarGet(ScriptReadHalfword(ctx)); u8 minute = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + RtcInitLocalTimeOffset(hour, minute); return FALSE; } bool8 ScrCmd_dotimebasedevents(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + DoTimeBasedEvents(); return FALSE; } bool8 ScrCmd_gettime(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + RtcCalcLocalTime(); gSpecialVar_0x8000 = gLocalTime.hours; gSpecialVar_0x8001 = gLocalTime.minutes; @@ -716,25 +896,35 @@ bool8 ScrCmd_setweather(struct ScriptContext *ctx) { u16 weather = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetSavedWeather(weather); return FALSE; } bool8 ScrCmd_resetweather(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetSavedWeatherFromCurrMapHeader(); return FALSE; } bool8 ScrCmd_doweather(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + DoCurrentWeather(); return FALSE; } bool8 ScrCmd_setstepcallback(struct ScriptContext *ctx) { - ActivatePerStepCallback(ScriptReadByte(ctx)); + u32 callbackId = ScriptReadByte(ctx); + + Script_RequestEffects(SCREFF_V1); + + ActivatePerStepCallback(callbackId); return FALSE; } @@ -742,6 +932,8 @@ bool8 ScrCmd_setmaplayoutindex(struct ScriptContext *ctx) { u16 value = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetCurrentMapLayout(value); return FALSE; } @@ -754,6 +946,8 @@ bool8 ScrCmd_warp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoWarp(); ResetInitialPlayerAvatarState(); @@ -768,6 +962,8 @@ bool8 ScrCmd_warpsilent(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoDiveWarp(); ResetInitialPlayerAvatarState(); @@ -782,6 +978,8 @@ bool8 ScrCmd_warpdoor(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoDoorWarp(); ResetInitialPlayerAvatarState(); @@ -795,6 +993,8 @@ bool8 ScrCmd_warphole(struct ScriptContext *ctx) s16 x; s16 y; + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + PlayerGetDestCoords(&x, &y); if (mapGroup == MAP_GROUP(UNDEFINED) && mapNum == MAP_NUM(UNDEFINED)) SetWarpDestinationToFixedHoleWarp(x - MAP_OFFSET, y - MAP_OFFSET); @@ -814,6 +1014,8 @@ bool8 ScrCmd_warpteleport(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoTeleportTileWarp(); ResetInitialPlayerAvatarState(); @@ -828,6 +1030,8 @@ bool8 ScrCmd_warpmossdeepgym(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoMossdeepGymWarp(); ResetInitialPlayerAvatarState(); @@ -842,6 +1046,8 @@ bool8 ScrCmd_setwarp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); return FALSE; } @@ -854,6 +1060,8 @@ bool8 ScrCmd_setdynamicwarp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetDynamicWarpWithCoords(0, mapGroup, mapNum, warpId, x, y); return FALSE; } @@ -866,6 +1074,8 @@ bool8 ScrCmd_setdivewarp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + SetFixedDiveWarp(mapGroup, mapNum, warpId, x, y); return FALSE; } @@ -878,6 +1088,8 @@ bool8 ScrCmd_setholewarp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + SetFixedHoleWarp(mapGroup, mapNum, warpId, x, y); return FALSE; } @@ -890,14 +1102,22 @@ bool8 ScrCmd_setescapewarp(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetEscapeWarp(mapGroup, mapNum, warpId, x, y); return FALSE; } bool8 ScrCmd_getplayerxy(struct ScriptContext *ctx) { - u16 *pX = GetVarPointer(ScriptReadHalfword(ctx)); - u16 *pY = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varIdX = ScriptReadHalfword(ctx); + u32 varIdY = ScriptReadHalfword(ctx); + u16 *pX = GetVarPointer(varIdX); + u16 *pY = GetVarPointer(varIdY); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varIdX); + Script_RequestWriteVar(varIdY); *pX = gSaveBlock1Ptr->pos.x; *pY = gSaveBlock1Ptr->pos.y; @@ -906,13 +1126,19 @@ bool8 ScrCmd_getplayerxy(struct ScriptContext *ctx) bool8 ScrCmd_getpartysize(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CalculatePlayerPartyCount(); return FALSE; } bool8 ScrCmd_playse(struct ScriptContext *ctx) { - PlaySE(ScriptReadHalfword(ctx)); + u32 songId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + PlaySE(songId); return FALSE; } @@ -926,13 +1152,19 @@ static bool8 WaitForSoundEffectFinish(void) bool8 ScrCmd_waitse(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, WaitForSoundEffectFinish); return TRUE; } bool8 ScrCmd_playfanfare(struct ScriptContext *ctx) { - PlayFanfare(ScriptReadHalfword(ctx)); + u32 songId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + PlayFanfare(songId); return FALSE; } @@ -943,6 +1175,8 @@ static bool8 WaitForFanfareFinish(void) bool8 ScrCmd_waitfanfare(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, WaitForFanfareFinish); return TRUE; } @@ -952,6 +1186,8 @@ bool8 ScrCmd_playbgm(struct ScriptContext *ctx) u16 songId = ScriptReadHalfword(ctx); bool8 save = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + if (save == TRUE) Overworld_SetSavedMusic(songId); PlayNewMapMusic(songId); @@ -960,18 +1196,24 @@ bool8 ScrCmd_playbgm(struct ScriptContext *ctx) bool8 ScrCmd_savebgm(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + Overworld_SetSavedMusic(ScriptReadHalfword(ctx)); return FALSE; } bool8 ScrCmd_fadedefaultbgm(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + Overworld_ChangeMusicToDefault(); return FALSE; } bool8 ScrCmd_fadenewbgm(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + Overworld_ChangeMusicTo(ScriptReadHalfword(ctx)); return FALSE; } @@ -980,6 +1222,8 @@ bool8 ScrCmd_fadeoutbgm(struct ScriptContext *ctx) { u8 speed = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + if (speed != 0) FadeOutBGMTemporarily(4 * speed); else @@ -992,6 +1236,8 @@ bool8 ScrCmd_fadeinbgm(struct ScriptContext *ctx) { u8 speed = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + if (speed != 0) FadeInBGM(4 * speed); else @@ -1005,6 +1251,8 @@ bool8 ScrCmd_applymovement(struct ScriptContext *ctx) const u8 *movementScript = (const u8 *)ScriptReadWord(ctx); struct ObjectEvent *objEvent; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + // When applying script movements to follower, it may have frozen animation that must be cleared if (localId == OBJ_EVENT_ID_FOLLOWER && (objEvent = GetFollowerObject()) && objEvent->frozen) { @@ -1037,6 +1285,8 @@ bool8 ScrCmd_applymovementat(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + gObjectEvents[GetObjectEventIdByLocalId(localId)].directionOverwrite = DIR_NONE; ScriptMovement_StartObjectMovementScript(localId, mapNum, mapGroup, movementScript); sMovingNpcId = localId; @@ -1061,6 +1311,8 @@ bool8 ScrCmd_waitmovement(struct ScriptContext *ctx) { u16 localId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (localId != 0) sMovingNpcId = localId; sMovingNpcMapGroup = gSaveBlock1Ptr->location.mapGroup; @@ -1075,6 +1327,8 @@ bool8 ScrCmd_waitmovementat(struct ScriptContext *ctx) u8 mapGroup; u8 mapNum; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (localId != 0) sMovingNpcId = localId; mapGroup = ScriptReadByte(ctx); @@ -1089,6 +1343,8 @@ bool8 ScrCmd_removeobject(struct ScriptContext *ctx) { u16 localId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + RemoveObjectEventByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); return FALSE; } @@ -1099,6 +1355,8 @@ bool8 ScrCmd_removeobjectat(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + RemoveObjectEventByLocalIdAndMap(objectId, mapNum, mapGroup); return FALSE; } @@ -1107,6 +1365,8 @@ bool8 ScrCmd_addobject(struct ScriptContext *ctx) { u16 objectId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + TrySpawnObjectEvent(objectId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); return FALSE; } @@ -1117,6 +1377,8 @@ bool8 ScrCmd_addobjectat(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + TrySpawnObjectEvent(objectId, mapNum, mapGroup); return FALSE; } @@ -1127,6 +1389,8 @@ bool8 ScrCmd_setobjectxy(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + TryMoveObjectEventToMapCoords(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, x, y); return FALSE; } @@ -1137,6 +1401,8 @@ bool8 ScrCmd_setobjectxyperm(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetObjEventTemplateCoords(localId, x, y); return FALSE; } @@ -1145,6 +1411,8 @@ bool8 ScrCmd_copyobjectxytoperm(struct ScriptContext *ctx) { u16 localId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + TryOverrideObjectEventTemplateCoords(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); return FALSE; } @@ -1155,6 +1423,8 @@ bool8 ScrCmd_showobjectat(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetObjectInvisibility(localId, mapNum, mapGroup, FALSE); return FALSE; } @@ -1165,6 +1435,8 @@ bool8 ScrCmd_hideobjectat(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetObjectInvisibility(localId, mapNum, mapGroup, TRUE); return FALSE; } @@ -1176,6 +1448,8 @@ bool8 ScrCmd_setobjectsubpriority(struct ScriptContext *ctx) u8 mapNum = ScriptReadByte(ctx); u8 priority = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetObjectSubpriority(localId, mapNum, mapGroup, priority + 83); return FALSE; } @@ -1186,12 +1460,16 @@ bool8 ScrCmd_resetobjectsubpriority(struct ScriptContext *ctx) u8 mapGroup = ScriptReadByte(ctx); u8 mapNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ResetObjectSubpriority(localId, mapNum, mapGroup); return FALSE; } bool8 ScrCmd_faceplayer(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (gObjectEvents[gSelectedObjectEvent].active) ObjectEventFaceOppositeDirection(&gObjectEvents[gSelectedObjectEvent], GetPlayerFacingDirection()); return FALSE; @@ -1202,6 +1480,8 @@ bool8 ScrCmd_turnobject(struct ScriptContext *ctx) u16 localId = VarGet(ScriptReadHalfword(ctx)); u8 direction = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ObjectEventTurnByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, direction); return FALSE; } @@ -1211,6 +1491,8 @@ bool8 ScrCmd_setobjectmovementtype(struct ScriptContext *ctx) u16 localId = VarGet(ScriptReadHalfword(ctx)); u8 movementType = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetObjEventTemplateMovementType(localId, movementType); return FALSE; } @@ -1224,6 +1506,8 @@ bool8 ScrCmd_createvobject(struct ScriptContext *ctx) u8 elevation = ScriptReadByte(ctx); u8 direction = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + CreateVirtualObject(graphicsId, virtualObjId, x, y, elevation, direction); return FALSE; } @@ -1233,6 +1517,8 @@ bool8 ScrCmd_turnvobject(struct ScriptContext *ctx) u8 virtualObjId = ScriptReadByte(ctx); u8 direction = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + TurnVirtualObject(virtualObjId, direction); return FALSE; } @@ -1241,6 +1527,11 @@ bool8 ScrCmd_turnvobject(struct ScriptContext *ctx) // The player is frozen after waiting for their current movement to finish. bool8 ScrCmd_lockall(struct ScriptContext *ctx) { + // As a special case, skip this during analysis. + if (Script_IsAnalyzingEffects()) + return FALSE; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (IsOverworldLinkActive()) { return FALSE; @@ -1257,6 +1548,11 @@ bool8 ScrCmd_lockall(struct ScriptContext *ctx) // The player and selected object are frozen after waiting for their current movement to finish. bool8 ScrCmd_lock(struct ScriptContext *ctx) { + // As a special case, skip this during analysis. + if (Script_IsAnalyzingEffects()) + return FALSE; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (IsOverworldLinkActive()) { return FALSE; @@ -1285,6 +1581,11 @@ bool8 ScrCmd_lock(struct ScriptContext *ctx) bool8 ScrCmd_releaseall(struct ScriptContext *ctx) { + // As a special case, skip this during analysis. + if (Script_IsAnalyzingEffects()) + return FALSE; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + u8 playerObjectId; struct ObjectEvent *followerObject = GetFollowerObject(); // Release follower from movement iff it exists and is in the shadowing state @@ -1302,6 +1603,11 @@ bool8 ScrCmd_releaseall(struct ScriptContext *ctx) bool8 ScrCmd_release(struct ScriptContext *ctx) { + // As a special case, skip this during analysis. + if (Script_IsAnalyzingEffects()) + return FALSE; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + u8 playerObjectId; struct ObjectEvent *followerObject = GetFollowerObject(); // Release follower from movement iff it exists and is in the shadowing state @@ -1323,6 +1629,8 @@ bool8 ScrCmd_message(struct ScriptContext *ctx) { const u8 *msg = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (msg == NULL) msg = (const u8 *)ctx->data[0]; ShowFieldMessage(msg); @@ -1333,6 +1641,8 @@ bool8 ScrCmd_pokenavcall(struct ScriptContext *ctx) { const u8 *msg = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (msg == NULL) msg = (const u8 *)ctx->data[0]; ShowPokenavFieldMessage(msg); @@ -1343,6 +1653,8 @@ bool8 ScrCmd_messageautoscroll(struct ScriptContext *ctx) { const u8 *msg = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (msg == NULL) msg = (const u8 *)ctx->data[0]; gTextFlags.autoScroll = TRUE; @@ -1356,6 +1668,8 @@ bool8 ScrCmd_messageinstant(struct ScriptContext *ctx) { const u8 *msg = (const u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (msg == NULL) msg = (const u8 *)ctx->data[0]; LoadMessageBoxAndBorderGfx(); @@ -1366,12 +1680,16 @@ bool8 ScrCmd_messageinstant(struct ScriptContext *ctx) bool8 ScrCmd_waitmessage(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, IsFieldMessageBoxHidden); return TRUE; } bool8 ScrCmd_closemessage(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + HideFieldMessageBox(); return FALSE; } @@ -1387,6 +1705,8 @@ static bool8 WaitForAorBPress(void) bool8 ScrCmd_waitbuttonpress(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, WaitForAorBPress); return TRUE; } @@ -1396,6 +1716,8 @@ bool8 ScrCmd_yesnobox(struct ScriptContext *ctx) u8 left = ScriptReadByte(ctx); u8 top = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (ScriptMenu_YesNo(left, top) == TRUE) { ScriptContext_Stop(); @@ -1442,6 +1764,8 @@ bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx) u32 argc = ScriptReadByte(ctx); struct ListMenuItem *items; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (argc == 0) return FALSE; @@ -1491,9 +1815,12 @@ bool8 ScrCmd_dynmultichoice(struct ScriptContext *ctx) bool8 ScrCmd_dynmultipush(struct ScriptContext *ctx) { - u8 *nameBuffer = Alloc(100); const u8 *name = (const u8*) ScriptReadWord(ctx); u32 id = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + u8 *nameBuffer = Alloc(100); struct ListMenuItem item; StringExpandPlaceholders(nameBuffer, name); item.name = nameBuffer; @@ -1509,6 +1836,8 @@ bool8 ScrCmd_multichoice(struct ScriptContext *ctx) u8 multichoiceId = ScriptReadByte(ctx); bool8 ignoreBPress = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (ScriptMenu_Multichoice(left, top, multichoiceId, ignoreBPress) == TRUE) { ScriptContext_Stop(); @@ -1528,6 +1857,8 @@ bool8 ScrCmd_multichoicedefault(struct ScriptContext *ctx) u8 defaultChoice = ScriptReadByte(ctx); bool8 ignoreBPress = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (ScriptMenu_MultichoiceWithDefault(left, top, multichoiceId, ignoreBPress, defaultChoice) == TRUE) { ScriptContext_Stop(); @@ -1558,6 +1889,8 @@ bool8 ScrCmd_multichoicegrid(struct ScriptContext *ctx) u8 numColumns = ScriptReadByte(ctx); bool8 ignoreBPress = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (ScriptMenu_MultichoiceGrid(left, top, multichoiceId, ignoreBPress, numColumns) == TRUE) { ScriptContext_Stop(); @@ -1601,12 +1934,16 @@ bool8 ScrCmd_showmonpic(struct ScriptContext *ctx) u8 x = ScriptReadByte(ctx); u8 y = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ScriptMenu_ShowPokemonPic(species, x, y); return FALSE; } bool8 ScrCmd_hidemonpic(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + // The hide function returns a pointer to a function // that returns true once the pic is hidden bool8 (*func)(void) = ScriptMenu_HidePokemonPic(); @@ -1621,6 +1958,8 @@ bool8 ScrCmd_showcontestpainting(struct ScriptContext *ctx) { u8 contestWinnerId = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + // Artist's painting is temporary and already has its data loaded if (contestWinnerId != CONTEST_WINNER_ARTIST) SetContestWinnerForPainting(contestWinnerId); @@ -1633,6 +1972,9 @@ bool8 ScrCmd_showcontestpainting(struct ScriptContext *ctx) bool8 ScrCmd_braillemessage(struct ScriptContext *ctx) { u8 *ptr = (u8 *)ScriptReadWord(ctx); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + struct WindowTemplate winTemplate; s32 i; u8 width, height; @@ -1686,6 +2028,8 @@ bool8 ScrCmd_braillemessage(struct ScriptContext *ctx) bool8 ScrCmd_closebraillemessage(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + CloseBrailleWindow(); return FALSE; } @@ -1694,6 +2038,8 @@ bool8 ScrCmd_vmessage(struct ScriptContext *ctx) { u32 msg = ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ShowFieldMessage((u8 *)(msg - sAddressOffset)); return FALSE; } @@ -1703,6 +2049,8 @@ bool8 ScrCmd_bufferspeciesname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 species = VarGet(ScriptReadHalfword(ctx)) & OBJ_EVENT_GFX_SPECIES_MASK; // ignore possible shiny / form bits + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], GetSpeciesName(species)); return FALSE; } @@ -1711,6 +2059,8 @@ bool8 ScrCmd_bufferleadmonspeciesname(struct ScriptContext *ctx) { u8 stringVarIndex = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + u8 *dest = sScriptStringVars[stringVarIndex]; u8 partyIndex = GetLeadMonIndex(); u32 species = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES, NULL); @@ -1722,6 +2072,8 @@ void BufferFirstLiveMonNickname(struct ScriptContext *ctx) { u8 stringVarIndex = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + GetMonData(GetFirstLiveMon(), MON_DATA_NICKNAME, sScriptStringVars[stringVarIndex]); StringGet_Nickname(sScriptStringVars[stringVarIndex]); } @@ -1731,6 +2083,8 @@ bool8 ScrCmd_bufferpartymonnick(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, sScriptStringVars[stringVarIndex]); StringGet_Nickname(sScriptStringVars[stringVarIndex]); return FALSE; @@ -1741,6 +2095,8 @@ bool8 ScrCmd_bufferitemname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 itemId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + CopyItemName(itemId, sScriptStringVars[stringVarIndex]); return FALSE; } @@ -1751,6 +2107,8 @@ bool8 ScrCmd_bufferitemnameplural(struct ScriptContext *ctx) u16 itemId = VarGet(ScriptReadHalfword(ctx)); u16 quantity = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + CopyItemNameHandlePlural(itemId, sScriptStringVars[stringVarIndex], quantity); return FALSE; } @@ -1760,6 +2118,8 @@ bool8 ScrCmd_bufferdecorationname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 decorId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], gDecorations[decorId].name); return FALSE; } @@ -1769,6 +2129,8 @@ bool8 ScrCmd_buffermovename(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 moveId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], GetMoveName(moveId)); return FALSE; } @@ -1779,6 +2141,8 @@ bool8 ScrCmd_buffernumberstring(struct ScriptContext *ctx) u16 num = VarGet(ScriptReadHalfword(ctx)); u8 numDigits = CountDigits(num); + Script_RequestEffects(SCREFF_V1); + ConvertIntToDecimalStringN(sScriptStringVars[stringVarIndex], num, STR_CONV_MODE_LEFT_ALIGN, numDigits); return FALSE; } @@ -1788,6 +2152,8 @@ bool8 ScrCmd_bufferstdstring(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 index = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], gStdStrings[index]); return FALSE; } @@ -1797,6 +2163,8 @@ bool8 ScrCmd_buffercontestname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 category = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + BufferContestName(sScriptStringVars[stringVarIndex], category); return FALSE; } @@ -1806,6 +2174,8 @@ bool8 ScrCmd_bufferstring(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); const u8 *text = (u8 *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], text); return FALSE; } @@ -1814,6 +2184,8 @@ bool8 ScrCmd_vbuffermessage(struct ScriptContext *ctx) { const u8 *ptr = (u8 *)(ScriptReadWord(ctx) - sAddressOffset); + Script_RequestEffects(SCREFF_V1); + StringExpandPlaceholders(gStringVar4, ptr); return FALSE; } @@ -1823,6 +2195,8 @@ bool8 ScrCmd_vbufferstring(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u32 addr = ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1); + const u8 *src = (u8 *)(addr - sAddressOffset); u8 *dest = sScriptStringVars[stringVarIndex]; StringCopy(dest, src); @@ -1834,6 +2208,8 @@ bool8 ScrCmd_bufferboxname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 boxId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], GetBoxNamePtr(boxId)); return FALSE; } @@ -1842,6 +2218,8 @@ bool8 ScrCmd_giveegg(struct ScriptContext *ctx) { u16 species = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = ScriptGiveEgg(species); return FALSE; } @@ -1852,6 +2230,8 @@ bool8 ScrCmd_setmonmove(struct ScriptContext *ctx) u8 slot = ScriptReadByte(ctx); u16 move = ScriptReadHalfword(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + ScriptSetMonMoveSlot(partyIndex, move, slot); return FALSE; } @@ -1861,6 +2241,8 @@ bool8 ScrCmd_checkpartymove(struct ScriptContext *ctx) u8 i; u16 moveId = ScriptReadHalfword(ctx); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = PARTY_SIZE; for (i = 0; i < PARTY_SIZE; i++) { @@ -1883,7 +2265,11 @@ bool8 ScrCmd_addmoney(struct ScriptContext *ctx) u8 ignore = ScriptReadByte(ctx); if (!ignore) + { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + AddMoney(&gSaveBlock1Ptr->money, amount); + } return FALSE; } @@ -1893,7 +2279,11 @@ bool8 ScrCmd_removemoney(struct ScriptContext *ctx) u8 ignore = ScriptReadByte(ctx); if (!ignore) + { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + RemoveMoney(&gSaveBlock1Ptr->money, amount); + } return FALSE; } @@ -1903,7 +2293,11 @@ bool8 ScrCmd_checkmoney(struct ScriptContext *ctx) u8 ignore = ScriptReadByte(ctx); if (!ignore) + { + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = IsEnoughMoney(&gSaveBlock1Ptr->money, amount); + } return FALSE; } @@ -1914,12 +2308,18 @@ bool8 ScrCmd_showmoneybox(struct ScriptContext *ctx) u8 ignore = ScriptReadByte(ctx); if (!ignore) + { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + DrawMoneyBox(GetMoney(&gSaveBlock1Ptr->money), x, y); + } return FALSE; } bool8 ScrCmd_hidemoneybox(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + /*u8 x = ScriptReadByte(ctx); u8 y = ScriptReadByte(ctx);*/ @@ -1934,7 +2334,11 @@ bool8 ScrCmd_updatemoneybox(struct ScriptContext *ctx) u8 ignore = ScriptReadByte(ctx); if (!ignore) + { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ChangeAmountInMoneyBox(GetMoney(&gSaveBlock1Ptr->money)); + } return FALSE; } @@ -1943,6 +2347,8 @@ bool8 ScrCmd_showcoinsbox(struct ScriptContext *ctx) u8 x = ScriptReadByte(ctx); u8 y = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ShowCoinsWindow(GetCoins(), x, y); return FALSE; } @@ -1952,6 +2358,8 @@ bool8 ScrCmd_hidecoinsbox(struct ScriptContext *ctx) u8 UNUSED x = ScriptReadByte(ctx); u8 UNUSED y = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + HideCoinsWindow(); return FALSE; } @@ -1961,30 +2369,40 @@ bool8 ScrCmd_updatecoinsbox(struct ScriptContext *ctx) u8 UNUSED x = ScriptReadByte(ctx); u8 UNUSED y = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + PrintCoinsString(GetCoins()); return FALSE; } bool8 ScrCmd_trainerbattle(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_TRAINERBATTLE); + ctx->scriptPtr = BattleSetup_ConfigureTrainerBattle(ctx->scriptPtr); return FALSE; } bool8 ScrCmd_dotrainerbattle(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + BattleSetup_StartTrainerBattle(); return TRUE; } bool8 ScrCmd_gotopostbattlescript(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + ctx->scriptPtr = BattleSetup_GetScriptAddrAfterBattle(); return FALSE; } bool8 ScrCmd_gotobeatenscript(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + ctx->scriptPtr = BattleSetup_GetTrainerPostBattleScript(); return FALSE; } @@ -1993,6 +2411,8 @@ bool8 ScrCmd_checktrainerflag(struct ScriptContext *ctx) { u16 index = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + ctx->comparisonResult = HasTrainerBeenFought(index); return FALSE; } @@ -2001,6 +2421,8 @@ bool8 ScrCmd_settrainerflag(struct ScriptContext *ctx) { u16 index = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetTrainerFlag(index); return FALSE; } @@ -2009,6 +2431,8 @@ bool8 ScrCmd_cleartrainerflag(struct ScriptContext *ctx) { u16 index = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + ClearTrainerFlag(index); return FALSE; } @@ -2022,6 +2446,8 @@ bool8 ScrCmd_setwildbattle(struct ScriptContext *ctx) u8 level2 = ScriptReadByte(ctx); u16 item2 = ScriptReadHalfword(ctx); + Script_RequestEffects(SCREFF_V1); + if(species2 == SPECIES_NONE) { CreateScriptedWildMon(species, level, item); @@ -2038,6 +2464,8 @@ bool8 ScrCmd_setwildbattle(struct ScriptContext *ctx) bool8 ScrCmd_dowildbattle(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (sIsScriptedWildDouble == FALSE) BattleSetup_StartScriptedWildBattle(); else @@ -2052,6 +2480,8 @@ bool8 ScrCmd_pokemart(struct ScriptContext *ctx) { const void *ptr = (void *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + CreatePokemartMenu(ptr); ScriptContext_Stop(); return TRUE; @@ -2061,6 +2491,8 @@ bool8 ScrCmd_pokemartdecoration(struct ScriptContext *ctx) { const void *ptr = (void *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + CreateDecorationShop1Menu(ptr); ScriptContext_Stop(); return TRUE; @@ -2071,6 +2503,8 @@ bool8 ScrCmd_pokemartdecoration2(struct ScriptContext *ctx) { const void *ptr = (void *)ScriptReadWord(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + CreateDecorationShop2Menu(ptr); ScriptContext_Stop(); return TRUE; @@ -2080,6 +2514,8 @@ bool8 ScrCmd_playslotmachine(struct ScriptContext *ctx) { u8 machineId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + PlaySlotMachine(machineId, CB2_ReturnToFieldContinueScriptPlayMapMusic); ScriptContext_Stop(); return TRUE; @@ -2091,6 +2527,8 @@ bool8 ScrCmd_setberrytree(struct ScriptContext *ctx) u8 berry = ScriptReadByte(ctx); u8 growthStage = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (berry == 0) PlantBerryTree(treeId, berry, growthStage, FALSE); else @@ -2102,12 +2540,16 @@ bool8 ScrCmd_getpokenewsactive(struct ScriptContext *ctx) { u16 newsKind = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = IsPokeNewsActive(newsKind); return FALSE; } bool8 ScrCmd_choosecontestmon(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ChooseContestMon(); ScriptContext_Stop(); return TRUE; @@ -2116,6 +2558,8 @@ bool8 ScrCmd_choosecontestmon(struct ScriptContext *ctx) bool8 ScrCmd_startcontest(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + StartContest(); ScriptContext_Stop(); return TRUE; @@ -2123,6 +2567,8 @@ bool8 ScrCmd_startcontest(struct ScriptContext *ctx) bool8 ScrCmd_showcontestresults(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ShowContestResults(); ScriptContext_Stop(); return TRUE; @@ -2130,6 +2576,8 @@ bool8 ScrCmd_showcontestresults(struct ScriptContext *ctx) bool8 ScrCmd_contestlinktransfer(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + ContestLinkTransfer(gSpecialVar_ContestCategory); ScriptContext_Stop(); return TRUE; @@ -2139,6 +2587,8 @@ bool8 ScrCmd_dofieldeffect(struct ScriptContext *ctx) { u16 effectId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + sFieldEffectScriptId = effectId; FieldEffectStart(sFieldEffectScriptId); return FALSE; @@ -2148,6 +2598,8 @@ bool8 ScrCmd_setfieldeffectargument(struct ScriptContext *ctx) { u8 argNum = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1); + gFieldEffectArguments[argNum] = (s16)VarGet(ScriptReadHalfword(ctx)); return FALSE; } @@ -2162,7 +2614,11 @@ static bool8 WaitForFieldEffectFinish(void) bool8 ScrCmd_waitfieldeffect(struct ScriptContext *ctx) { - sFieldEffectScriptId = VarGet(ScriptReadHalfword(ctx)); + u32 scriptId = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + + sFieldEffectScriptId = scriptId; SetupNativeScript(ctx, WaitForFieldEffectFinish); return TRUE; } @@ -2171,12 +2627,16 @@ bool8 ScrCmd_setrespawn(struct ScriptContext *ctx) { u16 healLocationId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetLastHealLocationWarp(healLocationId); return FALSE; } bool8 ScrCmd_checkplayergender(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = gSaveBlock2Ptr->playerGender; return FALSE; } @@ -2186,17 +2646,23 @@ bool8 ScrCmd_playmoncry(struct ScriptContext *ctx) u16 species = VarGet(ScriptReadHalfword(ctx)); u16 mode = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + PlayCry_Script(species, mode); return FALSE; } void PlayFirstMonCry(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + PlayCry_Script(GetMonData(GetFirstLiveMon(), MON_DATA_SPECIES), CRY_MODE_NORMAL); } bool8 ScrCmd_waitmoncry(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, IsCryFinished); return TRUE; } @@ -2208,6 +2674,8 @@ bool8 ScrCmd_setmetatile(struct ScriptContext *ctx) u16 metatileId = VarGet(ScriptReadHalfword(ctx)); bool16 isImpassable = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + x += MAP_OFFSET; y += MAP_OFFSET; if (!isImpassable) @@ -2222,6 +2690,8 @@ bool8 ScrCmd_opendoor(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + x += MAP_OFFSET; y += MAP_OFFSET; PlaySE(GetDoorSoundEffect(x, y)); @@ -2234,6 +2704,8 @@ bool8 ScrCmd_closedoor(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + x += MAP_OFFSET; y += MAP_OFFSET; FieldAnimateDoorClose(x, y); @@ -2250,6 +2722,8 @@ static bool8 IsDoorAnimationStopped(void) bool8 ScrCmd_waitdooranim(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + SetupNativeScript(ctx, IsDoorAnimationStopped); return TRUE; } @@ -2259,6 +2733,8 @@ bool8 ScrCmd_setdooropen(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + x += MAP_OFFSET; y += MAP_OFFSET; FieldSetDoorOpened(x, y); @@ -2270,6 +2746,8 @@ bool8 ScrCmd_setdoorclosed(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + x += MAP_OFFSET; y += MAP_OFFSET; FieldSetDoorClosed(x, y); @@ -2298,7 +2776,12 @@ bool8 ScrCmd_showelevmenu(struct ScriptContext *ctx) bool8 ScrCmd_checkcoins(struct ScriptContext *ctx) { - u16 *ptr = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + u16 *ptr = GetVarPointer(varId); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + *ptr = GetCoins(); return FALSE; } @@ -2307,6 +2790,8 @@ bool8 ScrCmd_addcoins(struct ScriptContext *ctx) { u16 coins = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (AddCoins(coins) == TRUE) gSpecialVar_Result = FALSE; else @@ -2318,6 +2803,8 @@ bool8 ScrCmd_removecoins(struct ScriptContext *ctx) { u16 coins = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (RemoveCoins(coins) == TRUE) gSpecialVar_Result = FALSE; else @@ -2329,12 +2816,16 @@ bool8 ScrCmd_moverotatingtileobjects(struct ScriptContext *ctx) { u16 puzzleNumber = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + sMovingNpcId = MoveRotatingTileObjects(puzzleNumber); return FALSE; } bool8 ScrCmd_turnrotatingtileobjects(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + TurnRotatingTileObjects(); return FALSE; } @@ -2343,24 +2834,35 @@ bool8 ScrCmd_initrotatingtilepuzzle(struct ScriptContext *ctx) { u16 isTrickHouse = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + InitRotatingTilePuzzle(isTrickHouse); return FALSE; } bool8 ScrCmd_freerotatingtilepuzzle(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + FreeRotatingTilePuzzle(); return FALSE; } bool8 ScrCmd_selectapproachingtrainer(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + gSelectedObjectEvent = GetCurrentApproachingTrainerObjectEventId(); return FALSE; } bool8 ScrCmd_lockfortrainer(struct ScriptContext *ctx) { + // As a special case, skip this during analysis. + if (Script_IsAnalyzingEffects()) + return FALSE; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if (IsOverworldLinkActive()) { return FALSE; @@ -2382,6 +2884,8 @@ bool8 ScrCmd_setmodernfatefulencounter(struct ScriptContext *ctx) bool8 isModernFatefulEncounter = TRUE; u16 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + SetMonData(&gPlayerParty[partyIndex], MON_DATA_MODERN_FATEFUL_ENCOUNTER, &isModernFatefulEncounter); return FALSE; } @@ -2390,6 +2894,8 @@ bool8 ScrCmd_checkmodernfatefulencounter(struct ScriptContext *ctx) { u16 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MODERN_FATEFUL_ENCOUNTER, NULL); return FALSE; } @@ -2400,6 +2906,8 @@ bool8 ScrCmd_trywondercardscript(struct ScriptContext *ctx) if (script) { + Script_RequestEffects(SCREFF_V1); + gRamScriptRetAddr = ctx->scriptPtr; ScriptJump(ctx, script); } @@ -2416,6 +2924,8 @@ bool8 ScrCmd_warpspinenter(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); SetSpinStartFacingDir(GetPlayerFacingDirection()); DoSpinEnterWarp(); @@ -2428,6 +2938,8 @@ bool8 ScrCmd_setmonmetlocation(struct ScriptContext *ctx) u16 partyIndex = VarGet(ScriptReadHalfword(ctx)); u8 location = ScriptReadByte(ctx); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (partyIndex < PARTY_SIZE) SetMonData(&gPlayerParty[partyIndex], MON_DATA_MET_LOCATION, &location); return FALSE; @@ -2444,6 +2956,8 @@ bool8 ScrCmd_buffertrainerclassname(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 trainerClassId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], GetTrainerClassNameFromId(trainerClassId)); return FALSE; } @@ -2453,6 +2967,8 @@ bool8 ScrCmd_buffertrainername(struct ScriptContext *ctx) u8 stringVarIndex = ScriptReadByte(ctx); u16 trainerClassId = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + StringCopy(sScriptStringVars[stringVarIndex], GetTrainerNameFromId(trainerClassId)); return FALSE; } @@ -2470,6 +2986,8 @@ bool8 ScrCmd_warpwhitefade(struct ScriptContext *ctx) u16 x = VarGet(ScriptReadHalfword(ctx)); u16 y = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE); + SetWarpDestination(mapGroup, mapNum, warpId, x, y); DoWhiteFadeWarp(); ResetInitialPlayerAvatarState(); @@ -2478,12 +2996,17 @@ bool8 ScrCmd_warpwhitefade(struct ScriptContext *ctx) void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) { + Script_RequestEffects(SCREFF_V1); + sIsScriptedWildDouble = TRUE; } bool8 ScrCmd_removeallitem(struct ScriptContext *ctx) { u32 itemId = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + u32 count = CountTotalItemQuantityInBag(itemId); gSpecialVar_Result = count; RemoveBagItem(itemId, count); @@ -2495,8 +3018,15 @@ bool8 ScrCmd_getobjectxy(struct ScriptContext *ctx) { u32 localId = VarGet(ScriptReadHalfword(ctx)); u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); - u16 *pX = GetVarPointer(ScriptReadHalfword(ctx)); - u16 *pY = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varIdX = ScriptReadHalfword(ctx); + u32 varIdY = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varIdX); + Script_RequestWriteVar(varIdY); + + u16 *pX = GetVarPointer(varIdX); + u16 *pY = GetVarPointer(varIdY); GetObjectPosition(pX, pY, localId, useTemplate); return FALSE; @@ -2506,7 +3036,12 @@ bool8 ScrCmd_checkobjectat(struct ScriptContext *ctx) { u32 x = VarGet(ScriptReadHalfword(ctx)) + 7; u32 y = VarGet(ScriptReadHalfword(ctx)) + 7; - u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + + u16 *varPointer = GetVarPointer(varId); *varPointer = CheckObjectAtXY(x, y); @@ -2516,7 +3051,13 @@ bool8 ScrCmd_checkobjectat(struct ScriptContext *ctx) bool8 Scrcmd_getsetpokedexflag(struct ScriptContext *ctx) { u32 speciesId = SpeciesToNationalPokedexNum(VarGet(ScriptReadHalfword(ctx))); - bool32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); + u32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); + + if (desiredFlag == FLAG_SET_CAUGHT || desiredFlag == FLAG_SET_SEEN) + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + else + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = GetSetPokedexFlag(speciesId, desiredFlag); if (desiredFlag == FLAG_SET_CAUGHT) @@ -2528,6 +3069,9 @@ bool8 Scrcmd_getsetpokedexflag(struct ScriptContext *ctx) bool8 Scrcmd_checkspecies(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = CheckPartyHasSpecies(givenSpecies); return FALSE; @@ -2536,6 +3080,9 @@ bool8 Scrcmd_checkspecies(struct ScriptContext *ctx) bool8 Scrcmd_checkspecies_choose(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == givenSpecies); return FALSE; @@ -2544,9 +3091,21 @@ bool8 Scrcmd_checkspecies_choose(struct ScriptContext *ctx) bool8 Scrcmd_getobjectfacingdirection(struct ScriptContext *ctx) { u32 objectId = VarGet(ScriptReadHalfword(ctx)); - u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); + u32 varId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + + u16 *varPointer = GetVarPointer(varId); *varPointer = gObjectEvents[GetObjectEventIdByLocalId(objectId)].facingDirection; return FALSE; } + +void Script_EndTrainerCanSeeIf(struct ScriptContext *ctx) +{ + u8 condition = ScriptReadByte(ctx); + if (ctx->breakOnTrainerBattle && sScriptConditionTable[condition][ctx->comparisonResult] == 1) + StopScript(ctx); +} diff --git a/src/script.c b/src/script.c index e6e2aa264d..b2730fb97f 100644 --- a/src/script.c +++ b/src/script.c @@ -2,6 +2,8 @@ #include "script.h" #include "event_data.h" #include "mystery_gift.h" +#include "random.h" +#include "trainer_see.h" #include "util.h" #include "constants/event_objects.h" #include "constants/map_scripts.h" @@ -50,6 +52,8 @@ void InitScriptContext(struct ScriptContext *ctx, void *cmdTable, void *cmdTable for (i = 0; i < (int)ARRAY_COUNT(ctx->stack); i++) ctx->stack[i] = NULL; + + ctx->breakOnTrainerBattle = FALSE; } u8 SetupBytecodeScript(struct ScriptContext *ctx, const u8 *ptr) @@ -258,6 +262,14 @@ void ScriptContext_SetupScript(const u8 *ptr) sGlobalScriptContextStatus = CONTEXT_RUNNING; } +// Moves a script from a local context to the global context and enables it. +void ScriptContext_ContinueScript(struct ScriptContext *ctx) +{ + sGlobalScriptContext = *ctx; + LockPlayerFieldControls(); + sGlobalScriptContextStatus = CONTEXT_RUNNING; +} + // Puts the script into waiting mode; usually called from a wait* script command. void ScriptContext_Stop(void) { @@ -281,7 +293,7 @@ void RunScriptImmediately(const u8 *ptr) while (RunScriptCommand(&sImmediateScriptContext) == TRUE); } -u8 *MapHeaderGetScriptTable(u8 tag) +const u8 *MapHeaderGetScriptTable(u8 tag) { const u8 *mapScripts = gMapHeader.mapScripts; @@ -303,14 +315,18 @@ u8 *MapHeaderGetScriptTable(u8 tag) void MapHeaderRunScriptType(u8 tag) { - u8 *ptr = MapHeaderGetScriptTable(tag); + const u8 *ptr = MapHeaderGetScriptTable(tag); if (ptr) - RunScriptImmediately(ptr); + { + struct ScriptContext ctx; + if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_HARDWARE, ptr, &ctx)) + ScriptContext_ContinueScript(&ctx); + } } -u8 *MapHeaderCheckScriptTable(u8 tag) +const u8 *MapHeaderCheckScriptTable(u8 tag) { - u8 *ptr = MapHeaderGetScriptTable(tag); + const u8 *ptr = MapHeaderGetScriptTable(tag); if (!ptr) return NULL; @@ -332,7 +348,12 @@ u8 *MapHeaderCheckScriptTable(u8 tag) // Run map script if vars are equal if (VarGet(varIndex1) == VarGet(varIndex2)) - return T2_READ_PTR(ptr); + { + const u8 *mapScript = T2_READ_PTR(ptr); + if (!Script_HasNoEffect(mapScript)) + return mapScript; + } + ptr += 4; } } @@ -364,7 +385,7 @@ void RunOnDiveWarpMapScript(void) bool8 TryRunOnFrameMapScript(void) { - u8 *ptr = MapHeaderCheckScriptTable(MAP_SCRIPT_ON_FRAME_TABLE); + const u8 *ptr = MapHeaderCheckScriptTable(MAP_SCRIPT_ON_FRAME_TABLE); if (!ptr) return FALSE; @@ -375,7 +396,7 @@ bool8 TryRunOnFrameMapScript(void) void TryRunOnWarpIntoMapScript(void) { - u8 *ptr = MapHeaderCheckScriptTable(MAP_SCRIPT_ON_WARP_INTO_MAP_TABLE); + const u8 *ptr = MapHeaderCheckScriptTable(MAP_SCRIPT_ON_WARP_INTO_MAP_TABLE); if (ptr) RunScriptImmediately(ptr); } @@ -504,3 +525,116 @@ void InitRamScript_NoObjectEvent(u8 *script, u16 scriptSize) InitRamScript(script, scriptSize, MAP_GROUP(UNDEFINED), MAP_NUM(UNDEFINED), NO_OBJECT); #endif //FREE_MYSTERY_EVENT_BUFFERS } + +bool8 LoadTrainerObjectScript(void) +{ + sGlobalScriptContext.scriptPtr = gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr; + return TRUE; +} + +struct ScriptEffectContext { + u32 breakOn; + intptr_t breakTo[5]; + const u8 *nextCmd; +}; + +struct ScriptEffectContext *gScriptEffectContext = NULL; + +static bool32 Script_IsEffectInstrumentedCommand(ScrCmdFunc func) +{ + // In ROM mirror 1. + return (((uintptr_t)func) & 0xE000000) == 0xA000000; +} + +/* 'setjmp' and 'longjmp' cause link errors, so we use + * '__builtin_setjmp' and '__builtin_longjmp' instead. + * See https://gcc.gnu.org/onlinedocs/gcc/Nonlocal-Gotos.html */ +static bool32 RunScriptImmediatelyUntilEffect_InternalLoop(struct ScriptContext *ctx) +{ + if (__builtin_setjmp(gScriptEffectContext->breakTo) == 0) + { + while (TRUE) + { + u32 cmdCode; + ScrCmdFunc *func; + + gScriptEffectContext->nextCmd = ctx->scriptPtr; + + if (!ctx->scriptPtr) + return FALSE; + + cmdCode = *ctx->scriptPtr; + ctx->scriptPtr++; + func = &ctx->cmdTable[cmdCode]; + + // Invalid script command. + if (func >= ctx->cmdTableEnd) + return TRUE; + + if (!Script_IsEffectInstrumentedCommand(*func)) + return TRUE; + + // Command which waits for a frame. + if ((*func)(ctx)) + { + gScriptEffectContext->nextCmd = ctx->scriptPtr; + return TRUE; + } + } + } + else + { + return TRUE; + } +} + +void Script_GotoBreak_Internal(void) +{ + __builtin_longjmp(gScriptEffectContext->breakTo, 1); +} + +bool32 RunScriptImmediatelyUntilEffect_Internal(u32 effects, const u8 *ptr, struct ScriptContext *ctx) +{ + bool32 result; + struct ScriptEffectContext seCtx; + seCtx.breakOn = effects & 0x7FFFFFFF; + + if (ctx == NULL) + ctx = &sImmediateScriptContext; + + InitScriptContext(ctx, gScriptCmdTable, gScriptCmdTableEnd); + if (effects & SCREFF_TRAINERBATTLE) + ctx->breakOnTrainerBattle = TRUE; + SetupBytecodeScript(ctx, ptr); + + rng_value_t rngValue = gRngValue; + gScriptEffectContext = &seCtx; + result = RunScriptImmediatelyUntilEffect_InternalLoop(ctx); + gScriptEffectContext = NULL; + gRngValue = rngValue; + + if (result) + ctx->scriptPtr = seCtx.nextCmd; + + return result; +} + +bool32 Script_HasNoEffect(const u8 *ptr) +{ + return !RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE, ptr, NULL); +} + +void Script_RequestEffects_Internal(u32 effects) +{ + if (gScriptEffectContext->breakOn & effects) + __builtin_longjmp(gScriptEffectContext->breakTo, 1); +} + +void Script_RequestWriteVar_Internal(u32 varId) +{ + if (varId == 0) + return; + if (SPECIAL_VARS_START <= varId && varId <= SPECIAL_VARS_END) + return; + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); +} diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index fc248435a9..94c7d7d8fa 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -240,6 +240,9 @@ void CanHyperTrain(struct ScriptContext *ctx) { u32 stat = ScriptReadByte(ctx); u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1); + if (stat < NUM_STATS && partyIndex < PARTY_SIZE && !GetMonData(&gPlayerParty[partyIndex], MON_DATA_HYPER_TRAINED_HP + stat) @@ -257,6 +260,9 @@ void HyperTrain(struct ScriptContext *ctx) { u32 stat = ScriptReadByte(ctx); u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (stat < NUM_STATS && partyIndex < PARTY_SIZE) { bool32 data = TRUE; @@ -268,6 +274,9 @@ void HyperTrain(struct ScriptContext *ctx) void HasGigantamaxFactor(struct ScriptContext *ctx) { u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1); + if (partyIndex < PARTY_SIZE) gSpecialVar_Result = GetMonData(&gPlayerParty[partyIndex], MON_DATA_GIGANTAMAX_FACTOR); else @@ -278,6 +287,8 @@ void ToggleGigantamaxFactor(struct ScriptContext *ctx) { u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + gSpecialVar_Result = FALSE; if (partyIndex < PARTY_SIZE) @@ -298,6 +309,8 @@ void CheckTeraType(struct ScriptContext *ctx) { u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = TYPE_NONE; if (partyIndex < PARTY_SIZE) @@ -309,6 +322,8 @@ void SetTeraType(struct ScriptContext *ctx) u32 type = ScriptReadByte(ctx); u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (type < NUMBER_OF_MON_TYPES && partyIndex < PARTY_SIZE) SetMonData(&gPlayerParty[partyIndex], MON_DATA_TERA_TYPE, &type); } @@ -542,6 +557,11 @@ void ScrCmd_createmon(struct ScriptContext *ctx) u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv}; u16 moves[MAX_MON_MOVES] = {move1, move2, move3, move4}; + if (side == 0) + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + else + Script_RequestEffects(SCREFF_V1); + gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, isShiny, ggMaxFactor, teraType); } @@ -580,6 +600,8 @@ void Script_SetStatus1(struct ScriptContext *ctx) u32 status1 = VarGet(ScriptReadHalfword(ctx)); u32 slot = VarGet(ScriptReadHalfword(ctx)); + Script_RequestEffects(SCREFF_V1 | SCREFF_SAVE); + if (slot >= PARTY_SIZE) { u16 species; diff --git a/src/trainer_see.c b/src/trainer_see.c index 2582637aa1..12c6cba612 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -2,6 +2,7 @@ #include "battle_setup.h" #include "event_data.h" #include "event_object_movement.h" +#include "event_scripts.h" #include "field_effect.h" #include "field_player_avatar.h" #include "pokemon.h" @@ -375,6 +376,16 @@ bool8 CheckForTrainersWantingBattle(void) continue; numTrainers = CheckTrainer(i); + if (numTrainers == 0xFF) // non-trainerbatle script + { + u32 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId; + gSelectedObjectEvent = objectEventId; + gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; + ScriptContext_SetupScript(EventScript_ObjectApproachPlayer); + LockPlayerFieldControls(); + return TRUE; + } + if (numTrainers == 2) break; @@ -417,14 +428,33 @@ bool8 CheckForTrainersWantingBattle(void) static u8 CheckTrainer(u8 objectEventId) { - const u8 *scriptPtr; + const u8 *scriptPtr, *trainerBattlePtr; u8 numTrainers = 1; - u8 approachDistance; + + u8 approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]); + if (approachDistance == 0) + return 0; if (InTrainerHill() == TRUE) - scriptPtr = GetTrainerHillTrainerScript(); + { + trainerBattlePtr = scriptPtr = GetTrainerHillTrainerScript(); + } else - scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); + { + trainerBattlePtr = scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); + struct ScriptContext ctx; + if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, scriptPtr, &ctx)) + { + if (*ctx.scriptPtr == 0x5c) // trainerbattle + trainerBattlePtr = ctx.scriptPtr; + else + trainerBattlePtr = NULL; + } + else + { + return 0; // no effect + } + } if (InBattlePyramid()) { @@ -436,36 +466,36 @@ static u8 CheckTrainer(u8 objectEventId) if (GetHillTrainerFlag(objectEventId)) return 0; } - else + else if (trainerBattlePtr) { - if (GetTrainerFlagFromScriptPointer(scriptPtr)) + if (GetTrainerFlagFromScriptPointer(trainerBattlePtr)) return 0; } - - approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]); - - if (approachDistance != 0) + else { - if (scriptPtr[1] == TRAINER_BATTLE_DOUBLE - || scriptPtr[1] == TRAINER_BATTLE_REMATCH_DOUBLE - || scriptPtr[1] == TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE) + numTrainers = 0xFF; + } + + if (trainerBattlePtr) + { + if (trainerBattlePtr[1] == TRAINER_BATTLE_DOUBLE + || trainerBattlePtr[1] == TRAINER_BATTLE_REMATCH_DOUBLE + || trainerBattlePtr[1] == TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE) { if (GetMonsStateToDoubles_2() != PLAYER_HAS_TWO_USABLE_MONS) return 0; numTrainers = 2; } - - gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId; - gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr; - gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance; - InitTrainerApproachTask(&gObjectEvents[objectEventId], approachDistance - 1); - gNoOfApproachingTrainers++; - - return numTrainers; } - return 0; + gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId; + gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr; + gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance; + InitTrainerApproachTask(&gObjectEvents[objectEventId], approachDistance - 1); + gNoOfApproachingTrainers++; + + return numTrainers; } static u8 GetTrainerApproachDistance(struct ObjectEvent *trainerObj) diff --git a/test/script.c b/test/script.c new file mode 100644 index 0000000000..5f185d72cf --- /dev/null +++ b/test/script.c @@ -0,0 +1,116 @@ +#include "global.h" +#include "test/test.h" +#include "test/overworld_script.h" +#include "script.h" +#include "constants/decorations.h" +#include "constants/moves.h" + +TEST("Script_HasNoEffect control flow") +{ + const u8 *script = OVERWORLD_SCRIPT( + nop; + nop1; + checkflag FLAG_TEMP_1; + checktrainerflag 0; + compare VAR_TEMP_0, 0; + goto_if_eq GoneTo; + GoneTo: + call Sub; + call_if_eq Sub; + end; + + Sub: + goto SubRet; + SubRet: + return; + ); + EXPECT(Script_HasNoEffect(script)); +} + +TEST("Script_HasNoEffect variables") +{ + // Writes to special variables are not considered player-visible + // because their values are indeterminate if the player has control. + const u8 *writeSpecial = OVERWORLD_SCRIPT( + setvar VAR_0x8000, 1; + addvar VAR_0x8000, 1; + subvar VAR_0x8000, VAR_TEMP_0; + copyvar VAR_0x8000, VAR_TEMP_0; + setorcopyvar VAR_0x8000, VAR_TEMP_0; + specialvar VAR_RESULT, GetPlayerFacingDirection; + getplayerxy VAR_0x8000, VAR_0x8001; + getpartysize; + checkitemspace ITEM_POTION, 1; + checkitem ITEM_POTION, 1; + checkitemtype ITEM_POTION; + checkpcitem ITEM_POTION, 1; + checkdecorspace DECOR_SNORLAX_DOLL; + checkdecor DECOR_SNORLAX_DOLL; + checkpartymove MOVE_CELEBRATE; + random 2; + checkmoney 5000; + getpokenewsactive POKENEWS_LILYCOVE; + checkplayergender; + checkcoins VAR_RESULT; + checkmodernfatefulencounter 0; + end; + ); + + // Writes to other variables are considered player-visible because + // their values are preserved even while the player has control. + const u8 *setVariable = OVERWORLD_SCRIPT( + setvar VAR_TEMP_0, 1; + end; + ); + + const u8 *addVariable = OVERWORLD_SCRIPT( + addvar VAR_TEMP_0, 1; + end; + ); + + const u8 *subVariable = OVERWORLD_SCRIPT( + subvar VAR_TEMP_0, 1; + end; + ); + + const u8 *copyVariable = OVERWORLD_SCRIPT( + copyvar VAR_TEMP_0, VAR_RESULT; + end; + ); + + const u8 *setorcopyVariable = OVERWORLD_SCRIPT( + setorcopyvar VAR_TEMP_0, VAR_RESULT; + end; + ); + + const u8 *specialvarVariable = OVERWORLD_SCRIPT( + specialvar VAR_TEMP_0, GetPlayerFacingDirection; + end; + ); + + const u8 *getPlayerXYVariable1 = OVERWORLD_SCRIPT( + getplayerxy VAR_TEMP_0, VAR_RESULT; + end; + ); + + const u8 *getPlayerXYVariable2 = OVERWORLD_SCRIPT( + getplayerxy VAR_RESULT, VAR_TEMP_0; + end; + ); + + const u8 *checkCoinsVariable = OVERWORLD_SCRIPT( + checkcoins VAR_TEMP_0; + end; + ); + + EXPECT(Script_HasNoEffect(writeSpecial)); + EXPECT(!Script_HasNoEffect(setVariable)); + EXPECT(!Script_HasNoEffect(addVariable)); + EXPECT(!Script_HasNoEffect(subVariable)); + EXPECT(!Script_HasNoEffect(copyVariable)); + EXPECT(!Script_HasNoEffect(setorcopyVariable)); + EXPECT(!Script_HasNoEffect(specialvarVariable)); + EXPECT(!Script_HasNoEffect(getPlayerXYVariable1)); + EXPECT(!Script_HasNoEffect(getPlayerXYVariable2)); + EXPECT(!Script_HasNoEffect(checkCoinsVariable)); +}