Document versus rankings and finish info (#766)

* Document versus rankings and finish info

* Document race states

* variable renames
This commit is contained in:
Jed Grabman
2026-07-18 18:21:51 -04:00
committed by GitHub
parent 44fe054525
commit 44c71a7978
12 changed files with 214 additions and 186 deletions
+11
View File
@@ -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
+4 -4
View File
@@ -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;
+3 -3
View File
@@ -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;
+67 -61
View File
@@ -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;
+3 -3
View File
@@ -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[];
+6 -6
View File
@@ -1,5 +1,5 @@
#ifndef GCC
#define D_800DC510_AS_U16
#define gRaceState_AS_U16
#endif
#include <ultra64.h>
#include <PR/os.h>
@@ -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
+4 -4
View File
@@ -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
+13 -12
View File
@@ -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 ///ー 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 ", 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 ", 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);
+2 -2
View File
@@ -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);
}
+99 -89
View File
@@ -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;
}
}
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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];
}