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.
This commit is contained in:
Martin Griffin 2025-01-08 10:27:00 +00:00 committed by GitHub
parent 5391b451ae
commit bb781f21a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1475 additions and 339 deletions

View File

@ -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

View File

@ -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

View File

@ -11,6 +11,7 @@ Route110_MapScripts::
Route110_OnResume:
special UpdateCyclingRoadState
msgbox Route110_Text_WeCantTalkAboutAquaActivities, MSGBOX_SIGN
end
Route110_OnTransition:

View File

@ -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

View File

@ -20,3 +20,9 @@ EventScript_GotoTrainerScript::
gotobeatenscript
releaseall
end
EventScript_ObjectApproachPlayer::
lock
special DoTrainerApproach
waitstate
gotonative LoadTrainerObjectScript

View File

@ -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

View File

@ -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[];

View File

@ -1,6 +1,8 @@
#ifndef GUARD_SCRIPT_H
#define GUARD_SCRIPT_H
#include <setjmp.h>
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

View File

@ -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");

View File

@ -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;
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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))

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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;

View File

@ -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,25 +466,28 @@ 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;
@ -463,9 +496,6 @@ static u8 CheckTrainer(u8 objectEventId)
gNoOfApproachingTrainers++;
return numTrainers;
}
return 0;
}
static u8 GetTrainerApproachDistance(struct ObjectEvent *trainerObj)

116
test/script.c Normal file
View File

@ -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));
}