mirror of https://github.com/nmlgc/ReC98.git
317 lines
8.5 KiB
NASM
317 lines
8.5 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 _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
|
||
|
|
||
|
PLAYER_ANGLE_FROM procdesc pascal near \
|
||
|
x:word, y:word, plus_angle:byte
|
||
|
VECTOR2_NEAR procdesc pascal near \
|
||
|
ret:word, angle:byte, length:word
|
||
|
|
||
|
MAIN_03 group MAIN_031_TEXT
|
||
|
|
||
|
; ----------------------------------------------------------------------------
|
||
|
|
||
|
MAIN_031_TEXT segment word public 'CODE' use16
|
||
|
assume cs:MAIN_03
|
||
|
|
||
|
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 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
|
||
|
MAIN_031_TEXT ends
|
||
|
|
||
|
end
|