[Decompilation] [th05] Lasers: Update function

In which the shrink types """conveniently""" use a signed comparison
that effectively limits their width to 127 pixels, which forces a
shrink/nonshrink distinction upon the entire rest of the code. 🙄

Part of P0228, funded by [Anonymous] and nrook.
This commit is contained in:
nmlgc 2023-01-13 19:57:05 +01:00
parent c8997cd500
commit 034a7db98c
8 changed files with 152 additions and 192 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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<uint8_t>(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<uint8_t>(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<uint8_t>(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;
}

View File

@ -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 {

View File

@ -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);

View File

@ -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 <?>

View File

@ -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?

View File

@ -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