diff --git a/th05/main/boss/b1.cpp b/th05/main/boss/b1.cpp index dd710c5e..64561be1 100644 --- a/th05/main/boss/b1.cpp +++ b/th05/main/boss/b1.cpp @@ -299,7 +299,7 @@ void near pattern_dense_spreads_and_random_balls_within_laser_walls(void) laser_template.coords.origin = boss.pos.cur; laser_template.coords.angle = (-0x40 + 0x20); laser_template.col = 8; - laser_template.coords.width = 8; + laser_template.coords.width.nonshrink = 8; laser_manual_fixed_spawn(X_RIGHT); laser_template.coords.angle = (-0x40 - 0x20); diff --git a/th05/main/boss/b6.cpp b/th05/main/boss/b6.cpp index bf0d19d2..f237a8d3 100644 --- a/th05/main/boss/b6.cpp +++ b/th05/main/boss/b6.cpp @@ -513,7 +513,7 @@ void near pattern_devil(void) // Laser activation if((boss.hp <= HP_PHASE_10_DEVIL_LASER) || (boss.phase_frame >= 1800)) { if(laser_grow_delay == 0) { - laser_template.coords.width = 6; + laser_template.coords.width.nonshrink = 6; laser_template.coords.angle = 0x40; laser_template.col = 0xE; diff --git a/th05/main/bullet/laser.cpp b/th05/main/bullet/laser.cpp index dc9659a6..6e8ead6b 100644 --- a/th05/main/bullet/laser.cpp +++ b/th05/main/bullet/laser.cpp @@ -8,10 +8,122 @@ extern "C" { #include "th04/math/vector.hpp" #include "th04/snd/snd.h" #include "th04/main/playfld.hpp" +#include "th04/main/bullet/clearzap.hpp" #include "th05/main/playperf.hpp" } #include "th05/main/bullet/laser.hpp" +// Segment 1 (as allocated in the header) +// --------- + +// ZUN bloat: Needed to circumvent 16-bit promotion in comparisons. +inline int8_t laser_width_min(void) { + return 1; +} + +void near lasers_update(void) +{ + #define shootout_update(laser) { \ + if(laser->coords.starts_at_distance < to_sp(LASER_DISTANCE_MAX)) { \ + laser->coords.ends_at_distance.v += laser->shootout_speed.v; \ + } \ + if(laser->age >= laser->active_at_age.moveout) { \ + laser->coords.starts_at_distance.v += laser->shootout_speed.v; \ + } \ + } + + Laser near *laser; + int i; + for((laser = lasers, i = 0); i < LASER_COUNT; (i++, laser++)) { + if(laser->flag == LF_FREE) { + continue; + } + + if(bullet_clear_time || bullet_zap.active) { + if(laser->flag == LF_SHOOTOUT) { + laser->flag = LF_SHOOTOUT_DECAY; + laser->shootout_speed.v /= 2; + } + } + + switch(laser->flag) { + case LF_SHOOTOUT: + shootout_update(laser); + laser_hittest(*laser); + break; + + case LF_FIXED_WAIT_TO_GROW: + if(laser->active_at_age.grow > 0) { + if(laser->age >= laser->active_at_age.grow) { + // laser->flag = LF_FIXED_GROW; + static_cast(laser->flag)++; + snd_se_play(6); + } + } + break; + + case LF_FIXED_GROW: + if(laser->age & 1) { + laser->coords.width.nonshrink += 2; + } + if(laser->coords.width.nonshrink >= laser->grow_to_width) { + // laser->flag = LF_FIXED_ACTIVE; + static_cast(laser->flag)++; + } + break; + + case LF_FIXED_ACTIVE: + laser_hittest(*laser); + if(laser->shrink_at_age > 0) { + if(laser->age >= laser->shrink_at_age) { + // laser->flag = LF_FIXED_SHRINK; + static_cast(laser->flag)++; + } + } + break; + + case LF_FIXED_SHRINK: + if(laser->age & 1) { + // MODDERS: The necessary clamp to 0 here is caught by the + // signed comparison below. + laser->coords.width.shrink -= 2; + } + if(laser->coords.width.shrink < laser_width_min()) { + laser->flag = LF_FREE; + } + break; + + case LF_FIXED_SHRINK_AND_WAIT_TO_GROW: + if(laser->age & 1) { + // MODDERS: See aboveā€¦ except that the signed comparison + // wouldn't even have been needed here, as the condition is + // `true` for both 1 and 0. + laser->coords.width.shrink -= 2; + } + if(laser->coords.width.shrink <= laser_width_min()) { + laser->flag = LF_FIXED_WAIT_TO_GROW; + laser->age = 0; + } + break; + + case LF_SHOOTOUT_DECAY: + shootout_update(laser); + laser->coords.width.nonshrink += 2; + if( + laser->coords.width.nonshrink >= + LASER_SHOOTOUT_DECAY_WIDTH_MAX + ) { + laser->flag = LF_FREE; + } + break; + } + laser->age++; + } + + #undef shootout_update +} +// --------- + // Segment 3 (as allocated in the header) // --------- @@ -33,7 +145,7 @@ void near lasers_shootout_add(void) laser->col = laser_template.col; laser->shootout_speed.v = speed; laser->grow_to_width = 6; - laser->coords.width = 6; + laser->coords.width.nonshrink = 6; laser->coords.angle = laser_template.coords.angle; vector2_at( laser->coords.origin, @@ -57,8 +169,8 @@ void pascal near laser_fixed_spawn(int slot) laser.active_at_age.grow = laser_template.active_at_age.grow; laser.shrink_at_age = laser_template.shrink_at_age; laser.col = laser_template.col; - laser.grow_to_width = laser_template.coords.width; - laser.coords.width = 1; + laser.grow_to_width = laser_template.coords.width.nonshrink; + laser.coords.width.nonshrink = 1; laser.coords.angle = laser_template.coords.angle; snd_se_play(5); } @@ -73,8 +185,8 @@ void pascal near laser_manual_fixed_spawn(int slot) laser.active_at_age.grow = -1; laser.shrink_at_age = -1; laser.col = laser_template.col; - laser.coords.width = 1; - laser.grow_to_width = laser_template.coords.width; + laser.coords.width.nonshrink = 1; + laser.grow_to_width = laser_template.coords.width.nonshrink; laser.coords.angle = laser_template.coords.angle; snd_se_play(5); } @@ -93,10 +205,10 @@ void pascal near laser_stop(int slot) if(lasers[slot].flag == LF_FIXED_ACTIVE) { lasers[slot].flag = LF_FIXED_SHRINK; } else if (lasers[slot].flag == LF_SHOOTOUT) { - lasers[slot].flag = LF_DECAY; + lasers[slot].flag = LF_SHOOTOUT_DECAY; } else if ( (lasers[slot].flag != LF_FIXED_SHRINK) && - (lasers[slot].flag != LF_DECAY) + (lasers[slot].flag != LF_SHOOTOUT_DECAY) ) { lasers[slot].flag = LF_FREE; } diff --git a/th05/main/bullet/laser.hpp b/th05/main/bullet/laser.hpp index ef6c4f88..3c3786e1 100644 --- a/th05/main/bullet/laser.hpp +++ b/th05/main/bullet/laser.hpp @@ -1,6 +1,9 @@ #define LASER_DISTANCE_MIN 16.0f #define LASER_DISTANCE_MAX 550.0f /* Far away enough? */ +static const pixel_t LASER_SHOOTOUT_DECAY_WIDTH = 6; +static const pixel_t LASER_SHOOTOUT_DECAY_WIDTH_MAX = 28; + enum laser_flag_t { LF_FREE = 0, LF_SHOOTOUT = 1, @@ -11,9 +14,15 @@ enum laser_flag_t { // player on contact. LF_FIXED_ACTIVE = 4, + // Fixed laser will shrink to a width of 1 and then either be removed + // (LF_FIXED_SHRINK) or go back to LF_FIXED_WAIT_TO_GROW with its [age] + // reset to 0 (LF_FIXED_SHRINK_AND_WAIT_TO_GROW). LF_FIXED_SHRINK = 5, LF_FIXED_SHRINK_AND_WAIT_TO_GROW = 6, - LF_DECAY = 7, + + LF_SHOOTOUT_DECAY = 7, + + _laser_flag_t_FORCE_UINT8 = 0xFF }; struct laser_coords_t { @@ -25,7 +34,22 @@ struct laser_coords_t { Subpixel ends_at_distance; unsigned char angle; - unsigned char width; // pixel_t + + // In pixels. + union { + // ZUN bug: LF_FIXED_SHRINK and LF_FIXED_SHRINK_AND_WAIT_TO_GROW are + // effectively limited to a maximum width of 127 pixels due to an + // implementation convenience in their update code. For larger values, + // their shrink animation wouldn't play, and the laser will transition + // to its next flag immediately. + int8_t shrink; + + // Other types have no limit besides the 8-bit one inherent to the + // type. Shootout lasers should probably still be kept below + // LASER_SHOOTOUT_DECAY_WIDTH_MAX though, as any larger value would + // skip the decay animation. + uint8_t nonshrink; + } width; }; struct Laser { diff --git a/th05/main/bullet/laser_rh.cpp b/th05/main/bullet/laser_rh.cpp index 4b31dd8c..88f0389d 100644 --- a/th05/main/bullet/laser_rh.cpp +++ b/th05/main/bullet/laser_rh.cpp @@ -164,7 +164,7 @@ bool16 pascal near laser_render_ray(const laser_coords_t near& coords) SpaceChangingPoint bp[8]; } ps; - v_length_low = coords.width; + v_length_low = coords.width.nonshrink; v_length_high = 0; v_length <<= (SUBPIXEL_BITS - 1); diff --git a/th05/main/bullet/lasers[bss].asm b/th05/main/bullet/lasers[bss].asm index 9b45c6cf..8dab0652 100644 --- a/th05/main/bullet/lasers[bss].asm +++ b/th05/main/bullet/lasers[bss].asm @@ -1,11 +1,7 @@ LF_FREE = 0 -LF_SHOOTOUT = 1 LF_FIXED_WAIT_TO_GROW = 2 -LF_FIXED_GROW = 3 -LF_FIXED_ACTIVE = 4 -LF_FIXED_SHRINK = 5 LF_FIXED_SHRINK_AND_WAIT_TO_GROW = 6 -LF_DECAY = 7 +LF_SHOOTOUT_DECAY = 7 laser_coords_t struc origin Point diff --git a/th05/main/bullet/lasers_update_render.asm b/th05/main/bullet/lasers_update_render.asm index 8b30709a..9a5978b5 100644 --- a/th05/main/bullet/lasers_update_render.asm +++ b/th05/main/bullet/lasers_update_render.asm @@ -1,179 +1,5 @@ @LASER_RENDER_RAY$QRX14LASER_COORDS_T procdesc pascal near \ coords:word - @LASER_HITTEST$QR5LASER procdesc pascal near \ - laser:word - -public @lasers_update$qv -@lasers_update$qv proc near - push bp - mov bp, sp - push si - push di - mov si, offset _lasers - xor di, di - jmp @@more? - -@@loop: - cmp [si+laser_t.flag], LF_FREE - jz @@next - cmp _bullet_clear_time, 0 - jnz short @@shootout - cmp _bullet_zap_active, 0 - jz short @@switch - -@@shootout: - cmp [si+laser_t.flag], LF_SHOOTOUT - jnz short @@switch - mov [si+laser_t.flag], LF_DECAY - mov ax, [si+laser_t.shootout_speed] - cwd - sub ax, dx - sar ax, 1 - mov [si+laser_t.shootout_speed], ax - -@@switch: - mov al, [si+laser_t.flag] - mov ah, 0 - dec ax - mov bx, ax - cmp bx, 6 - ja @@age_inc - add bx, bx - jmp cs:lasers_update_switch[bx] - -laser_update_shootout: - cmp [si+laser_t.coords.starts_at_distance], (550 shl 4) - jge short loc_FC52 - mov ax, [si+laser_t.shootout_speed] - add [si+laser_t.coords.ends_at_distance], ax - -loc_FC52: - mov ax, [si+laser_t.LASER_age] - cmp ax, [si+laser_t.moveout_at_age] - jl short loc_FC60 - mov ax, [si+laser_t.shootout_speed] - add [si+laser_t.coords.starts_at_distance], ax - -loc_FC60: - call @laser_hittest$qr5Laser pascal, si - jmp @@age_inc -; --------------------------------------------------------------------------- - -laser_update_fixed_wait_to_grow: - cmp [si+laser_t.grow_at_age], 0 - jle @@age_inc - mov ax, [si+laser_t.LASER_age] - cmp ax, [si+laser_t.grow_at_age] - jl @@age_inc - inc [si+laser_t.flag] - call snd_se_play pascal, 6 - jmp @@age_inc -; --------------------------------------------------------------------------- - -laser_update_fixed_grow: - test byte ptr [si+laser_t.LASER_age], 1 - jz short @@grow_step - mov al, [si+laser_t.coords.LASER_width] - add al, 2 - mov [si+laser_t.coords.LASER_width], al - -@@grow_step: - mov al, [si+laser_t.coords.LASER_width] - cmp al, [si+laser_t.grow_to_width] - jb short @@age_inc - jmp short @@next_mode -; --------------------------------------------------------------------------- - -laser_update_fixed_active: - call @laser_hittest$qr5Laser pascal, si - cmp [si+laser_t.shrink_at_age], 0 - jle short @@age_inc - mov ax, [si+laser_t.LASER_age] - cmp ax, [si+laser_t.shrink_at_age] - jl short @@age_inc - -@@next_mode: - inc [si+laser_t.flag] - jmp short @@age_inc -; --------------------------------------------------------------------------- - -laser_update_fixed_shrink: - test byte ptr [si+laser_t.LASER_age], 1 - jz short @@delete? - mov al, [si+laser_t.coords.LASER_width] - add al, -2 - mov [si+laser_t.coords.LASER_width], al - -@@delete?: - cmp [si+laser_t.coords.LASER_width], 1 - jge short @@age_inc - jmp short @@delete -; --------------------------------------------------------------------------- - -laser_update_fixed_shrink_and_wait_to_grow: - test byte ptr [si+laser_t.LASER_age], 1 - jz short @@shrink_and_wait_to_grow_step - mov al, [si+laser_t.coords.LASER_width] - add al, -2 - mov [si+laser_t.coords.LASER_width], al - -@@shrink_and_wait_to_grow_step: - cmp [si+laser_t.coords.LASER_width], 1 - jg short @@age_inc - mov [si+laser_t.flag], LF_FIXED_WAIT_TO_GROW - mov [si+laser_t.LASER_age], 0 - jmp short @@age_inc -; --------------------------------------------------------------------------- - -laser_update_decay: - cmp [si+laser_t.coords.starts_at_distance], (550 shl 4) - jge short loc_FCF4 - mov ax, [si+laser_t.shootout_speed] - add [si+laser_t.coords.ends_at_distance], ax - -loc_FCF4: - mov ax, [si+laser_t.LASER_age] - cmp ax, [si+laser_t.moveout_at_age] - jl short loc_FD02 - mov ax, [si+laser_t.shootout_speed] - add [si+laser_t.coords.starts_at_distance], ax - -loc_FD02: - mov al, [si+laser_t.coords.LASER_width] - add al, 2 - mov [si+laser_t.coords.LASER_width], al - cmp [si+laser_t.coords.LASER_width], 28 - jb short @@age_inc - -@@delete: - mov [si+laser_t.flag], LF_FREE - -@@age_inc: - inc [si+laser_t.LASER_age] - -@@next: - inc di - add si, size laser_t - -@@more?: - cmp di, LASER_COUNT - jl @@loop - pop di - pop si - pop bp - retn -@lasers_update$qv endp - -; --------------------------------------------------------------------------- -lasers_update_switch label word - dw offset laser_update_shootout - dw offset laser_update_fixed_wait_to_grow - dw offset laser_update_fixed_grow - dw offset laser_update_fixed_active - dw offset laser_update_fixed_shrink - dw offset laser_update_fixed_shrink_and_wait_to_grow - dw offset laser_update_decay -; --------------------------------------------------------------------------- public @lasers_render$qv @lasers_render$qv proc near @@ -217,7 +43,7 @@ public @lasers_render$qv sar ax, 4 add ax, 16 mov [bp+@@draw_y], ax - cmp [si+laser_t.flag], LF_DECAY + cmp [si+laser_t.flag], LF_SHOOTOUT_DECAY jz @@decay cmp [bp+@@radius], 2 jnb short @@draw_outer_circle? diff --git a/th05_main.asm b/th05_main.asm index 498274c2..fc87b34d 100644 --- a/th05_main.asm +++ b/th05_main.asm @@ -4368,6 +4368,8 @@ loc_FBF7: leave retn sub_FAA3 endp + + @lasers_update$qv procdesc near main__TEXT ends PLAYFLD_TEXT segment byte public 'CODE' use16