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.
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.
12-6 Instant death glitch
Specifications
- Versions:
1.00a
- Shottype:
Aya -
Hatate
- Scenes: 12-6
What happens
On scene 12-6, you will sometimes be killed instantly despite the bullets not being near you. This happens at random, including during the replay.
How it happens
Play scene 12-6.
Why it happens
The code of the attack is scored in the ecl25_c.ecl file. The full code for the attack goes as follows:
void Boss1_at1_at() {
var A, B, C, D, E;
flagSet(291);
setHurtbox(8.0f, 48.0f);
setHitbox(8.0f, 48.0f);
%C = %F2;
%D = %F3;
moveVel(%C + (0.17453292f * %D), 4.0f);
moveVelTime(30, 0, %C - (0.17453292f * %D), 8.0f);
wait(30);
etNew(0);
etSprite(0, 0, 2);
etAngle(0, 1.5707964f + (0.34906584f * %D), 1.5707964f + (0.34906584f * %D));
etSpeed(0, 2.0f, 2.0f);
laserNew(0, -1.0f, -1.0f, -1.0f, 16.0f);
laserTiming(0, 27, -1, -1, -1, 0);
etSound(0, 19, -1);
etEx(0, 0, 0, 1024, 120, 0, -999999.0f, -999999.0f);
etEx(0, 1, 1, 8, 30, 0, 0.13333334f, (-0.17453292f / _f(30)) * %D);
etSound(0, 19, -1);
etAngle(0, %C + (0.5235988f * %D), 0.0f);
laserCuOn(0);
etAngle(0, %C + (0.17453292f * %D), 0.0f);
laserCuOn(0);
etAngle(0, %C - (0.5235988f * %D), 0.0f);
laserCuOn(0);
etAngle(0, %C - (0.87266463f * %D), 0.0f);
laserCuOn(0);
etNew(0);
etAim(0, 3);
etSprite(0, 24, 0);
etCount(0, 3, 1);
etAngle(0, 0.0f, 0.0f);
etSpeed(0, 0.1f, 1.0f);
etOffsetAbs(0, %A, %B);
etEx(0, 0, 0, 4096, 60, -999999, -999999.0f, -999999.0f);
$E = 4 + $DS1;
goto Boss1_at1_at_2132 @ 0;
Boss1_at1_at_1944:
etEx(0, 1, 0, 4, 60, -999999, (%RANDF * 0.02f) + 0.02f, -999.0f);
etAngle(0, %RANDRAD, 0.0f);
etOn(0);
Boss1_at1_at_2132:
if ($E--) goto Boss1_at1_at_1944 @ 0;
delete();
}
The first line var A, B, C, D, E; declares the variables A, B, C, D, and E. The variables aren't too useful in this stage - the variables need to be initialised first before usage, which is done by assigning the variables a value. All variables are assigned a value before they are read, except for variables A and B. Its value is read in the line etOffsetAbs(0, %A, %B), but A and B are not assigned a value prior to this. This means that A and B are uninitialised variables. The uninitialised variables A and B will have some value, but its value is not predictable.
Because of the unpredictable value of the uninitialised variables A and B, this can result in serious side effects such as instantly killing the player.
More detailed explanation
The position of the blue bullets are initialised using the value of field B of some object A. This object A itself is initialized with the code at addresses 0x410240-0x410482, but field B is uninitialized. Therefore, the memory content originally in object A's region is used as the value of field B.
Object A is allocated from the heap area, but this area is not necessarily cleared to 0 and may be dirty with old data. This means that field B is dirty with old data, and the position of the blue bullet is also dirty.
The way field B is set may change with each game play or replay. Therefore, the following phenomena occur:
- The location of the blue bullets changes each time you play the game or replay.
- The random number changes due to the change in the position where the blue bullet is generated, and as a result other elements such as the laser also change.
Links
Replays
Any replay of 12-6 suffices, so none are provided.
Videos
- Vexe1. 「March 17, 2010」. "Touhou 12.5 ~ Double Spoiler, Stage 12-6 GLITCH" https://www.youtube.com/watch?v=3KlN0x4I4HM
- Luthier is best boy. 「August 15, 2019」. "Touhou 12.5 Double Spoiler: 12-6 bug" https://www.youtube.com/watch?v=PkxhyCMozo4
Other
- ide_an. 「June 8, 2019」. "hanakopatch" https://bitbucket.org/ide_an/hanakopatch/src/master/
All Double Spoiler pages: | |
|---|---|
| 12-6 Instant death glitch | All Uninitialized-related pages: | All Ecl-related pages: |