[Decompilation] [th03] Character selection: Main functions

Completes P0297, funded by 32th System.
This commit is contained in:
nmlgc 2024-11-10 19:27:02 +01:00
parent 3bf8a9099a
commit c879dfcbff
3 changed files with 231 additions and 421 deletions

View File

@ -4,9 +4,12 @@
#pragma option -zPgroup_01 #pragma option -zPgroup_01
#include "th03/op/m_select.hpp" #include "th03/op/m_select.hpp"
#include "decomp.hpp"
#include "libs/master.lib/master.hpp" #include "libs/master.lib/master.hpp"
#include "libs/master.lib/pc98_gfx.hpp" #include "libs/master.lib/pc98_gfx.hpp"
#include "th01/math/clamp.hpp" #include "th01/math/clamp.hpp"
#include "th02/v_colors.hpp"
#include "th02/hardware/frmdelay.h"
#include "th02/formats/bfnt.h" #include "th02/formats/bfnt.h"
#include "th03/common.h" #include "th03/common.h"
#include "th03/resident.hpp" #include "th03/resident.hpp"
@ -179,7 +182,13 @@ void near select_init_and_load(void)
while(vsync_Count1 < 30) { while(vsync_Count1 < 30) {
} }
// ZUN bug: [vsync_Count1] is not reset after the loop above. This causes
// the menu functions to only actually render 8 trailing curves on their
// first iteration: The first call to select_wait_flip_and_clear_vram()
// will then think that the frame took longer than 30 VSync interrupts to
// render and will consequently always decrement the count to 7.
curve_trail_count = 8; curve_trail_count = 8;
playchars_available = playchars_available_load(); playchars_available = playchars_available_load();
} }
@ -428,6 +437,14 @@ void near select_curves_update_and_render(void)
#undef cycle_triangle #undef cycle_triangle
} }
inline void select_base_render(void near (*pics_put)()) {
select_curves_update_and_render();
pics_put();
stats_put();
names_put();
extras_put();
}
void pascal near cursor_put(pid2 pid, vc_t col) void pascal near cursor_put(pid2 pid, vc_t col)
{ {
// ZUN bloat: `* NAME_W` would have done the job. // ZUN bloat: `* NAME_W` would have done the job.
@ -441,6 +458,13 @@ void pascal near cursor_put(pid2 pid, vc_t col)
graph_gaiji_puts(left, (top + GLYPH_H), GAIJI_W, g_CURSOR_BOTTOM[pid], col); graph_gaiji_puts(left, (top + GLYPH_H), GAIJI_W, g_CURSOR_BOTTOM[pid], col);
} }
inline void cursor_put_p1(void) {
cursor_put(0, (sel_confirmed[0] ? V_WHITE : 8));
}
inline void cursor_put_p2(void) {
cursor_put(1, (sel_confirmed[1] ? V_WHITE : 10));
}
#define select_confirm(pid, palette_id, swap_func) { \ #define select_confirm(pid, palette_id, swap_func) { \
int playchar = sel[pid]; \ int playchar = sel[pid]; \
resident->playchar_paletted[pid].v = ( \ resident->playchar_paletted[pid].v = ( \
@ -464,6 +488,10 @@ void pascal near cursor_put(pid2 pid, vc_t col)
\ \
static_assert(PLAYER_COUNT == 2); \ static_assert(PLAYER_COUNT == 2); \
if(sel_confirmed[1 - pid]) { \ if(sel_confirmed[1 - pid]) { \
/**
* ZUN bloat: Should only be done in the main function, see the \
* comment there. \
*/ \
fadeout_frames = 0; \ fadeout_frames = 0; \
} \ } \
sel_confirmed[pid] = true; \ sel_confirmed[pid] = true; \
@ -497,3 +525,191 @@ void pascal near select_update_player(input_t input, pid2 pid)
select_confirm(pid, 1, resident->playchar_paletted[pid].v--); select_confirm(pid, 1, resident->playchar_paletted[pid].v--);
} }
} }
inline void sel_init_vs(void) {
static_assert(PLAYER_COUNT == 2);
sel[0] = resident->playchar_paletted[0].char_id();
sel[1] = resident->playchar_paletted[1].char_id();
sel_confirmed[0] = false;
sel_confirmed[1] = false;
}
inline void select_input_sense(void) {
// ZUN bloat: Already part of all four possible [input_mode]s.
input_reset_sense_key_held();
input_mode();
}
inline bool select_cancel(void) {
// ZUN bloat: palette_black() is a more performant way to achieve the same
// without raising any questions about whether this targets the correct
// VRAM page.
graph_accesspage(0);
graph_clear();
graph_showpage(0);
// Redundant in the released game, but makes sense for clearing potential
// debug output.
text_clear();
select_free();
snd_kaja_func(KAJA_SONG_STOP, 0);
return true;
}
// ZUN landmine: Should be done at the beginning of select_base_render() to
// ensure that the palette applies to the entire frame. Plotting the curves
// takes a while, and doing this afterward all but ensures palette tearing.
#define select_fadeout_render(quit_label) { \
text_clear(); /* ZUN bloat: No point to this one. */ \
\
/** \
* ZUN quirk: Should have maybe been `>` rather than `>=`. Since \
* [fadeout_frames] is technically off-by-one (frame 0 is the last frame \
* of palette_white_in()), this sets the palette tone to 104 on frame #15. \
*/ \
if(fadeout_frames >= 16) { \
palette_settone(200 - (fadeout_frames * 6)); \
} \
if(fadeout_frames > 32) { \
goto quit_label; \
} \
optimization_barrier(); \
}
#define select_wait_flip_and_clear_vram() { \
while(vsync_Count1 < 3) { \
} \
/** \
* ZUN landmine: We're not waiting for VSync if the code took longer than \
* 3 VSync events, ensuring screen tearing in this case. \
*/ \
\
if((vsync_Count1 > 4) && (curve_trail_count > 1)) { \
curve_trail_count--; \
} \
vsync_Count1 = 0; \
graph_accesspage(page_shown); \
page_shown = (1 - page_shown); \
graph_showpage(page_shown); \
\
/**
* ZUN bloat: Slower than graph_clear(), which uses the GRCG's TDW \
* mode and a single REP STOS instruction. \
*/ \
grcg_setcolor(GC_RMW, 0); \
grcg_boxfill_8(0, 0, (RES_X - 1), (RES_Y - 1)); \
grcg_off(); \
\
/** \
* ZUN bloat: Doing this in the fade-out branch would have avoided the \
* need to reset it for the beginning of the animation. \
*/ \
fadeout_frames++; \
\
resident->rand++; \
}
// ZUN bloat: These three could have been merged into a single function.
bool near select_1p_vs_2p_menu(void)
{
select_init_and_load();
// ZUN bloat: Redundant, already done in select_init_and_load().
text_clear();
sel_init_vs();
if(resident->key_mode == KM_KEY_KEY) {
input_mode = input_mode_key_vs_key;
} else if(resident->key_mode == KM_JOY_KEY) {
input_mode = input_mode_joy_vs_key;
} else {
input_mode = input_mode_key_vs_joy;
}
// ZUN quirk: Not used in the other modes, and completely unnecessary.
frame_delay(16);
fadeout_frames = 0;
while(1) {
select_base_render(vs_sel_pics_put);
cursor_put_p1();
cursor_put_p2();
select_input_sense();
select_update_player(input_mp_p1, 0);
select_update_player(input_mp_p2, 1);
if(input_sp & INPUT_CANCEL) {
return select_cancel();
}
if(sel_confirmed[0] && sel_confirmed[1]) {
select_fadeout_render(done);
}
select_wait_flip_and_clear_vram();
}
done:
select_free();
return false;
}
bool near select_vs_cpu_menu(void)
{
select_init_and_load();
sel_init_vs();
input_mode = input_mode_interface;
for(int pid_cur = 0; pid_cur < PLAYER_COUNT; pid_cur++) {
fadeout_frames = 0;
while(1) {
select_base_render(vs_sel_pics_put);
select_input_sense();
cursor_put_p1();
if(sel_confirmed[0]) {
cursor_put_p2();
}
select_update_player(input_sp, pid_cur);
if(input_sp & INPUT_CANCEL) {
return select_cancel();
}
if((pid_cur == 0) && sel_confirmed[0] && (fadeout_frames > 12)) {
break;
}
if((pid_cur != 0) && sel_confirmed[1]) {
select_fadeout_render(done);
}
select_wait_flip_and_clear_vram();
}
done:
}
select_free();
return false;
}
bool near select_story_menu(void)
{
select_init_and_load();
sel[0] = PLAYCHAR_REIMU;
sel_confirmed[0] = false;
sel_confirmed[1] = true;
input_mode = input_mode_interface;
fadeout_frames = 0;
while(1) {
select_base_render(story_sel_pics_put);
cursor_put_p1();
select_input_sense();
select_update_player(input_sp, 0);
if(input_sp & INPUT_CANCEL) {
return select_cancel();
}
if(sel_confirmed[0]) {
select_fadeout_render(done);
}
select_wait_flip_and_clear_vram();
}
done:
select_free();
return false;
}

