546 - bombShield(int a, int script)


Sets the bomb invulnerability flag (a=0 false, a=1 true), the caller's ANM script will change to script when a bomb is active.

611 - etEx(int etId, int async, int type, float a, float b)


Adds bullet transformation to bullet manager etId, Transformation of flag type determines the specific behaviour of the bullet. Documentation can be found here.

BulletEffectType-67108864 - etExDelay(string et, string isAsync, string time)


Hide the bullet for time frames. During this time, it will not move, will be invisible and won't have a hitbox. If used as a first transformation, can be used to delay when the bullet actually appears.

6 - isDelayedSpawn


While this flag is set, the bullet will be invisible and not have a hitbox. This flag is cleared once the delay timer ends.

4 - short_timer


Integer value between -1 and 15 inclusive (by default -1). If non-negative, shooters using short_timer shoot their bullets and timer increments by 1 per frame until 15 is reached. Once 15, timer reset to -1 or 0 depending on Z key state.

5 - long_timer


Integer value between -1 and 120 inclusive (by default -1). If non-negative, shooters using long_timer shoot their bullets and timer increments by 1 per frame until 120 is reached. Once 120, timer reset to -1 or 0 depending on Z key state.

503 - flagClear(int n)


Clears flag(s) according to n. Refer to the flag table here for exact values.

91 - floatTime(int slot, float var, int time, int mode, float start, float final)


In time frames using mode mode, variable var changes from start to final. slot is used to set the slot to be used by this ins, every enemy has 8 slots.

-9980 - F1


Local float variable, inherited by spawned enemies.

-9984 - I1


Local integer variable, inherited by spawned enemies.

-9989 - ANGLE_PLAYER


Angle from the enemy to the player.

-9962 - BOSS_Y


Final Y position of the boss.

-9990 - PLAYER_Y


Player's Y position.

-9963 - BOSS_X


Final X position of the boss.

-9991 - PLAYER_X


Player's X position.

81 - circlePos(float varX, float varY, float angle, float radius)


Performs following operation: varX = cos(angle) * radius and varY = cos(angle) * radius

-9981 - F0


Local float variable, inherited by spawned enemies.

300 - enmCreate(string sub, float x, float y, int hp, int score, int item)


Creates an enemy using subroutine sub at coordinates (x, y) (relative to position of the parent), health of created enemy is hp, score bonus is score and item drop is item.

601 - etOn(int etId)


Shoots bullet(s) using properties from bullet manager etId.

23 - wait(int time)


Stops sub execution for time frames.

1 - delete()


Returns to the top of current call stack.

2 - GAME_SPEED


The value controls by how much faster the game speed is run. This variable is often set to a number between 0 and 1. This value is often changed when a (final) boss dies or when a photo is taken. Its default value is 1.

-9985 - I0


Local integer variable, inherited by spawned enemies.

-9983 - I2


Local integer variable, inherited by spawned enemies.

-9982 - I3


Local integer variable, inherited by spawned enemies.

-9954 - ENEMY_HP


Enemy's current HP.

3 - goheiExists


A flag that determines if a gohei exists on-screen or not. Flag is set to true by active gohei. Flag is set to false if gohei goes off-screen or is despawning.

-9926 - GI0


Global integer value. Value is initialised to 0 on stage load but not on stage reset.

-9925 - GI1


Global integer value. Value is initialised to 0 on stage load but not on stage reset.

-9924 - GI2


Global integer value. Value is initialised to 0 on stage load but not on stage reset.

-9923 - GI3


Global integer value. Value is initialised to 0 on stage load but not on stage reset.

Yoshika Revival Bug


Specifications


  • Versions: 1.00a - 1.00b - 1.00c
  • Difficulty: Easy - Normal - Hard - Lunatic - Extra - Overdrive
  • Mode: Main game - Practice mode - Spell practice
  • Shottype: Reimu - Marisa - Sanae - Youmu

What happens


Sometimes when Yoshika is dead, she will not be revived by Seiga: Seiga continues to shoot bullets as if Yoshika is still alive. This behaviour only happens on the first two spell cards of Seiga regardless of difficulty. Additionally, this behaviour is most commonly encountered in practice mode (especially using practice tools), but it is also possible to encounter this issue in a real run.

Seiga fighting Youmu while Yoshika is dead
Figure 1: Seiga on her second spell. She keeps firing bullets despite Yoshika being dead.

How it happens


While the cause of this bug is the same on both the first spell-card and second spell-card, the set-ups are slightly different:

