From 44c71a7978e6abae95399db732a40186a165e2ee Mon Sep 17 00:00:00 2001 From: Jed Grabman Date: Sat, 18 Jul 2026 18:21:51 -0400 Subject: [PATCH] Document versus rankings and finish info (#766) * Document versus rankings and finish info * Document race states * variable renames --- include/defines.h | 11 ++ src/code_800029B0.c | 8 +- src/code_800029B0.h | 6 +- src/cpu_vehicles_camera_path.c | 128 +++++++++++----------- src/cpu_vehicles_camera_path.h | 6 +- src/main.c | 12 +-- src/main.h | 8 +- src/menu_items.c | 25 ++--- src/player_controller.c | 4 +- src/racing/race_logic.c | 188 +++++++++++++++++---------------- src/racing/race_logic.h | 2 +- src/render_objects.c | 2 +- 12 files changed, 214 insertions(+), 186 deletions(-) diff --git a/include/defines.h b/include/defines.h index 331dbcc51..6bb445909 100644 --- a/include/defines.h +++ b/include/defines.h @@ -529,3 +529,14 @@ player spins. Something with avoding rollover of aniamation frame data? */ #define FACING_Y_AXIS 0x4000 #define FACING_X_AXIS 0x8000 #define FACING_Z_AXIS 0x2000 + +// race states + +#define RACE_NONE 0 +#define RACE_PREP 1 +#define RACE_STAGING 2 +#define RACE_IN_PROGRESS 3 +#define RACE_HUMAN_FINISHED 4 // At least 1 human has finished +#define RACE_DONE 5 +#define RACE_QUITTING 6 +#define RACE_QUADRANT_RESULTS 7 // The results screen in GP and Time trials mode diff --git a/src/code_800029B0.c b/src/code_800029B0.c index a351de8ec..a4651347f 100644 --- a/src/code_800029B0.c +++ b/src/code_800029B0.c @@ -130,9 +130,9 @@ u16 D_8015F894; // Indexed by Player ID. Track time in seconds since player has last crossed the finish line f32 gTimePlayerLastTouchedFinishLine[8]; -u8* gNmiUnknown1; -u8* gNmiUnknown2; -u8* gNmiUnknown3; +u8* nmi_gVersusResults2P; +u8* nmi_gVersusResults3P; +u8* nmi_gVersusResults4P; u8* gNmiUnknown4; u8* gNmiUnknown5; u8* gNmiUnknown6; @@ -204,7 +204,7 @@ void setup_race(void) { func_80005310(); func_8003D080(); init_hud(); - D_800DC510 = 0; + gRaceState = RACE_NONE; gNumSpawnedShells = 0; D_800DC5B8 = 0; D_80152308 = 0; diff --git a/src/code_800029B0.h b/src/code_800029B0.h index 0b7335383..3b98ab740 100644 --- a/src/code_800029B0.h +++ b/src/code_800029B0.h @@ -118,9 +118,9 @@ extern u16 D_8015F892; extern u16 D_8015F894; extern f32 gTimePlayerLastTouchedFinishLine[]; -extern u8* gNmiUnknown1; -extern u8* gNmiUnknown2; -extern u8* gNmiUnknown3; +extern u8* nmi_gVersusResults2P; +extern u8* nmi_gVersusResults3P; +extern u8* nmi_gVersusResults4P; extern u8* gNmiUnknown4; extern u8* gNmiUnknown5; extern u8* gNmiUnknown6; diff --git a/src/cpu_vehicles_camera_path.c b/src/cpu_vehicles_camera_path.c index 5352dfd28..05d312e38 100644 --- a/src/cpu_vehicles_camera_path.c +++ b/src/cpu_vehicles_camera_path.c @@ -88,7 +88,7 @@ u16 D_801631F8[10]; f32 gCurrentCpuTargetSpeed; f32 gPreviousCpuTargetSpeed[10]; s32 D_80163238; -u16 D_80163240[12]; +u16 gCrossedFinishLine[12]; u16 gWrongDirectionCounter[12]; u16 gIsPlayerWrongDirection[12]; s32 gPreviousLapProgressScore[10]; @@ -154,7 +154,7 @@ s16 D_80164358; s16 D_8016435A; s16 D_8016435C; s16 gGPCurrentRacePlayerIdByRank[12]; // D_80164360 -s16 D_80164378[12]; +s16 gPrevPlayerIdByRank[12]; s32 gLapCountByPlayerId[10]; // D_80164390 s32 gGPCurrentRaceRankByPlayerId[10]; // D_801643B8 s32 gPreviousGPCurrentRaceRankByPlayerId[10]; @@ -707,15 +707,15 @@ void detect_wrong_player_direction(s32 playerId, Player* player) { } void set_places(void) { - s32 temp_s2; - f32 temp_f0; - s32 rankPlayer[8]; - s32 a_really_cool_variable_name; + s32 playerIdToSwap; + f32 courseCompletionToSwap; + s32 playerIdByRank[8]; + s32 prevPlayerId; UNUSED s32 pad; s32 numPlayer; + s32 rank; s32 playerId; - s32 temp_a0; - s32 var_t1_3; + s32 rankHigh; switch (gModeSelection) { case BATTLE: @@ -731,69 +731,70 @@ void set_places(void) { } if (D_8016348C == 0) { - for (playerId = 0; playerId < numPlayer; playerId++) { - temp_a0 = gGPCurrentRacePlayerIdByRank[playerId]; - rankPlayer[playerId] = temp_a0; - gCourseCompletionPercentByRank[playerId] = gCourseCompletionPercentByPlayerId[temp_a0]; + for (rank = 0; rank < numPlayer; rank++) { + playerId = gGPCurrentRacePlayerIdByRank[rank]; + playerIdByRank[rank] = playerId; + gCourseCompletionPercentByRank[rank] = gCourseCompletionPercentByPlayerId[playerId]; } } else { - for (playerId = 0; playerId < numPlayer; playerId++) { - temp_a0 = gGPCurrentRacePlayerIdByRank[playerId]; - rankPlayer[playerId] = temp_a0; - gCourseCompletionPercentByRank[playerId] = -gTimePlayerLastTouchedFinishLine[temp_a0]; + // used in grand Prix mode once all players cross finish line to determine finish order + for (rank = 0; rank < numPlayer; rank++) { + playerId = gGPCurrentRacePlayerIdByRank[rank]; + playerIdByRank[rank] = playerId; + gCourseCompletionPercentByRank[rank] = -gTimePlayerLastTouchedFinishLine[playerId]; } } - for (playerId = 0; playerId < numPlayer - 1; playerId++) { - if ((gPlayers[gGPCurrentRacePlayerIdByRank[playerId]].type & PLAYER_CINEMATIC_MODE)) { + for (rank = 0; rank < numPlayer - 1; rank++) { + if ((gPlayers[gGPCurrentRacePlayerIdByRank[rank]].type & PLAYER_CINEMATIC_MODE)) { continue; } - for (var_t1_3 = playerId + 1; var_t1_3 < numPlayer; var_t1_3++) { - if (gCourseCompletionPercentByRank[playerId] < gCourseCompletionPercentByRank[var_t1_3]) { - if (!(gPlayers[gGPCurrentRacePlayerIdByRank[var_t1_3]].type & 0x800)) { - temp_s2 = rankPlayer[playerId]; - rankPlayer[playerId] = rankPlayer[var_t1_3]; - rankPlayer[var_t1_3] = temp_s2; - temp_f0 = gCourseCompletionPercentByRank[playerId]; - gCourseCompletionPercentByRank[playerId] = gCourseCompletionPercentByRank[var_t1_3]; - gCourseCompletionPercentByRank[var_t1_3] = temp_f0; + for (rankHigh = rank + 1; rankHigh < numPlayer; rankHigh++) { + if (gCourseCompletionPercentByRank[rank] < gCourseCompletionPercentByRank[rankHigh]) { + if (!(gPlayers[gGPCurrentRacePlayerIdByRank[rankHigh]].type & PLAYER_CINEMATIC_MODE)) { + playerIdToSwap = playerIdByRank[rank]; + playerIdByRank[rank] = playerIdByRank[rankHigh]; + playerIdByRank[rankHigh] = playerIdToSwap; + courseCompletionToSwap = gCourseCompletionPercentByRank[rank]; + gCourseCompletionPercentByRank[rank] = gCourseCompletionPercentByRank[rankHigh]; + gCourseCompletionPercentByRank[rankHigh] = courseCompletionToSwap; } } } } - for (playerId = 0; playerId < NUM_PLAYERS; playerId++) { - gPreviousGPCurrentRaceRankByPlayerId[playerId] = gGPCurrentRaceRankByPlayerId[playerId]; + // actually player_id, not rank + for (rank = 0; rank < NUM_PLAYERS; rank++) { + gPreviousGPCurrentRaceRankByPlayerId[rank] = gGPCurrentRaceRankByPlayerId[rank]; } - for (playerId = 0; playerId < numPlayer; playerId++) { - gGPCurrentRacePlayerIdByRank[playerId] = rankPlayer[playerId]; - gGPCurrentRaceRankByPlayerId[rankPlayer[playerId]] = playerId; + for (rank = 0; rank < numPlayer; rank++) { + gGPCurrentRacePlayerIdByRank[rank] = playerIdByRank[rank]; + gGPCurrentRaceRankByPlayerId[playerIdByRank[rank]] = rank; + } + for (rank = 0; rank < numPlayer; rank++) { + prevPlayerId = gPrevPlayerIdByRank[rank]; + playerIdByRank[rank] = prevPlayerId; + gCourseCompletionPercentByRank[rank] = gCourseCompletionPercentByPlayerId[prevPlayerId]; } - for (playerId = 0; playerId < numPlayer; playerId++) { - a_really_cool_variable_name = D_80164378[playerId]; - rankPlayer[playerId] = a_really_cool_variable_name; - gCourseCompletionPercentByRank[playerId] = gCourseCompletionPercentByPlayerId[a_really_cool_variable_name]; - } - - for (playerId = 0; playerId < numPlayer - 1; playerId++) { - for (var_t1_3 = playerId + 1; var_t1_3 < numPlayer; var_t1_3++) { - if (gCourseCompletionPercentByRank[playerId] < gCourseCompletionPercentByRank[var_t1_3]) { - temp_s2 = rankPlayer[playerId]; - rankPlayer[playerId] = rankPlayer[var_t1_3]; - rankPlayer[var_t1_3] = temp_s2; - temp_f0 = gCourseCompletionPercentByRank[playerId]; - gCourseCompletionPercentByRank[playerId] = gCourseCompletionPercentByRank[var_t1_3]; - gCourseCompletionPercentByRank[var_t1_3] = temp_f0; + for (rank = 0; rank < numPlayer - 1; rank++) { + for (rankHigh = rank + 1; rankHigh < numPlayer; rankHigh++) { + if (gCourseCompletionPercentByRank[rank] < gCourseCompletionPercentByRank[rankHigh]) { + playerIdToSwap = playerIdByRank[rank]; + playerIdByRank[rank] = playerIdByRank[rankHigh]; + playerIdByRank[rankHigh] = playerIdToSwap; + courseCompletionToSwap = gCourseCompletionPercentByRank[rank]; + gCourseCompletionPercentByRank[rank] = gCourseCompletionPercentByRank[rankHigh]; + gCourseCompletionPercentByRank[rankHigh] = courseCompletionToSwap; } } } - for (playerId = 0; playerId < numPlayer; playerId++) { - gGPCurrentRaceRankByPlayerIdDup[rankPlayer[playerId]] = playerId; - D_80164378[playerId] = rankPlayer[playerId]; + for (rank = 0; rank < numPlayer; rank++) { + gGPCurrentRaceRankByPlayerIdDup[playerIdByRank[rank]] = rank; + gPrevPlayerIdByRank[rank] = playerIdByRank[rank]; } } @@ -992,7 +993,7 @@ bool func_800088D8(s32 playerId, s16 arg1, s16 arg2) { if (arg2 == 0) { if (gDemoMode == 1) { STEMP_V0 = gNumPathPointsTraversed[playerId]; - STEMP_V1 = gNumPathPointsTraversed[D_80164378[7]]; + STEMP_V1 = gNumPathPointsTraversed[gPrevPlayerIdByRank[7]]; progress = STEMP_V0 - STEMP_V1; if (progress < 0) { progress = -progress; @@ -1018,7 +1019,7 @@ bool func_800088D8(s32 playerId, s16 arg1, s16 arg2) { rank = gGPCurrentRaceRankByPlayerId[gBestRankedHumanPlayer]; if (((((gPathCountByPathIndex[0] * 2) / 3)) < progress) && ((rank) >= 6)) { STEMP_V0 = gNumPathPointsTraversed[playerId]; - STEMP_V1 = temp = gNumPathPointsTraversed[D_80164378[rank - 1]]; + STEMP_V1 = temp = gNumPathPointsTraversed[gPrevPlayerIdByRank[rank - 1]]; progress = STEMP_V0 - STEMP_V1; } if (progress < 0) { @@ -1200,11 +1201,12 @@ void update_cpu_path_completion(s32 playerId, Player* player) { /** * Helps calculate time since player last touched finishline. + * Assumes constant z-speed and subtracts portion of frame where the finish line was already crossed **/ -f32 func_80009258(UNUSED s32 playerId, f32 arg1, f32 arg2) { - f32 temp_f2 = gPathStartZ - arg2; - f32 temp_f12 = arg1 - gPathStartZ; - return gCourseTimer - ((COURSE_TIMER_ITER_f * temp_f2) / (temp_f2 + temp_f12)); +f32 time_crossed_finish_line(UNUSED s32 playerId, f32 previousPlayerZ, f32 playerZ) { + f32 z_change_after_cross = gPathStartZ - playerZ; + f32 z_change_before_cross = previousPlayerZ - gPathStartZ; + return gCourseTimer - ((COURSE_TIMER_ITER_f * z_change_after_cross) / (z_change_after_cross + z_change_before_cross)); } void update_player_path_completion(s32 playerId, Player* player) { @@ -1220,7 +1222,7 @@ void update_player_path_completion(s32 playerId, Player* player) { playerZ = player->pos[2]; previousPlayerZ = gPreviousPlayerZ[playerId]; gIsPlayerNewPathPoint = false; - D_80163240[playerId] = 0; + gCrossedFinishLine[playerId] = 0; sSomeNearestPathPoint = update_player_path(playerX, playerY, playerZ, gNearestPathPointByPlayerId[playerId], player, playerId, gPlayerPathIndex); gCurrentNearestPathPoint = sSomeNearestPathPoint; @@ -1276,12 +1278,12 @@ void update_player_path_completion(s32 playerId, Player* player) { // clang-format on } } - D_80163240[playerId] = 1; + gCrossedFinishLine[playerId] = 1; update_player_completion(playerId); reset_cpu_behaviour(playerId); cpu_ItemStrategy[playerId].numItemUse = 0; if ((D_8016348C == 0) && !(player->type & PLAYER_CINEMATIC_MODE)) { - gTimePlayerLastTouchedFinishLine[playerId] = func_80009258(playerId, previousPlayerZ, playerZ); + gTimePlayerLastTouchedFinishLine[playerId] = time_crossed_finish_line(playerId, previousPlayerZ, playerZ); } } } @@ -1461,7 +1463,11 @@ void update_player(s32 playerId) { player->kartProps &= ~BACK_UP; } update_player_path_completion(playerId, player); - if ((gCurrentCourseId != COURSE_AWARD_CEREMONY) && ((D_80163240[playerId] == 1) || (playerId == 0))) { + + // Because this takes place in a per-player loop and runs primarily when player 1 crosses the finish line, + // player 1 effectively has a 1 frame advantage when determining places. However, it runs for everyone when + // they cross the finish line, resulting in lower port numbers always winning ties in versus mode. + if ((gCurrentCourseId != COURSE_AWARD_CEREMONY) && ((gCrossedFinishLine[playerId] == 1) || (playerId == 0))) { set_places(); } if (player->type & PLAYER_CPU) { @@ -2100,7 +2106,7 @@ void init_players(void) { } temp_v0_3 = gGPCurrentRaceRankByPlayerId[i]; gGPCurrentRacePlayerIdByRank[temp_v0_3] = (s16) i; - D_80164378[temp_v0_3] = (s16) i; + gPrevPlayerIdByRank[temp_v0_3] = (s16) i; gGPCurrentRaceRankByPlayerIdDup[i] = temp_v0_3; gWrongDirectionCounter[i] = 0; gIsPlayerWrongDirection[i] = 0; diff --git a/src/cpu_vehicles_camera_path.h b/src/cpu_vehicles_camera_path.h index ae46283e8..858b44486 100644 --- a/src/cpu_vehicles_camera_path.h +++ b/src/cpu_vehicles_camera_path.h @@ -115,7 +115,7 @@ void update_player_completion(s32); void yoshi_valley_cpu_path(s32); void update_cpu_path_completion(s32, Player*); -f32 func_80009258(s32, f32, f32); +f32 time_crossed_finish_line(s32, f32, f32); void update_player_path_completion(s32, Player*); void update_vehicles(void); void play_cpu_sound_effect(s32, Player*); @@ -323,7 +323,7 @@ extern u16 D_801631F8[]; extern f32 gCurrentCpuTargetSpeed; extern f32 gPreviousCpuTargetSpeed[]; extern s32 D_80163238; -extern u16 D_80163240[]; +extern u16 gCrossedFinishLine[]; extern u16 gWrongDirectionCounter[]; extern u16 gIsPlayerWrongDirection[]; extern s32 gPreviousLapProgressScore[]; @@ -383,7 +383,7 @@ extern s16 D_80164358; extern s16 D_8016435A; extern s16 D_8016435C; extern s16 gGPCurrentRacePlayerIdByRank[]; // D_80164360 -extern s16 D_80164378[]; +extern s16 gPrevPlayerIdByRank[]; // D_80164378 extern s32 gLapCountByPlayerId[]; // D_80164390 extern s32 gGPCurrentRaceRankByPlayerId[]; // D_801643B8 extern s32 gPreviousGPCurrentRaceRankByPlayerId[]; diff --git a/src/main.c b/src/main.c index b97fa8699..0826f42ac 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ #ifndef GCC -#define D_800DC510_AS_U16 +#define gRaceState_AS_U16 #endif #include #include @@ -162,8 +162,8 @@ OSMesg gPIMesgBuf[32]; OSMesgQueue gPIMesgQueue; s32 gGamestate = 0xFFFF; -// D_800DC510 is externed as an s32 in other files. D_800DC514 is only used in main.c, likely a developer mistake. -u16 D_800DC510 = 0; +// gRaceState is externed as an s32 in other files. D_800DC514 is only used in main.c, likely a developer mistake. +u16 gRaceState = RACE_NONE; u16 D_800DC514 = 0; u16 creditsRenderMode = 0; // Renders the whole track. Displays red if used in normal race mode. u16 gDemoMode = DEMO_MODE_INACTIVE; @@ -1179,10 +1179,10 @@ void thread5_game_loop(UNUSED void* arg) { set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) OS_EVENT_SW2); // These variables track stats such as player wins. // In the event of a console reset, it remembers them. - gNmiUnknown1 = &pAppNmiBuffer[0]; // 2 u8's, tracks number of times player 1/2 won a VS race - gNmiUnknown2 = + nmi_gVersusResults2P = &pAppNmiBuffer[0]; // 2 u8's, tracks number of times player 1/2 won a VS race + nmi_gVersusResults3P = &pAppNmiBuffer[2]; // 9 u8's, 3x3, tracks number of times player 1/2/3 has placed in 1st/2nd/3rd in a VS race - gNmiUnknown3 = &pAppNmiBuffer[11]; // 12 u8's, 4x3, tracks number of times player 1/2/3/4 has placed in 1st/2nd/3rd + nmi_gVersusResults4P = &pAppNmiBuffer[11]; // 12 u8's, 4x3, tracks number of times player 1/2/3/4 has placed in 1st/2nd/3rd // in a VS race gNmiUnknown4 = &pAppNmiBuffer[23]; // 2 u8's, tracking number of Battle mode wins by player 1/2 gNmiUnknown5 = &pAppNmiBuffer[25]; // 3 u8's, tracking number of Battle mode wins by player 1/2/3 diff --git a/src/main.h b/src/main.h index 6d7ac7aac..3398ce885 100644 --- a/src/main.h +++ b/src/main.h @@ -198,13 +198,13 @@ extern OSMesg gPIMesgBuf[]; extern OSMesgQueue gPIMesgQueue; void race_logic_loop(void); extern s32 gGamestate; -#ifndef D_800DC510_AS_U16 +#ifndef gRaceState_AS_U16 // Prevent overlapping writes in gcc -// Whether D_800DC510 was intended to be a separate variable in main.c from the rest of the game is unknown +// Whether gRaceState was intended to be a separate variable in main.c from the rest of the game is unknown #ifdef GCC -extern u16 D_800DC510; +extern u16 gRaceState; #else -extern s32 D_800DC510; +extern s32 gRaceState; #endif #endif diff --git a/src/menu_items.c b/src/menu_items.c index c8e9e7a4b..ae7c7335b 100644 --- a/src/menu_items.c +++ b/src/menu_items.c @@ -210,6 +210,7 @@ Unk_D_800E70A0 D_800E7300[] = { { 0x18, 0x23, 0x00, 0x00 }, { 0x5d, 0x23, 0x00, 0x00 }, { 0xa2, 0x23, 0x00, 0x00 }, { 0xe7, 0x23, 0x00, 0x00 }, }; +// Versus menu coordinates Unk_D_800E70A0 D_800E7360[] = { { 0x61, 0xa7, 0x00, 0x00 }, { 0x61, 0xb6, 0x00, 0x00 }, @@ -8118,7 +8119,7 @@ void func_800A638C(MenuItem* arg0) { if (arg0->state >= 10) { for (var_s1 = 0; var_s1 < 4; var_s1++) { text_rainbow_effect(arg0->state - 0xA, var_s1, TEXT_GREEN); - print_text_mode_1(0x00000069, 0xAE + (0xF * var_s1), gTextPauseButton[var_s1 + 1], 0, 0.8f, 0.8f); + print_text_mode_1(0x00000069, 0xAE + (0xF * var_s1), gTextPauseButton[var_s1 + 1], 0, 0.8f, 0.8f); // Where Retry, course change, etc printed } func_800A66A8(arg0, &D_800E7360[arg0->state - 10]); } @@ -8184,7 +8185,7 @@ void func_800A69C8(UNUSED MenuItem* arg0) { if (gGPCurrentRaceRankByPlayerId[var_s0] != 0) { var_v1 = 1; } - var_s4 = &gNmiUnknown1[var_s0]; + var_s4 = &nmi_gVersusResults2P[var_s0]; break; case BATTLE: if (var_s0 != gPlayerWinningIndex) { @@ -8214,7 +8215,7 @@ void func_800A6BEC(UNUSED MenuItem* arg0) { for (var_s0 = 0; var_s0 < gPlayerCount; var_s0++) { switch (gModeSelection) { /* irregular */ case VERSUS: - func_800A6E94(3, var_s0, gNmiUnknown2); + func_800A6E94(3, var_s0, nmi_gVersusResults3P); break; case BATTLE: func_800A6D94(3, var_s0, gNmiUnknown5); @@ -8229,7 +8230,7 @@ void func_800A6CC0(UNUSED MenuItem* arg0) { for (var_s0 = 0; var_s0 < gPlayerCount; var_s0++) { switch (gModeSelection) { /* irregular */ case VERSUS: - func_800A6E94(4, var_s0, gNmiUnknown3); + func_800A6E94(4, var_s0, nmi_gVersusResults4P); break; case BATTLE: func_800A6D94(4, var_s0, gNmiUnknown6); @@ -8264,27 +8265,27 @@ void func_800A6D94(s32 arg0, s32 arg1, u8* arg2) { } // The s/n/r/ー are not ASCII characters, they're EUC-JP characters -void func_800A6E94(s32 arg0, s32 arg1, u8* arg2) { +void func_800A6E94(s32 playerCount, s32 playerId, u8* placeAry) { UNUSED s32 stackPadding0; u8* temp_v0; Unk_D_800E70A0* temp_s0; char sp40[3]; s32 rank; // Everything about this variable is bizarre - s32 sp38 = -1; + s32 rankIdx = -1; - temp_s0 = &D_800E7300[((arg0 - 2) * 4) + arg1]; - rank = gGPCurrentRaceRankByPlayerId[arg1]; - if (rank == ++sp38) { + temp_s0 = &D_800E7300[((playerCount - 2) * 4) + playerId]; + rank = gGPCurrentRaceRankByPlayerId[playerId]; + if (rank == ++rankIdx) { set_text_color(gGlobalTimer % 3); } else { set_text_color(TEXT_YELLOW); } text_draw(temp_s0->column + 4, temp_s0->row + 0x5A, "1 s ー", 0, 0.8f, 0.8f); - temp_v0 = arg2 + (arg1 * 3); + temp_v0 = placeAry + (playerId * 3); convert_number_to_ascii(temp_v0[0], sp40); text_draw(temp_s0->column + 0x2D, temp_s0->row + 0x5A, sp40, 0, 0.8f, 0.8f); - if (rank == ++sp38) { + if (rank == ++rankIdx) { set_text_color(gGlobalTimer % 3); } else { set_text_color(TEXT_BLUE); @@ -8292,7 +8293,7 @@ void func_800A6E94(s32 arg0, s32 arg1, u8* arg2) { text_draw(temp_s0->column + 4, temp_s0->row + 0x69, "2 n ー", 0, 0.8f, 0.8f); convert_number_to_ascii(temp_v0[1], sp40); text_draw(temp_s0->column + 0x2D, temp_s0->row + 0x69, sp40, 0, 0.8f, 0.8f); - if (++sp38 == rank) { + if (++rankIdx == rank) { set_text_color(gGlobalTimer % 3); } else { set_text_color(TEXT_RED); diff --git a/src/player_controller.c b/src/player_controller.c index 18cfd9f4d..5573a68bc 100644 --- a/src/player_controller.c +++ b/src/player_controller.c @@ -591,7 +591,7 @@ void func_80028C44(Player* player, Camera* camera, s8 playerId, s8 screenId) { } void func_80028D3C(Player* player, Camera* camera, s8 playerId, s8 screenId) { - if ((((player->type & PLAYER_START_SEQUENCE) == 0) && (D_800DC510 != 5)) || (player->lakituProps & 2) != 0 || + if ((((player->type & PLAYER_START_SEQUENCE) == 0) && (gRaceState != RACE_DONE)) || (player->lakituProps & 2) != 0 || (player->lakituProps & LAKITU_SCENE) != 0 || (player->effects & (LIGHTNING_EFFECT | EXPLOSION_CRASH_EFFECT | HIT_BY_STAR_EFFECT | SQUISH_EFFECT | POST_SQUISH_EFFECT | TERRAIN_TUMBLE_EFFECT | 0xC00 | 0xC0)) != 0) { @@ -2039,7 +2039,7 @@ void apply_effect(Player* player, s8 playerIndex, s8 arg2) { func_8008D8B4(player, playerIndex); player_decelerate_alternative(player, 10.0f); } - if (D_800DC510 != 5) { + if (gRaceState != RACE_DONE) { if (player->triggers & LOSE_BATTLE_EFFECT) { func_8008FC64(player, playerIndex); } diff --git a/src/racing/race_logic.c b/src/racing/race_logic.c index c209d2e9a..841ac01e9 100644 --- a/src/racing/race_logic.c +++ b/src/racing/race_logic.c @@ -81,7 +81,7 @@ void func_8028E028(void) { break; } func_800CA118((u8) gPlayerWinningIndex); - D_800DC510 = 5; + gRaceState = RACE_DONE; gDemoTimer = 10; } @@ -225,7 +225,7 @@ void func_8028E438(void) { func_80019DF4(); } else { func_80092564(); - D_800DC510 = 7; + gRaceState = RACE_QUADRANT_RESULTS; } } break; @@ -399,7 +399,7 @@ void func_8028E678(void) { case 4: gIsInQuitToMenuTransition = 1; gQuitToMenuTransitionCounter = 5; - D_800DC510 = 7; + gRaceState = RACE_QUADRANT_RESULTS; func_8028E3A0(); break; } @@ -407,7 +407,7 @@ void func_8028E678(void) { UNUSED void func_8028EC38(s32 arg0) { gGotoMode = arg0; - D_800DC510 = 6; + gRaceState = RACE_QUITTING; func_800CA330(25); func_800CA388(25); D_800DC5B4 = 1; @@ -497,8 +497,8 @@ void start_race(void) { play_music_for_current_track(gCurrentCourseId); } - if (D_800DC510 == 2) { - D_800DC510 = 3; + if (gRaceState == RACE_STAGING) { + gRaceState = RACE_IN_PROGRESS; } for (i = 0; i < NUM_PLAYERS; i++) { @@ -522,38 +522,38 @@ f32 func_8028EE8C(s32 arg0) { return gCourseTimer - ((COURSE_TIMER_ITER_f * temp_f14) / (temp_f14 + temp_f16)); } -void func_8028EEF0(s32 i) { +void add_cinematic_mode(s32 i) { gPlayers[i].type |= PLAYER_CINEMATIC_MODE; } void func_8028EF28(void) { s16 currentPosition; - s32 i; + s32 playerId; - for (i = 0; i < NUM_PLAYERS; i++) { - Player* player = &gPlayers[i]; + for (playerId = 0; playerId < NUM_PLAYERS; playerId++) { + Player* player = &gPlayers[playerId]; - if ((gPlayers[i].type & PLAYER_EXISTS) == 0) { + if ((player->type & PLAYER_EXISTS) == 0) { continue; } - if (gLapCountByPlayerId[i] < gPlayers[i].lapCount) { - gPlayers[i].lapCount--; - } else if (gLapCountByPlayerId[i] > gPlayers[i].lapCount) { - gPlayers[i].lapCount++; + if (gLapCountByPlayerId[playerId] < player->lapCount) { + player->lapCount--; + } else if (gLapCountByPlayerId[playerId] > player->lapCount) { + player->lapCount++; + if ((player->type & PLAYER_HUMAN) != 0) { + // When player finishes the race + if (player->lapCount == 3) { + add_cinematic_mode(playerId); - if ((gPlayers[i].type & PLAYER_HUMAN) != 0) { - if (gPlayers[i].lapCount == 3) { - func_8028EEF0(i); - - currentPosition = gPlayers[i].currentRank; - gPlayers[i].type |= PLAYER_CPU; + currentPosition = player->currentRank; + player->type |= PLAYER_CPU; if (currentPosition < 4) { D_80150120 = 1; } - func_800CA118((u8) i); + func_800CA118((u8) playerId); if ((D_802BA032 & PLAYER_EXISTS) == 0) { D_802BA032 |= PLAYER_EXISTS; } @@ -561,8 +561,8 @@ void func_8028EF28(void) { if (gModeSelection == GRAND_PRIX && gPlayerCountSelection1 == 2 && D_802BA048 == 0) { D_802BA048 = 1; } - if ((gPlayers[i].type & PLAYER_INVISIBLE_OR_BOMB) == 0) { - D_800DC510 = 4; + if ((player->type & PLAYER_INVISIBLE_OR_BOMB) == 0) { + gRaceState = RACE_HUMAN_FINISHED; } if (gModeSelection == TIME_TRIALS) { func_80005AE8(player); @@ -571,70 +571,80 @@ void func_8028EF28(void) { if (gModeSelection == VERSUS) { gDemoTimer = 180; if (currentPosition == 0) { - gPlayerWinningIndex = i; + gPlayerWinningIndex = playerId; } switch (gPlayerCountSelection1) { case 2: if (currentPosition == 0) { - *(gNmiUnknown1 + i) += 1; + *(nmi_gVersusResults2P + playerId) += 1; } - if (*(gNmiUnknown1 + i) > 99) { - *(gNmiUnknown1 + i) = 99; + if (*(nmi_gVersusResults2P + playerId) > 99) { + *(nmi_gVersusResults2P + playerId) = 99; } - D_800DC510 = 5; - i = gPlayerPositionLUT[1]; - gPlayers[i].triggers |= SPINOUT_TRIGGER; - gPlayers[i].type |= PLAYER_CPU; - func_800CA118((u8) i); + gRaceState = RACE_DONE; + playerId = gPlayerPositionLUT[1]; + gPlayers[playerId].triggers |= SPINOUT_TRIGGER; + gPlayers[playerId].type |= PLAYER_CPU; + func_800CA118((u8) playerId); break; case 3: if (currentPosition < 3) { - *(gNmiUnknown2 + i * 3 + currentPosition) += 1; + *(nmi_gVersusResults3P + playerId * 3 + currentPosition) += 1; } - if (*(gNmiUnknown2 + i * 3 + currentPosition) > 99) { - *(gNmiUnknown2 + i * 3 + currentPosition) = 99; + if (*(nmi_gVersusResults3P + playerId * 3 + currentPosition) > 99) { + *(nmi_gVersusResults3P + playerId * 3 + currentPosition) = 99; } + /* Because the last player may not finish, their score must be updated when the 2nd + to last racer finishes. */ if (currentPosition == 1) { - D_800DC510 = 5; - i = gPlayerPositionLUT[2]; - *(gNmiUnknown2 + i * 3 + 2) += 1; - if (*(gNmiUnknown2 + i * 3 + 2) > 99) { - *(gNmiUnknown2 + i * 3 + 2) = 99; + gRaceState = RACE_DONE; // triggers results screen + + /* This messes with the loop index by setting it to the index of the last player. + But, because versus always gives the player with the lower slot/port number + the advantage if 2 players finish at the same time, it can only skip finished + players who do not need more processing. It can run the same index twice, but + any player who finished this frame already had their lap count updated, so + nothing will happen */ + playerId = gPlayerPositionLUT[2]; + *(nmi_gVersusResults3P + playerId * 3 + 2) += 1; + if (*(nmi_gVersusResults3P + playerId * 3 + 2) > 99) { + *(nmi_gVersusResults3P + playerId * 3 + 2) = 99; } - gPlayers[i].triggers |= SPINOUT_TRIGGER; - gPlayers[i].type |= PLAYER_CPU; - func_800CA118((u8) i); + gPlayers[playerId].triggers |= SPINOUT_TRIGGER; + gPlayers[playerId].type |= PLAYER_CPU; + func_800CA118((u8) playerId); } break; case 4: if (currentPosition < 3) { - *(gNmiUnknown3 + i * 3 + currentPosition) += 1; + *(nmi_gVersusResults4P + playerId * 3 + currentPosition) += 1; } - if (*(gNmiUnknown3 + i * 3 + currentPosition) > 99) { - *(gNmiUnknown3 + i * 3 + currentPosition) = 99; + if (*(nmi_gVersusResults4P + playerId * 3 + currentPosition) > 99) { + *(nmi_gVersusResults4P + playerId * 3 + currentPosition) = 99; } + // if second to last, race is over if (currentPosition == 2) { - D_800DC510 = 5; - i = gPlayerPositionLUT[3]; - gPlayers[i].triggers |= SPINOUT_TRIGGER; - gPlayers[i].type |= PLAYER_CPU; - func_800CA118((u8) i); + gRaceState = RACE_DONE; + playerId = gPlayerPositionLUT[3]; + gPlayers[playerId].triggers |= SPINOUT_TRIGGER; + gPlayers[playerId].type |= PLAYER_CPU; + func_800CA118((u8) playerId); } break; } } - } else if (gPlayers[i].lapCount == 2) { - if ((gPlayers[i].type & 0x100) != 0) { + } else if (player->lapCount == 2) { + if ((player->type & 0x100) != 0) { return; } if ((D_802BA032 & 0x4000) == 0) { D_802BA032 |= 0x4000; - func_800CA49C((u8) i); + func_800CA49C((u8) playerId); } } - } else if (gPlayers[i].lapCount == 3) { - func_8028EEF0(i); + } else if (player->lapCount == 3) { + add_cinematic_mode(playerId); if (gModeSelection == TIME_TRIALS) { func_80005AE8(player); } @@ -652,15 +662,15 @@ void func_8028F3E8(void) { // func_8028F3F0 void update_race_position_data(void) { - s16 i; + s16 playerId; s16 position; - for (i = 0; i < NUM_PLAYERS; i++) { - if (((gPlayers[i].type & PLAYER_EXISTS) != 0) && ((gPlayers[i].type & PLAYER_CINEMATIC_MODE) == 0) && - ((gPlayers[i].type & PLAYER_INVISIBLE_OR_BOMB) == 0)) { - position = gGPCurrentRaceRankByPlayerId[i]; - gPlayers[i].currentRank = position; - gPlayerPositionLUT[position] = i; + for (playerId = 0; playerId < NUM_PLAYERS; playerId++) { + if (((gPlayers[playerId].type & PLAYER_EXISTS) != 0) && ((gPlayers[playerId].type & PLAYER_CINEMATIC_MODE) == 0) && + ((gPlayers[playerId].type & PLAYER_INVISIBLE_OR_BOMB) == 0)) { + position = gGPCurrentRaceRankByPlayerId[playerId]; + gPlayers[playerId].currentRank = position; + gPlayerPositionLUT[position] = playerId; } } } @@ -668,16 +678,16 @@ void update_race_position_data(void) { void func_8028F474(void) { s32 i; - switch (D_800DC510) { - case 3: - case 4: - case 5: - case 7: + switch (gRaceState) { + case RACE_IN_PROGRESS: + case RACE_HUMAN_FINISHED: + case RACE_DONE: + case RACE_QUADRANT_RESULTS: for (i = 0; i < NUM_PLAYERS; i++) { update_player(i); } - case 1: - case 2: + case RACE_PREP: + case RACE_STAGING: update_vehicles(); break; } @@ -691,7 +701,7 @@ void func_8028F4E8(void) { func_800CA330(0x19); func_800CA388(0x19); gGotoMode = START_MENU_FROM_QUIT; - D_800DC510 = 6; + gRaceState = RACE_QUITTING; D_800DC5B4 = 1; D_800DC5B0 = 1; D_800DC5B8 = 0; @@ -874,7 +884,7 @@ void func_8028F970(void) { void func_8028FBD4(void) { gGotoMode = START_MENU_FROM_QUIT; - D_800DC510 = 6; + gRaceState = RACE_QUITTING; func_800CA330(25); func_800CA388(25); D_800DC5B4 = 1; @@ -914,8 +924,8 @@ void func_8028FCBC(void) { if (gDemoUseController) { end_demo_update(); } - switch (D_800DC510) { - case 0: + switch (gRaceState) { + case RACE_NONE: if (!gDemoMode) { // If we're not in the demo mode, play the starting fanfare for the current mode (Grand Prix / Time Trials / VS / Battle) if (gModeSelection == GRAND_PRIX) { play_sequence2(SEQ_EVENT_RACE_STARTING); @@ -926,7 +936,7 @@ void func_8028FCBC(void) { } } func_80002DAC(); - D_800DC510 = 1; + gRaceState = RACE_PREP; D_80150118 = 3.0f; creditsRenderMode = 0; D_802BA032 = 0; @@ -949,7 +959,7 @@ void func_8028FCBC(void) { } D_800DC5B8 = 1; break; - case 1: + case RACE_PREP: func_8028F914(); if (D_802BA034 == 1.0f) { if (gActiveScreenMode != SCREEN_MODE_1P) { @@ -959,7 +969,7 @@ void func_8028FCBC(void) { func_802A7728(); } } - D_800DC510 = 2; + gRaceState = RACE_STAGING; D_800DC5B0 = 0; D_800DC5B8 = 1; func_80078F64(); @@ -979,7 +989,7 @@ void func_8028FCBC(void) { } func_8028F4E8(); break; - case 2: + case RACE_STAGING: if (gDemoMode) { start_race(); } @@ -988,7 +998,7 @@ void func_8028FCBC(void) { } func_8028F4E8(); break; - case 3: + case RACE_IN_PROGRESS: if (gModeSelection == BATTLE) { update_player_battle_status(); } else { @@ -998,7 +1008,7 @@ void func_8028FCBC(void) { func_8028F4E8(); func_8028F970(); break; - case 4: + case RACE_HUMAN_FINISHED: switch (gModeSelection) { case GRAND_PRIX: @@ -1010,7 +1020,7 @@ void func_8028FCBC(void) { switch (gScreenModeSelection) { case SCREEN_MODE_1P: gDemoTimer = 690; - D_800DC510 = 5; + gRaceState = RACE_DONE; func_8028E298(); break; case SCREEN_MODE_2P_SPLITSCREEN_HORIZONTAL: @@ -1026,7 +1036,7 @@ void func_8028FCBC(void) { func_8028E298(); gDemoTimer = 600; - D_800DC510 = 5; + gRaceState = RACE_DONE; } break; } @@ -1040,14 +1050,14 @@ void func_8028FCBC(void) { case TIME_TRIALS: gDemoTimer = 360; if (D_8015F890 != 0) { - D_800DC510 = 7; + gRaceState = RACE_QUADRANT_RESULTS; } else { - D_800DC510 = 5; + gRaceState = RACE_DONE; } break; } break; - case 5: + case RACE_DONE: if (gDemoTimer != 0) { gDemoTimer--; } else { @@ -1057,7 +1067,7 @@ void func_8028FCBC(void) { func_8028E678(); } else if (gScreenModeSelection == SCREEN_MODE_1P) { func_80092564(); - D_800DC510 = 7; + gRaceState = RACE_QUADRANT_RESULTS; } else { func_8028E438(); } @@ -1073,14 +1083,14 @@ void func_8028FCBC(void) { } func_8028F4E8(); break; - case 6: + case RACE_QUITTING: func_8028F8BC(); if (D_802BA034 <= 0) { gIsInQuitToMenuTransition = 1; gQuitToMenuTransitionCounter = 5; } break; - case 7: + case RACE_QUADRANT_RESULTS: break; } } diff --git a/src/racing/race_logic.h b/src/racing/race_logic.h index 3ab60a14e..8e7f71ec4 100644 --- a/src/racing/race_logic.h +++ b/src/racing/race_logic.h @@ -16,7 +16,7 @@ void func_8028EC38(s32); void play_music_for_current_track(s32); void start_race(void); f32 func_8028EE8C(s32); -void func_8028EEF0(s32); +void add_cinematic_mode(s32); void func_8028EF28(void); void func_8028F3E8(void); void update_race_position_data(void); diff --git a/src/render_objects.c b/src/render_objects.c index 66cba01ac..049962085 100644 --- a/src/render_objects.c +++ b/src/render_objects.c @@ -2847,7 +2847,7 @@ void func_800508C0(void) { u16 var_s2; if (gModeSelection == TIME_TRIALS) { - var_s0 = D_80164378[0]; + var_s0 = gPrevPlayerIdByRank[0]; } else { var_s0 = gGPCurrentRacePlayerIdByRank[0]; }