diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index d925fbdf..b4dca766 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -3846,6 +3846,7 @@
+
@@ -4326,6 +4327,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index 844a2f38..f064ced0 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -15084,6 +15084,9 @@
Source Files\src\pc\network
+
+ Source Files\src\game
+
@@ -16045,5 +16048,8 @@
Header Files\src\pc\network
+
+ Header Files\src\game
+
\ No newline at end of file
diff --git a/include/types.h b/include/types.h
index 6c614eb1..a809d9ce 100644
--- a/include/types.h
+++ b/include/types.h
@@ -6,7 +6,7 @@
#include
#include "macros.h"
-
+#include "src/game/characters.h"
// Certain functions are marked as having return values, but do not
// actually return a value. This causes undefined behavior, which we'd rather
@@ -377,6 +377,7 @@ struct MarioState
/*????*/ int splineState;
/*????*/ Vec3f nonInstantWarpPos;
+ /*????*/ struct Character* character;
};
#define PLAY_MODE_NORMAL 0
diff --git a/src/game/characters.c b/src/game/characters.c
new file mode 100644
index 00000000..3b507ae8
--- /dev/null
+++ b/src/game/characters.c
@@ -0,0 +1,16 @@
+#include "characters.h"
+#include "hud.h"
+#include "model_ids.h"
+
+struct Character gCharacters[CT_MAX] = {
+ [CT_MARIO] = {
+ .hudHead = ',',
+ .cameraHudHead = GLYPH_CAM_MARIO_HEAD,
+ .modelId = MODEL_MARIO,
+ },
+ [CT_LUIGI] = {
+ .hudHead = '.',
+ .cameraHudHead = GLYPH_CAM_LUIGI_HEAD,
+ .modelId = MODEL_LUIGI,
+ },
+};
\ No newline at end of file
diff --git a/src/game/characters.h b/src/game/characters.h
new file mode 100644
index 00000000..7f099893
--- /dev/null
+++ b/src/game/characters.h
@@ -0,0 +1,22 @@
+#ifndef CHARACTERS_H
+#define CHARACTERS_H
+#include "PR/ultratypes.h"
+// NOTE: do not include any additional headers
+
+enum CharacterType {
+ CT_MARIO,
+ CT_LUIGI,
+
+ // must be last
+ CT_MAX
+};
+
+struct Character {
+ char hudHead;
+ u32 cameraHudHead;
+ u32 modelId;
+};
+
+extern struct Character gCharacters[];
+
+#endif // CHARACTERS_H
diff --git a/src/game/hud.c b/src/game/hud.c
index a5d8338a..632a85ff 100644
--- a/src/game/hud.c
+++ b/src/game/hud.c
@@ -292,11 +292,10 @@ void render_hud_power_meter(void) {
* Renders the amount of lives Mario has.
*/
void render_hud_mario_lives(void) {
- // two-player hack
#ifdef VERSION_JP
char* displayHead = ",";
#else
- char* displayHead = (gNetworkType == NT_SERVER) ? "," : ".";
+ char* displayHead = (gMarioStates[0].character) ? &gMarioStates[0].character->hudHead : ",";
#endif
print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(22), HUD_TOP_Y, displayHead); // 'Mario Head' glyph
if (gHudDisplay.lives == -1) {
@@ -436,7 +435,7 @@ void render_hud_camera_status(void) {
switch (sCameraHUD.status & CAM_STATUS_MODE_GROUP) {
case CAM_STATUS_MARIO:
- render_hud_tex_lut(x + 16, y, (*cameraLUT)[(gNetworkType == NT_SERVER) ? GLYPH_CAM_MARIO_HEAD : GLYPH_CAM_LUIGI_HEAD]);
+ render_hud_tex_lut(x + 16, y, (*cameraLUT)[(gMarioStates[0].character) ? gMarioStates[0].character->cameraHudHead : GLYPH_CAM_MARIO_HEAD]);
break;
case CAM_STATUS_LAKITU:
render_hud_tex_lut(x + 16, y, (*cameraLUT)[GLYPH_CAM_LAKITU_HEAD]);
diff --git a/src/game/mario.c b/src/game/mario.c
index 9d6d3e79..ad46e044 100644
--- a/src/game/mario.c
+++ b/src/game/mario.c
@@ -2123,8 +2123,9 @@ static void init_single_mario(struct MarioState* m) {
}
// set mario/luigi model
- // two-player hack
- m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[(gNetworkPlayers[0].globalIndex == 1) ? MODEL_LUIGI : MODEL_MARIO];
+ enum CharacterType characterType = (gNetworkPlayers[0].globalIndex == 1) ? CT_LUIGI : CT_MARIO;
+ m->character = &gCharacters[characterType];
+ m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId];
}
void init_mario(void) {
diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c
index d8d38bea..aebd4b4e 100644
--- a/src/game/mario_actions_cutscene.c
+++ b/src/game/mario_actions_cutscene.c
@@ -2494,16 +2494,16 @@ static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) {
}
static void end_peach_cutscene_star_dance(struct MarioState *m) {
- u8 sadLuigi = (gNetworkType == NT_SERVER) ? (m->playerIndex != 0) : (m->playerIndex == 0);
- s32 animFrame = set_mario_animation(m, sadLuigi ? MARIO_ANIM_START_SLEEP_SITTING : MARIO_ANIM_CREDITS_PEACE_SIGN);
+ u8 nonMario = (m->character != &gCharacters[CT_MARIO]);
+ s32 animFrame = set_mario_animation(m, nonMario ? MARIO_ANIM_START_SLEEP_SITTING : MARIO_ANIM_CREDITS_PEACE_SIGN);
- if (animFrame == (sadLuigi ? 0 : 77)) {
+ if (animFrame == (nonMario ? 0 : 77)) {
cutscene_put_cap_on(m);
}
if (animFrame == 88 && m->playerIndex == 0) {
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
}
- if (!sadLuigi && animFrame >= 98) {
+ if (!nonMario && animFrame >= 98) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
@@ -2548,8 +2548,8 @@ static void end_peach_cutscene_star_dance(struct MarioState *m) {
// "let's bake a delicious cake..."
// "...for Mario..."
static void end_peach_cutscene_dialog_3(struct MarioState *m) {
- u8 sadLuigi = (gNetworkType == NT_SERVER) ? (m->playerIndex != 0) : (m->playerIndex == 0);
- set_mario_animation(m, sadLuigi ? MARIO_ANIM_SLEEP_IDLE : MARIO_ANIM_FIRST_PERSON);
+ u8 nonMario = (m->character != &gCharacters[CT_MARIO]);
+ set_mario_animation(m, nonMario ? MARIO_ANIM_SLEEP_IDLE : MARIO_ANIM_FIRST_PERSON);
if (m->playerIndex != 0) { return; }
sEndPeachObj->oPosY = end_obj_set_visual_pos(sEndPeachObj);
sEndRightToadObj->oPosY = end_obj_set_visual_pos(sEndRightToadObj);
@@ -2586,8 +2586,8 @@ static void end_peach_cutscene_dialog_3(struct MarioState *m) {
// "Mario!"
static void end_peach_cutscene_run_to_castle(struct MarioState *m) {
- u8 sadLuigi = (gNetworkType == NT_SERVER) ? (m->playerIndex != 0) : (m->playerIndex == 0);
- if (sadLuigi) {
+ u8 nonMario = (m->character != &gCharacters[CT_MARIO]);
+ if (nonMario) {
set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_SLEEP_START_LYING
: MARIO_ANIM_SLEEP_LYING);
} else {
diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c
index af020518..8606ec9a 100644
--- a/src/pc/network/packets/packet_player.c
+++ b/src/pc/network/packets/packet_player.c
@@ -330,7 +330,9 @@ void network_receive_player(struct Packet* p) {
}
// set model
- m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[(np->globalIndex == 1) ? MODEL_LUIGI : MODEL_MARIO];
+ enum CharacterType characterType = (np->globalIndex == 1) ? CT_LUIGI : CT_MARIO;
+ m->character = &gCharacters[characterType];
+ m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId];
}
void network_update_player(void) {