From 7a0e5d8d850921a8626203e2ac3a6b768660dbf2 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Mon, 18 Oct 2021 15:18:47 +0200 Subject: [PATCH] [Decompilation] [th01] Player: Miss animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And even the final function in this 3-push series comes with a potential visual glitch… Completes P0164, funded by Yanga. --- th01/main/player/player.cpp | 108 ++++++++++++++++++ th01/main/player/player.hpp | 6 + th01/sprites/main_ptn.h | 7 ++ th01_reiiden.asm | 217 +----------------------------------- 4 files changed, 125 insertions(+), 213 deletions(-) diff --git a/th01/main/player/player.cpp b/th01/main/player/player.cpp index 02e19392..27cc04b0 100644 --- a/th01/main/player/player.cpp +++ b/th01/main/player/player.cpp @@ -1,5 +1,7 @@ extern "C" { +#include "th01/hardware/frmdelay.h" #include "th01/hardware/input.hpp" +#include "th01/hardware/scrollup.hpp" #include "th01/snd/mdrv2.h" #include "th01/sprites/main_ptn.h" #include "th01/main/vars.hpp" @@ -7,11 +9,14 @@ extern "C" { } #include "th01/formats/pf.hpp" #include "th01/resident.hpp" +#include "th01/math/subpixel.hpp" #include "th01/main/hud/hud.hpp" #include "th01/main/player/anim.hpp" #include "th01/main/player/orb.hpp" #include "th01/main/player/shots.hpp" +#include "th01/main/bullet/pellet.hpp" #include "th01/main/bullet/pellet_s.hpp" +#include "th01/main/stage/timer.hpp" /// Durations /// --------- @@ -957,3 +962,106 @@ void orb_player_hittest(int repel_friction) orb_force_new(ORB_FORCE_REPEL_CONSTANT, OF_IMMEDIATE); } } + +#define miss_effect_put(left, frame) \ + ptn_put_8(left, player_top, (PTN_MISS_EFFECT + (frame % MISS_EFFECT_CELS))) + +void player_miss_animate_and_update(void) +{ + int frame; + screen_x_t effect_left; + screen_x_t moveout_target_right; + screen_x_t moveout_target_left; + int prev_bombs; + + // Miss sprite and shake + // --------------------- + // ZUN bug: This should have been the 48×48 unblitting call that's done + // after the shake. It's easily possible to get hit during an 48×48 + // animation, which will end up leaving the extra pixels on screen during + // the next 16 frames. + ptn_sloppy_unput_16(player_left, player_top); + + ptn_put_8(orb_cur_left, orb_cur_top, PTN_ORB); + player_put(PTN_MIKO_MISS + ( + (rand() % 8) == 0 ? (PTN_MIKO_MISS_ALTERNATE - PTN_MIKO_MISS) : 0 + )); + + for(frame = 0; frame < 16; frame++) { + z_vsync_wait_and_scrollup(RES_Y - ((frame % 2) * 8)); + frame_delay(2); + } + z_vsync_wait_and_scrollup(0); + // --------------------- + + egc_copy_rect_1_to_0_16( + player_48_left_for(player_left), player_48x48_top(), 48, 48 + ); + + Shots.unput_and_reset_all(); + Pellets.decay_all(); + player_unput_update_render(false); + + // Effect + // ------ + enum { + MOVEOUT_FRAMES = 10, + MOVEIN_FRAMES = 20, + FRAMES = (MOVEOUT_FRAMES + MOVEIN_FRAMES), + + MOVEOUT_DISTANCE_X = (PLAYFIELD_W / 4), + MOVEOUT_DISTANCE_PER_FRAME_X = (MOVEOUT_DISTANCE_X / MOVEOUT_FRAMES), + }; + + moveout_target_right = (player_left + MOVEOUT_DISTANCE_X); + if(moveout_target_right >= PLAYER_LEFT_MAX) { + moveout_target_right = PLAYER_LEFT_MAX; + } + moveout_target_left = (player_left - MOVEOUT_DISTANCE_X); + if(moveout_target_left < PLAYER_LEFT_MIN) { + moveout_target_left = PLAYER_LEFT_MIN; + } + + effect_left = player_left; + for(frame = 0; frame < FRAMES; frame++) { + ptn_sloppy_unput_16(player_left, player_top); + ptn_sloppy_unput_16(effect_left, player_top); + + player_left += (frame < MOVEOUT_FRAMES) + ? +MOVEOUT_DISTANCE_PER_FRAME_X + : ((PLAYER_LEFT_START - moveout_target_right) / MOVEIN_FRAMES); + effect_left += (frame < MOVEOUT_FRAMES) + ? -MOVEOUT_DISTANCE_PER_FRAME_X + : ((PLAYER_LEFT_START - moveout_target_left) / MOVEIN_FRAMES); + + if(player_left >= PLAYER_LEFT_MAX) { + player_left = PLAYER_LEFT_MAX; + } + if(effect_left < PLAYER_LEFT_MIN) { + effect_left = PLAYER_LEFT_MIN; + } + + // We might have accidentally unblitted it earlier, after all. + ptn_put_8(orb_cur_left, orb_cur_top, PTN_ORB); + + miss_effect_put(player_left, frame); + miss_effect_put(effect_left, frame); + frame_delay((frame < 6) ? 1 : (((FRAMES - frame) / 10) + 1)); + } + + ptn_sloppy_unput_16(player_left, player_top); + ptn_sloppy_unput_16(effect_left, player_top); + // ------ + + hud_lives_put(lives + 1); + + prev_bombs = bombs; + bombs = (credit_bombs + bombs); + if(bombs > BOMBS_MAX) { + bombs = BOMBS_MAX; + } else { + hud_bombs_put(prev_bombs); + } + timer_extend_and_put(); + pellet_speed_lower(0.0f, -0.05f); +} diff --git a/th01/main/player/player.hpp b/th01/main/player/player.hpp index 719aa20d..31a3fce8 100644 --- a/th01/main/player/player.hpp +++ b/th01/main/player/player.hpp @@ -35,6 +35,12 @@ void invincibility_sprites_update_and_render(bool16 invincible); // instead. void player_unput_update_render(bool16 do_not_reset_player_state); +// Shows the player hit/respawn animation in a blocking way, and updates the +// HUD to reflect the lost life, together with all related game state. Except +// for, ironically, [lives], which is assumed to have been decremented prior +// to calling this function. +void player_miss_animate_and_update(void); + extern bool player_deflecting; extern bool player_sliding; extern bool16 player_invincible; diff --git a/th01/sprites/main_ptn.h b/th01/sprites/main_ptn.h index b823e1d5..99988809 100644 --- a/th01/sprites/main_ptn.h +++ b/th01/sprites/main_ptn.h @@ -8,6 +8,7 @@ static const int CARDCOMBO_DIGITS = 2; static const int PORTAL_ANIM_CELS = 2; static const int TIMER_DIGITS = 4; +static const int MISS_EFFECT_CELS = 2; static const int DASH_CELS = 2; typedef enum { @@ -65,6 +66,12 @@ typedef enum { PTN_MIKO_R_CAST, PTN_MIKO_R_DASH_SHOOT, PTN_MIKO_R_DASH_SHOOT_last = (PTN_MIKO_R_DASH_SHOOT + DASH_CELS - 1), + + PTN_MISS_EFFECT = PTN_ID(PTN_SLOT_MIKO, 20), + PTN_MISS_EFFECT_last = (PTN_MISS_EFFECT + MISS_EFFECT_CELS - 1), + + PTN_MIKO_MISS, + PTN_MIKO_MISS_ALTERNATE, // -------- // HUD (snapped backgrounds) diff --git a/th01_reiiden.asm b/th01_reiiden.asm index 8044f8bf..8292533e 100644 --- a/th01_reiiden.asm +++ b/th01_reiiden.asm @@ -63,7 +63,6 @@ main_01 group main_010_TEXT, main_011_TEXT, main_012_TEXT, main_013_TEXT main_15 group main_15_TEXT, main_15__TEXT main_19 group main_19_TEXT, main_19__TEXT main_21 group main_21_TEXT, main_21__TEXT -main_27 group main_27_TEXT, main_27__TEXT main_29 group main_29_TEXT, main_29__TEXT main_31 group main_31_TEXT, main_31__TEXT main_32 group main_32_TEXT, main_32__TEXT @@ -3163,7 +3162,7 @@ loc_DE72: les bx, _resident dec es:[bx+reiidenconfig_t.rem_lives] dec _lives - call sub_1AE0D + call _player_miss_animate_and_update mov _done, 0 inc si mov _player_invincibility_time, MISS_INVINCIBILITY_FRAMES @@ -5987,7 +5986,6 @@ main_25_TEXT segment byte public 'CODE' use16 extern @hud_score_and_cardcombo_render$qv:proc extern @hud_bg_load$qnxc:proc extern @hud_lives_put$qi:proc - extern @hud_bombs_put$qi:proc extern @hud_bg_snap_and_put$qv:proc main_25_TEXT ends @@ -5996,228 +5994,21 @@ main_25_TEXT ends ; Segment type: Pure code main_26_TEXT segment byte public 'CODE' use16 extern @timer_tick_and_put$qv:proc - extern @timer_extend_and_put$qv:proc main_26_TEXT ends ; =========================================================================== ; Segment type: Pure code main_27_TEXT segment byte public 'CODE' use16 -main_27_TEXT ends - -main_27__TEXT segment byte public 'CODE' use16 - assume cs:main_27 - ;org 0Eh - assume es:nothing, ss:nothing, ds:_DATA, fs:nothing, gs:nothing - -OR_NONE = 0 - extern _ptn_unput_8:proc extern _ptn_put_8:proc extern _ptn_put_quarter_8:proc extern _ptn_put_quarter:proc extern _player_unput_update_render:proc +OR_NONE = 0 extern @orb_player_hittest$qi:proc - -; =============== S U B R O U T I N E ======================================= - -; Attributes: bp-based frame - -sub_1AE0D proc far - -@@prev_bombs = word ptr -6 -var_4 = word ptr -4 -var_2 = word ptr -2 - - enter 6, 0 - push si - push di - call _egc_copy_rect_1_to_0_16 c, _player_left, _player_top, large (32 shl 16) or 32 - call _ptn_put_8 c, _orb_cur_left, _orb_cur_top, PTN_ORB - call IRand - mov bx, 8 - cwd - idiv bx - or dx, dx - jnz short loc_1AE4D - mov ax, 1 - jmp short loc_1AE4F -; --------------------------------------------------------------------------- - -loc_1AE4D: - xor ax, ax - -loc_1AE4F: - add ax, 56h - call _ptn_put_8 c, _player_left, _player_top, ax - xor si, si - jmp short loc_1AE85 -; --------------------------------------------------------------------------- - -loc_1AE65: - mov ax, si - mov bx, 2 - cwd - idiv bx - shl dx, 3 - mov ax, RES_Y - sub ax, dx - call _z_vsync_wait_and_scrollup stdcall, ax - pop cx - push 2 - call _frame_delay - pop cx - inc si - -loc_1AE85: - cmp si, 16 - jl short loc_1AE65 - call _z_vsync_wait_and_scrollup stdcall, 0 - pop cx - push (48 shl 16) or 48 - push 352 - mov ax, _player_left - add ax, -8 - push ax - call _egc_copy_rect_1_to_0_16 - add sp, 8 - call @CShots@unput_and_reset_all$qv c, offset _Shots, ds - call @CPellets@decay_all$qv c, offset _Pellets, ds - call _player_unput_update_render stdcall, 0 - pop cx - mov ax, _player_left - add ax, 160 - mov [bp+var_2], ax - cmp [bp+var_2], PLAYER_LEFT_MAX - jl short loc_1AEDE - mov [bp+var_2], PLAYER_LEFT_MAX - -loc_1AEDE: - mov ax, _player_left - add ax, -160 - mov [bp+var_4], ax - cmp [bp+var_4], PLAYER_LEFT_MIN - jge short loc_1AEF2 - mov [bp+var_4], PLAYER_LEFT_MIN - -loc_1AEF2: - mov di, _player_left - xor si, si - jmp loc_1AFC8 -; --------------------------------------------------------------------------- - -loc_1AEFB: - call _egc_copy_rect_1_to_0_16 c, _player_left, _player_top, large (32 shl 16) or 32 - call _egc_copy_rect_1_to_0_16 c, di, _player_top, large (32 shl 16) or 32 - cmp si, 0Ah - jge short loc_1AF2C - mov ax, 16 - jmp short loc_1AF38 -; --------------------------------------------------------------------------- - -loc_1AF2C: - mov ax, 304 - sub ax, [bp+var_2] - mov bx, 20 - cwd - idiv bx - -loc_1AF38: - add _player_left, ax - cmp si, 0Ah - jge short loc_1AF46 - mov ax, 0FFF0h - jmp short loc_1AF52 -; --------------------------------------------------------------------------- - -loc_1AF46: - mov ax, 304 - sub ax, [bp+var_4] - mov bx, 20 - cwd - idiv bx - -loc_1AF52: - add di, ax - cmp _player_left, PLAYER_LEFT_MAX - jl short loc_1AF62 - mov _player_left, PLAYER_LEFT_MAX - -loc_1AF62: - or di, di - jge short loc_1AF68 - xor di, di - -loc_1AF68: - call _ptn_put_8 c, _orb_cur_left, _orb_cur_top, PTN_ORB - mov ax, si - mov bx, 2 - cwd - idiv bx - add dx, 54h - call _ptn_put_8 c, _player_left, _player_top, dx - mov ax, si - mov bx, 2 - cwd - idiv bx - add dx, 54h - call _ptn_put_8 c, di, _player_top, dx - cmp si, 6 - jge short loc_1AFB4 - mov ax, 1 - jmp short loc_1AFC0 -; --------------------------------------------------------------------------- - -loc_1AFB4: - mov ax, 30 - sub ax, si - mov bx, 10 - cwd - idiv bx - inc ax - -loc_1AFC0: - push ax - call _frame_delay - pop cx - inc si - -loc_1AFC8: - cmp si, 1Eh - jl loc_1AEFB - call _egc_copy_rect_1_to_0_16 c, _player_left, _player_top, large (32 shl 16) or 32 - call _egc_copy_rect_1_to_0_16 c, di, _player_top, large (32 shl 16) or 32 - mov ax, _lives - inc ax - call @hud_lives_put$qi stdcall, ax - pop cx - mov al, _bombs - cbw - mov [bp+@@prev_bombs], ax - mov al, _credit_bombs - add al, _bombs - mov _bombs, al - cbw - cmp ax, BOMBS_MAX - jle short loc_1B01F - mov _bombs, BOMBS_MAX - jmp short loc_1B028 -; --------------------------------------------------------------------------- - -loc_1B01F: - call @hud_bombs_put$qi stdcall, [bp+@@prev_bombs] - pop cx - -loc_1B028: - call @timer_extend_and_put$qv - call @pellet_speed_lower$qii c, large 0 or (-2 shl 16) - pop di - pop si - leave - retf -sub_1AE0D endp - -main_27__TEXT ends + extern _player_miss_animate_and_update:proc +main_27_TEXT ends ; ===========================================================================