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.
60 lines
2.0 KiB
C
60 lines
2.0 KiB
C
/* Embedded DSL for testing overworld scripts in isolation.
|
|
* The overworld is not available, so it is only possible to test
|
|
* commands which don't affect the overworld itself, e.g. givemon can
|
|
* be tested because it only alters gPlayerParty, but addobject cannot
|
|
* because it affects object events (which aren't loaded).
|
|
*
|
|
* OVERWORLD_SCRIPT(instructions...)
|
|
* Returns a pointer to a compiled overworld script. Cannot be used to
|
|
* initialize global const data, although the pointer IS to const data.
|
|
* Note that each script command must be followed by a ;, e.g.:
|
|
* const u8 *myScript = OVERWORLD_SCRIPT(
|
|
* random 2;
|
|
* addvar VAR_RESULT, 1;
|
|
* );
|
|
*
|
|
* RUN_OVERWORLD_SCRIPT(instructions...)
|
|
* Runs an overworld script in the immediate script context, which means
|
|
* that commands like waitstate are not supported.
|
|
* RUN_OVERWORLD_SCRIPT(
|
|
* setvar VAR_RESULT, 3;
|
|
* );
|
|
* EXPECT_EQ(GetVar(VAR_RESULT), 3); */
|
|
#ifndef GUARD_TEST_OVERWORLD_SCRIPT
|
|
#define GUARD_TEST_OVERWORLD_SCRIPT
|
|
|
|
#include "script.h"
|
|
#include "test/test.h"
|
|
|
|
#define OVERWORLD_SCRIPT(...) \
|
|
({ \
|
|
const u8 *_script; \
|
|
asm("mov %0, pc\n" \
|
|
"b .Lend" STR(__LINE__) "\n" \
|
|
STR(__VA_ARGS__) \
|
|
"\n" \
|
|
"end\n" \
|
|
".balign 2\n" \
|
|
".Lend" STR(__LINE__) ":\n" \
|
|
: "=r" (_script)); \
|
|
_script; \
|
|
})
|
|
|
|
#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");
|
|
|
|
#endif
|