Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrary trainer scripts + on frame/trigger softlock prevention #5033

Merged
merged 8 commits into from
Jan 8, 2025

Conversation

mrgriffin
Copy link
Collaborator

@mrgriffin mrgriffin commented Jul 25, 2024

RunScriptImmediatelyUntilEffect runs a script until it either exits with end/return, or would have an effect. The possible effects are:

  • SCREFF_SAVE an effect on the save (so a write to anything in gSaveBlock1/gSaveBlock2/gSaveBlock3/gPokemonStorage/etc).
  • SCREFF_HARDWARE an effect on the hardware registers (e.g. playing a sound, displaying graphics, waiting for a button input, etc).
  • SCREFF_TRAINERBATTLE literally just the trainerbattle command.

Opcodes have been manually tagged with whether they call Script_RequestEffects to warn about the effects they would have if executed. Natives and specials can be tagged too (and a few have been).

Using these, we're able to:

  1. Not run on frame map scripts or triggers if they would have no effect.
  2. 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). (Currently broken—reverted by Revert #5033 change to MapHeaderRunScriptType #5975)
  3. 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.
  4. 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.

Things to note in the release changelog:

No script which was correctly written before this PR should have changed behavior after it (if you find an instance, it's either an incorrect script or a bug in this PR).

Script commands, targets of callnative, and specials need to be tagged with requests_effects=1 and call Script_RequestEffects before performing those effects to be supported by RunScriptImmediatelyUntilEffect/Script_HasNoEffect/trainer sight.

If your code has no effects, it must call Script_RequestEffects(SCREFF_V1); to make that explicit.

@mrgriffin
Copy link
Collaborator Author

There's obviously a comparison to be drawn between point 3 and ghoulslash's trainer_see_scripts branch.

The small difference is that this PR puts the check to see if the script should run inside the script (via cant_see/cant_see_if_*), whereas trainer_see_scripts puns the "Trainer Type" field in Porymap as a flag which controls whether the trainer should run (equivalent to cant_see_if_flag_set).

A consequence of punning the flag is iiuc that flags 1-3 are treated as trainer types, meaning that FLAG_TEMP_1-FLAG_TEMP_3 don't work in trainer_see_scripts, and out of the box nor do any flags with IDs greater than 0xFF (which is most of them, including all trainer flags).

I personally prefer the design in this branch—if a user has a script that doesn't start with trainerbattle then it will automatically work, but they might get immediately re-seen after the script finishes. imo this behavior is preferable to a crash and/or an unexpected battle, and I would expect that if somebody described their situation on Discord the helpers there would quickly learn to suggest a cant_see_if_* check.

@mrgriffin mrgriffin force-pushed the rhh-script-has-no-effect branch from 2c04041 to 1eb51c4 Compare July 25, 2024 07:59
@mrgriffin mrgriffin marked this pull request as ready for review July 25, 2024 09:06
Copy link
Collaborator

@Bassoonian Bassoonian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite a bit of this is out of my league, but some preliminary thoughts before other people review this

asm/macros/event.inc Outdated Show resolved Hide resolved
asm/macros/event.inc Outdated Show resolved Hide resolved
asm/macros/event.inc Outdated Show resolved Hide resolved
data/script_cmd_table.inc Outdated Show resolved Hide resolved
@mrgriffin
Copy link
Collaborator Author

mrgriffin commented Jul 25, 2024

EDIT: special flags are no longer supported due to the issue mentioned at the bottom of this message.

Oh, to quickly justify why I think that special vars, special flags, and string buffers do not count as player-visible but everything else does:

  1. Soft-resets will clear special vars/flags and string buffers. Therefore no script should assume that they hold any particular value when it starts, and so Script_HasNoEffect mutating those values just moves them from one indeterminate value to another.
  2. Interacting with the UI and/or walking around the map can easily alter special vars and string buffers.

I think you could argue that special flags shouldn't be counted as non-visible, e.g. FLAG_HIDE_MAP_NAME_POPUP or FLAG_DONT_TRANSITION_MUSIC do have visible effects if the script executes at the right time. I'd be happy to drop the idea of special flags being non-player-visible if pushed.

@mrgriffin mrgriffin added this to the 1.10 milestone Jul 26, 2024
asm/macros/event.inc Outdated Show resolved Hide resolved
src/script.c Outdated Show resolved Hide resolved
src/script.c Outdated Show resolved Hide resolved
@mrgriffin mrgriffin marked this pull request as draft August 5, 2024 08:35
@mrgriffin
Copy link
Collaborator Author

Temporarily marked as draft to give myself time to think about Deokishisu's comments on Discord.

@Bassoonian Bassoonian removed this from the 1.10 milestone Oct 18, 2024
@mrgriffin mrgriffin added the category: overworld Pertains to out-of-battle mechanics label Oct 22, 2024
Script_RunImmediatelyUntilEffect runs a script until either a specified
effect may occur or it reaches an end.

All existing script commands now call Script_RequestEffects which allow
us to analyze them. Any downstream script commands will be statically
known not to call Script_RequestEffects and treated as if they have all
effects. Specials and natives are also able to opt-in to
Script_RequestEffects.

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. 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.
4. 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.
@mrgriffin mrgriffin force-pushed the rhh-script-has-no-effect branch from 6f8c414 to 8a7553a Compare January 7, 2025 08:08
@mrgriffin mrgriffin marked this pull request as ready for review January 7, 2025 08:08
@mrgriffin
Copy link
Collaborator Author

Temporarily marked as draft to give myself time to think about Deokishisu's comments on Discord.

I've changed how analysis works to account for these—in addition to Script_HasNoEffect there's now a RunScriptImmediatelyUntilEffect which is able to choose which effects cause the script to be suspended (for Deo's example, just SCREFF_HARDWARE) and a ScriptContext_ContinueScript which can be used to resume the hardware effects on the global script context.

This allows the player to move through the Cycling Road gate without losing any momentum on their bike.

@mrgriffin
Copy link
Collaborator Author

The reason why there's a version number (SCREFF_V1) is that in the future we may find a need to tag another kind of effect, and without a version number there wouldn't be any way to tell if RequestEffects(SCREFF_SAVE) only has the save effect, or only had the save effect at the time it was written.

RequestEffects(SCREFF_V1) calls are thus saying "this code has no v1 effects", and I've used a macro to optimize those no-op calls out. The calls are still important, because in the future if we had v2 effects then the v1 call would no longer be a no-op (it would abort if breakOn contained any v2 effects).

@mrgriffin
Copy link
Collaborator Author

ty Egg! I'll squash this so I can get a proper commit message written :)

@mrgriffin mrgriffin merged commit bb781f2 into rh-hideout:upcoming Jan 8, 2025
1 check passed
hedara90 pushed a commit that referenced this pull request Jan 8, 2025
mrgriffin added a commit that referenced this pull request Jan 9, 2025
AsparagusEduardo pushed a commit that referenced this pull request Jan 9, 2025
@mrgriffin mrgriffin added the type: big feature A feature with big diffs and / or high impact / subjectivity / pervasiveness label Jan 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category: overworld Pertains to out-of-battle mechanics type: big feature A feature with big diffs and / or high impact / subjectivity / pervasiveness
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants