[Reverse-engineering] [th04/th05] Stage enemy structure

Last one of the shared entity types! The TH05 version of the .STD enemy
VM would now be ready for decompilation in one single future push.

Completes P0088, funded by -Tom-.
This commit is contained in:
nmlgc 2020-04-19 19:38:09 +02:00
parent ba66fcc801
commit da6b856dc5
19 changed files with 1048 additions and 950 deletions

1
th02/sprites/cels.h Normal file
View File

@ -0,0 +1 @@
#define ENEMY_KILL_CELS 8

1
th02/sprites/cels.inc Normal file
View File

@ -0,0 +1 @@
ENEMY_KILL_CELS = 8

View File

@ -0,0 +1,15 @@
ENEMY_W = 32
ENEMY_H = 32
EF_FREE = 0
EF_ALIVE = 1
EF_KILLED = 2
EF_ALIVE_FIRST_FRAME = 3
EF_KILL_ANIM = 80h
ENEMY_COUNT = 32
ENEMY_POS_RANDOM = (999 shl 4)
public _enemies
_enemies enemy_t ENEMY_COUNT dup (<?>)

90
th04/main/enemy/enemy.hpp Normal file
View File

@ -0,0 +1,90 @@
#define ENEMY_W 32
#define ENEMY_H 32
enum enemy_flag_t {
EF_FREE = 0,
EF_ALIVE = 1,
EF_KILLED = 2,
EF_ALIVE_FIRST_FRAME = 3,
// Yes, the kill animation doesn't use the perfectly suitable animation
// system, but is implemented in terms of the [flag].
EF_KILL_ANIM = 0x80,
EF_KILL_ANIM_last = (EF_KILL_ANIM + PAT_ENEMY_KILL - 1)
};
#if GAME == 4
struct enemy_t {
unsigned char flag;
unsigned char age;
motion_t pos;
unsigned char patnum_base;
int8_t unused_1;
int hp;
int16_t unused_2;
int score;
unsigned char near *script;
int script_ip;
// Certain instructions are executed once per frame, up to a number of
// frames given in some parameter of the instruction, before [script_ip]
// is pointed to the next one. This member tracks the current frame of
// this enemy's currently running blocking multi-frame instruction.
unsigned char cur_instr_frame;
// Current loop counter for the LOOP instruction. Resets to 0 once the
// amount of loops given in the instruction's parameter has been reached,
// allowing a new loop to run. Since there's only one such counter, LOOP
// instructions can't be nested.
unsigned char loop_i;
Subpixel speed;
unsigned char angle;
// Certain instructions add this to [angle] for every frame they are
// executed.
unsigned char angle_delta;
// Clips the enemy once it leaves the playfield. If not clipped along the
// X or Y axis, the [script] will continue to run if the enemy has left
// the playfield on that axis, and it will continue to take up a slot in
// [enemies].
bool clip_x;
bool clip_y;
int8_t unused_3;
item_type_t item;
bool damaged_this_frame;
// Animation parameters. Final patnum is
// [patnum_base] + (([age] / [anim_frames_per_cel]) % [anim_cels]
unsigned char anim_cels;
unsigned char anim_frames_per_cel;
unsigned char anim_cur_cel; // technically unnecessary
bool can_be_damaged;
bool autofire;
bool kills_player_on_collision;
// Not updated to reflect the current playfield half the enemy is in!
bool spawned_in_left_half;
bullet_template_t bullet_template;
// If [autofire] is true, the enemy fires bullets, according to its
// template, every [autofire_interval] number of frames, with
// [autofire_cur_frame] tracking the current one.
unsigned char autofire_cur_frame;
unsigned char autofire_interval;
};
#endif
#define ENEMY_COUNT 32
extern enemy_t enemies[ENEMY_COUNT];
extern enemy_t near *enemy_cur;
#define ENEMY_POS_RANDOM 999.0f
void pascal near enemies_invalidate(void);
void pascal near enemies_render(void);

33
th04/main/enemy/enemy.inc Normal file
View File

@ -0,0 +1,33 @@
enemy_t struc
flag db ?
age db ?
pos motion_t <?>
E_patnum_base db ?
db ?
E_hp dw ?
db ?
db ?
E_score dw ?
E_script dw ?
E_script_ip dw ?
E_cur_instr_frame db ?
E_loop_i db ?
E_speed dw ?
E_angle db ?
E_angle_delta db ?
E_clip_x db ?
E_clip_y db ?
db ?
E_item db ?
E_damaged_this_frame db ?
E_anim_cels db ?
E_anim_frames_per_cel db ?
E_anim_cur_cel db ?
E_can_be_damaged db ?
E_autofire db ?
E_kills_player_on_collision db ?
E_spawned_in_left_half db ?
E_bullet_template bullet_template_t <?>
E_autofire_cur_frame db ?
E_autofire_interval db ?
enemy_t ends

