mirror of https://github.com/nmlgc/ReC98.git
396 lines
10 KiB
NASM
396 lines
10 KiB
NASM
.386
|
|
locals
|
|
|
|
include libs/master.lib/macros.inc
|
|
include th04/math/motion.inc
|
|
include th04/main/bullet/bullet.inc
|
|
include th05/main/bullet/types.inc
|
|
|
|
extrn _randring:byte
|
|
extrn _randring_p:word
|
|
extrn _playperf:byte
|
|
|
|
extrn _group_is_special:byte
|
|
|
|
extrn _group_fixedspeed:byte
|
|
extrn _group_i_spread_angle:byte
|
|
extrn _group_i:byte
|
|
extrn _group_i_absolute_angle:byte
|
|
extrn _group_i_speed:byte
|
|
extrn _group_i_velocity:Point
|
|
extrn _bullet_template:bullet_template_t
|
|
extrn _bullet_zap:byte
|
|
|
|
@PLAYER_ANGLE_FROM$Q20%SUBPIXELBASE$TI$TI%T1UC procdesc pascal near \
|
|
x:word, y:word, plus_angle:byte
|
|
VECTOR2_NEAR procdesc pascal near \
|
|
ret:word, angle:byte, length:word
|
|
_bullets_add_raw procdesc near
|
|
|
|
MAIN_03 group BULLET_A_TEXT
|
|
|
|
; ----------------------------------------------------------------------------
|
|
|
|
BULLET_A_TEXT segment word public 'CODE' use16
|
|
assume cs:MAIN_03
|
|
|
|
; The main algorithm is identical to the TH04 version...
|
|
public @playperf_speedtune
|
|
@playperf_speedtune proc near
|
|
@@speed_from_playperf equ <cl>
|
|
|
|
shr al, 1
|
|
mov @@speed_from_playperf, al
|
|
mul _playperf
|
|
; ...except for this divisor (32 here vs. 16 in TH04)
|
|
shr ax, 5
|
|
add al, @@speed_from_playperf
|
|
cmp al, (8 shl 4)
|
|
jbe short @@below_half_a_pixel_per_frame?
|
|
mov al, (8 shl 4)
|
|
|
|
@@below_half_a_pixel_per_frame?:
|
|
cmp al, ((1 shl 4) / 2)
|
|
jnb short @@ret
|
|
mov al, ((1 shl 4) / 2)
|
|
|
|
@@ret:
|
|
ret
|
|
@playperf_speedtune endp
|
|
|
|
|
|
public _bullets_add_regular
|
|
_bullets_add_regular proc near
|
|
cmp _bullet_zap, 0
|
|
jnz short @@ret
|
|
push word ptr _bullet_template.BT_angle
|
|
call _bullets_add_raw
|
|
pop word ptr _bullet_template.BT_angle
|
|
|
|
@@ret:
|
|
retn
|
|
_bullets_add_regular endp
|
|
even
|
|
|
|
|
|
public _bullets_add_special
|
|
_bullets_add_special proc near
|
|
cmp _bullet_zap, 0
|
|
jnz short @@ret
|
|
mov _group_is_special, 1
|
|
push word ptr _bullet_template.BT_angle
|
|
call _bullets_add_raw
|
|
pop word ptr _bullet_template.BT_angle
|
|
mov _group_is_special, 0
|
|
|
|
@@ret:
|
|
retn
|
|
_bullets_add_special endp
|
|
even
|
|
|
|
|
|
public _bullets_add_regular_fixedspeed
|
|
_bullets_add_regular_fixedspeed proc near
|
|
mov _group_fixedspeed, 1
|
|
call _bullets_add_regular
|
|
mov _group_fixedspeed, 0
|
|
retn
|
|
_bullets_add_regular_fixedspeed endp
|
|
|
|
|
|
public _bullets_add_special_fixedspeed
|
|
_bullets_add_special_fixedspeed proc near
|
|
mov _group_fixedspeed, 1
|
|
call _bullets_add_special
|
|
mov _group_fixedspeed, 0
|
|
retn
|
|
_bullets_add_special_fixedspeed endp
|
|
|
|
|
|
public _bullet_velocity_and_angle_set
|
|
_bullet_velocity_and_angle_set proc pascal near
|
|
uses si, di
|
|
@@spread equ <bl>
|
|
@@stack equ <bh>
|
|
@@speed equ <cl>
|
|
@@group_i equ <ch>
|
|
@@angle equ <si> ; Why 16-bit? Just makes the ASM harder to understand.
|
|
@@done equ <di>
|
|
|
|
set_done_if_last macro value:req
|
|
local @@nope
|
|
|
|
ifdifi <value>, <al>
|
|
mov al, value
|
|
endif
|
|
dec al
|
|
cmp al, @@group_i
|
|
ja short @@nope
|
|
inc @@done
|
|
@@nope:
|
|
endm
|
|
|
|
xor @@angle, @@angle
|
|
mov @@group_i, _group_i
|
|
mov @@speed, _bullet_template.speed
|
|
mov bl, _bullet_template.BT_group
|
|
xor bh, bh
|
|
|
|
; This effectively defaults invalid group values to BG_FORCESINGLE, fixing
|
|
; TH01's and TH04's bug that completely filled the array in that case.
|
|
; But come on, that's one MOV instruction we're wastefully executing for
|
|
; regularly conforming code!
|
|
cmp bx, BG_FORCESINGLE_AIMED
|
|
mov @@done, 1
|
|
ja @@no_aim
|
|
|
|
xor @@done, @@done
|
|
add bx, bx
|
|
mov ax, cs:@@switch_table[bx]
|
|
mov @@spread, _bullet_template.spread
|
|
mov @@stack, _bullet_template.BT_stack
|
|
|
|
; Make sure @@spread is ≥ 1, so that we can safely divide by it below.
|
|
or @@spread, @@spread
|
|
jnz short @@switch
|
|
inc @@spread
|
|
|
|
@@switch:
|
|
jmp ax
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_single_aimed:
|
|
inc @@done
|
|
jmp @@aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_single:
|
|
inc @@done
|
|
jmp @@no_aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_spread_or_ring_stack:
|
|
; Spawns the @@spread bullets within a single level of the stack first,
|
|
; before moving to the next level on the stack.
|
|
|
|
; @@speed += ((group_i / @@spread) * bullet_template.stack_speed_delta)
|
|
xor ah, ah
|
|
mov al, @@group_i
|
|
div @@spread
|
|
mov dl, _bullet_template.stack_speed_delta
|
|
; Ignores the remainder that the DIV above put into AH.
|
|
; Also note that this can easily overflow...
|
|
mul dl
|
|
add @@speed, al
|
|
|
|
; A ring or spread stack consists of (@@spread * @@stack) bullets in total.
|
|
mov al, @@spread
|
|
mul @@stack
|
|
set_done_if_last al
|
|
|
|
; @@group_i %= @@spread
|
|
; Then we can continue with regular spread / ring angle calculation.
|
|
xor ah, ah
|
|
mov al, @@group_i
|
|
div @@spread
|
|
mov @@group_i, ah ; Again, that's where `DIV r8` leaves the remainder!
|
|
|
|
; We arrive here for:
|
|
; | not aimed | BG_RING_STACK | BG_SPREAD_STACK |
|
|
; | aimed | BG_RING_STACK_AIMED | BG_SPREAD_STACK_AIMED |
|
|
cmp _bullet_template.BT_group, BG_RING_STACK
|
|
jz short @@calculate_ring_angle
|
|
cmp _bullet_template.BT_group, BG_RING_STACK_AIMED
|
|
jz short @@calculate_ring_angle
|
|
jmp short @@calculate_spread_angle
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_spread:
|
|
set_done_if_last @@spread
|
|
|
|
; Mostly identical to TH04's code, except that odd- and even-numbered
|
|
; spreads now use the same code beyond the first bullet.
|
|
@@calculate_spread_angle:
|
|
xor ah, ah ; Remember, @@angle is 16-bit...
|
|
test @@spread, 1
|
|
jz short @@even_spread
|
|
|
|
@@odd_spread:
|
|
or @@group_i, @@group_i
|
|
jnz short @@odd_spread_and_not_first_bullet
|
|
|
|
@@odd_spread_and_first_bullet:
|
|
mov _group_i_spread_angle, 0
|
|
; Remember, @@angle is still 0x00, from the beginning of the function.
|
|
jmp short @@got_spread_angle
|
|
|
|
@@odd_spread_and_not_first_bullet:
|
|
test @@group_i, 1
|
|
jz short @@even_numbered_bullet
|
|
|
|
@@odd_numbered_bullet:
|
|
; @@angle = (group_i_spread_angle += bullet_template.spread_angle_delta)
|
|
mov al, _bullet_template.spread_angle_delta
|
|
add _group_i_spread_angle, al
|
|
mov al, _group_i_spread_angle
|
|
mov @@angle, ax
|
|
jmp short @@got_spread_angle
|
|
|
|
@@even_numbered_bullet:
|
|
; @@angle = (0x100 - group_i_spread_angle)
|
|
mov al, _group_i_spread_angle
|
|
neg al
|
|
mov @@angle, ax
|
|
jmp short @@got_spread_angle
|
|
|
|
@@even_spread:
|
|
or @@group_i, @@group_i
|
|
jnz short @@even_spread_and_not_first_bullet
|
|
|
|
@@even_spread_and_first_bullet:
|
|
; @@angle = group_i_spread_angle = (bullet_template.spread_angle_delta / 2)
|
|
mov al, _bullet_template.spread_angle_delta
|
|
shr al, 1
|
|
mov _group_i_spread_angle, al
|
|
mov @@angle, ax
|
|
jmp short @@got_spread_angle
|
|
|
|
@@even_spread_and_not_first_bullet:
|
|
test @@group_i, 1
|
|
jnz short @@even_numbered_bullet
|
|
jmp short @@odd_numbered_bullet
|
|
|
|
@@got_spread_angle:
|
|
; We arrive here for:
|
|
; | not aimed | BG_SPREAD | BG_SPREAD_STACK |
|
|
; | aimed | BG_SPREAD_AIMED | BG_SPREAD_STACK_AIMED |
|
|
cmp _bullet_template.BT_group, BG_SPREAD
|
|
jz @@no_aim
|
|
cmp _bullet_template.BT_group, BG_SPREAD_STACK
|
|
jz @@no_aim
|
|
jmp short @@aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_ring:
|
|
set_done_if_last @@spread
|
|
|
|
@@calculate_ring_angle:
|
|
; @@angle = ((@@group_i * 0x100) / @@spread)
|
|
; Setting AL to 0 and loading @@group_i directly into AH optimizes away the
|
|
; otherwise necessary multiplication / bit shift. Nifty!
|
|
xor al, al
|
|
mov ah, @@group_i
|
|
div @@spread
|
|
xor ah, ah
|
|
mov @@angle, ax
|
|
|
|
; We arrive here for:
|
|
; | not aimed | BG_RING | BG_RING_STACK |
|
|
; | aimed | BG_RING_AIMED | BG_RING_STACK_AIMED |
|
|
cmp _bullet_template.BT_group, BG_RING
|
|
jz short @@no_aim
|
|
cmp _bullet_template.BT_group, BG_RING_STACK
|
|
jz short @@no_aim
|
|
jmp short @@aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_random_angle_and_added_speed:
|
|
; So that's why [randring_p] was needlessly turned into a 16-bit variable
|
|
; in TH04! x86 doesn't support indexing with an 8-bit register. (And who
|
|
; uses XLAT anyway, right?)
|
|
|
|
; @@speed += randring_next8_and(to_sp(2.0f) - 1)
|
|
mov si, _randring_p
|
|
mov al, _randring[si]
|
|
inc byte ptr _randring_p
|
|
and al, 1Fh
|
|
add @@speed, al
|
|
|
|
@@case_random_angle:
|
|
; Equivalent to `set_done_if_last @@spread`. Looks like somewhere down the
|
|
; line, ZUN realized that incrementing @@group_i itself is superior over
|
|
; moving to a register and decrementing? At least once we're done with that
|
|
; register.
|
|
inc @@group_i
|
|
cmp @@group_i, @@spread
|
|
jb short @@random_angle_not_done_yet
|
|
inc @@done
|
|
|
|
@@random_angle_not_done_yet:
|
|
; @@angle = randring_next8()
|
|
mov si, _randring_p
|
|
mov @@angle, word ptr _randring[si]
|
|
and @@angle, (100h - 1)
|
|
inc byte ptr _randring_p
|
|
|
|
jmp short @@no_aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@case_stack:
|
|
; @@speed += (bullet_template.stack_speed_delta * group_i)
|
|
mov al, _bullet_template.stack_speed_delta
|
|
mul @@group_i
|
|
add @@speed, al
|
|
|
|
; Equivalent to `set_done_if_last @@stack`.
|
|
mov al, @@stack
|
|
dec al
|
|
cmp al, @@group_i
|
|
jle short @@stack_done
|
|
|
|
; Stop spawning bullets if @@speed >= 10.0f, just like in TH04
|
|
cmp @@speed, (10 shl 4)
|
|
jb short @@stack_not_speed_capped_yet
|
|
|
|
@@stack_done:
|
|
inc @@done
|
|
|
|
@@stack_not_speed_capped_yet:
|
|
cmp _bullet_template.BT_group, BG_STACK
|
|
jz short @@no_aim
|
|
; ---------------------------------------------------------------------------
|
|
|
|
@@aim:
|
|
; Note that the high byte of @@angle is in fact ignored everywhere.
|
|
mov _group_i_speed, @@speed
|
|
call @player_angle_from$q20%SubpixelBase$ti$ti%t1uc pascal, _bullet_template.BT_origin.x, _bullet_template.BT_origin.y, @@angle
|
|
mov @@speed, _group_i_speed
|
|
mov @@angle, ax
|
|
|
|
@@no_aim:
|
|
push offset _group_i_velocity ; ret
|
|
mov ax, @@angle
|
|
add al, _bullet_template.BT_angle
|
|
push ax ; angle
|
|
mov _group_i_absolute_angle, al
|
|
mov _group_i_speed, @@speed
|
|
mov al, @@speed
|
|
mov ah, 0
|
|
push ax ; length
|
|
call vector2_near
|
|
mov ax, @@done
|
|
ret
|
|
|
|
; ---------------------------------------------------------------------------
|
|
@@switch_table label word
|
|
dw offset @@case_single
|
|
dw offset @@case_single_aimed
|
|
dw offset @@case_spread
|
|
dw offset @@case_spread ; (aimed)
|
|
dw offset @@case_ring
|
|
dw offset @@case_ring ; (aimed)
|
|
dw offset @@case_stack
|
|
dw offset @@case_stack ; (aimed)
|
|
dw offset @@case_spread_or_ring_stack
|
|
dw offset @@case_spread_or_ring_stack ; (aimed)
|
|
dw offset @@case_spread_or_ring_stack
|
|
dw offset @@case_spread_or_ring_stack ; (aimed)
|
|
dw offset @@case_random_angle
|
|
dw offset @@case_random_angle_and_added_speed
|
|
dw offset @@case_single
|
|
dw offset @@case_single_aimed
|
|
_bullet_velocity_and_angle_set endp
|
|
BULLET_A_TEXT ends
|
|
|
|
end
|