View File

@ -1,3 +1,5 @@
#include "platform.h"
// The character selection menu needs to load exactly 255,216 bytes of sprite // The character selection menu needs to load exactly 255,216 bytes of sprite
// data, excluding headers, from a total of 14 files. This amount of loading // data, excluding headers, from a total of 14 files. This amount of loading
// was probably very noticeable on hard drives of the day, and so ZUN decided // was probably very noticeable on hard drives of the day, and so ZUN decided
@ -5,3 +7,10 @@
void near select_cdg_load_part1_of_4(void); void near select_cdg_load_part1_of_4(void);
void near select_cdg_load_part2_of_4(void); void near select_cdg_load_part2_of_4(void);
void near select_cdg_load_part3_of_4(void); void near select_cdg_load_part3_of_4(void);
// Shows the respective character selection menu and writes the selected
// characters to [resident->playchar_paletted]. Returns `true` if the selection
// was canceled.
bool near select_1p_vs_2p_menu(void);
bool near select_vs_cpu_menu(void); // 1P vs. CPU or CPU vs. CPU
bool near select_story_menu(void);

View File

@ -88,7 +88,7 @@ var_2 = word ptr -2
mov es:[bx+resident_t.story_lives], CREDIT_LIVES mov es:[bx+resident_t.story_lives], CREDIT_LIVES
mov es:[bx+resident_t.show_score_menu], 0 mov es:[bx+resident_t.show_score_menu], 0
mov es:[bx+resident_t.RESIDENT_playchar_paletted][1], -1 mov es:[bx+resident_t.RESIDENT_playchar_paletted][1], -1
call sub_BD9A call @select_story_menu$qv
or al, al or al, al
jz short loc_9A59 jz short loc_9A59
mov al, 1 mov al, 1
@ -391,14 +391,14 @@ loc_9CBB:
mov es:[bx+resident_t.show_score_menu], 0 mov es:[bx+resident_t.show_score_menu], 0
cmp [bp+var_2], 1 cmp [bp+var_2], 1
jnz short loc_9CEF jnz short loc_9CEF
call sub_BA88 call @select_1p_vs_2p_menu$qv
or al, al or al, al
jz short loc_9D03 jz short loc_9D03
jmp short loc_9CF6 jmp short loc_9CF6
; --------------------------------------------------------------------------- ; ---------------------------------------------------------------------------
loc_9CEF: loc_9CEF:
call sub_BC1F call @select_vs_cpu_menu$qv
or al, al or al, al
jz short loc_9D03 jz short loc_9D03
@ -1628,419 +1628,9 @@ OP_SEL_TEXT segment byte public 'CODE' use16
@select_cdg_load_part1_of_4$qv procdesc near @select_cdg_load_part1_of_4$qv procdesc near
@select_cdg_load_part2_of_4$qv procdesc near @select_cdg_load_part2_of_4$qv procdesc near
@select_cdg_load_part3_of_4$qv procdesc near @select_cdg_load_part3_of_4$qv procdesc near
@select_init_and_load$qv procdesc near @select_1p_vs_2p_menu$qv procdesc near
@select_free$qv procdesc near @select_vs_cpu_menu$qv procdesc near
@vs_sel_pics_put$qv procdesc near @select_story_menu$qv procdesc near
@story_sel_pics_put$qv procdesc near
@stats_put$qv procdesc near
@names_put$qv procdesc near
@extras_put$qv procdesc near
@select_curves_update_and_render$qv procdesc near
@CURSOR_PUT$QIUC procdesc pascal near \
pid:word, col:byte
@SELECT_UPDATE_PLAYER$QUII procdesc pascal near \
input:word, pid:word
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_BA88 proc near
push bp
mov bp, sp
call @select_init_and_load$qv
call text_clear
les bx, _resident
mov al, es:[bx+resident_t.RESIDENT_playchar_paletted][0]
mov ah, 0
dec ax
cwd
sub ax, dx
sar ax, 1
mov _sel[0], al
mov al, es:[bx+resident_t.RESIDENT_playchar_paletted][1]
mov ah, 0
dec ax
cwd
sub ax, dx
sar ax, 1
mov _sel[1], al
mov _sel_confirmed_p1, 0
mov _sel_confirmed_p2, 0
cmp es:[bx+resident_t.key_mode], KM_KEY_KEY
jnz short loc_BAD4
setfarfp _input_mode, @input_mode_key_vs_key$qv
jmp short loc_BAF9
; ---------------------------------------------------------------------------
loc_BAD4:
les bx, _resident
cmp es:[bx+resident_t.key_mode], KM_JOY_KEY
jnz short loc_BAED
setfarfp _input_mode, @input_mode_joy_vs_key$qv
jmp short loc_BAF9
; ---------------------------------------------------------------------------
loc_BAED:
setfarfp _input_mode, @input_mode_key_vs_joy$qv
loc_BAF9:
call @frame_delay$qi pascal, 16
mov _fadeout_frames, 0
loc_BB06:
call @select_curves_update_and_render$qv
call @vs_sel_pics_put$qv
call @stats_put$qv
call @names_put$qv
call @extras_put$qv
push 0 ; pid
cmp _sel_confirmed_p1, 0
jz short loc_BB22
mov al, V_WHITE
jmp short loc_BB24
; ---------------------------------------------------------------------------
loc_BB22:
mov al, 8
loc_BB24:
push ax ; col
call @cursor_put$qiuc
push 1 ; pid
cmp _sel_confirmed_p2, 0
jz short loc_BB35
mov al, V_WHITE
jmp short loc_BB37
; ---------------------------------------------------------------------------
loc_BB35:
mov al, 10
loc_BB37:
push ax ; col
call @cursor_put$qiuc
call @input_reset_sense_key_held$qv
call _input_mode
call @select_update_player$quii pascal, _input_mp_p1, 0
call @select_update_player$quii pascal, _input_mp_p2, 1
test _input_sp.hi, high INPUT_CANCEL
jz short loc_BB82
graph_accesspage 0
call graph_clear
graph_showpage 0
call text_clear
call @select_free$qv
kajacall KAJA_SONG_STOP
mov al, 1
pop bp
retn
; ---------------------------------------------------------------------------
loc_BB82:
cmp _sel_confirmed_p1, 0
jz short loc_BBB7
cmp _sel_confirmed_p2, 0
jz short loc_BBB7
call text_clear
cmp _fadeout_frames, 16
jb short loc_BBB0
mov ax, _fadeout_frames
imul ax, 6
mov dx, 200
sub dx, ax
mov PaletteTone, dx
call far ptr palette_show
loc_BBB0:
cmp _fadeout_frames, 32
ja short loc_BC18
loc_BBB7:
cmp vsync_Count1, 3
jb short loc_BBB7
cmp vsync_Count1, 4
jbe short loc_BBD0
cmp _curve_trail_count, 1
jle short loc_BBD0
dec _curve_trail_count
loc_BBD0:
mov vsync_Count1, 0
graph_accesspage _page_shown
mov al, 1
sub al, _page_shown
mov _page_shown, al
graph_showpage al
call grcg_setcolor pascal, (GC_RMW shl 16) + 0
call grcg_byteboxfill_x pascal, large 0, (((RES_X - 1) / 8) shl 16) or (RES_Y - 1)
call grcg_off
inc _fadeout_frames
les bx, _resident
inc es:[bx+resident_t.rand]
jmp loc_BB06
; ---------------------------------------------------------------------------
loc_BC18:
call @select_free$qv
mov al, 0
pop bp
retn
sub_BA88 endp
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_BC1F proc near
push bp
mov bp, sp
push si
call @select_init_and_load$qv
les bx, _resident
mov al, es:[bx+resident_t.RESIDENT_playchar_paletted][0]
mov ah, 0
dec ax
cwd
sub ax, dx
sar ax, 1
mov _sel[0], al
mov al, es:[bx+resident_t.RESIDENT_playchar_paletted][1]
mov ah, 0
dec ax
cwd
sub ax, dx
sar ax, 1
mov _sel[1], al
mov _sel_confirmed_p1, 0
mov _sel_confirmed_p2, 0
setfarfp _input_mode, @input_mode_interface$qv
xor si, si
jmp loc_BD8B
; ---------------------------------------------------------------------------
loc_BC63:
mov _fadeout_frames, 0
loc_BC69:
call @select_curves_update_and_render$qv
call @vs_sel_pics_put$qv
call @stats_put$qv
call @names_put$qv
call @extras_put$qv
call @input_reset_sense_key_held$qv
call _input_mode
push 0 ; pid
cmp _sel_confirmed_p1, 0
jz short loc_BC8E
mov al, V_WHITE
jmp short loc_BC90
; ---------------------------------------------------------------------------
loc_BC8E:
mov al, 8
loc_BC90:
push ax ; col
call @cursor_put$qiuc
cmp _sel_confirmed_p1, 0
jz short loc_BCAE
push 1 ; pid
cmp _sel_confirmed_p2, 0
jz short loc_BCA8
mov al, V_WHITE
jmp short loc_BCAA
; ---------------------------------------------------------------------------
loc_BCA8:
mov al, 10
loc_BCAA:
push ax ; col
call @cursor_put$qiuc
loc_BCAE:
call @select_update_player$quii pascal, _input_sp, si
test _input_sp.hi, high INPUT_CANCEL
jz short loc_BCE3
graph_accesspage 0
call graph_clear
graph_showpage 0
call text_clear
call @select_free$qv
kajacall KAJA_SONG_STOP
mov al, 1
jmp loc_BD97
; ---------------------------------------------------------------------------
loc_BCE3:
or si, si
jnz short loc_BCF7
cmp _sel_confirmed_p1, 0
jz short loc_BCF7
cmp _fadeout_frames, 12
ja loc_BD8A
loc_BCF7:
or si, si
jz short loc_BD29
cmp _sel_confirmed_p2, 0
jz short loc_BD29
call text_clear
cmp _fadeout_frames, 16
jb short loc_BD22
mov ax, _fadeout_frames
imul ax, 6
mov dx, 200
sub dx, ax
mov PaletteTone, dx
call far ptr palette_show
loc_BD22:
cmp _fadeout_frames, 32
ja short loc_BD8A
loc_BD29:
cmp vsync_Count1, 3
jb short loc_BD29
cmp vsync_Count1, 4
jbe short loc_BD42
cmp _curve_trail_count, 1
jle short loc_BD42
dec _curve_trail_count
loc_BD42:
mov vsync_Count1, 0
graph_accesspage _page_shown
mov al, 1
sub al, _page_shown
mov _page_shown, al
graph_showpage al
call grcg_setcolor pascal, (GC_RMW shl 16) + 0
call grcg_byteboxfill_x pascal, large 0, (((RES_X - 1) / 8) shl 16) or (RES_Y - 1)
call grcg_off
inc _fadeout_frames
les bx, _resident
inc es:[bx+resident_t.rand]
jmp loc_BC69
; ---------------------------------------------------------------------------
loc_BD8A:
inc si
loc_BD8B:
cmp si, 2
jl loc_BC63
call @select_free$qv
mov al, 0
loc_BD97:
pop si
pop bp
retn
sub_BC1F endp
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
sub_BD9A proc near
push bp
mov bp, sp
call @select_init_and_load$qv
mov _sel[0], 0
mov _sel_confirmed_p1, 0
mov _sel_confirmed_p2, 1
setfarfp _input_mode, @input_mode_interface$qv
mov _fadeout_frames, 0
loc_BDC1:
call @select_curves_update_and_render$qv
call @story_sel_pics_put$qv
call @stats_put$qv
call @names_put$qv
call @extras_put$qv
push 0 ; pid
cmp _sel_confirmed_p1, 0
jz short loc_BDDD
mov al, V_WHITE
jmp short loc_BDDF
; ---------------------------------------------------------------------------
loc_BDDD:
mov al, 8
loc_BDDF:
push ax ; col
call @cursor_put$qiuc
call @input_reset_sense_key_held$qv
call _input_mode
call @select_update_player$quii pascal, _input_sp, 0
test _input_sp.hi, high INPUT_CANCEL
jz short loc_BE21
graph_accesspage 0
call graph_clear
graph_showpage 0
call text_clear
call @select_free$qv
kajacall KAJA_SONG_STOP
mov al, 1
pop bp
retn
; ---------------------------------------------------------------------------
loc_BE21:
cmp _sel_confirmed_p1, 0
jz short loc_BE4F
call text_clear
cmp _fadeout_frames, 16
jb short loc_BE48
mov ax, _fadeout_frames
imul ax, 6
mov dx, 200
sub dx, ax
mov PaletteTone, dx
call far ptr palette_show
loc_BE48:
cmp _fadeout_frames, 32
ja short loc_BEB0
loc_BE4F:
cmp vsync_Count1, 3
jb short loc_BE4F
cmp vsync_Count1, 4
jbe short loc_BE68
cmp _curve_trail_count, 1
jle short loc_BE68
dec _curve_trail_count
loc_BE68:
mov vsync_Count1, 0
graph_accesspage _page_shown
mov al, 1
sub al, _page_shown
mov _page_shown, al
graph_showpage al
call grcg_setcolor pascal, (GC_RMW shl 16) + 0
call grcg_byteboxfill_x pascal, large 0, (((RES_X - 1) / 8) shl 16) or (RES_Y - 1)
call grcg_off
inc _fadeout_frames
les bx, _resident
inc es:[bx+resident_t.rand]
jmp loc_BDC1
; ---------------------------------------------------------------------------
loc_BEB0:
call @select_free$qv
mov al, 0
pop bp
retn
sub_BD9A endp
db 0
OP_SEL_TEXT ends OP_SEL_TEXT ends
; =========================================================================== ; ===========================================================================
@ -2059,9 +1649,6 @@ include th02/snd/snd.inc
extern @game_init_op$qnxuc:proc extern @game_init_op$qnxuc:proc
extern @PI_LOAD$QINXC:proc extern @PI_LOAD$QINXC:proc
extern @INPUT_MODE_INTERFACE$QV:proc extern @INPUT_MODE_INTERFACE$QV:proc
extern @INPUT_MODE_KEY_VS_KEY$QV:proc
extern @INPUT_MODE_JOY_VS_KEY$QV:proc
extern @INPUT_MODE_KEY_VS_JOY$QV:proc
SHARED ends SHARED ends
.data .data
@ -2198,8 +1785,6 @@ _SELECT_PALETTE_FN db 'TLSL.RGB',0
extern _snd_active:byte extern _snd_active:byte
extern _input_sp:word extern _input_sp:word
extern _input_mp_p1:word
extern _input_mp_p2:word
extern _pi_buffers:dword extern _pi_buffers:dword
extern _pi_headers:PiHeader extern _pi_headers:PiHeader