Document time trial input replay system (#730)

* Document time trial input replay system

Documents how the input replay system works in time trials. This is used for
* Course ghosts (on the raceways)
* Player ghosts
* Replays

* Rename ghosts/replays and minor cleanup

Refer to staff and player ghosts in time trials as replays.
Refer to the basic replay as a "post time trial replay"
Change variable / function names to reflect terminology change

* Define replay magic values

* Rename staff_ghosts files to replays

Renames staff_ghosts files to replays, since they also deal with player ghosts and post time trial replays.

Also, changes staff_ghosts_loop -> replays_loop

* REPLAY_NOT_FRAME_COUNTER -> REPLAY_CLEAR_FRAME_COUNTER

* Add replays header and format

* Update replays.c

* Name load replay functions

---------

Co-authored-by: MegaMech <MegaMech@users.noreply.github.com>
This commit is contained in:
Jed Grabman 2025-06-28 18:36:36 -04:00 committed by GitHub
parent 91ee36c539
commit 6b08e60fe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 678 additions and 620 deletions

View File

@ -45,6 +45,25 @@
#define HOLD_ALL_DPAD_AND_C_BUTTONS \
(U_JPAD | L_JPAD | R_JPAD | D_JPAD | U_CBUTTONS | L_CBUTTONS | R_CBUTTONS | D_CBUTTONS)
#define ALL_BUTTONS \
(A_BUTTON | B_BUTTON | L_TRIG | R_TRIG | Z_TRIG | START_BUTTON | U_JPAD | L_JPAD | R_JPAD | D_JPAD | U_CBUTTONS | \
L_CBUTTONS | R_CBUTTONS | D_CBUTTONS)
/**
* Replay controller buttons
* Used for time trial replays (including staff and player ghosts)
* Each entry is converted to a u32 value
* This allows access to the button struct member
*/
#define REPLAY_A_BUTTON (1 << 31) // 0x80000000
#define REPLAY_B_BUTTON (1 << 30) // 0x40000000
#define REPLAY_Z_TRIG (1 << 29) // 0x20000000
#define REPLAY_R_TRIG (1 << 28) // 0x10000000
#define REPLAY_FRAME_COUNTER 0xFF0000
#define REPLAY_CLEAR_FRAME_COUNTER (0xFFFFFFFF & ~REPLAY_FRAME_COUNTER)
#define REPLAY_STICK_Y 0xFF00
#define REPLAY_STICK_X 0xFF
#define REPLAY_FRAME_INCREMENT 0x10000
/**
* @brief Jump to demo mode from the debug menu using L and A

View File

@ -50,7 +50,7 @@ SECTIONS
BUILD_DIR/src/profiler.o(.text*);
BUILD_DIR/src/crash_screen.o(.text*);
BUILD_DIR/src/animation.o(.text*);
BUILD_DIR/src/staff_ghosts.o(.text*);
BUILD_DIR/src/replays.o(.text*);
BUILD_DIR/asm/unused_overflow_check.o(.text);
BUILD_DIR/src/cpu_vehicles_camera_path.jp.o(.text*);
BUILD_DIR/src/camera.o(.text*);
@ -218,7 +218,7 @@ SECTIONS
BUILD_DIR/src/code_800029B0.o(.data*);
BUILD_DIR/src/profiler.o(.data*);
BUILD_DIR/src/crash_screen.o(.data*);
BUILD_DIR/src/staff_ghosts.o(.data*);
BUILD_DIR/src/replays.o(.data*);
BUILD_DIR/src/data/path_spawn_metadata.o(.data*);
BUILD_DIR/src/camera.o(.data*);
BUILD_DIR/src/render_player.o(.data*);
@ -327,7 +327,7 @@ SECTIONS
BUILD_DIR/src/profiler.o(.bss*);
BUILD_DIR/src/crash_screen.o(.bss*);
BUILD_DIR/src/animation.o(.bss*);
BUILD_DIR/src/staff_ghosts.o(.bss*);
BUILD_DIR/src/replays.o(.bss*);
BUILD_DIR/src/cpu_vehicles_camera_path.jp.o(.bss*);
BUILD_DIR/src/camera.o(.bss*);
BUILD_DIR/src/render_player.o(.bss*);

View File

@ -58,7 +58,7 @@ void update_actor_kiwano_fruit(struct KiwanoFruit* fruit) {
player->velocity[2] -= temp_f14 * 0.7f;
func_800C9060(player - gPlayerOne, SOUND_ARG_LOAD(0x19, 0x00, 0x70, 0x18));
if (gModeSelection != GRAND_PRIX) {
D_80162DF8 = 1;
gPostTTReplayCannotSave = 1;
}
}
}

View File

@ -22,7 +22,7 @@ typedef struct {
} struct_D_802BFB80_4; // size = 0x1000
/*
* In render_player, spawn_players, and staff_ghosts D_802BFB80 is the arraySize8 entry
* In render_player, spawn_players, and replays D_802BFB80 is the arraySize8 entry
* But in menu_item its the arraySize4 entry
* The only way to unify those 2 things is to use a union
*/

View File

@ -16,7 +16,7 @@
#include "skybox_and_splitscreen.h"
#include "code_8006E9C0.h"
#include "spawn_players.h"
#include "staff_ghosts.h"
#include "replays.h"
#include "render_courses.h"
#include "main.h"
#include "courses/all_course_data.h"

View File

@ -46,7 +46,7 @@
#include <render_courses.h>
#include <skybox_and_splitscreen.h>
#include <spawn_players.h>
#include <staff_ghosts.h>
#include <replays.h>
#include <textures.h>
#include <actor_types.h>

View File

@ -35,7 +35,7 @@
#include "render_player.h"
#include "render_courses.h"
#include "actors.h"
#include "staff_ghosts.h"
#include "replays.h"
#include <debug.h>
#include "crash_screen.h"
#include "buffers/gfx_output_buffer.h"
@ -595,7 +595,7 @@ void race_logic_loop(void) {
switch (gActiveScreenMode) {
case SCREEN_MODE_1P:
gTickSpeed = 2;
staff_ghosts_loop();
replays_loop();
if (gIsGamePaused == 0) {
for (i = 0; i < gTickSpeed; i++) {
if (D_8015011E) {

View File

@ -21,7 +21,7 @@
#include "memory.h"
#include "audio/external.h"
#include "render_objects.h"
#include "staff_ghosts.h"
#include "replays.h"
#include <assets/common_data.h>
#include "textures.h"
#include "math_util.h"
@ -5624,9 +5624,9 @@ void add_menu_item(s32 type, s32 column, s32 row, s8 priority) {
case MENU_ITEM_TYPE_0BB:
menuItem->param1 = func_800B5020(playerHUD[0].someTimer, gCharacterSelections[0]);
menuItem->param2 = func_800B5218();
if (D_80162DD4 != 1) {
if (bPlayerGhostDisabled != 1) {
if (func_800051C4() > 0x3C00) {
D_80162DD4 = 1;
bPlayerGhostDisabled = 1;
}
}
if ((menuItem->param1 == 0) || (menuItem->param2 != 0)) {
@ -7346,12 +7346,12 @@ void func_800A3E60(MenuItem* arg0) {
text_rainbow_effect(arg0->state - 5, var_s1, 1);
switch (var_s1) { /* switch 3; irregular */
case 4: /* switch 3 */
if (D_80162DF8 == 1) {
if (gPostTTReplayCannotSave == 1) {
var_v1 = 1;
}
break;
case 5: /* switch 3 */
if (D_80162DD4 != 0) {
if (bPlayerGhostDisabled != 0) {
var_v1 = 2;
}
break;
@ -11062,7 +11062,7 @@ void func_800AD2E8(MenuItem* arg0) {
arg0->param2 = 0;
arg0->column = 0;
arg0->state = gTimeTrialsResultCursorSelection;
if ((arg0->state == 9) && (D_80162DF8 == 1)) {
if ((arg0->state == 9) && (gPostTTReplayCannotSave == 1)) {
arg0->state--;
}
D_800DC5EC->screenStartX = 0x00F0;
@ -11079,7 +11079,7 @@ void func_800AD2E8(MenuItem* arg0) {
if ((gControllerOne->buttonPressed | gControllerOne->stickPressed) & 0x800) {
if (arg0->state >= 6) {
arg0->state--;
if ((D_80162DF8 == 1) && (arg0->state == 9)) {
if ((gPostTTReplayCannotSave == 1) && (arg0->state == 9)) {
arg0->state--;
}
play_sound2(SOUND_MENU_CURSOR_MOVE);
@ -11092,10 +11092,10 @@ void func_800AD2E8(MenuItem* arg0) {
if ((gControllerOne->buttonPressed | gControllerOne->stickPressed) & 0x400) {
if (arg0->state < 0xA) {
arg0->state++;
if ((D_80162DF8 == 1) && (arg0->state == 9)) {
if ((gPostTTReplayCannotSave == 1) && (arg0->state == 9)) {
arg0->state++;
}
if ((arg0->state == 0x0000000A) && (D_80162DD4 != 0)) {
if ((arg0->state == 0x0000000A) && (bPlayerGhostDisabled != 0)) {
arg0->state -= 2;
} else {
play_sound2(SOUND_MENU_CURSOR_MOVE);
@ -11821,7 +11821,7 @@ void func_800AEF14(MenuItem* arg0) {
void func_800AEF74(MenuItem* arg0) {
switch (arg0->state) { /* irregular */
case 0:
if (D_80162DF8 == 1) {
if (gPostTTReplayCannotSave == 1) {
arg0->state = 1;
arg0->param1 = 0;
} else if (playerHUD[PLAYER_ONE].raceCompleteBool == (s8) 1) {

View File

@ -15,7 +15,7 @@
#include "menu_items.h"
#include "code_800AF9B0.h"
#include "save.h"
#include "staff_ghosts.h"
#include "replays.h"
#include "save_data.h"
#include <sounds.h>
#include "spawn_players.h"
@ -1801,7 +1801,7 @@ void load_menu_states(s32 menuSelection) {
gDebugGotoScene = DEBUG_GOTO_RACING;
gGhostPlayerInit = 0;
D_8016556E = 0;
D_80162DD4 = 1;
bPlayerGhostDisabled = 1;
D_80162DD8 = 1;
D_80162E00 = 0;
D_80162DC8 = 1;

View File

@ -1660,7 +1660,7 @@ bool collision_yoshi_egg(Player* player, struct YoshiValleyEgg* egg) {
} else {
apply_hit_sound_effect(player, player - gPlayerOne);
if ((gModeSelection == TIME_TRIALS) && ((player->type & PLAYER_CPU) == 0)) {
D_80162DF8 = 1;
gPostTTReplayCannotSave = 1;
}
}
} else {
@ -2200,7 +2200,7 @@ void evaluate_collision_between_player_actor(Player* player, struct Actor* actor
if (query_collision_player_vs_actor_item(player, actor) == COLLISION) {
func_800C98B8(actor->pos, actor->velocity, SOUND_ACTION_EXPLOSION);
if ((gModeSelection == TIME_TRIALS) && !(player->type & PLAYER_CPU)) {
D_80162DF8 = 1;
gPostTTReplayCannotSave = 1;
}
if (player->effects & STAR_EFFECT) {
actor->velocity[1] = 10.0f;

View File

@ -145,7 +145,7 @@ extern Gfx toads_turnpike_dl_9[];
extern Gfx toads_turnpike_dl_10[];
extern Gfx toads_turnpike_dl_11[];
extern s32 D_80162DF8;
extern s32 gPostTTReplayCannotSave;
extern Gfx D_0D001750[];
extern Gfx D_0D001780[];

View File

@ -8,7 +8,7 @@
#include "camera.h"
#include "path.h"
#include "staff_ghosts.h"
#include "replays.h"
#include "main.h"
#include "code_800029B0.h"
#include "code_80057C60.h"
@ -813,7 +813,7 @@ void func_8028F970(void) {
gIsGamePaused = (controller - gControllerOne) + 1;
controller->buttonPressed = 0;
func_800C9F90(1);
D_80162DF0 = 1;
gPauseTriggered = 1;
if (gModeSelection == TIME_TRIALS) {
if (gPlayerOne->type & (PLAYER_EXISTS | PLAYER_INVISIBLE_OR_BOMB)) {
func_80005AE8(gPlayerOne);
@ -945,7 +945,7 @@ void func_8028FCBC(void) {
D_800DC5B0 = 0;
D_800DC5B8 = 1;
func_80078F64();
if ((gModeSelection == TIME_TRIALS) && (D_80162DD6 == 0)) {
if ((gModeSelection == TIME_TRIALS) && (bCourseGhostDisabled == 0)) {
phi_v0_4 = 0x1;
for (i = 0; i < gCurrentCourseId; i++) {
phi_v0_4 <<= 1;

View File

@ -39,6 +39,6 @@ void func_80290B14(void);
extern f32 gLapCompletionPercentByPlayerId[];
extern s32 gGPCurrentRaceRankByPlayerId[]; // D_801643B8 (position for each player)
extern u16 D_80162DD6;
extern u16 COURSE_GHOST_LOCKED;
#endif

611
src/replays.c Normal file
View File

@ -0,0 +1,611 @@
/*
* @file Replays
* Handles 3 types of time trial replays:
* 1. Post time trial replays, which a player can watch after a time trial
* 2. Player ghosts, which were driven by a player and can be raced against
* 3. Staff ghosts, which can be unlocked on the raceway tracks with a fast enough time
* All 3 use the same system for storing / replaying the inputs to reproduce a time trial
* See process_post_TT_replay for additional technical details
*/
#include <ultra64.h>
#include <macros.h>
#include <common_structs.h>
#include <defines.h>
#include <decode.h>
#include <mk64.h>
#include <course.h>
#include "main.h"
#include "code_800029B0.h"
#include "buffers.h"
#include "save.h"
#include "replays.h"
#include "code_8006E9C0.h"
#include "menu_items.h"
#include "code_80057C60.h"
#include "kart_dma.h"
extern s32 mio0encode(s32 input, s32, s32);
extern s32 func_80040174(void*, s32, s32);
u8* D_80162D80;
s16 D_80162D84;
s16 D_80162D86;
static u16 sPlayerGhostButtonsPrev;
static u32 sPlayerGhostFramesRemaining;
static s16 sPlayerGhostReplayIdx;
static u32* sPlayerGhostReplay;
static u16 sButtonsPrevCourseGhost;
static u32 sCourseGhostFramesRemaining;
static s16 sCourseGhostReplayIdx;
static u32* sCourseGhostReplay;
static u16 sPostTTButtonsPrev;
static s32 sPostTTFramesRemaining;
static s16 sPostTTReplayIdx;
static u32* sPostTTReplay;
static s16 sPlayerInputIdx;
static u32* sPlayerInputs;
static u16 sPrevCourseId;
u32 D_80162DC4;
s32 D_80162DC8;
s32 D_80162DCC;
s32 D_80162DD0;
u16 bPlayerGhostDisabled;
u16 bCourseGhostDisabled;
u16 D_80162DD8;
s32 D_80162DDC;
s32 D_80162DE0; // ghost kart id?
s32 D_80162DE4;
s32 D_80162DE8;
UNUSED static s32 sUnusedReplayCounter;
s32 gPauseTriggered;
UNUSED static s32 bUnusedCourseGhostDisabled;
s32 gPostTTReplayCannotSave;
s32 D_80162DFC;
s32 D_80162E00;
u32* D_800DC710 = (u32*) &D_802BFB80.arraySize8[0][2][3];
u32* D_800DC714 = (u32*) &D_802BFB80.arraySize8[1][1][3];
extern s32 gLapCountByPlayerId[];
extern StaffGhost* d_mario_raceway_staff_ghost;
extern StaffGhost* d_royal_raceway_staff_ghost;
extern StaffGhost* d_luigi_raceway_staff_ghost;
void load_course_ghost(void) {
sCourseGhostReplay = (u32*) &D_802BFB80.arraySize8[0][2][3];
osInvalDCache(&sCourseGhostReplay[0], 0x4000);
osPiStartDma(&gDmaIoMesg, 0, 0, (uintptr_t) &_kart_texturesSegmentRomStart[SEGMENT_OFFSET(D_80162DC4)],
sCourseGhostReplay, 0x4000, &gDmaMesgQueue);
osRecvMesg(&gDmaMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
sCourseGhostFramesRemaining = (*sCourseGhostReplay & REPLAY_FRAME_COUNTER);
sCourseGhostReplayIdx = 0;
}
void load_post_tt_replay(void) {
sPostTTReplay = (u32*) &D_802BFB80.arraySize8[0][D_80162DD0][3];
sPostTTFramesRemaining = *sPostTTReplay & REPLAY_FRAME_COUNTER;
sPostTTReplayIdx = 0;
}
void load_player_ghost(void) {
sPlayerGhostReplay = (u32*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
sPlayerGhostFramesRemaining = (s32) *sPlayerGhostReplay & REPLAY_FRAME_COUNTER;
sPlayerGhostReplayIdx = 0;
}
/**
* Activates staff ghost if time trial lap time is low enough
*
*/
#ifdef VERSION_EU
#define GHOST_UNLOCK_MARIO 10700
#define GHOST_UNLOCK_ROYAL 19300
#define GHOST_UNLOCK_LUIGI 13300
#else
#define GHOST_UNLOCK_MARIO 9000
#define GHOST_UNLOCK_ROYAL 16000
#define GHOST_UNLOCK_LUIGI 11200
#endif
void set_staff_ghost(void) {
u32 bestTime; // Appears to be best player 3lap time.
#if !ENABLE_CUSTOM_COURSE_ENGINE
switch (gCurrentCourseId) {
case COURSE_MARIO_RACEWAY:
bestTime = func_800B4E24(0) & 0xfffff;
if (bestTime <= GHOST_UNLOCK_MARIO) {
bCourseGhostDisabled = 0;
bUnusedCourseGhostDisabled = 0;
} else {
bCourseGhostDisabled = 1;
bUnusedCourseGhostDisabled = 1;
}
D_80162DC4 = (u32) &d_mario_raceway_staff_ghost;
D_80162DE4 = 0;
break;
case COURSE_ROYAL_RACEWAY:
bestTime = func_800B4E24(0) & 0xfffff;
if (bestTime <= GHOST_UNLOCK_ROYAL) {
bCourseGhostDisabled = 0;
bUnusedCourseGhostDisabled = 0;
} else {
bCourseGhostDisabled = 1;
bUnusedCourseGhostDisabled = 1;
}
D_80162DC4 = (u32) &d_royal_raceway_staff_ghost;
D_80162DE4 = 6;
break;
case COURSE_LUIGI_RACEWAY:
bestTime = func_800B4E24(0) & 0xfffff;
if (bestTime <= GHOST_UNLOCK_LUIGI) {
bCourseGhostDisabled = 0;
bUnusedCourseGhostDisabled = 0;
} else {
bCourseGhostDisabled = 1;
bUnusedCourseGhostDisabled = 1;
}
D_80162DC4 = (u32) &d_luigi_raceway_staff_ghost;
D_80162DE4 = 1;
break;
default:
bCourseGhostDisabled = 1;
bUnusedCourseGhostDisabled = 1;
}
#else
#endif
}
s32 func_800051C4(void) {
s32 phi_v0;
if (D_80162D84 != 0) {
// func_80040174 in mio0_decode.s
func_80040174((void*) D_80162D80, (D_80162D84 * 4) + 0x20, (s32) D_800DC710);
phi_v0 = mio0encode((s32) D_800DC710, (D_80162D84 * 4) + 0x20, (s32) D_800DC714);
return phi_v0 + 0x1e;
}
}
void func_8000522C(void) {
sPlayerGhostReplay = (u32*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
mio0decode((u8*) D_800DC714, (u8*) sPlayerGhostReplay);
sPlayerGhostFramesRemaining = (s32) (*sPlayerGhostReplay & REPLAY_FRAME_COUNTER);
sPlayerGhostReplayIdx = 0;
D_80162E00 = 1;
}
void func_800052A4(void) {
s16 temp_v0;
if (D_80162DC8 == 1) {
D_80162DC8 = 0;
D_80162DCC = 1;
} else {
D_80162DC8 = 1;
D_80162DCC = 0;
}
temp_v0 = sPlayerInputIdx;
D_80162D80 = (void*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
D_80162D84 = temp_v0;
D_80162D86 = temp_v0;
}
void func_80005310(void) {
if (gModeSelection == TIME_TRIALS) {
set_staff_ghost();
if (sPrevCourseId != gCurrentCourseId) {
bPlayerGhostDisabled = 1;
}
sPrevCourseId = (u16) gCurrentCourseId;
gPauseTriggered = 0;
sUnusedReplayCounter = 0;
gPostTTReplayCannotSave = 0;
if (gModeSelection == TIME_TRIALS && gActiveScreenMode == SCREEN_MODE_1P) {
if (D_8015F890 == 1) {
load_post_tt_replay();
if (D_80162DD8 == 0) {
load_player_ghost();
}
if (bCourseGhostDisabled == 0) {
load_course_ghost();
}
} else {
D_80162DD8 = 1U;
sPlayerInputs = (u32*) &D_802BFB80.arraySize8[0][D_80162DCC][3];
sPlayerInputs[0] = -1;
sPlayerInputIdx = 0;
D_80162DDC = 0;
func_80091EE4();
if (bPlayerGhostDisabled == 0) {
load_player_ghost();
}
if (bCourseGhostDisabled == 0) {
load_course_ghost();
}
}
}
}
}
/* Special handling for buttons saved in replays. The listing of L_TRIG here is odd
* because it is not saved in the replay data structure. Possibly, L was initially deleted
* here to make way for the frame counter, but then the format changed when the stick
* coordinates were added */
#define REPLAY_MASK (ALL_BUTTONS ^ (A_BUTTON | B_BUTTON | Z_TRIG | R_TRIG | L_TRIG))
/* Inputs for replays (including player and course ghosts) are saved in a s32[] where
each entry is a combination of the inputs and how long those inputs were held for.
In essence it's "These buttons were pressed and the joystick was in this position.
This was the case for X frames".
bits 1-8: Stick X
bits 9-16: Stick Y
bits 17-24: Frame counter
bits 25-28: Unused
bit 29: R
bit 30: Z
bit 31: B
bit 32: A
*/
void process_post_TT_replay(void) {
u32 inputs;
u32 stickBytes;
UNUSED u16 unk;
u16 buttons_temp;
s16 stickVal;
s16 buttons = 0;
if (sPostTTReplayIdx >= 0x1000) {
gPlayerOne->type = PLAYER_CINEMATIC_MODE | PLAYER_START_SEQUENCE | PLAYER_CPU;
return;
}
inputs = sPostTTReplay[sPostTTReplayIdx];
stickBytes = inputs & REPLAY_STICK_X;
// twos complement trick, converting singned 8-bit value to signed 16 bit
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | (~0xFF));
}
stickBytes = (u32) (inputs & REPLAY_STICK_Y) >> 8;
gControllerEight->rawStickX = stickVal;
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | (~0xFF));
}
gControllerEight->rawStickY = stickVal;
if (inputs & REPLAY_A_BUTTON) {
buttons |= A_BUTTON;
}
if (inputs & REPLAY_B_BUTTON) {
buttons |= B_BUTTON;
}
if (inputs & REPLAY_Z_TRIG) {
buttons |= Z_TRIG;
}
if (inputs & REPLAY_R_TRIG) {
buttons |= R_TRIG;
}
buttons_temp = gControllerEight->buttonPressed & REPLAY_MASK;
gControllerEight->buttonPressed = (buttons & (buttons ^ sPostTTButtonsPrev)) | buttons_temp;
buttons_temp = gControllerEight->buttonDepressed & REPLAY_MASK;
gControllerEight->buttonDepressed = (sPostTTButtonsPrev & (buttons ^ sPostTTButtonsPrev)) | buttons_temp;
sPostTTButtonsPrev = buttons;
gControllerEight->button = buttons;
if (sPostTTFramesRemaining == 0) {
sPostTTReplayIdx++;
sPostTTFramesRemaining = (s32) (sPostTTReplay[sPostTTReplayIdx] & REPLAY_FRAME_COUNTER);
} else {
sPostTTFramesRemaining -= REPLAY_FRAME_INCREMENT;
}
}
// See process_post_TT_replay comment
void process_course_ghost_replay(void) {
u32 inputs;
u32 stickBytes;
UNUSED u16 unk;
u16 buttonsTemp;
s16 stickVal;
s16 buttons = 0;
if (sCourseGhostReplayIdx >= 0x1000) {
func_80005AE8(gPlayerThree);
return;
}
inputs = sCourseGhostReplay[sCourseGhostReplayIdx];
stickBytes = inputs & REPLAY_STICK_X;
// converting signed 8-bit values to signed 16-bit values
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | (~0xFF));
}
stickBytes = (u32) (inputs & REPLAY_STICK_Y) >> 8;
gControllerSeven->rawStickX = stickVal;
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | (~0xFF));
}
gControllerSeven->rawStickY = stickVal;
if (inputs & REPLAY_A_BUTTON) {
buttons = A_BUTTON;
}
if (inputs & REPLAY_B_BUTTON) {
buttons |= B_BUTTON;
}
if (inputs & REPLAY_Z_TRIG) {
buttons |= Z_TRIG;
}
if (inputs & REPLAY_R_TRIG) {
buttons |= R_TRIG;
}
// Blanks the A, B, Z, R and L buttons
buttonsTemp = gControllerSeven->buttonPressed & REPLAY_MASK;
gControllerSeven->buttonPressed = (buttons & (buttons ^ sButtonsPrevCourseGhost)) | buttonsTemp;
buttonsTemp = gControllerSeven->buttonDepressed & REPLAY_MASK;
gControllerSeven->buttonDepressed = (sButtonsPrevCourseGhost & (buttons ^ sButtonsPrevCourseGhost)) | buttonsTemp;
sButtonsPrevCourseGhost = buttons;
gControllerSeven->button = buttons;
if (sCourseGhostFramesRemaining == 0) {
sCourseGhostReplayIdx++;
sCourseGhostFramesRemaining = (s32) (sCourseGhostReplay[sCourseGhostReplayIdx] & REPLAY_FRAME_COUNTER);
} else {
sCourseGhostFramesRemaining -= (s32) REPLAY_FRAME_INCREMENT;
}
}
// See process_post_TT_replay comment
void process_player_ghost_replay(void) {
u32 inputs;
u32 stickBytes;
UNUSED u16 unk;
u16 buttons_temp;
s16 stickVal;
s16 buttons = 0;
if (sPlayerGhostReplayIdx >= 0x1000) {
func_80005AE8(gPlayerTwo);
return;
}
inputs = sPlayerGhostReplay[sPlayerGhostReplayIdx];
stickBytes = inputs & REPLAY_STICK_X;
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | ~0xFF);
}
stickBytes = (u32) (inputs & REPLAY_STICK_Y) >> 8;
gControllerSix->rawStickX = stickVal;
if (stickBytes < 0x80U) {
stickVal = (s16) (stickBytes & 0xFF);
} else {
stickVal = (s16) (stickBytes | (~0xFF));
}
gControllerSix->rawStickY = stickVal;
if (inputs & REPLAY_A_BUTTON) {
buttons = A_BUTTON;
}
if (inputs & REPLAY_B_BUTTON) {
buttons |= B_BUTTON;
}
if (inputs & REPLAY_Z_TRIG) {
buttons |= Z_TRIG;
}
if (inputs & REPLAY_R_TRIG) {
buttons |= R_TRIG;
}
buttons_temp = gControllerSix->buttonPressed & REPLAY_MASK;
gControllerSix->buttonPressed = (buttons & (buttons ^ sPlayerGhostButtonsPrev)) | buttons_temp;
buttons_temp = gControllerSix->buttonDepressed & REPLAY_MASK;
gControllerSix->buttonDepressed = (sPlayerGhostButtonsPrev & (buttons ^ sPlayerGhostButtonsPrev)) | buttons_temp;
sPlayerGhostButtonsPrev = buttons;
gControllerSix->button = buttons;
if (sPlayerGhostFramesRemaining == 0) {
sPlayerGhostReplayIdx++;
sPlayerGhostFramesRemaining = (s32) (sPlayerGhostReplay[sPlayerGhostReplayIdx] & REPLAY_FRAME_COUNTER);
} else {
sPlayerGhostFramesRemaining -= (s32) REPLAY_FRAME_INCREMENT;
}
}
// See process_post_TT_replay comment
void save_player_replay(void) {
s16 buttons;
u32 inputs;
u32 stickX;
u32 stickY;
u32 inputCounter;
u32 prevInputsWCounter;
u32 prevInputs;
/* Input file is too long or picked up by lakitu or Out of bounds
Not sure if there is any way to be considered out of bounds without lakitu getting called */
if (((sPlayerInputIdx >= 0x1000) || ((gPlayerOne->unk_0CA & 2) != 0)) || ((gPlayerOne->unk_0CA & 8) != 0)) {
gPostTTReplayCannotSave = 1;
return;
}
stickX = gControllerOne->rawStickX;
stickX &= 0xFF;
stickY = gControllerOne->rawStickY;
stickY = (stickY & 0xFF) << 8;
buttons = gControllerOne->button;
inputs = 0;
if (buttons & A_BUTTON) {
inputs |= REPLAY_A_BUTTON;
}
if (buttons & B_BUTTON) {
inputs |= REPLAY_B_BUTTON;
}
if (buttons & Z_TRIG) {
inputs |= REPLAY_Z_TRIG;
}
if (buttons & R_TRIG) {
inputs |= REPLAY_R_TRIG;
}
inputs |= stickX;
inputs |= stickY;
prevInputsWCounter = sPlayerInputs[sPlayerInputIdx];
/* The 5th and 6th bytes from the right are counters. Instead of saving the same inputs over and over,
it says "these inputs were played for __ frames" */
prevInputs = prevInputsWCounter & REPLAY_CLEAR_FRAME_COUNTER;
// first frame of inputs
if ((*sPlayerInputs) == -1) {
sPlayerInputs[sPlayerInputIdx] = inputs;
} else if (prevInputs == inputs) {
inputCounter = prevInputsWCounter & REPLAY_FRAME_COUNTER;
if (inputCounter == REPLAY_FRAME_COUNTER) {
sPlayerInputIdx++;
sPlayerInputs[sPlayerInputIdx] = inputs;
} else {
// increment counter by 1
prevInputsWCounter += REPLAY_FRAME_INCREMENT;
sPlayerInputs[sPlayerInputIdx] = prevInputsWCounter;
}
} else {
sPlayerInputIdx++;
sPlayerInputs[sPlayerInputIdx] = inputs;
}
}
// sets player to AI? (unconfirmed)
void func_80005AE8(Player* ply) {
if (((ply->type & PLAYER_INVISIBLE_OR_BOMB) != 0) && (ply != gPlayerOne)) {
ply->type = PLAYER_CINEMATIC_MODE | PLAYER_START_SEQUENCE | PLAYER_CPU;
}
}
void func_80005B18(void) {
if (gModeSelection == TIME_TRIALS) {
if ((gLapCountByPlayerId[0] == 3) && (D_80162DDC == 0) && (gPostTTReplayCannotSave != 1)) {
if (bPlayerGhostDisabled == 1) {
D_80162DD0 = D_80162DCC;
func_800052A4();
bPlayerGhostDisabled = 0;
D_80162DDC = 1;
D_80162DE0 = gPlayerOne->characterId;
D_80162DE8 = gPlayerOne->characterId;
D_80162E00 = 0;
D_80162DFC = playerHUD[PLAYER_ONE].someTimer;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else if (gLapCountByPlayerId[1] != 3) {
D_80162DD0 = D_80162DCC;
func_800052A4();
D_80162DDC = 1;
D_80162DE0 = gPlayerOne->characterId;
D_80162DFC = playerHUD[PLAYER_ONE].someTimer;
D_80162E00 = 0;
D_80162DE8 = gPlayerOne->characterId;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else {
D_80162D80 = D_802BFB80.arraySize8[0][D_80162DC8][3].pixel_index_array;
D_80162D84 = D_80162D86;
D_80162DD0 = D_80162DCC;
D_80162DE8 = gPlayerOne->characterId;
D_80162DD8 = 0;
bPlayerGhostDisabled = 0;
D_80162DDC = 1;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
}
} else {
if ((gLapCountByPlayerId[0] == 3) && (D_80162DDC == 0) && (gPostTTReplayCannotSave == 1)) {
D_80162D80 = D_802BFB80.arraySize8[0][D_80162DC8][3].pixel_index_array;
D_80162D84 = D_80162D86;
D_80162DDC = 1;
}
if ((gPlayerOne->type & 0x800) == 0x800) {
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else {
sUnusedReplayCounter += 1;
if (sUnusedReplayCounter > 100) {
sUnusedReplayCounter = 100;
}
if ((gModeSelection == TIME_TRIALS) && (gActiveScreenMode == SCREEN_MODE_1P)) {
if ((bPlayerGhostDisabled == 0) && (gLapCountByPlayerId[1] != 3)) {
process_player_ghost_replay();
}
if ((bCourseGhostDisabled == 0) && (gLapCountByPlayerId[2] != 3)) {
process_course_ghost_replay();
}
if (!(gPlayerOne->type & PLAYER_CINEMATIC_MODE)) {
save_player_replay();
}
}
}
}
}
}
void func_80005E6C(void) {
if ((gModeSelection == TIME_TRIALS) && (gModeSelection == TIME_TRIALS) && (gActiveScreenMode == SCREEN_MODE_1P)) {
if ((D_80162DD8 == 0) && (gLapCountByPlayerId[1] != 3)) {
process_player_ghost_replay(); // 3
}
if ((bCourseGhostDisabled == 0) && (gLapCountByPlayerId[2] != 3)) {
process_course_ghost_replay(); // 2
}
if ((gPlayerOne->type & PLAYER_CINEMATIC_MODE) != PLAYER_CINEMATIC_MODE) {
process_post_TT_replay(); // 1
return;
}
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
}
}
void replays_loop(void) {
if (D_8015F890 == 1) {
func_80005E6C();
return;
}
if (!gPauseTriggered) {
func_80005B18();
return;
}
/* This only gets triggered when the previous if-statements are not met
Seems like just for pausing */
gPostTTReplayCannotSave = 1;
}

View File

@ -5,21 +5,21 @@
#include <common_structs.h>
void func_80005B18(void);
void func_80004EF0(void);
void func_80004FB0(void);
void func_80004FF8(void);
void load_course_ghost(void);
void load_post_tt_replay(void);
void load_player_ghost(void);
void set_staff_ghost(void);
s32 func_800051C4(void);
void func_8000522C(void);
void func_800052A4(void);
void func_80005310(void);
void func_8000546C(void);
void func_8000561C(void);
void func_800057DC(void);
void process_replay_replay(void);
void process_course_ghost_replay(void);
void process_player_ghost_replay(void);
void func_8000599C(void);
void func_80005AE8(Player*);
void func_80005E6C(void);
void staff_ghosts_loop(void);
void replays_loop(void);
// mi0decode
@ -28,14 +28,14 @@ extern s32 func_80040174(void*, s32, s32);
extern s32 D_80162DC8;
extern s32 D_80162DCC;
extern u16 D_80162DD4;
extern u16 D_80162DD6;
extern u16 bPlayerGhostDisabled;
extern u16 bCourseGhostDisabled;
extern u16 D_80162DD8;
extern s32 D_80162E00;
extern s32 D_80162DE0;
extern s32 D_80162DE4;
extern s32 D_80162DE8;
extern s32 D_80162DF0;
extern s32 D_80162DF8;
extern s32 gPauseTriggered;
extern s32 gPostTTReplayCannotSave;
#endif /* STAFF_GHOSTS_H */

View File

@ -8,7 +8,7 @@
#include "menu_items.h"
#include "menus.h"
#include "save_data.h"
#include "staff_ghosts.h"
#include "replays.h"
#include "code_80057C60.h"
/*** macros ***/
@ -814,7 +814,7 @@ s32 func_800B63F0(s32 arg0) {
s32 phi_s3;
func_800051C4();
D_80162DD6 = 1;
bCourseGhostDisabled = 1;
func_80005AE8(gPlayerThree);
phi_s3 = 0;
@ -868,7 +868,7 @@ s32 func_800B64EC(s32 arg0) {
++phi_s1;
if ((++temp_s0) == 0x3C) {
func_8000522C();
D_80162DD4 = 0;
bPlayerGhostDisabled = 0;
D_80162DE0 = (s32) D_8018EE10[arg0].characterId;
D_80162DFC = D_8018EE10[arg0].unk_00;
break;

View File

@ -15,7 +15,7 @@
#include "code_80057C60.h"
#include "collision.h"
#include "render_courses.h"
#include "staff_ghosts.h"
#include "replays.h"
#include "cpu_vehicles_camera_path.h"
#include "render_player.h"
#include "podium_ceremony_actors.h"
@ -570,14 +570,14 @@ void spawn_players_versus_one_player(f32* arg0, f32* arg1, f32 arg2) {
} else if (D_8015F890 != 1) {
spawn_player(gPlayerOneCopy, 0, arg0[0], arg1[0], arg2, 32768.0f, gCharacterSelections[0],
PLAYER_EXISTS | PLAYER_START_SEQUENCE | PLAYER_HUMAN);
if (D_80162DD4 == 0) {
if (bPlayerGhostDisabled == 0) {
spawn_player(gPlayerTwo, 1, arg0[0], arg1[0], arg2, 32768.0f, D_80162DE0,
PLAYER_EXISTS | PLAYER_HUMAN | PLAYER_START_SEQUENCE | PLAYER_INVISIBLE_OR_BOMB);
} else {
spawn_player(gPlayerTwo, 1, arg0[0], arg1[0], arg2, 32768.0f, gCharacterSelections[0],
PLAYER_START_SEQUENCE | PLAYER_CPU);
}
if (D_80162DD6 == 0) {
if (bCourseGhostDisabled == 0) {
spawn_player(gPlayerThree, 2, arg0[0], arg1[0], arg2, 32768.0f, D_80162DE4,
PLAYER_EXISTS | PLAYER_HUMAN | PLAYER_START_SEQUENCE | PLAYER_INVISIBLE_OR_BOMB);
} else {
@ -594,7 +594,7 @@ void spawn_players_versus_one_player(f32* arg0, f32* arg1, f32 arg2) {
spawn_player(gPlayerTwo, 1, arg0[0], arg1[0], arg2, 32768.0f, gCharacterSelections[0],
PLAYER_START_SEQUENCE | PLAYER_CPU);
}
if (D_80162DD6 == 0) {
if (bCourseGhostDisabled == 0) {
spawn_player(gPlayerThree, 2, arg0[0], arg1[0], arg2, 32768.0f, D_80162DE4,
PLAYER_EXISTS | PLAYER_HUMAN | PLAYER_START_SEQUENCE | PLAYER_INVISIBLE_OR_BOMB);
} else {

View File

@ -1,572 +0,0 @@
#include <ultra64.h>
#include <macros.h>
#include <common_structs.h>
#include <defines.h>
#include <decode.h>
#include <mk64.h>
#include <course.h>
#include "main.h"
#include "code_800029B0.h"
#include "buffers.h"
#include "save.h"
#include "staff_ghosts.h"
#include "code_8006E9C0.h"
#include "menu_items.h"
#include "code_80057C60.h"
#include "kart_dma.h"
extern s32 mio0encode(s32 input, s32, s32);
extern s32 func_80040174(void*, s32, s32);
u8* D_80162D80;
s16 D_80162D84;
s16 D_80162D86;
u16 D_80162D88;
u32 D_80162D8C;
s16 D_80162D90;
u32* D_80162D94;
u16 D_80162D98;
u32 D_80162D9C;
s16 D_80162DA0;
u32* D_80162DA4;
u16 D_80162DA8;
s32 D_80162DAC;
s16 D_80162DB0;
u32* D_80162DB4;
s16 D_80162DB8;
u32* D_80162DBC;
u16 D_80162DC0;
u32 D_80162DC4;
s32 D_80162DC8;
s32 D_80162DCC;
s32 D_80162DD0;
u16 D_80162DD4;
u16 D_80162DD6;
u16 D_80162DD8;
s32 D_80162DDC;
s32 D_80162DE0; // ghost kart id?
s32 D_80162DE4;
s32 D_80162DE8;
s32 D_80162DEC;
s32 D_80162DF0;
s32 D_80162DF4;
s32 D_80162DF8;
s32 D_80162DFC;
s32 D_80162E00;
u32* D_800DC710 = (u32*) &D_802BFB80.arraySize8[0][2][3];
u32* D_800DC714 = (u32*) &D_802BFB80.arraySize8[1][1][3];
extern s32 gLapCountByPlayerId[];
extern StaffGhost* d_mario_raceway_staff_ghost;
extern StaffGhost* d_royal_raceway_staff_ghost;
extern StaffGhost* d_luigi_raceway_staff_ghost;
void func_80004EF0(void) {
D_80162DA4 = (u32*) &D_802BFB80.arraySize8[0][2][3];
osInvalDCache(&D_80162DA4[0], 0x4000);
osPiStartDma(&gDmaIoMesg, 0, 0, (uintptr_t) &_kart_texturesSegmentRomStart[SEGMENT_OFFSET(D_80162DC4)], D_80162DA4,
0x4000, &gDmaMesgQueue);
osRecvMesg(&gDmaMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
D_80162D9C = (*D_80162DA4 & 0xFF0000);
D_80162DA0 = 0;
}
void func_80004FB0(void) {
D_80162DB4 = (u32*) &D_802BFB80.arraySize8[0][D_80162DD0][3];
D_80162DAC = *D_80162DB4 & 0xFF0000;
D_80162DB0 = 0;
}
void func_80004FF8(void) {
D_80162D94 = (u32*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
D_80162D8C = (s32) *D_80162D94 & 0xFF0000;
D_80162D90 = 0;
}
/**
* Activates staff ghost if time trial lap time is lower enough
*
*/
#ifdef VERSION_EU
#define BLAH 10700
#define BLAH2 19300
#define BLAH3 13300
#else
#define BLAH 9000
#define BLAH2 16000
#define BLAH3 11200
#endif
void set_staff_ghost(void) {
u32 temp_v0; // Appears to be player total lap time.
#if !ENABLE_CUSTOM_COURSE_ENGINE
switch (gCurrentCourseId) {
case COURSE_MARIO_RACEWAY:
temp_v0 = func_800B4E24(0) & 0xfffff;
if (temp_v0 <= BLAH) {
D_80162DD6 = 0;
D_80162DF4 = 0;
} else {
D_80162DD6 = 1;
D_80162DF4 = 1;
}
D_80162DC4 = (u32) &d_mario_raceway_staff_ghost;
D_80162DE4 = 0;
break;
case COURSE_ROYAL_RACEWAY:
temp_v0 = func_800B4E24(0) & 0xfffff;
if (temp_v0 <= BLAH2) {
D_80162DD6 = 0;
D_80162DF4 = 0;
} else {
D_80162DD6 = 1;
D_80162DF4 = 1;
}
D_80162DC4 = (u32) &d_royal_raceway_staff_ghost;
D_80162DE4 = 6;
break;
case COURSE_LUIGI_RACEWAY:
temp_v0 = func_800B4E24(0) & 0xfffff;
if (temp_v0 <= BLAH3) {
D_80162DD6 = 0;
D_80162DF4 = 0;
} else {
D_80162DD6 = 1;
D_80162DF4 = 1;
}
D_80162DC4 = (u32) &d_luigi_raceway_staff_ghost;
D_80162DE4 = 1;
break;
default:
D_80162DD6 = 1;
D_80162DF4 = 1;
}
#else
#endif
}
s32 func_800051C4(void) {
s32 phi_v0;
if (D_80162D84 != 0) {
// func_80040174 in mio0_decode.s
func_80040174((void*) D_80162D80, (D_80162D84 * 4) + 0x20, (s32) D_800DC710);
phi_v0 = mio0encode((s32) D_800DC710, (D_80162D84 * 4) + 0x20, (s32) D_800DC714);
return phi_v0 + 0x1e;
}
}
void func_8000522C(void) {
D_80162D94 = (u32*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
mio0decode((u8*) D_800DC714, (u8*) D_80162D94);
D_80162D8C = (s32) (*D_80162D94 & 0xFF0000);
D_80162D90 = 0;
D_80162E00 = 1;
}
void func_800052A4(void) {
s16 temp_v0;
if (D_80162DC8 == 1) {
D_80162DC8 = 0;
D_80162DCC = 1;
} else {
D_80162DC8 = 1;
D_80162DCC = 0;
}
temp_v0 = D_80162DB8;
D_80162D80 = (void*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
D_80162D84 = temp_v0;
D_80162D86 = temp_v0;
}
void func_80005310(void) {
if (gModeSelection == TIME_TRIALS) {
set_staff_ghost();
if (D_80162DC0 != gCurrentCourseId) {
D_80162DD4 = 1;
}
D_80162DC0 = (u16) gCurrentCourseId;
D_80162DF0 = 0;
D_80162DEC = 0;
D_80162DF8 = 0;
if (gModeSelection == TIME_TRIALS && gActiveScreenMode == SCREEN_MODE_1P) {
if (D_8015F890 == 1) {
func_80004FB0();
if (D_80162DD8 == 0) {
func_80004FF8();
}
if (D_80162DD6 == 0) {
func_80004EF0();
}
} else {
D_80162DD8 = 1U;
D_80162DBC = (u32*) &D_802BFB80.arraySize8[0][D_80162DCC][3];
D_80162DBC[0] = -1;
D_80162DB8 = 0;
D_80162DDC = 0;
func_80091EE4();
if (D_80162DD4 == 0) {
func_80004FF8();
}
if (D_80162DD6 == 0) {
func_80004EF0();
}
}
}
}
}
void func_8000546C(void) {
u32 temp_a0;
u32 temp_a1;
UNUSED u16 unk;
u16 temp_v1;
s16 phi_v1;
s16 phi_v0 = 0;
if (D_80162DB0 >= 0x1000) {
gPlayerOne->type = PLAYER_CINEMATIC_MODE | PLAYER_START_SEQUENCE | PLAYER_CPU;
return;
}
temp_a0 = D_80162DB4[D_80162DB0];
temp_a1 = temp_a0 & 0xFF;
if (temp_a1 < 0x80U) {
phi_v1 = (s16) (temp_a1 & 0xFF);
} else {
phi_v1 = (s16) (temp_a1 | (~0xFF));
}
temp_a1 = (u32) (temp_a0 & 0xFF00) >> 8;
gControllerEight->rawStickX = phi_v1;
if (temp_a1 < 0x80U) {
phi_v1 = (s16) (temp_a1 & 0xFF);
} else {
phi_v1 = (s16) (temp_a1 | (~0xFF));
}
gControllerEight->rawStickY = phi_v1;
if (temp_a0 & 0x80000000) {
phi_v0 |= A_BUTTON;
}
if (temp_a0 & 0x40000000) {
phi_v0 |= B_BUTTON;
}
if (temp_a0 & 0x20000000) {
phi_v0 |= Z_TRIG;
}
if (temp_a0 & 0x10000000) {
phi_v0 |= R_TRIG;
}
temp_v1 = gControllerEight->buttonPressed & 0x1F0F;
gControllerEight->buttonPressed = (phi_v0 & (phi_v0 ^ D_80162DA8)) | temp_v1;
temp_v1 = gControllerEight->buttonDepressed & 0x1F0F;
gControllerEight->buttonDepressed = (D_80162DA8 & (phi_v0 ^ D_80162DA8)) | temp_v1;
D_80162DA8 = phi_v0;
gControllerEight->button = phi_v0;
if (D_80162DAC == 0) {
D_80162DB0++;
D_80162DAC = (s32) (D_80162DB4[D_80162DB0] & 0xFF0000);
} else {
D_80162DAC += 0xFFFF0000;
}
}
void func_8000561C(void) {
u32 temp_a0;
u32 temp_v0;
UNUSED u16 unk;
u16 temp_v1;
s16 phi_v1;
s16 phi_a2 = 0;
if (D_80162DA0 >= 0x1000) {
func_80005AE8(gPlayerThree);
return;
}
temp_a0 = D_80162DA4[D_80162DA0];
temp_v0 = temp_a0 & 0xFF;
if (temp_v0 < 0x80U) {
phi_v1 = (s16) (temp_v0 & 0xFF);
} else {
phi_v1 = (s16) (temp_v0 | (~0xFF));
}
temp_v0 = (u32) (temp_a0 & 0xFF00) >> 8;
gControllerSeven->rawStickX = phi_v1;
if (temp_v0 < 0x80U) {
phi_v1 = (s16) (temp_v0 & 0xFF);
} else {
phi_v1 = (s16) (temp_v0 | (~0xFF));
}
gControllerSeven->rawStickY = phi_v1;
if (temp_a0 & 0x80000000) {
phi_a2 = A_BUTTON;
}
if (temp_a0 & 0x40000000) {
phi_a2 |= B_BUTTON;
}
if (temp_a0 & 0x20000000) {
phi_a2 |= Z_TRIG;
}
if (temp_a0 & 0x10000000) {
phi_a2 |= R_TRIG;
}
temp_v1 = gControllerSeven->buttonPressed & 0x1F0F;
gControllerSeven->buttonPressed = (phi_a2 & (phi_a2 ^ D_80162D98)) | temp_v1;
temp_v1 = gControllerSeven->buttonDepressed & 0x1F0F;
gControllerSeven->buttonDepressed = (D_80162D98 & (phi_a2 ^ D_80162D98)) | temp_v1;
D_80162D98 = phi_a2;
gControllerSeven->button = phi_a2;
if (D_80162D9C == 0) {
D_80162DA0++;
D_80162D9C = (s32) (D_80162DA4[D_80162DA0] & 0xFF0000);
} else {
D_80162D9C += (s32) 0xFFFF0000;
}
}
void func_800057DC(void) {
u32 temp_a0;
u32 temp_v0;
UNUSED u16 unk;
u16 temp_v1;
s16 phi_v1;
s16 phi_a2 = 0;
if (D_80162D90 >= 0x1000) {
func_80005AE8(gPlayerTwo);
return;
}
temp_a0 = D_80162D94[D_80162D90];
temp_v0 = temp_a0 & 0xFF;
if (temp_v0 < 0x80U) {
phi_v1 = (s16) (temp_v0 & 0xFF);
} else {
phi_v1 = (s16) (temp_v0 | ~0xFF);
}
temp_v0 = (u32) (temp_a0 & 0xFF00) >> 8;
gControllerSix->rawStickX = phi_v1;
if (temp_v0 < 0x80U) {
phi_v1 = (s16) (temp_v0 & 0xFF);
} else {
phi_v1 = (s16) (temp_v0 | (~0xFF));
}
gControllerSix->rawStickY = phi_v1;
if (temp_a0 & 0x80000000) {
phi_a2 |= A_BUTTON;
}
if (temp_a0 & 0x40000000) {
phi_a2 |= B_BUTTON;
}
if (temp_a0 & 0x20000000) {
phi_a2 |= Z_TRIG;
}
if (temp_a0 & 0x10000000) {
phi_a2 |= R_TRIG;
}
temp_v1 = gControllerSix->buttonPressed & 0x1F0F;
gControllerSix->buttonPressed = (phi_a2 & (phi_a2 ^ D_80162D88)) | temp_v1;
temp_v1 = gControllerSix->buttonDepressed & 0x1F0F;
gControllerSix->buttonDepressed = (D_80162D88 & (phi_a2 ^ D_80162D88)) | temp_v1;
D_80162D88 = phi_a2;
gControllerSix->button = phi_a2;
if (D_80162D8C == 0) {
D_80162D90++;
D_80162D8C = (s32) (D_80162D94[D_80162D90] & 0xFF0000);
} else {
D_80162D8C += (s32) 0xFFFF0000;
}
}
void func_8000599C(void) {
s16 temp_a2;
u32 phi_a3;
u32 temp_v1;
u32 temp_v2;
u32 temp_v0;
u32 temp_t0;
u32 temp_a0_2;
if (((D_80162DB8 >= 0x1000) || ((gPlayerOne->unk_0CA & 2) != 0)) || ((gPlayerOne->unk_0CA & 8) != 0)) {
D_80162DF8 = 1;
return;
}
temp_v1 = gControllerOne->rawStickX;
temp_v1 &= 0xFF;
temp_v2 = gControllerOne->rawStickY;
temp_v2 = (temp_v2 & 0xFF) << 8;
temp_a2 = gControllerOne->button;
phi_a3 = 0;
if (temp_a2 & 0x8000) {
phi_a3 |= 0x80000000;
}
if (temp_a2 & 0x4000) {
phi_a3 |= 0x40000000;
}
if (temp_a2 & 0x2000) {
phi_a3 |= 0x20000000;
}
if (temp_a2 & 0x0010) {
phi_a3 |= 0x10000000;
}
phi_a3 |= temp_v1;
phi_a3 |= temp_v2;
temp_t0 = D_80162DBC[D_80162DB8];
temp_a0_2 = temp_t0 & 0xFF00FFFF;
if ((*D_80162DBC) == 0xFFFFFFFF) {
D_80162DBC[D_80162DB8] = phi_a3;
} else if (temp_a0_2 == phi_a3) {
temp_v0 = temp_t0 & 0xFF0000;
if (temp_v0 == 0xFF0000) {
D_80162DB8++;
D_80162DBC[D_80162DB8] = phi_a3;
} else {
temp_t0 += 0x10000;
D_80162DBC[D_80162DB8] = temp_t0;
}
} else {
D_80162DB8++;
D_80162DBC[D_80162DB8] = phi_a3;
}
}
// sets player to AI? (unconfirmed)
void func_80005AE8(Player* ply) {
if (((ply->type & PLAYER_INVISIBLE_OR_BOMB) != 0) && (ply != gPlayerOne)) {
ply->type = PLAYER_CINEMATIC_MODE | PLAYER_START_SEQUENCE | PLAYER_CPU;
}
}
void func_80005B18(void) {
if (gModeSelection == TIME_TRIALS) {
if ((gLapCountByPlayerId[0] == 3) && (D_80162DDC == 0) && (D_80162DF8 != 1)) {
if (D_80162DD4 == 1) {
D_80162DD0 = D_80162DCC;
func_800052A4();
D_80162DD4 = 0;
D_80162DDC = 1;
D_80162DE0 = gPlayerOne->characterId;
D_80162DE8 = gPlayerOne->characterId;
D_80162E00 = 0;
D_80162DFC = playerHUD[PLAYER_ONE].someTimer;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else if (gLapCountByPlayerId[1] != 3) {
D_80162DD0 = D_80162DCC;
func_800052A4();
D_80162DDC = 1;
D_80162DE0 = gPlayerOne->characterId;
D_80162DFC = playerHUD[PLAYER_ONE].someTimer;
D_80162E00 = 0;
D_80162DE8 = gPlayerOne->characterId;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else {
D_80162D80 = D_802BFB80.arraySize8[0][D_80162DC8][3].pixel_index_array;
D_80162D84 = D_80162D86;
D_80162DD0 = D_80162DCC;
D_80162DE8 = gPlayerOne->characterId;
D_80162DD8 = 0;
D_80162DD4 = 0;
D_80162DDC = 1;
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
}
} else {
if ((gLapCountByPlayerId[0] == 3) && (D_80162DDC == 0) && (D_80162DF8 == 1)) {
D_80162D80 = D_802BFB80.arraySize8[0][D_80162DC8][3].pixel_index_array;
D_80162D84 = D_80162D86;
D_80162DDC = 1;
}
if ((gPlayerOne->type & 0x800) == 0x800) {
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
} else {
D_80162DEC += 1;
if (D_80162DEC >= 0x65) {
D_80162DEC = 0x00000064;
}
if ((gModeSelection == TIME_TRIALS) && (gActiveScreenMode == SCREEN_MODE_1P)) {
if ((D_80162DD4 == 0) && (gLapCountByPlayerId[1] != 3)) {
func_800057DC();
}
if ((D_80162DD6 == 0) && (gLapCountByPlayerId[2] != 3)) {
func_8000561C();
}
if (!(gPlayerOne->type & 0x800)) {
func_8000599C();
}
}
}
}
}
}
void func_80005E6C(void) {
if ((gModeSelection == TIME_TRIALS) && (gModeSelection == TIME_TRIALS) && (gActiveScreenMode == SCREEN_MODE_1P)) {
if ((D_80162DD8 == 0) && (gLapCountByPlayerId[1] != 3)) {
func_800057DC(); // 3
}
if ((D_80162DD6 == 0) && (gLapCountByPlayerId[2] != 3)) {
func_8000561C(); // 2
}
if ((gPlayerOne->type & PLAYER_CINEMATIC_MODE) != PLAYER_CINEMATIC_MODE) {
func_8000546C(); // 1
return;
}
func_80005AE8(gPlayerTwo);
func_80005AE8(gPlayerThree);
}
}
void staff_ghosts_loop(void) {
if (D_8015F890 == 1) {
func_80005E6C();
return;
}
if (!D_80162DF0) {
func_80005B18();
return;
}
D_80162DF8 = 1;
}

View File

@ -206,7 +206,7 @@ void func_80072180(void) {
if (gModeSelection == TIME_TRIALS) {
if (((gPlayerOne->type & PLAYER_EXISTS) != 0) &&
((gPlayerOne->type & (PLAYER_INVISIBLE_OR_BOMB | PLAYER_CPU)) == 0)) {
D_80162DF8 = 1;
gPostTTReplayCannotSave = 1;
}
}
}

View File

@ -385,7 +385,7 @@ extern u16* gHudLapTextures[];
extern u16* gPortraitTLUTs[];
extern u8* gPortraitTextures[];
extern s32 D_80162DF8;
extern s32 gPostTTReplayCannotSave;
extern s16 D_8016347C;
extern s32 D_80165594;
extern s32 D_80165598;