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.

Shinmyoumaru's 2nd Non-spell Bowl Breaking


Specifications


  • Versions: 1.00a - 1.00b
  • Difficulty: Easy - Normal - Hard - Lunatic - Extra
  • Mode: Main game - Practice mode - Spell practice
  • Shottype: ReimuA - ReimuB - MarisaA - MarisaB - SakuyaA - SakuyaB

What happens


It is possible for Shinmyoumaru to fire her fireball bullets despite her not having her bowl.

A screenshot of Shinmyoumaru firing the bullets she is supposed to fire when her bowl is present.
Figure 1: Shinmyoumaru firing the wrong type of bullets on her second non-spell: she is firing the bullets that are supposed to be fired when her bowl is intact.

How it happens


This can be achieved by dealing a lot of damage within the first second of Shinmyoumaru's 2nd non-spell starting. This is only feasable as ReimuA, as her bomb deals enough burst damage to achieve this bug.

Why it happens


To understand why this happens we must look at her ECL code. For convenience, her relevant bits of code have been provided below. The code has been altered slightly for the sake of understanding it. Also, for convenience I have separated this non-spell into two phases for explanation purposes later in this page:

  1. Phase 1: the boss fires large fireball bullets.
  2. Phase 2: the boss fires small pellet bullets.
void Boss2() {
    [...]
    $I1 = 0;
    @BossCup() async;
    wait(60);
    $I1 = 10;
    @Boss2_at() async;
    [...]
}
void BossCup() {
    [...]
    while ($LIFE >= 20000) {
        wait(1);
    }
    $I1 = 0;
    @DestroyBowl();
}
void Boss2_at() {
    while ($I1 != 0) {
        @Phase1();
    }
    @Phase2();
}

All the aforementioned code is explained in the following section. Also, the important instructions will be highlighted !

Code Explanation


  • Boss2 is what Shinmyoumaru is doing currently.
  • BossCup spawns Shinmyoumaru's bowl.
  • Boss2_at is what Shinmyoumaru attacks with. This attack is divided into two phases, Phase1 and Phase2.

The async keyword indicates that both BossCup and Boss2_at are run asynchronously, that is to say that those two subroutines are run simultaneously.

In short, Shinmyoumaru uses the code at Boss2 to spawn her bowl BossCup and to fire her bullets Boss2_at at the same time.

The local variable I1


The game uses local variables (e.g. I0, I1, I2, I3) for certain patterns. These local variables are inherited by spawned enemies. This means that if I1 is written to in BossCup, then reading I1 in Boss2_at will also change.

Below are two tables describing what happens when playing normally and when reproducing this bug. The tables mention the value of I1 and its current state in the code:

Normal routine


I1 State
0 Start
0 BossCup called
0 wait for 60 frames
10 I1 = 10;
10 Boss2_at called, Phase1 starts
0 ENEMY_HP < 20000
0 Boss2_at progresses, Phase2 starts

Bugged routine


I1 State
0 Start
0 BossCup called
0 wait for 60 frames
0 ENEMY_HP < 20000
10 I1 = 10;
10 Boss2_at called, Phase1 starts
10 Boss2_at remains in Phase1 indefinitely

Conclusion


In summary, this bug occurs because a shared local variable controlling Shinmyoumaru's attack phase is modified at an unintended time due to asynchronous execution between her bowl logic and attack routine. By dealing heavy burst damage early, players can desynchronize the intended routine. This causes her pattern to remain stuck in her first phase even after the bowl is destroyed, making her fire the wrong bullets.


Replays


This section has no content yet. Would you like to add to this section? Contact me if you are interested!

Videos


This section has no content yet. Would you like to add to this section? Contact me if you are interested!