24
th04/main/enemy/inv.asm Normal file
View File

@ -0,0 +1,24 @@
public ENEMIES_INVALIDATE
enemies_invalidate proc near
push si
push di
mov _tile_invalidate_box, (ENEMY_W shl 16) or ENEMY_H
mov si, offset _enemies
mov di, ENEMY_COUNT
@@loop:
cmp [si+enemy_t.flag], EF_FREE
jz short @@next
cmp [si+enemy_t.flag], EF_ALIVE_FIRST_FRAME
jz short @@next
call tiles_invalidate_around pascal, large dword ptr [si+enemy_t.pos.prev]
@@next:
add si, size enemy_t
dec di
jnz short @@loop
pop di
pop si
retn
enemies_invalidate endp
even

View File

@ -0,0 +1,98 @@
public ENEMIES_RENDER
enemies_render proc near
@@patnum = byte ptr -5
@@top = word ptr -4
@@i = word ptr -2
enter 6, 0
push si
push di
mov si, offset _enemies
mov [bp+@@i], 0
jmp @@more?
; ---------------------------------------------------------------------------
@@loop:
cmp byte ptr [si+enemy_t.flag], EF_ALIVE
jz short @@outside_playfield?
cmp byte ptr [si+enemy_t.flag], EF_KILL_ANIM
jb @@next
@@outside_playfield?:
cmp [si+enemy_t.pos.prev.y], (-(ENEMY_H / 2) shl 4)
jle @@next
cmp [si+enemy_t.pos.prev.y], ((PLAYFIELD_H + (ENEMY_H / 2)) shl 4)
jge @@next
mov al, [si+enemy_t.E_patnum_base]
mov [bp+@@patnum], al
cmp [si+enemy_t.E_anim_cels], 1
jbe short @@put
mov al, [si+enemy_t.age]
mov ah, 0
mov dl, [si+enemy_t.E_anim_frames_per_cel]
mov dh, 0
push dx
cwd
pop bx
idiv bx
or dx, dx
jnz short @@still_same_cel
inc [si+enemy_t.E_anim_cur_cel]
mov al, [si+enemy_t.E_anim_cur_cel]
cmp al, [si+enemy_t.E_anim_cels]
jb short @@still_same_cel
mov [si+enemy_t.E_anim_cur_cel], 0
@@still_same_cel:
mov al, [si+enemy_t.E_anim_cur_cel]
add [bp+@@patnum], al
@@put:
mov ax, [si+enemy_t.cur.pos.x]
sar ax, 4
add ax, (PLAYFIELD_X - (ENEMY_W / 2))
mov di, ax
call scroll_subpixel_y_to_vram_seg1 pascal, [si+enemy_t.pos.cur.y]
mov [bp+@@top], ax
or di, di
jle short @@next
cmp di, PLAYFIELD_RIGHT
jge short @@next
cmp [si+enemy_t.pos.cur.y], (-(ENEMY_H / 2) shl 4)
jle short @@next
cmp [si+enemy_t.pos.cur.y], ((PLAYFIELD_H + (ENEMY_H / 2)) shl 4)
jge short @@next
cmp [si+enemy_t.E_damaged_this_frame], 0
jnz short @@damaged
push di
push ax
mov al, [bp+@@patnum]
mov ah, 0
push ax
call super_roll_put
jmp short @@next
; ---------------------------------------------------------------------------
@@damaged:
push di
push [bp+@@top]
mov al, [bp+@@patnum]
mov ah, 0
push ax
pushd PLANE_PUT or GC_BRGI
call super_roll_put_1plane
mov [si+enemy_t.E_damaged_this_frame], 0
@@next:
inc [bp+@@i]
add si, size enemy_t
@@more?:
cmp [bp+@@i], ENEMY_COUNT
jl @@loop
pop di
pop si
leave
retn
enemies_render endp

View File

