From 40ac9a7aa196ec4e00afd7b543334f19200495d3 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Thu, 30 Dec 2021 03:49:06 +0100 Subject: [PATCH] [Decompilation] [th01] Sariel: Pattern 3/16 The one where red birds hatch on an ellipse arc, then fly away while shooting a randomly selected pellet group type. Containing Sariel's first ZUN bug, resulting in unused sprite movement. Completes P0175, funded by Ember2528. --- th01/main/boss/b20m.cpp | 185 +++++++++++++++++- th01_reiiden.asm | 407 ++-------------------------------------- 2 files changed, 201 insertions(+), 391 deletions(-) diff --git a/th01/main/boss/b20m.cpp b/th01/main/boss/b20m.cpp index 666f39de..22564f1e 100644 --- a/th01/main/boss/b20m.cpp +++ b/th01/main/boss/b20m.cpp @@ -3,6 +3,7 @@ extern "C" { #include +#include #include #include "platform.h" #include "pc98.h" @@ -14,6 +15,7 @@ extern "C" { #include "th01/math/dir.hpp" #include "th01/math/overlap.hpp" #include "th01/math/subpixel.hpp" +#include "th01/math/vector.hpp" #include "th01/hardware/frmdelay.h" #include "th01/hardware/graph.h" #include "th01/hardware/egc.h" @@ -47,10 +49,17 @@ extern "C" { static const screen_x_t SHIELD_LEFT = 304; static const screen_y_t SHIELD_TOP = 144; static const screen_x_t DRESS_LEFT = 280; -static const screen_x_t DRESS_TOP = 192; +static const screen_y_t DRESS_TOP = 192; static const screen_x_t WAND_LEFT = 296; static const screen_y_t WAND_TOP = 48; +static const screen_y_t SHIELD_CENTER_X = 320; +static const screen_y_t SHIELD_CENTER_Y = 164; + +// That's… not quite where the sphere is? +static const screen_x_t WAND_EMIT_LEFT = 340; +static const screen_y_t WAND_EMIT_TOP = 64; + // MODDERS: That's 32 more than BOSS6_2.BOS is wide? Reducing it to 96 works // fine as well. static const pixel_t WAND_W = 128; @@ -71,6 +80,7 @@ enum sariel_colors_t { extern union { int frame; int speed_multiplied_by_8; + int interval; int unknown; } pattern_state; extern bool16 invincible; @@ -162,7 +172,8 @@ static const int BIRD_HATCH_CELS = 5; static const int BIRD_FLY_CELS = 2; enum bird_cel_t { - C_HATCH = 1, + C_EGG = 0, + C_HATCH, C_HATCH_last = (C_HATCH + BIRD_HATCH_CELS - 1), C_FLY, C_FLY_last = (C_FLY + BIRD_FLY_CELS - 1), @@ -171,6 +182,14 @@ enum bird_cel_t { #define bird_put_8(left, top, cel) \ grc_put_8(left, top, GRC_SLOT_BIRD, cel, COL_BIRD); // -------------------- + +// Spawn cross (BOSS6GR3.GRC) +// -------------------------- + +static const pixel_t SPAWNCROSS_W = 32; +static const pixel_t SPAWNCROSS_H = 32; +static const int SPAWNCROSS_CELS = 2; +// -------------------------- /// ------------- // .PTN @@ -480,6 +499,14 @@ void pascal near birds_reset_fire_spawn_unput_update_render( } } +#define birds_spawn(left, top, velocity_x, velocity_y) \ + birds_reset_fire_spawn_unput_update_render( \ + left, top, velocity_x, velocity_y, 1 \ + ); + +#define birds_fire(pellet_group) \ + birds_reset_fire_spawn_unput_update_render(BF_FIRE + pellet_group); + void near shield_render_both(void) { enum { @@ -809,3 +836,157 @@ void near pattern_random_purple_lasers(void) #undef spawner_y #undef spawner_x } + +void near pattern_birds_on_ellipse_arc(void) +{ + #define wand_raise_animation_done pattern2_wand_raise_animation_done + #define pellet_group pattern2_pellet_group + #define eggs_alive pattern2_eggs_alive + #define spawner_left pattern2_spawner_left + #define spawner_top pattern2_spawner_top + #define spawner_velocity_x pattern2_spawner_velocity_x + #define spawner_velocity_y pattern2_spawner_velocity_y + #define egg_left pattern2_egg_left + #define egg_top pattern2_egg_top + + extern bool wand_raise_animation_done; + extern bird_pellet_group_t pellet_group; + extern int eggs_alive; + extern screen_x_t egg_left[BIRD_COUNT]; + extern vram_y_t egg_top[BIRD_COUNT]; + extern Subpixel spawner_left; + extern Subpixel spawner_top; + extern Subpixel spawner_velocity_y; + extern Subpixel spawner_velocity_x; + + point_t velocity; + + if(boss_phase_frame < 40) { + return; + } + + if(wand_raise_animation_done == false) { + wand_raise_animation_done = wand_render_raise_both(); + } + if(boss_phase_frame < 50) { + return; + } else if(boss_phase_frame == 50) { + eggs_alive = 0; + spawner_left.v = to_sp(WAND_EMIT_LEFT); + spawner_top.v = to_sp(WAND_EMIT_TOP); + spawner_velocity_x.v = TO_SP(4 - ((rand() % 2) * 8)); + spawner_velocity_y.v = TO_SP(2 - ((rand() % 2) * 4)); + select_for_rank(pattern_state.interval, 20, 15, 10, 8); + mdrv2_se_play(8); + } else if(boss_phase_frame < 200) { + if(spawner_left.v < to_sp(SHIELD_CENTER_X)) { + spawner_velocity_x.v += to_sp(0.0625f); + } else { + spawner_velocity_x.v -= to_sp(0.0625f); + } + if(spawner_top.v < to_sp(SHIELD_CENTER_Y)) { + spawner_velocity_y.v += to_sp(0.0625f); + } else { + spawner_velocity_y.v -= to_sp(0.0625f); + } + sloppy_unput_32x32(spawner_left.to_pixel(), spawner_top.to_pixel()); + for(int i = (eggs_alive - 1); i >= 0; i--) { + bird_put_8(egg_left[i], egg_top[i], C_EGG); + } + spawner_left.v += spawner_velocity_x.v; + spawner_top.v += spawner_velocity_y.v; + if((boss_phase_frame % pattern_state.interval) == 0) { + if(overlap_xy_lrtb_le_ge( + spawner_left.v, spawner_top.v, + to_sp(0.0), to_sp(0.0f), + to_sp(RES_X - SPAWNCROSS_W), to_sp(RES_Y - SPAWNCROSS_H) + )) { + egg_left[eggs_alive] = spawner_left.to_pixel(); + egg_top[eggs_alive] = spawner_top.to_pixel(); + bird_put_8( + spawner_left.to_pixel(), spawner_top.to_pixel(), C_EGG + ); + eggs_alive++; + } + } + grc_put_8( + spawner_left.to_pixel(), + spawner_top.to_pixel(), + GRC_SLOT_SPAWNCROSS, + (boss_phase_frame % SPAWNCROSS_CELS), + ((boss_phase_frame % 4) + 2) + ); + } else if(boss_phase_frame < 400) { + if(boss_phase_frame == 200) { + if(abs(spawner_velocity_y.v) < to_sp(0.25f)) { + spawner_velocity_y.v = (spawner_velocity_y.v < 0) + ? to_sp(-0.25f) + : to_sp( 0.25f); + } + if(abs(spawner_velocity_x.v) < to_sp(0.25f)) { + spawner_velocity_x.v = (spawner_velocity_x.v < 0) + ? to_sp(-0.25f) + : to_sp( 0.25f); + } + for(int i = (eggs_alive - 1); i >= 0; i--) { + vector2(velocity.x, velocity.y, 3, ((rand() & 0x7F) + 0x80)); + birds_spawn(egg_left[i], egg_top[i], velocity.x, velocity.y); + } + wand_lower_both(); + pellet_group = static_cast( + rand() % (BPG_6_RING + 1) // excluding random rain here + ); + } + + if(overlap_xy_lrtb_le_ge( + spawner_left, spawner_top, + to_sp(-SPAWNCROSS_W), to_sp(-SPAWNCROSS_H), + to_sp(RES_X), to_sp(RES_Y) + )) { + sloppy_unput_32x32(spawner_left.to_pixel(), spawner_top.to_pixel()); + spawner_left.v += spawner_velocity_x.v; + spawner_top.v += spawner_velocity_y.v; + + // ZUN bug: ZUN suddenly forgot that [spawner] uses subpixels, not + // pixels. Comparing its position to the pixel equivalent of + // ((RES_X - SPAWNCROSS_W), (RES_Y - SPAWNCROSS_H)) + // effectively clips the top-left coordinate of the spawner to a + // screen-space rectangle from (0, 0) to (38, 23). Since that's + // quite far from its actual position, the code below never gets + // executed. + if(overlap_xy_lrtb_le_ge( + spawner_left, spawner_top, + 0, 0, + (RES_X - SPAWNCROSS_W), // should be subpixels + (RES_Y - SPAWNCROSS_H) // should be subpixels + )) { + // ZUN bug: Also, here. Quite ironic given that there's a + // correct version of this blitting call above… Copy-pasta + // confirmed. + grc_put_8( + spawner_left, // should be pixels + spawner_top, // should be pixels + GRC_SLOT_SPAWNCROSS, + (boss_phase_frame % SPAWNCROSS_CELS), + ((boss_phase_frame % 4) + 2) + ); + } + } + if(((boss_phase_frame % 16) == 0) || ((boss_phase_frame % 16) == 2)) { + birds_fire(pellet_group); + } + } else if(boss_phase_frame == 400) { + boss_phase_frame = 0; + wand_raise_animation_done = false; + } + + #undef egg_top + #undef egg_left + #undef spawner_velocity_y + #undef spawner_velocity_x + #undef spawner_top + #undef spawner_left + #undef eggs_alive + #undef pellet_group + #undef wand_raise_animation_done +} diff --git a/th01_reiiden.asm b/th01_reiiden.asm index c451894c..f436e556 100644 --- a/th01_reiiden.asm +++ b/th01_reiiden.asm @@ -22440,6 +22440,7 @@ main_36_TEXT segment byte public 'CODE' use16 @dress_render_both$qv procdesc near @pattern_vortices$qv procdesc near @pattern_random_purple_lasers$qv procdesc near + @pattern_birds_on_ellipse_arc$qv procdesc near main_36_TEXT ends main_36__TEXT segment byte public 'CODE' use16 @@ -22451,387 +22452,6 @@ include th01/main/boss/anim.inc sariel_shield equ -; =============== S U B R O U T I N E ======================================= - -; Attributes: bp-based frame - -sub_296F0 proc near - -var_2C = qword ptr -2Ch -var_24 = qword ptr -24h -var_1C = qword ptr -1Ch -var_14 = qword ptr -14h -var_A = word ptr -0Ah -var_8 = word ptr -8 -var_6 = word ptr -6 -@@vector_x = word ptr -4 -@@vector_y = word ptr -2 - - enter 0Ah, 0 - push si - cmp _boss_phase_frame, 40 - jl loc_29A96 - cmp byte_35DE1, 0 - jnz short loc_2970D - call @wand_render_raise_both$qi pascal, 0 - mov byte_35DE1, al - -loc_2970D: - cmp _boss_phase_frame, 50 - jl loc_29A96 - cmp _boss_phase_frame, 50 - jnz short loc_29783 - mov word_35DE3, 0 - mov subpixel_point_3AC50.x, (340 shl 4) - mov subpixel_point_3AC50.y, (64 shl 4) - call IRand - mov bx, 2 - cwd - idiv bx - shl dx, 3 - mov ax, 4 - sub ax, dx - shl ax, 4 - mov word_3AC56, ax - call IRand - mov bx, 2 - cwd - idiv bx - shl dx, 2 - mov ax, 2 - sub ax, dx - shl ax, 4 - mov word_3AC54, ax - call @sariel_select_for_rank$qmiiiii c, offset _sariel_pattern_state, ds, large 20 or (15 shl 16), large 10 or (8 shl 16) - push 8 - call _mdrv2_se_play - pop cx - jmp loc_29A96 -; --------------------------------------------------------------------------- - -loc_29783: - cmp _boss_phase_frame, 200 - jge loc_298A5 - cmp subpixel_point_3AC50.x, (320 shl 4) - jge short loc_2979B - inc word_3AC56 - jmp short loc_2979F -; --------------------------------------------------------------------------- - -loc_2979B: - dec word_3AC56 - -loc_2979F: - cmp subpixel_point_3AC50.y, (164 shl 4) - jge short loc_297AD - inc word_3AC54 - jmp short loc_297B1 -; --------------------------------------------------------------------------- - -loc_297AD: - dec word_3AC54 - -loc_297B1: - push (32 shl 16) or 48 - mov ax, subpixel_point_3AC50.y - sar ax, 4 - push ax - mov ax, subpixel_point_3AC50.x - sar ax, 4 - push ax - call _egc_copy_rect_1_to_0_16 - add sp, 8 - mov ax, word_35DE3 - dec ax - mov si, ax - jmp short loc_297F6 -; --------------------------------------------------------------------------- - -loc_297D5: - push 0 or (15 shl 16) ; (image) or (col) - push GRC_SLOT_BOSS_2 ; slot - mov bx, si - add bx, bx - push word ptr [bx+6274h] ; top - mov bx, si - add bx, bx - push word ptr [bx+6238h] ; left - call _grc_put_8 - add sp, 0Ah - dec si - -loc_297F6: - or si, si - jge short loc_297D5 - mov ax, word_3AC56 - add subpixel_point_3AC50.x, ax - mov ax, word_3AC54 - add subpixel_point_3AC50.y, ax - mov ax, _boss_phase_frame - cwd - idiv _sariel_pattern_state - or dx, dx - jnz short loc_29874 - cmp subpixel_point_3AC50.x, 0 - jl short loc_29874 - cmp subpixel_point_3AC50.x, (608 shl 4) - jg short loc_29874 - cmp subpixel_point_3AC50.y, 0 - jl short loc_29874 - cmp subpixel_point_3AC50.y, (368 shl 4) - jg short loc_29874 - mov ax, subpixel_point_3AC50.x - sar ax, 4 - mov bx, word_35DE3 - add bx, bx - mov [bx+6238h], ax - mov ax, subpixel_point_3AC50.y - sar ax, 4 - mov bx, word_35DE3 - add bx, bx - mov [bx+6274h], ax - push 0 or (15 shl 16) ; (image) or (col) - push GRC_SLOT_BOSS_2 ; slot - mov ax, subpixel_point_3AC50.y - sar ax, 4 - push ax ; top - mov ax, subpixel_point_3AC50.x - sar ax, 4 - push ax ; left - call _grc_put_8 - add sp, 0Ah - inc word_35DE3 - -loc_29874: - mov ax, _boss_phase_frame - mov bx, 4 - cwd - idiv bx - add dx, 2 - push dx ; col - mov ax, _boss_phase_frame - mov bx, 2 - cwd - idiv bx - push dx ; image - push bx ; slot - mov ax, subpixel_point_3AC50.y - sar ax, 4 - push ax ; top - mov ax, subpixel_point_3AC50.x - sar ax, 4 - push ax ; left - call _grc_put_8 - add sp, 0Ah - jmp loc_29A96 -; --------------------------------------------------------------------------- - -loc_298A5: - cmp _boss_phase_frame, 400 - jge loc_29A83 - cmp _boss_phase_frame, 200 - jnz loc_29995 - mov ax, word_3AC54 - mov [bp+var_6], ax - cwd - xor ax, dx - sub ax, dx - cmp ax, 4 - jge short loc_298DB - cmp word_3AC54, 0 - jge short loc_298D5 - mov ax, 0FFFCh - jmp short loc_298D8 -; --------------------------------------------------------------------------- - -loc_298D5: - mov ax, 4 - -loc_298D8: - mov word_3AC54, ax - -loc_298DB: - mov ax, word_3AC56 - mov [bp+var_8], ax - cwd - xor ax, dx - sub ax, dx - cmp ax, 4 - jge short loc_298FD - cmp word_3AC56, 0 - jge short loc_298F7 - mov ax, 0FFFCh - jmp short loc_298FA -; --------------------------------------------------------------------------- - -loc_298F7: - mov ax, 4 - -loc_298FA: - mov word_3AC56, ax - -loc_298FD: - mov ax, word_35DE3 - dec ax - mov si, ax - jmp short loc_2997F -; --------------------------------------------------------------------------- - -loc_29905: - call IRand - and al, 7Fh - add al, 128 - push ax - push 3 - push ss - lea ax, [bp+@@vector_y] - push ax - push ss - lea ax, [bp+@@vector_x] - push ax - call _vector2 - add sp, 0Ch - mov bx, si - add bx, bx - mov ax, [bx+6238h] - mov [bp+var_A], ax - fild [bp+var_A] - sub sp, 8 - fstp [bp+var_14] - mov bx, si - add bx, bx - fwait - mov ax, [bx+6274h] - mov [bp+var_A], ax - fild [bp+var_A] - sub sp, 8 - fstp [bp+var_1C] - fwait - mov ax, [bp+@@vector_x] - mov [bp+var_A], ax - fild [bp+var_A] - sub sp, 8 - fstp [bp+var_24] - fwait - mov ax, [bp+var_2] - mov [bp+var_A], ax - fild [bp+var_A] - sub sp, 8 - fstp [bp+var_2C] - push 1 ; char - fwait - call @birds_reset_fire_spawn_unput_upd$qddddc - dec si - -loc_2997F: - or si, si - jge short loc_29905 - call @wand_lower_both$qv - call IRand - mov bx, 2 - cwd - idiv bx - mov byte_35DE2, dl - -loc_29995: - cmp subpixel_point_3AC50.x, (-32 shl 4) - jl loc_29A29 - cmp subpixel_point_3AC50.x, (640 shl 4) - jg loc_29A29 - cmp subpixel_point_3AC50.y, (-32 shl 4) - jl short loc_29A29 - cmp subpixel_point_3AC50.y, (400 shl 4) - jg short loc_29A29 - push (32 shl 16) or 48 - mov ax, subpixel_point_3AC50.y - sar ax, 4 - push ax - mov ax, subpixel_point_3AC50.x - sar ax, 4 - push ax - call _egc_copy_rect_1_to_0_16 - add sp, 8 - mov ax, word_3AC56 - add subpixel_point_3AC50.x, ax - mov ax, word_3AC54 - add subpixel_point_3AC50.y, ax - cmp subpixel_point_3AC50.x, 0 - jl short loc_29A29 - cmp subpixel_point_3AC50.x, (38 shl 4) - jg short loc_29A29 - cmp subpixel_point_3AC50.y, 0 - jl short loc_29A29 - cmp subpixel_point_3AC50.y, (23 shl 4) - jg short loc_29A29 - mov ax, _boss_phase_frame - mov bx, 4 - cwd - idiv bx - add dx, 2 - push dx ; col - mov ax, _boss_phase_frame - mov bx, 2 - cwd - idiv bx - push dx ; image - push bx ; slot - push subpixel_point_3AC50.y ; top - push subpixel_point_3AC50.x ; left - call _grc_put_8 - add sp, 0Ah - -loc_29A29: - mov ax, _boss_phase_frame - mov bx, 16 - cwd - idiv bx - or dx, dx - jz short loc_29A41 - mov ax, _boss_phase_frame - cwd - idiv bx - cmp dx, 2 - jnz short loc_29A96 - -loc_29A41: - mov al, byte_35DE2 - cbw - add ax, 3E9h - mov [bp+var_A], ax - fild [bp+var_A] - sub sp, 8 - fstp [bp+var_14] - fldz - fwait - sub sp, 8 - fstp [bp+var_1C] - fldz - fwait - sub sp, 8 - fstp [bp+var_24] - fldz - fwait - sub sp, 8 - fstp [bp+var_2C] - push 0 ; char - fwait - call @birds_reset_fire_spawn_unput_upd$qddddc - jmp short loc_29A96 -; --------------------------------------------------------------------------- - -loc_29A83: - cmp _boss_phase_frame, 400 - jnz short loc_29A96 - mov _boss_phase_frame, 0 - mov byte_35DE1, 0 - -loc_29A96: - pop si - leave - retn -sub_296F0 endp - - ; =============== S U B R O U T I N E ======================================= ; Attributes: bp-based frame @@ -26894,7 +26514,7 @@ loc_2C40D: loc_2C419: cmp word_35E95, 2 jnz short loc_2C423 - call sub_296F0 + call @pattern_birds_on_ellipse_arc$qv loc_2C423: cmp _boss_phase_frame, 0 @@ -28433,9 +28053,12 @@ _wand_raise_frames dw 0 public _pattern1_wand_raise_animation_do _pattern1_wand_raise_animation_do dw 0 -byte_35DE1 db 0 -byte_35DE2 db 0 -word_35DE3 dw 0 +public _pattern2_wand_raise_animation_do, _pattern2_pellet_group +public _pattern2_eggs_alive +_pattern2_wand_raise_animation_do db 0 +_pattern2_pellet_group db 0 +_pattern2_eggs_alive dw 0 + dd 0 dd 0 dd 0 @@ -28890,10 +28513,16 @@ public _pattern0_spawner_x, _pattern0_spawner_y _pattern0_spawner_x dw 10 dup(?) _pattern0_spawner_y dw 10 dup(?) - db 120 dup(?) -subpixel_point_3AC50 Point -word_3AC54 dw ? -word_3AC56 dw ? +public _pattern2_egg_left, _pattern2_egg_top +public _pattern2_spawner_left, _pattern2_spawner_top, +public _pattern2_spawner_velocity_x, _pattern2_spawner_velocity_y +_pattern2_egg_left dw BIRD_COUNT dup(?) +_pattern2_egg_top dw BIRD_COUNT dup(?) +_pattern2_spawner_left dw ? +_pattern2_spawner_top dw ? +_pattern2_spawner_velocity_y dw ? +_pattern2_spawner_velocity_x dw ? + word_3AC58 dw ? word_3AC5A dw ? word_3AC5C dw ?