Spell 1


  1. On this stage, reset while Yoshika is alive on any of the following sections:
    • 240 frames after Seiga's first spell started.
    • 160 frames after Seiga's second spell started.
    • During Seiga's third non-spell started.
    • 160 frames after Seiga's third spell started.
  2. Then reset the stage by doing Esc + R. DO NOT restart the stage from the main menu, but do it from the pause menu instead.
  3. Reach Seiga's first spell and kill Yoshika within the first 240 frames of this spell-card.

Note that this bug works with replays too. This means that if you have a functional Stage 4 replay that finishes Seiga's first spell within 240 frames, it's possible to encounter this bug when you restart the stage from the pause menu. By restarting, the replay desyncs on Seiga's first spell, but this can easily be fixed by going back to the main menu and replaying the replay.

Spell 2


  1. On Seiga's first spell, finish the spell-card while Yoshika is still alive.
  2. Within the first 160 frames of Seiga's second spell, kill Yoshika.

Unlike the bug with the first spell-card, this bug does not involve resetting the stage, meaning you can have a full game replay that encounters this issue.

Why it happens


Global variables


The game uses global variables (e.g. GI0, GI1, GI2, GI3) for certain patterns. When a stage is loaded, every global variable's value is set to 0. Here's an example of how the initialisation would look for every global integer variable:

GI0 = 0;
GI1 = 0;
GI2 = 0;
GI3 = 0;

This global variable initialisation is done every time a new stage is loaded into the memory, which is when you load a stage through either a stage transition or from the main menu. Note that this initialisation is not done when you reload the stage by e.g. Esc + R. For our interest, this issue is related to how global variable GI1 is accessed and written to by the game's code.

In the context of this bug, only the global variable GI1 is used. This global variable GI1 can have two values in this stage:

  1. GI1 = 0, which is the state when Yoshika is dead on attacks Spell 1, Spell 2, Non-spell 3, and Spell 3
  2. GI1 = 1, which is the state when Yoshika is alive on attacks Spell 1, Spell 2, Non-spell 3, and Spell 3

This variable is used by Seiga, who will proceed to do a different action depending on the attack the value of GI1 was changed on. For example, when GI1 = 0 on the third non-spell, Seiga shoots more knives at the player, or when GI1 = 0 on the first spell, Seiga will resurrect Yoshika.

Seiga fighting Youmu while Yoshika is alive
Figure 2: Third non-spell attack where GI1 = 1, meaning that Yoshika is alive.
Seiga fighting Youmu while Yoshika is dead
Figure 3: Third non-spell attack where GI1 = 0, meaning that Yoshika is dead.

Accessing the global variable GI1


In this stage, only Yoshika is able to write to GI1. On the other hand, Seiga reads GI1 and does certain actions depending on its value. The next section explains how GI1 is accessed on the second non-spell and second spell-card using ECL code.

Below the following subroutines are denoted:

  • Boss2 being Seiga's second non-spell.
  • BossMiyako2 being Yoshika, which is called right as Boss2 is called.
  • BossMiyako2Dead being Yoshika's death subroutine, i.e. this gets called when Yoshika dies on the second non-spell
  • BossCard2 being Seiga's second spell-card
  • BossMiyakoCard2init being Yoshika, which is called right as Boss2 is called.
  • BossMiyakoCard2 also being Yoshika, but this is called 160 frames after BossMiyakoCard2init is called. When called, GI1 is set to 1.
  • BossMiyakoCard2Dead being Yoshika's death subroutine after BossMiyakoCard2 is called. When called, GI1 is set to 0.

To get more comfortable with these subroutines, some examples will be provided below with how the order of subroutines executed would look like in various scenarios.

Example 1 - Normal


Imagine you are fighting against Seiga and you are on her second non-spell and you do not kill Yoshika. Once the second spell starts, you stop shooting. The order of subroutines executed would be as follows:

Boss2 → BossMiyako2 → BossCard2 → BossMiyakoCard2init → BossMiyakoCard2

Once BossMiyakoCard2 is called, the value of GI1 is set to 1. Below is a table that summarises the GI1 value per subroutine.

Subroutine GI1 value
Boss2 0 or 1
BossMiyako2 0 or 1
BossCard2 0 or 1
BossMiyakoCard2init 0 or 1
BossMiyakoCard2 1

Example 2 - Normal


Imagine you are fighting against Seiga and you are on her second non-spell and you kill Yoshika. Once the second spell starts, you kill Yoshika two times. The order of subroutines executed would be as follows:

Boss2 → BossMiyako2 → BossMiyako2Dead (killed) → BossCard2 → BossMiyakoCard2init → BossMiyakoCard2 → BossMiyakoCard2Dead (killed) → BossMiyakoCard2init (revive 1) → BossMiyakoCard2 → BossMiyakoCard2Dead (killed) → BossMiyakoCard2init (revive 2) → BossMiyakoCard2

Once BossMiyakoCard2 is called, the value of GI1 is set to 1. When Yoshika dies, BossMiyakoCard2Dead is called, and the new value of GI1 is 0. Note that when Seiga revives Yoshika, Seiga calls for BossMiyakoCard2init. This subroutine then calls for BossMiyakoCard2, which continues the spell-card. In this example, reviving Yoshika is done twice.

Below is a table that summarises the GI1 value per subroutine.

Subroutine GI1 value
Boss2 0 or 1
BossMiyako2 0 or 1
BossMiyako2Dead 0 or 1
BossCard2 0 or 1
BossMiyakoCard2init 0 or 1
BossMiyakoCard2 1
BossMiyakoCard2Dead 0
BossMiyakoCard2init 0
BossMiyakoCard2 1
BossMiyakoCard2Dead 0
BossMiyakoCard2init 0
BossMiyakoCard2 1

Example 3 - Bugged


Imagine you are fighting against Seiga and you are on her second non-spell and you do not kill Yoshika. Once the second spell starts, you kill Yoshika within 160 frames of the spell starting. The order of subroutines executed would be as follows:

Boss2 → BossMiyako2 → BossCard2 → BossMiyakoCard2init → BossMiyako2Dead

In this example, the subroutines BossMiyakoCard2 and BossMiyakoCard2Dead are never called because Yoshika was killed before they were able to be called. This means that Yoshika never writes to GI1, meaning the behaviour of Seiga depends on an earlier subroutine that set GI1.

Below is a table that summarises the GI1 value per subroutine.

Subroutine GI1 value
Boss2 0 or 1
BossMiyako2 0 or 1
BossCard2 0 or 1
BossMiyakoCard2init 0 or 1
BossMiyako2Dead 0 or 1

For example, if the player ended spell-card 1 with Yoshika dead (so GI1 = 0), then Seiga will revive Yoshika in the earlier given example. However, if the player ended spell-card 1 with Yoshika alive (so GI1 = 1), then Seiga will not revive Yoshika.

Seiga fighting Youmu while Yoshika is dead
Figure 4: Seiga on her second spell. Spell 1 ended with Yoshika alive (so GI1 = 1). Yoshika was killed in the first 160 frames of the spell-card starting, meaning the subroutines that usually alter GI1 (BossMiyakoCard2, BossMiyakoCard2Dead) are skipped over so GI1 = 1 remains true. Seiga keeps firing bullets as if Yoshika is alive despite her being dead.

Why this issue cannot happen elsewhere


While it may seem that more of Seiga's patterns could be affected, this is not the case.

Non-spell 1 & Non-spell 2


This non-spell does not read or write to GI1, so it never bugs out.

Non-spell 3


This non-spell does read and write to GI1. However, once the third non-spell Boss3 is called, Yoshika's subroutines are immediately called and her death subroutine is immediately overwritten meaning there is no window of time provided in which it is possible to achieve this bug.

Spell-card 3


Similarly, this spell has the same logic as spell-cards 1 and 2. However, the bug cannot be triggered on this spell because of the death call back on non-spell 3 (BossMiyako3Dead) setting GI1 to 0. This is unlike the death call backs of non-spell 1 and non-spell 2, which do not change the value of GI1.

Spell practice mode


This bug cannot happen on Spell practice mode. This is because when Yoshika is loaded first, she does not have a death call back. This behaviour means that she can have negative HP without her dying, meaning the spell-cards work as normal.

Conclusion


The Yoshika revival bug occurs in Seiga's first two spell-cards across every difficulty, mostly encountered in stage practice mode. This issue arises from the management of global variables and subroutines, which can vary based on the earlier state of these variables such as restarting the stage. The bug happens when when Yoshika is killed within a small timeframe with a pre-misconfigured global variable value. This results in Seiga not reviving Yoshika and continuing to shoot as if Yoshika were still alive.


Replays


  • Replay Nylilsa by "Nylilsa". Lunatic, Youmu, 1.00c. 「February 11, 2025」. Download link (Note: Glitch showcase on Spell 2)

Videos