diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index b7d8d4371..5d9882a1c 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3957,6 +3957,7 @@ + @@ -4405,6 +4406,7 @@ + @@ -4449,4 +4451,4 @@ - + \ No newline at end of file diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index adc2b0ad9..e99524087 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -15270,6 +15270,9 @@ Source Files\src\pc\controller + + Source Files\src\pc\djui\panel + @@ -16360,5 +16363,8 @@ Source Files\src\pc\djui\component\compound + + Source Files\src\pc\djui\panel + \ No newline at end of file diff --git a/src/game/mario.c b/src/game/mario.c index ba564fca5..e8bcc175d 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -2130,8 +2130,9 @@ static void init_single_mario(struct MarioState* m) { } // set mario/luigi model - enum CharacterType characterType = (globalIndex == 0) ? CT_MARIO : CT_LUIGI; - m->character = &gCharacters[characterType]; + u8 modelIndex = gNetworkPlayers[playerIndex].modelIndex; + if (modelIndex >= CT_MAX) { modelIndex = 0; } + m->character = &gCharacters[modelIndex]; m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId]; } diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 8665e96a8..579195422 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -2501,7 +2501,7 @@ static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) { } static void end_peach_cutscene_star_dance(struct MarioState *m) { - u8 nonMario = (m->character != &gCharacters[CT_MARIO]); + u8 nonMario = (gNetworkPlayers[m->playerIndex].globalIndex != 0); s32 animFrame = set_mario_animation(m, nonMario ? MARIO_ANIM_START_SLEEP_SITTING : MARIO_ANIM_CREDITS_PEACE_SIGN); if (animFrame == (nonMario ? 0 : 77)) { @@ -2555,7 +2555,7 @@ 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 nonMario = (m->character != &gCharacters[CT_MARIO]); + u8 nonMario = (gNetworkPlayers[m->playerIndex].globalIndex != 0); 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); @@ -2593,7 +2593,7 @@ static void end_peach_cutscene_dialog_3(struct MarioState *m) { // "Mario!" static void end_peach_cutscene_run_to_castle(struct MarioState *m) { - u8 nonMario = (m->character != &gCharacters[CT_MARIO]); + u8 nonMario = (gNetworkPlayers[m->playerIndex].globalIndex != 0); if (nonMario) { set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_SLEEP_START_LYING : MARIO_ANIM_SLEEP_LYING); diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index 589e6b857..780daf093 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -90,27 +90,28 @@ struct PlayerColor gPlayerColors[] = { DEFINE_PLAYER_COLOR(0xff, 0x00, 0x00, /**/ 0x00, 0x00, 0xff), // default luigi DEFINE_PLAYER_COLOR(0x00, 0x98, 0x00, /**/ 0x00, 0x00, 0xfe), -#if MAX_PLAYERS > 2 // fake waluigi DEFINE_PLAYER_COLOR(0x6d, 0x3c, 0x9a, /**/ 0x2c, 0x26, 0x3f), // fake wario DEFINE_PLAYER_COLOR(0xf9, 0xeb, 0x30, /**/ 0x7f, 0x20, 0x7a), - // light blue - DEFINE_PLAYER_COLOR(0x00, 0xdf, 0xff, /**/ 0x00, 0x00, 0xf0), - // sponge - DEFINE_PLAYER_COLOR(0xff, 0x7f, 0x00, /**/ 0x00, 0x7f, 0xa0), - // blue man group - DEFINE_PLAYER_COLOR(0x00, 0x00, 0xf0, /**/ 0x00, 0x00, 0x4f), - // thanks doc - DEFINE_PLAYER_COLOR(0xff, 0x00, 0xff, /**/ 0x00, 0xff, 0x00), - // white - DEFINE_PLAYER_COLOR(0xff, 0xff, 0xff, /**/ 0x10, 0x10, 0x10), - // grey - DEFINE_PLAYER_COLOR(0x6f, 0x6f, 0x6f, /**/ 0xe0, 0xe0, 0xe0), -#endif + + DEFINE_PLAYER_COLOR(0x00, 0x2f, 0xc8, /**/ 0xbf, 0xde, 0xff), + DEFINE_PLAYER_COLOR(0xc1, 0x2c, 0x72, /**/ 0x34, 0x16, 0x0d), + DEFINE_PLAYER_COLOR(0x4c, 0xff, 0x4c, /**/ 0x81, 0x00, 0x00), + DEFINE_PLAYER_COLOR(0xa9, 0x78, 0xfc, /**/ 0x61, 0x3d, 0x2e), + + DEFINE_PLAYER_COLOR(0x84, 0x60, 0x00, /**/ 0x00, 0x46, 0x5c), + DEFINE_PLAYER_COLOR(0x5a, 0x94, 0xff, /**/ 0x4f, 0x31, 0x8b), + DEFINE_PLAYER_COLOR(0x68, 0x0a, 0x17, /**/ 0x23, 0x11, 0x03), + DEFINE_PLAYER_COLOR(0x95, 0xd0, 0x8f, /**/ 0x53, 0x39, 0x3d), + + DEFINE_PLAYER_COLOR(0x37, 0x32, 0x42, /**/ 0xe6, 0xe3, 0xff), + DEFINE_PLAYER_COLOR(0xff, 0x8a, 0x00, /**/ 0x00, 0x51, 0x10), + DEFINE_PLAYER_COLOR(0x65, 0xfa, 0xff, /**/ 0x4c, 0x1e, 0x3f), + DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0xb2, 0x28, 0x18), }; -static const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerColors); +const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerColors); // This whole file is weirdly organized. It has to be the same file due // to rodata boundaries and function aligns, which means the programmer @@ -123,14 +124,14 @@ static const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerCo * The 4th component is the shade factor (difference between ambient and diffuse), * usually set to 1. */ -void set_player_colors(u8 globalIndex, const u8 shirt[4], const u8 pants[4]) { +void set_player_colors(u8 paletteIndex, const u8 shirt[4], const u8 pants[4]) { // choose the last color in the table for extra players - if (globalIndex >= gNumPlayerColors) globalIndex = gNumPlayerColors - 1; + if (paletteIndex >= gNumPlayerColors) paletteIndex = gNumPlayerColors - 1; const u8 pAmb[3] = { pants[0] >> pants[4], pants[1] >> pants[4], pants[2] >> pants[4] }; const u8 sAmb[3] = { shirt[0] >> shirt[4], shirt[1] >> shirt[4], shirt[2] >> shirt[4] }; - gPlayerColors[globalIndex].pants = + gPlayerColors[paletteIndex].pants = (Lights1) gdSPDefLights1(pAmb[0], pAmb[1], pAmb[2], pants[0], pants[1], pants[2], 0x28, 0x28, 0x28); - gPlayerColors[globalIndex].shirt = + gPlayerColors[paletteIndex].shirt = (Lights1) gdSPDefLights1(sAmb[0], sAmb[1], sAmb[2], shirt[0], shirt[1], shirt[2], 0x28, 0x28, 0x28); } @@ -139,13 +140,13 @@ void set_player_colors(u8 globalIndex, const u8 shirt[4], const u8 pants[4]) { * 0 = shirt, 1 = pants * Returns RGB, not RGBA! */ -u8 *get_player_color(u8 globalIndex, const int which) { +u8 *get_player_color(u8 paletteIndex, const int which) { // choose the last color in the table for extra players - if (globalIndex >= gNumPlayerColors) globalIndex = gNumPlayerColors - 1; + if (paletteIndex >= gNumPlayerColors) paletteIndex = gNumPlayerColors - 1; if (which == 0) - return gPlayerColors[globalIndex].shirt.l[0].l.col; + return gPlayerColors[paletteIndex].shirt.l[0].l.col; else - return gPlayerColors[globalIndex].pants.l[0].l.col; + return gPlayerColors[paletteIndex].pants.l[0].l.col; } /** @@ -802,7 +803,7 @@ Gfx* geo_mario_set_player_colors(s32 callContext, struct GraphNode* node, UNUSED struct GraphNodeGenerated* asGenerated = (struct GraphNodeGenerated*) node; Gfx* gfx = NULL; u8 index = geo_get_processing_object_index(); - u8 colorIndex = gNetworkPlayers[index].globalIndex; + u8 colorIndex = gNetworkPlayers[index].paletteIndex; struct MarioBodyState* bodyState = &gBodyStates[index]; if (callContext == GEO_CONTEXT_RENDER) { diff --git a/src/game/mario_misc.h b/src/game/mario_misc.h index 3c4a76b6b..b87366e59 100644 --- a/src/game/mario_misc.h +++ b/src/game/mario_misc.h @@ -8,9 +8,10 @@ extern struct GraphNodeObject gMirrorMario[MAX_PLAYERS]; extern struct MarioBodyState gBodyStates[MAX_PLAYERS]; +extern const size_t gNumPlayerColors; -void set_player_colors(u8 globalIndex, const u8 shirt[4], const u8 pants[4]); -u8 *get_player_color(u8 globalIndex, const int which); +void set_player_colors(u8 paletteIndex, const u8 shirt[4], const u8 pants[4]); +u8 *get_player_color(u8 paletteIndex, const int which); Gfx *geo_draw_mario_head_goddard(s32 callContext, struct GraphNode *node, Mat4 *c); void bhv_toad_message_loop(void); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 101b247c8..110817501 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -100,13 +100,16 @@ bool configDiscordRPC = true; #endif // coop-specific char configJoinIp[MAX_CONFIG_STRING] = ""; -unsigned int configJoinPort = DEFAULT_PORT; -unsigned int configHostPort = DEFAULT_PORT; -unsigned int configHostSaveSlot = 1; -unsigned int configPlayerInteraction = 1; -unsigned int configPlayerKnockbackStrength = 25; -bool configStayInLevelAfterStar = 0; -unsigned int configNetworkSystem = 0; +unsigned int configJoinPort = DEFAULT_PORT; +unsigned int configHostPort = DEFAULT_PORT; +unsigned int configHostSaveSlot = 1; +unsigned int configPlayerInteraction = 1; +unsigned int configPlayerKnockbackStrength = 25; +bool configStayInLevelAfterStar = 0; +unsigned int configNetworkSystem = 0; +char configPlayerName[MAX_PLAYER_STRING] = ""; +unsigned int configPlayerModel = 0; +unsigned int configPlayerPalette = 0; static const struct ConfigOption options[] = { {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configWindow.fullscreen}, @@ -167,6 +170,9 @@ static const struct ConfigOption options[] = { {.name = "coop_player_knockback_strength", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerKnockbackStrength}, {.name = "coop_stay_in_level_after_star", .type = CONFIG_TYPE_UINT , .boolValue = &configStayInLevelAfterStar}, {.name = "coop_network_system", .type = CONFIG_TYPE_UINT , .uintValue = &configNetworkSystem}, + {.name = "coop_player_name", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configPlayerName}, + {.name = "coop_player_model", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerModel}, + {.name = "coop_player_palette", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerPalette}, }; // Reads an entire line from a file (excluding the newline character) and returns an allocated string diff --git a/src/pc/configfile.h b/src/pc/configfile.h index 0059b0c4b..9718596a9 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -8,6 +8,7 @@ #define MAX_BINDS 3 #define MAX_VOLUME 127 #define MAX_CONFIG_STRING 64 +#define MAX_PLAYER_STRING 20 #define DEFAULT_PORT 7777 @@ -73,6 +74,9 @@ extern unsigned int configPlayerInteraction; extern unsigned int configPlayerKnockbackStrength; extern bool configStayInLevelAfterStar; extern unsigned int configNetworkSystem; +extern char configPlayerName[]; +extern unsigned int configPlayerModel; +extern unsigned int configPlayerPalette; void configfile_load(const char *filename); void configfile_save(const char *filename); diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h index 44aab57f6..d0b161f49 100644 --- a/src/pc/djui/djui.h +++ b/src/pc/djui/djui.h @@ -42,6 +42,7 @@ #include "djui_panel_join_message.h" #include "djui_panel_pause.h" #include "djui_panel_options.h" +#include "djui_panel_player.h" #include "djui_panel_camera.h" #include "djui_panel_controls.h" #include "djui_panel_display.h" diff --git a/src/pc/djui/djui_chat_message.c b/src/pc/djui/djui_chat_message.c index b4b6ab377..b91b5905b 100644 --- a/src/pc/djui/djui_chat_message.c +++ b/src/pc/djui/djui_chat_message.c @@ -44,11 +44,10 @@ static void djui_chat_message_destroy(struct DjuiBase* base) { } struct DjuiChatMessage* djui_chat_message_create_from(u8 globalIndex, char* message) { - u8* rgb = get_player_color(globalIndex, 0); - u8 rgb2[3] = { 0 }; - for (int i = 0; i < 3; i++) { rgb2[i] = fmin((f32)rgb[i] * 1.3f + 32.0f, 255); } + struct NetworkPlayer* np = network_player_from_global_index(globalIndex); + u8* rgb = get_player_color(np->paletteIndex, 0); char chatMsg[256] = { 0 }; - snprintf(chatMsg, 256, "\\#%02x%02x%02x\\%s:\\#dcdcdc\\ %s", rgb2[0], rgb2[1], rgb2[2], "Player", message); + snprintf(chatMsg, 256, "\\#%02x%02x%02x\\%s:\\#dcdcdc\\ %s", rgb[0], rgb[1], rgb[2], (np != NULL) ? np->name : "Player", message); play_sound((globalIndex == gNetworkPlayerLocal->globalIndex) ? SOUND_MENU_MESSAGE_DISAPPEAR : SOUND_MENU_MESSAGE_APPEAR, gDefaultSoundArgs); return djui_chat_message_create(chatMsg); } diff --git a/src/pc/djui/djui_panel_options.c b/src/pc/djui/djui_panel_options.c index 653d2edd0..52a335607 100644 --- a/src/pc/djui/djui_panel_options.c +++ b/src/pc/djui/djui_panel_options.c @@ -1,5 +1,6 @@ #include "djui.h" -#include "src/pc/utils/misc.h" +#include "pc/network/network.h" +#include "pc/utils/misc.h" void djui_panel_options_back(struct DjuiBase* caller) { configfile_save(configfile_name()); @@ -8,21 +9,29 @@ void djui_panel_options_back(struct DjuiBase* caller) { void djui_panel_options_create(struct DjuiBase* caller) { f32 bodyHeight = 64 * 5 + 16 * 4; + if (gNetworkType == NT_NONE) { + bodyHeight += 64 + 16; + } struct DjuiBase* defaultBase = NULL; struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\O\\#1be700\\P\\#00b3ff\\T\\#ffef00\\I\\#ff0800\\O\\#1be700\\N\\#00b3ff\\S"); struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); { - /*struct DjuiButton* button1 = djui_button_create(&body->base, "Player"); - djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(&button1->base, 1.0f, 64); - defaultBase = &button1->base;*/ + if (gNetworkType == NT_NONE) { + struct DjuiButton* button1 = djui_button_create(&body->base, "Player"); + djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&button1->base, 1.0f, 64); + djui_interactable_hook_click(&button1->base, djui_panel_player_create); + defaultBase = &button1->base; + } struct DjuiButton* button2 = djui_button_create(&body->base, "Camera"); djui_base_set_size_type(&button2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&button2->base, 1.0f, 64); djui_interactable_hook_click(&button2->base, djui_panel_camera_create); - defaultBase = &button2->base; + if (defaultBase == NULL) { + defaultBase = &button2->base; + } struct DjuiButton* button3 = djui_button_create(&body->base, "Controls"); djui_base_set_size_type(&button3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); diff --git a/src/pc/djui/djui_panel_player.c b/src/pc/djui/djui_panel_player.c new file mode 100644 index 000000000..37291b48b --- /dev/null +++ b/src/pc/djui/djui_panel_player.c @@ -0,0 +1,115 @@ +#include +#include "djui.h" +#include "pc/configfile.h" +#include "pc/network/network_player.h" +#include "game/level_update.h" +#include "game/area.h" + +static bool djui_panel_player_name_valid(char* buffer) { + if (buffer[0] == '\0') { return false; } + while (*buffer != '\0') { + if (*buffer >= '!' && *buffer <= '~') { + buffer++; + continue; + } + return false; + } + return true; +} + +static void djui_panel_player_name_text_change(struct DjuiBase* caller) { + struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller; + if (djui_panel_player_name_valid(inputbox1->buffer)) { + djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255); + } else { + djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255); + } +} + +static void djui_panel_player_name_on_focus_end(struct DjuiBase* caller) { + struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller; + if (!djui_panel_player_name_valid(inputbox1->buffer)) { + djui_inputbox_set_text(inputbox1, "Player"); + } + snprintf(configPlayerName, 20, "%s", inputbox1->buffer); + djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255); +} + +void djui_panel_player_value_changed(struct DjuiBase* caller) { + struct MarioState* m = &gMarioStates[0]; + if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; } + gNetworkPlayers[0].modelIndex = configPlayerModel; + gNetworkPlayers[0].paletteIndex = configPlayerPalette; + network_player_update_model(0); +} + +void djui_panel_player_create(struct DjuiBase* caller) { + f32 bodyHeight = 32 * 3 + 64 * 1 + 16 * 4; + + struct DjuiBase* defaultBase = NULL; + struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\P\\#1be700\\L\\#00b3ff\\A\\#ffef00\\Y\\#ff0800\\E\\#1be700\\R"); + struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); + + { + struct DjuiRect* rect1 = djui_rect_create(&body->base); + djui_base_set_size_type(&rect1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&rect1->base, 1.0f, 32); + djui_base_set_color(&rect1->base, 0, 0, 0, 0); + { + struct DjuiText* text1 = djui_text_create(&rect1->base, "Name"); + djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_color(&text1->base, 200, 200, 200, 255); + djui_base_set_size(&text1->base, 0.485f, 64); + djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + + struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 20); + djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&inputbox1->base, 0.5f, 32); + djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + if (djui_panel_player_name_valid(configPlayerName)) { + djui_inputbox_set_text(inputbox1, configPlayerName); + } else { + djui_inputbox_set_text(inputbox1, "Player"); + } + djui_interactable_hook_value_change(&inputbox1->base, djui_panel_player_name_text_change); + djui_interactable_hook_focus(&inputbox1->base, NULL, NULL, djui_panel_player_name_on_focus_end); + } + + char* modelChoices[2] = { "Mario", "Luigi" }; + struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(&body->base, "Model", modelChoices, 2, &configPlayerModel); + djui_base_set_size_type(&selectionbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&selectionbox1->base, 1.0f, 32); + djui_interactable_hook_value_change(&selectionbox1->base, djui_panel_player_value_changed); + + char* paletteChoices[16] = { + "mario", + "luigi", + "waluigi", + "wario", + "cobalt", + "hot pink", + "seafoam", + "lilac", + "copper", + "azure", + "burgundy", + "mint", + "eggplant", + "orange", + "arctic", + "fire", + }; + struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "Palette", paletteChoices, 16, &configPlayerPalette); + djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&selectionbox2->base, 1.0f, 32); + djui_interactable_hook_value_change(&selectionbox2->base, djui_panel_player_value_changed); + + struct DjuiButton* button6 = djui_button_create(&body->base, "Back"); + djui_base_set_size_type(&button6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&button6->base, 1.0f, 64); + djui_button_set_style(button6, 1); + djui_interactable_hook_click(&button6->base, djui_panel_menu_back); + } + + djui_panel_add(caller, &panel->base, defaultBase); +} diff --git a/src/pc/djui/djui_panel_player.h b/src/pc/djui/djui_panel_player.h new file mode 100644 index 000000000..0a84adbaf --- /dev/null +++ b/src/pc/djui/djui_panel_player.h @@ -0,0 +1,4 @@ +#pragma once +#include "djui.h" + +void djui_panel_player_create(struct DjuiBase* caller); diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 25ccd740b..37490593d 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -80,13 +80,13 @@ bool network_init(enum NetworkType inNetworkType) { gNetworkType = inNetworkType; if (gNetworkType == NT_SERVER) { - network_player_connected(NPT_LOCAL, 0); + network_player_connected(NPT_LOCAL, 0, configPlayerModel, configPlayerPalette, configPlayerName); extern u8* gOverrideEeprom; gOverrideEeprom = NULL; djui_chat_box_create(); } else if (gNetworkType == NT_CLIENT) { - network_player_connected(NPT_SERVER, 0); + network_player_connected(NPT_SERVER, 0, 0, 0, "Player"); } LOG_INFO("initialized"); diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index f639b9f65..bde91cd56 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -5,10 +5,41 @@ #include "pc/djui/djui.h" #include "pc/debuglog.h" #include "pc/utils/misc.h" +#include "game/area.h" struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 }; struct NetworkPlayer* gNetworkPlayerLocal = NULL; struct NetworkPlayer* gNetworkPlayerServer = NULL; +static char sDefaultPlayerName[] = "Player"; + +void network_player_init(void) { + gNetworkPlayers[0].modelIndex = configPlayerModel; + gNetworkPlayers[0].paletteIndex = configPlayerPalette; +} + +void network_player_update_model(u8 localIndex) { + struct MarioState* m = &gMarioStates[localIndex]; + if (m == NULL) { return; } + m->character = &gCharacters[gNetworkPlayers[localIndex].modelIndex]; + if (m->marioObj == NULL) { return; } + m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId]; +} + +u8 network_player_unique_palette(u8 palette) { + u16 iterations = 0; + retry_palette: + for (int i = 0; i < MAX_PLAYERS; i++) { + if (!gNetworkPlayers[i].connected) { continue; } + if (gNetworkPlayers[i].paletteIndex == palette) { + palette = (palette + 1) % gNumPlayerColors; + if (iterations++ >= gNumPlayerColors) { + return palette; + } + goto retry_palette; + } + } + return palette; +} bool network_player_any_connected(void) { for (int i = 1; i < MAX_PLAYERS; i++) { @@ -116,7 +147,12 @@ void network_player_update(void) { //#endif } -u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex) { +u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, u8 paletteIndex, char* name) { + // ensure that a name is given + if (name[0] == '\0') { + name = sDefaultPlayerName; + } + if (type == NPT_LOCAL) { struct NetworkPlayer* np = &gNetworkPlayers[0]; if (np->connected) { @@ -137,6 +173,10 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex) { np->currAreaIndex = gCurrAreaIndex; np->currLevelSyncValid = false; np->currAreaSyncValid = false; + np->modelIndex = modelIndex; + np->paletteIndex = paletteIndex; + snprintf(np->name, MAX_PLAYER_STRING, "%s", name); + network_player_update_model(0); gNetworkPlayerLocal = np; @@ -155,6 +195,10 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex) { np->localIndex = i; np->lastReceived = clock_elapsed(); np->lastSent = clock_elapsed(); + np->modelIndex = modelIndex; + np->paletteIndex = paletteIndex; + snprintf(np->name, MAX_PLAYER_STRING, "%s", name); + network_player_update_model(i); if (gNetworkType == NT_SERVER || type == NPT_SERVER) { gNetworkSystem->save_id(i, 0); } LOG_ERROR("player connected, reusing local %d, global %d, duplicate event?", i, globalIndex); return i; @@ -181,17 +225,19 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex) { np->type = type; np->lastReceived = clock_elapsed(); np->lastSent = clock_elapsed(); + np->modelIndex = modelIndex; + np->paletteIndex = paletteIndex; + snprintf(np->name, MAX_PLAYER_STRING, "%s", name); + network_player_update_model(i); if (gNetworkType == NT_SERVER || type == NPT_SERVER) { gNetworkSystem->save_id(i, 0); } for (int j = 0; j < MAX_SYNC_OBJECTS; j++) { gSyncObjects[j].rxEventId[i] = 0; } if (type == NPT_SERVER) { gNetworkPlayerServer = np; } else { // display popup - u8* rgb = get_player_color(np->globalIndex, 0); - u8 rgb2[3] = { 0 }; - for (int i = 0; i < 3; i++) { rgb2[i] = fmin((f32)rgb[i] * 1.3f + 32.0f, 255); } + u8* rgb = get_player_color(np->paletteIndex, 0); char popupMsg[128] = { 0 }; - snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ connected.", rgb2[0], rgb2[1], rgb2[2], "Player"); + snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ connected.", rgb[0], rgb[1], rgb[2], np->name); djui_popup_create(popupMsg, 1); } LOG_INFO("player connected, local %d, global %d", i, np->globalIndex); @@ -235,11 +281,9 @@ u8 network_player_disconnected(u8 globalIndex) { LOG_INFO("player disconnected, local %d, global %d", i, globalIndex); // display popup - u8* rgb = get_player_color(np->globalIndex, 0); - u8 rgb2[3] = { 0 }; - for (int i = 0; i < 3; i++) { rgb2[i] = fmin((f32)rgb[i] * 1.3f + 32.0f, 255); } + u8* rgb = get_player_color(np->paletteIndex, 0); char popupMsg[128] = { 0 }; - snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ disconnected.", rgb2[0], rgb2[1], rgb2[2], "Player"); + snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ disconnected.", rgb[0], rgb[1], rgb[2], np->name); djui_popup_create(popupMsg, 1); packet_ordered_clear(globalIndex); diff --git a/src/pc/network/network_player.h b/src/pc/network/network_player.h index ea9048c6b..cc3ab31b1 100644 --- a/src/pc/network/network_player.h +++ b/src/pc/network/network_player.h @@ -3,6 +3,7 @@ #include #include "network.h" +#include "pc/configfile.h" #define UNKNOWN_LOCAL_INDEX ((u8)-1) #define UNKNOWN_GLOBAL_INDEX ((u8)-1) @@ -19,7 +20,7 @@ enum NetworkPlayerType { struct NetworkPlayer { bool connected; - enum NetworkPlayerType type; + u8 type; u8 localIndex; u8 globalIndex; f32 lastReceived; @@ -32,14 +33,20 @@ struct NetworkPlayer { bool currLevelSyncValid; bool currAreaSyncValid; u8 fadeOpacity; - u16 rxSeqIds[MAX_RX_SEQ_IDS]; u8 onRxSeqId; + u8 modelIndex; + u8 paletteIndex; + char name[MAX_PLAYER_STRING]; + u16 rxSeqIds[MAX_RX_SEQ_IDS]; }; extern struct NetworkPlayer gNetworkPlayers[]; extern struct NetworkPlayer* gNetworkPlayerLocal; extern struct NetworkPlayer* gNetworkPlayerServer; +void network_player_init(void); +void network_player_update_model(u8 localIndex); +u8 network_player_unique_palette(u8 palette); bool network_player_any_connected(void); u8 network_player_connected_count(void); struct NetworkPlayer* network_player_from_global_index(u8 globalIndex); @@ -47,7 +54,7 @@ struct NetworkPlayer* get_network_player_from_level(s16 courseNum, s16 actNum, s struct NetworkPlayer* get_network_player_from_area(s16 courseNum, s16 actNum, s16 levelNum, s16 areaIndex); struct NetworkPlayer* get_network_player_smallest_global(void); void network_player_update(void); -u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex); +u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, u8 paletteIndex, char* name); u8 network_player_disconnected(u8 globalIndex); void network_player_shutdown(void); diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index 31aee61ff..db15aa0b7 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -20,6 +20,10 @@ extern u8* gOverrideEeprom; static u8 eeprom[512] = { 0 }; +static u8 sJoinRequestPlayerModel; +static u8 sJoinRequestPlayerPalette; +static char sJoinRequestPlayerName[MAX_PLAYER_STRING]; + void network_send_join_request(void) { assert(gNetworkType == NT_CLIENT); @@ -27,6 +31,11 @@ void network_send_join_request(void) { struct Packet p; packet_init(&p, PACKET_JOIN_REQUEST, true, false); + + packet_write(&p, &configPlayerModel, sizeof(u8)); + packet_write(&p, &configPlayerPalette, sizeof(u8)); + packet_write(&p, &configPlayerName, sizeof(u8) * MAX_PLAYER_STRING); + network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p); LOG_INFO("sending join request"); } @@ -34,14 +43,28 @@ void network_send_join_request(void) { void network_receive_join_request(struct Packet* p) { assert(gNetworkType == NT_SERVER); LOG_INFO("received join request"); + + if (p->dataLength > 5) { + packet_read(p, &sJoinRequestPlayerModel, sizeof(u8)); + packet_read(p, &sJoinRequestPlayerPalette, sizeof(u8)); + packet_read(p, &sJoinRequestPlayerName, sizeof(u8) * MAX_PLAYER_STRING); + } else { + sJoinRequestPlayerModel = 0; + sJoinRequestPlayerPalette = 0; + snprintf(sJoinRequestPlayerName, MAX_PLAYER_STRING, "%s", "Player"); + } + network_send_join(p); } void network_send_join(struct Packet* joinRequestPacket) { assert(gNetworkType == NT_SERVER); + // make palette unique + sJoinRequestPlayerPalette = network_player_unique_palette(sJoinRequestPlayerPalette); + // do connection event - joinRequestPacket->localIndex = network_player_connected(NPT_CLIENT, joinRequestPacket->localIndex); + joinRequestPacket->localIndex = network_player_connected(NPT_CLIENT, joinRequestPacket->localIndex, sJoinRequestPlayerModel, sJoinRequestPlayerPalette, sJoinRequestPlayerName); if (joinRequestPacket->localIndex == UNKNOWN_LOCAL_INDEX) { network_send_kick(EKT_FULL_PARTY); return; @@ -177,8 +200,8 @@ void network_receive_join(struct Packet* p) { } string_linked_list_free(&head); - network_player_connected(NPT_SERVER, 0); - network_player_connected(NPT_LOCAL, myGlobalIndex); + network_player_connected(NPT_SERVER, 0, 0, 0, "Player"); + network_player_connected(NPT_LOCAL, myGlobalIndex, configPlayerModel, configPlayerPalette, configPlayerName); djui_chat_box_create(); save_file_load_all(TRUE); diff --git a/src/pc/network/packets/packet_network_players.c b/src/pc/network/packets/packet_network_players.c index 104d757ab..c356ddd62 100644 --- a/src/pc/network/packets/packet_network_players.c +++ b/src/pc/network/packets/packet_network_players.c @@ -4,6 +4,7 @@ #include "behavior_data.h" #include "src/game/behavior_actions.h" #include "pc/debuglog.h" +#include "pc/configfile.h" static void network_send_to_network_players(u8 sendToLocalIndex) { assert(gNetworkType == NT_SERVER); @@ -30,6 +31,9 @@ static void network_send_to_network_players(u8 sendToLocalIndex) { packet_write(&p, &gNetworkPlayers[i].currLevelSyncValid, sizeof(u8)); packet_write(&p, &gNetworkPlayers[i].currAreaSyncValid, sizeof(u8)); packet_write(&p, &networkId, sizeof(s64)); + packet_write(&p, &gNetworkPlayers[i].modelIndex, sizeof(u8)); + packet_write(&p, &gNetworkPlayers[i].paletteIndex, sizeof(u8)); + packet_write(&p, &gNetworkPlayers[i].name, sizeof(u8) * MAX_PLAYER_STRING); LOG_INFO("send network player [%d == %d]", gNetworkPlayers[i].globalIndex, npType); } @@ -60,6 +64,9 @@ void network_receive_network_players(struct Packet* p) { s16 courseNum, actNum, levelNum, areaIndex; u8 levelSyncValid, areaSyncValid; s64 networkId; + u8 modelIndex, paletteIndex; + char playerName[MAX_PLAYER_STRING] = { 0 }; + packet_read(p, &npType, sizeof(u8)); packet_read(p, &globalIndex, sizeof(u8)); packet_read(p, &levelAreaSeqId, sizeof(u16)); @@ -70,21 +77,26 @@ void network_receive_network_players(struct Packet* p) { packet_read(p, &levelSyncValid, sizeof(u8)); packet_read(p, &areaSyncValid, sizeof(u8)); packet_read(p, &networkId, sizeof(s64)); + packet_read(p, &modelIndex, sizeof(u8)); + packet_read(p, &paletteIndex, sizeof(u8)); + packet_read(p, &playerName, sizeof(u8) * MAX_PLAYER_STRING); - u8 localIndex = network_player_connected(npType, globalIndex); + u8 localIndex = network_player_connected(npType, globalIndex, modelIndex, paletteIndex, playerName); LOG_INFO("received network player [%d == %d] (%d)", globalIndex, npType, localIndex); - if (localIndex != UNKNOWN_GLOBAL_INDEX && localIndex != 0) { + if (localIndex != UNKNOWN_GLOBAL_INDEX) { struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; - np->currLevelAreaSeqId = levelAreaSeqId; - np->currCourseNum = courseNum; - np->currActNum = actNum; - np->currLevelNum = levelNum; - np->currAreaIndex = areaIndex; - np->currLevelSyncValid = levelSyncValid; - np->currAreaSyncValid = areaSyncValid; - LOG_INFO("received network player location (%d, %d, %d, %d)", courseNum, actNum, levelNum, areaIndex); - if (gNetworkType == NT_CLIENT && globalIndex != 0 && localIndex != 0) { - gNetworkSystem->save_id(localIndex, networkId); + if (localIndex != 0) { + np->currLevelAreaSeqId = levelAreaSeqId; + np->currCourseNum = courseNum; + np->currActNum = actNum; + np->currLevelNum = levelNum; + np->currAreaIndex = areaIndex; + np->currLevelSyncValid = levelSyncValid; + np->currAreaSyncValid = areaSyncValid; + LOG_INFO("received network player location (%d, %d, %d, %d)", courseNum, actNum, levelNum, areaIndex); + if (gNetworkType == NT_CLIENT && globalIndex != 0 && localIndex != 0) { + gNetworkSystem->save_id(localIndex, networkId); + } } } } diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index d18f42c48..9b144ed6b 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -325,11 +325,9 @@ void network_receive_player(struct Packet* p) { // inform of player death if (oldData.action != ACT_BUBBLED && data.action == ACT_BUBBLED) { // display popup - u8* rgb = get_player_color(np->globalIndex, 0); - u8 rgb2[3] = { 0 }; - for (int i = 0; i < 3; i++) { rgb2[i] = fmin((f32)rgb[i] * 1.3f + 32.0f, 255); } + u8* rgb = get_player_color(np->paletteIndex, 0); char popupMsg[128] = { 0 }; - snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ died.", rgb2[0], rgb2[1], rgb2[2], "Player"); + snprintf(popupMsg, 128, "\\#%02x%02x%02x\\%s\\#dcdcdc\\ died.", rgb[0], rgb[1], rgb[2], np->name); djui_popup_create(popupMsg, 1); } @@ -337,11 +335,6 @@ void network_receive_player(struct Packet* p) { if (m->action != oldData.action) { m->actionTimer = 0; } - - // set model - enum CharacterType characterType = (np->globalIndex == 0) ? CT_MARIO : CT_LUIGI; - m->character = &gCharacters[characterType]; - m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId]; } void network_update_player(void) { diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index b37f8b1a8..3e16fc43c 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -42,6 +42,7 @@ #include "pc/discord/discordrpc.h" #endif #include "pc/network/version.h" +#include "pc/network/network_player.h" #include "pc/djui/djui.h" OSMesg D_80339BEC; @@ -288,6 +289,7 @@ void main_func(void) { audio_init(); sound_init(); + network_player_init(); thread5_game_loop(NULL);