@ -1,4 +1,8 @@
enum item_type_t {
#if GAME == 5
IT_NONE = -2,
#endif
IT_ENEMY_DROP_NEXT = -1,
IT_POWER = 0,
IT_POINT = 1,
IT_DREAM = 2,

View File

@ -1,4 +1,8 @@
; item_type_t
if GAME eq 5
IT_NONE = -2
endif
IT_ENEMY_DROP_NEXT = -1
IT_POWER = 0
IT_POINT = 1
IT_DREAM = 2

View File

@ -1,5 +1,7 @@
/// Animation frame counts
/// ----------------------
#include "th02/sprites/cels.h"
#define HITSHOT_CELS 4
#define BULLET_CLOUD_CELS 4
#define BULLET_DECAY_CELS 4

View File

@ -1,3 +1,5 @@
include th02/sprites/cels.inc
HITSHOT_CELS = 4
BULLET_CLOUD_CELS = 4
BULLET_DECAY_CELS = 4

View File

@ -7,6 +7,8 @@
typedef enum {
// miko32.bft
// ----------
PAT_ENEMY_KILL = 4,
PAT_ENEMY_KILL_last = (PAT_ENEMY_KILL + ENEMY_KILL_CELS - 1),
PAT_CLOUD_BULLET16_BLUE = 20,
PAT_CLOUD_BULLET16_BLUE_last = (PAT_CLOUD_BULLET16_BLUE + BULLET_CLOUD_CELS - 1),
PAT_CLOUD_BULLET16_RED,

View File

@ -1,5 +1,6 @@
include th04/sprites/cels.inc
PAT_ENEMY_KILL = 4
PAT_CLOUD_BULLET16_BLUE = 20
PAT_CLOUD_BULLET16_RED = 24
PAT_OPTION_REIMU = 38

File diff suppressed because it is too large Load Diff

47
th05/main/enemy/enemy.hpp Normal file
View File

@ -0,0 +1,47 @@
#define ENEMY_CLIP_NONE 0x00
#define ENEMY_CLIP_X 0x01
#define ENEMY_CLIP_Y 0x10
// See TH04's version for documentation on the fields that originated in that
// game.
struct enemy_t {
unsigned char flag;
unsigned char age;
motion_t pos;
int hp;
int score;
unsigned char near *script;
int script_ip;
SubpixelLength8 speed;
unsigned char patnum_base;
unsigned char cur_instr_frame;
unsigned char loop_i;
unsigned char angle;
unsigned char angle_delta;
unsigned char anim_cels;
unsigned char anim_frames_per_cel;
unsigned char anim_cur_cel; // still technically unnecessary
char clip;
item_type_t item;
bool damaged_this_frame;
bool can_be_damaged;
bool autofire;
bool kills_player_on_collision;
bool spawned_in_left_half;
unsigned char autofire_cur_frame;
unsigned char autofire_interval;
bullet_template_t bullet_template;
// Custom type ID, set in the spawn parameters. Can be used to parametrize
// scripts via conditional jumps based on this value.
unsigned char subtype;
int8_t unused_1;
int16_t unused_2;
int8_t unused_3;
int8_t padding[5];
};
#include "th04/main/enemy/enemy.hpp"

33
th05/main/enemy/enemy.inc Normal file
View File

@ -0,0 +1,33 @@
ENEMY_CLIP_X = 01h
ENEMY_CLIP_Y = 10h
enemy_t struc
flag db ?
age db ?
pos motion_t <?>
E_hp dw ?
E_score dw ?
E_script dw ?
E_script_ip dw ?
E_speed db ?
E_patnum_base db ?
E_cur_instr_frame db ?
E_loop_i db ?
E_angle db ?
E_angle_delta db ?
E_anim_cels db ?
E_anim_frames_per_cel db ?
E_anim_cur_cel db ?
E_clip db ?
E_item db ?
E_damaged_this_frame db ?
E_can_be_damaged db ?
E_autofire db ?
E_kills_player_on_collision db ?
E_spawned_in_left_half db ?
E_autofire_cur_frame db ?
E_autofire_interval db ?
E_bullet_template bullet_template_t <?>
E_subtype db ?
db 9 dup(?)
enemy_t ends

View File

@ -10,7 +10,9 @@
typedef enum {
// miko32.bft
// ----------
PAT_CLOUD_BULLET16_BLUE = 12,
PAT_ENEMY_KILL = 4,
PAT_ENEMY_KILL_last = (PAT_ENEMY_KILL + ENEMY_KILL_CELS - 1),
PAT_CLOUD_BULLET16_BLUE,
PAT_CLOUD_BULLET16_BLUE_last = (PAT_CLOUD_BULLET16_BLUE + BULLET_CLOUD_CELS - 1),
PAT_CLOUD_BULLET16_RED,
// ----------

View File

@ -3,6 +3,7 @@ include th04/sprites/cels.inc
PARTICLE_CELS = 4
B4BALL_CELS = 4
PAT_ENEMY_KILL = 4
PAT_CLOUD_BULLET16_BLUE = 12
PAT_CLOUD_BULLET16_RED = 16
PAT_SHOT_SUB = 22

File diff suppressed because it is too large Load Diff