diff --git a/bin/segment2.c b/bin/segment2.c index 080e1dfa..22794f14 100644 --- a/bin/segment2.c +++ b/bin/segment2.c @@ -209,19 +209,19 @@ ALIGNED8 static const u8 texture_hud_char_coin[] = { #include "textures/segment2/segment2.05800.rgba16.inc.c" }; -ALIGNED8 static const u8 texture_hud_char_mario_head[] = { +ALIGNED8 const u8 texture_hud_char_mario_head[] = { #include "textures/segment2/segment2.05A00.rgba16.inc.c" }; -ALIGNED8 static const u8 texture_hud_char_luigi_head[] = { +ALIGNED8 const u8 texture_hud_char_luigi_head[] = { #include "textures/segment2/custom_luigi_head.rgba16.inc.c" }; -ALIGNED8 static const u8 texture_hud_char_toad_head[] = { +ALIGNED8 const u8 texture_hud_char_toad_head[] = { #include "textures/segment2/custom_toad_head.rgba16.inc.c" }; -ALIGNED8 static const u8 texture_hud_char_waluigi_head[] = { +ALIGNED8 const u8 texture_hud_char_waluigi_head[] = { #include "textures/segment2/custom_waluigi_head.rgba16.inc.c" }; diff --git a/src/game/characters.c b/src/game/characters.c index 5590a236..3e528a29 100644 --- a/src/game/characters.c +++ b/src/game/characters.c @@ -16,10 +16,16 @@ extern Gfx toad_player_dl_cap[]; extern Gfx toad_player_dl_cap_decal[]; extern Gfx waluigi_cap_seg3_dl_03022F48[]; +extern ALIGNED8 const u8 texture_hud_char_mario_head[]; +extern ALIGNED8 const u8 texture_hud_char_luigi_head[]; +extern ALIGNED8 const u8 texture_hud_char_toad_head[]; +extern ALIGNED8 const u8 texture_hud_char_waluigi_head[]; + struct Character gCharacters[CT_MAX] = { [CT_MARIO] = { .name = "Mario", .hudHead = ',', + .hudHeadTexture = texture_hud_char_mario_head, .cameraHudHead = GLYPH_CAM_MARIO_HEAD, .modelId = MODEL_MARIO, .capModelId = MODEL_MARIOS_CAP, @@ -80,6 +86,7 @@ struct Character gCharacters[CT_MAX] = { [CT_LUIGI] = { .name = "Luigi", .hudHead = '.', + .hudHeadTexture = texture_hud_char_luigi_head, .cameraHudHead = GLYPH_CAM_LUIGI_HEAD, .modelId = MODEL_LUIGI, .capModelId = MODEL_LUIGIS_CAP, @@ -140,6 +147,7 @@ struct Character gCharacters[CT_MAX] = { [CT_TOAD] = { .name = "Toad", .hudHead = '/', + .hudHeadTexture = texture_hud_char_toad_head, .cameraHudHead = GLYPH_CAM_TOAD_HEAD, .modelId = MODEL_TOAD_PLAYER, .capModelId = MODEL_TOADS_CAP, @@ -200,6 +208,7 @@ struct Character gCharacters[CT_MAX] = { [CT_WALUIGI] = { .name = "Waluigi", .hudHead = 'z', + .hudHeadTexture = texture_hud_char_waluigi_head, .cameraHudHead = GLYPH_CAM_WALUIGI_HEAD, .modelId = MODEL_WALUIGI, .capModelId = MODEL_WALUIGIS_CAP, diff --git a/src/game/characters.h b/src/game/characters.h index e53c6225..f610953f 100644 --- a/src/game/characters.h +++ b/src/game/characters.h @@ -16,6 +16,7 @@ enum CharacterType { struct Character { char* name; char hudHead; + const u8* hudHeadTexture; u32 cameraHudHead; u32 modelId; u32 capModelId; diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 71ff03a1..f5ef8638 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -73,6 +73,7 @@ unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID }; unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID }; unsigned int configKeyChat[MAX_BINDS] = { 0x001C, VK_INVALID, VK_INVALID }; +unsigned int configKeyPlayerList[MAX_BINDS] = { 0x000F, VK_INVALID, 0x1004 }; unsigned int configStickDeadzone = 16; // 16*DEADZONE_STEP=4960 (the original default deadzone) unsigned int configRumbleStrength = 50; #ifdef EXTERNAL_DATA @@ -141,6 +142,7 @@ static const struct ConfigOption options[] = { {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft}, {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight}, {.name = "key_chat", .type = CONFIG_TYPE_BIND, .uintValue = configKeyChat}, + {.name = "key_playerlist", .type = CONFIG_TYPE_BIND, .uintValue = configKeyPlayerList}, {.name = "stick_deadzone", .type = CONFIG_TYPE_UINT, .uintValue = &configStickDeadzone}, {.name = "rumble_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configRumbleStrength}, #ifdef EXTERNAL_DATA diff --git a/src/pc/configfile.h b/src/pc/configfile.h index 7a156c4e..47cfcdf5 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -42,6 +42,7 @@ extern unsigned int configKeyStickDown[]; extern unsigned int configKeyStickLeft[]; extern unsigned int configKeyStickRight[]; extern unsigned int configKeyChat[]; +extern unsigned int configKeyPlayerList[]; extern unsigned int configStickDeadzone; extern unsigned int configRumbleStrength; #ifdef EXTERNAL_DATA diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 835705f8..75ec306a 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -152,8 +152,15 @@ static SDL_Haptic *controller_sdl_init_haptics(const int joy) { static inline void update_button(const int i, const bool new) { const bool pressed = !joy_buttons[i] && new; + const bool unpressed = joy_buttons[i] && !new; joy_buttons[i] = new; - if (pressed) last_joybutton = i; + if (pressed) { + last_joybutton = i; + djui_interactable_on_key_down(VK_BASE_SDL_GAMEPAD + i); + } + if (unpressed) { + djui_interactable_on_key_up(VK_BASE_SDL_GAMEPAD + i); + } } static void controller_sdl_read(OSContPad *pad) { diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c index fd5c1144..e0768536 100644 --- a/src/pc/djui/djui.c +++ b/src/pc/djui/djui.c @@ -2,6 +2,7 @@ #include "../debuglog.h" #include "pc/cliopts.h" #include "game/level_update.h" +#include "djui_panel_playerlist.h" static Gfx* sSavedDisplayListHead = NULL; @@ -20,6 +21,8 @@ void djui_init(void) { djui_text_set_alignment(sDjuiPauseOptions, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); djui_base_set_visible(&sDjuiPauseOptions->base, false); + djui_panel_playerlist_create(NULL); + if (gCLIOpts.Network != NT_SERVER) { djui_panel_main_create(NULL); //djui_panel_debug_create(); diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h index f864946f..fc8c88dc 100644 --- a/src/pc/djui/djui.h +++ b/src/pc/djui/djui.h @@ -43,6 +43,7 @@ #include "djui_panel_pause.h" #include "djui_panel_options.h" #include "djui_panel_player.h" +#include "djui_panel_playerlist.h" #include "djui_panel_camera.h" #include "djui_panel_controls.h" #include "djui_panel_display.h" diff --git a/src/pc/djui/djui_interactable.c b/src/pc/djui/djui_interactable.c index 346ffa52..847ce4d7 100644 --- a/src/pc/djui/djui_interactable.c +++ b/src/pc/djui/djui_interactable.c @@ -198,6 +198,15 @@ bool djui_interactable_on_key_down(int scancode) { } } + if (gDjuiPlayerList != NULL) { + for (int i = 0; i < MAX_BINDS; i++) { + if (scancode == (int)configKeyPlayerList[i]) { + djui_base_set_visible(&gDjuiPlayerList->base, true); + break; + } + } + } + if (gDjuiChatBoxFocus || djui_panel_is_active()) { switch (scancode) { case SCANCODE_UP: sKeyboardHoldDirection = PAD_HOLD_DIR_UP; return true; @@ -217,6 +226,15 @@ void djui_interactable_on_key_up(int scancode) { && (sInteractableFocus->interactable != NULL) && (sInteractableFocus->interactable->on_key_up != NULL); + if (gDjuiPlayerList != NULL) { + for (int i = 0; i < MAX_BINDS; i++) { + if (scancode == (int)configKeyPlayerList[i]) { + djui_base_set_visible(&gDjuiPlayerList->base, false); + break; + } + } + } + if (keyFocused) { sInteractableFocus->interactable->on_key_up(sInteractableFocus, scancode); sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; diff --git a/src/pc/djui/djui_panel_controls.c b/src/pc/djui/djui_panel_controls.c index 8806e894..a3520a99 100644 --- a/src/pc/djui/djui_panel_controls.c +++ b/src/pc/djui/djui_panel_controls.c @@ -8,7 +8,7 @@ void djui_panel_controls_value_change(UNUSED struct DjuiBase* caller) { } void djui_panel_controls_create(struct DjuiBase* caller) { - f32 bindBodyHeight = 32 * 11 + 1 * 10; + f32 bindBodyHeight = 32 * 12 + 1 * 11; f32 bodyHeight = bindBodyHeight + 16 * 3 + 32 * 2 + 64; struct DjuiBase* defaultBase = NULL; @@ -22,16 +22,17 @@ void djui_panel_controls_create(struct DjuiBase* caller) { djui_flow_layout_set_margin(bindBody, 1); { struct DjuiBind* bind1 = djui_bind_create(&bindBody->base, "A", configKeyA); - djui_bind_create(&bindBody->base, "B", configKeyB); - djui_bind_create(&bindBody->base, "Start", configKeyStart); - djui_bind_create(&bindBody->base, "L", configKeyL); - djui_bind_create(&bindBody->base, "R", configKeyR); - djui_bind_create(&bindBody->base, "Z", configKeyZ); - djui_bind_create(&bindBody->base, "C Up", configKeyCUp); - djui_bind_create(&bindBody->base, "C Down", configKeyCDown); - djui_bind_create(&bindBody->base, "C Left", configKeyCLeft); - djui_bind_create(&bindBody->base, "C Right", configKeyCRight); - djui_bind_create(&bindBody->base, "Chat", configKeyChat); + djui_bind_create(&bindBody->base, "B", configKeyB); + djui_bind_create(&bindBody->base, "Start", configKeyStart); + djui_bind_create(&bindBody->base, "L", configKeyL); + djui_bind_create(&bindBody->base, "R", configKeyR); + djui_bind_create(&bindBody->base, "Z", configKeyZ); + djui_bind_create(&bindBody->base, "C Up", configKeyCUp); + djui_bind_create(&bindBody->base, "C Down", configKeyCDown); + djui_bind_create(&bindBody->base, "C Left", configKeyCLeft); + djui_bind_create(&bindBody->base, "C Right", configKeyCRight); + djui_bind_create(&bindBody->base, "Chat", configKeyChat); + djui_bind_create(&bindBody->base, "Player List", configKeyPlayerList); defaultBase = &bind1->buttons[0]->base; } diff --git a/src/pc/djui/djui_panel_playerlist.c b/src/pc/djui/djui_panel_playerlist.c new file mode 100644 index 00000000..9bfd38f0 --- /dev/null +++ b/src/pc/djui/djui_panel_playerlist.c @@ -0,0 +1,218 @@ +#include + +#include "pc/network/network.h" +#include "djui.h" +#include "djui_panel_playerlist.h" +#include "src/pc/utils/misc.h" +#include "src/pc/configfile.h" +#include "game/mario_misc.h" + +struct DjuiThreePanel* gDjuiPlayerList = NULL; + +static struct DjuiFlowLayout* djuiRow[MAX_PLAYERS] = { 0 }; +static struct DjuiImage* djuiImages[MAX_PLAYERS] = { 0 }; +static struct DjuiText* djuiTextNames[MAX_PLAYERS] = { 0 }; +static struct DjuiText* djuiTextLocations[MAX_PLAYERS] = { 0 }; + +extern u8 seg2_course_name_table[]; +extern u8 seg2_act_name_table[]; + +static char stage[188]; +static char act[188]; + +static const char charset[0xFF + 1] = { + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 7 + ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', // 15 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 23 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 31 + 'w', 'x', 'y', 'z', ' ', ' ', ' ', ' ', // 39 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 49 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 55 + ' ', ' ', ' ', ' ', ' ', ' ', '\'', ' ', // 63 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 71 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 79 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 87 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 95 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 103 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ',', // 111 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 119 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 127 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 135 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 143 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 151 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', '-', // 159 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 167 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 175 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 183 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 192 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 199 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 207 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 215 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 223 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 231 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 239 + ' ', ' ', '!', ' ', ' ', ' ', ' ', ' ', // 247 + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' // 255 +}; + +static void convert_string(const u8* str, char* output) { + s32 strPos = 0; + bool capitalizeChar = true; + + while (str[strPos] != 0xFF) { + if (str[strPos] < 0xFF) { + output[strPos] = charset[str[strPos]]; + + // if the char is a letter we can capatalize it + if (capitalizeChar && 0x0A <= str[strPos] && str[strPos] <= 0x23) { + output[strPos] -= ('a' - 'A'); + capitalizeChar = false; + } + + } + else { + output[strPos] = ' '; + } + + // decide if the next character should be capitalized + switch (output[strPos]) { + case ' ': + //if (str[strPos] != 158) + //fprintf(stdout, "Unknown Character (%i)\n", str[strPos]); // inform that an unknown char was found + case '-': + capitalizeChar = true; + break; + default: + capitalizeChar = false; + break; + } + + strPos++; + } + + output[strPos] = '\0'; +} + +static void set_details(s16 courseNum, s16 levelNum, s16 areaIndex) { + // overrides for castle locations + if (courseNum == 0 && levelNum == 16) { + strcpy(stage, "Castle Grounds"); + return; + } + if (courseNum == 0 && levelNum == 6) { + if (areaIndex == 1) { + strcpy(stage, "Castle Main Floor"); + return; + } else if (areaIndex == 2) { + strcpy(stage, "Castle Upper Floor"); + return; + } else if (areaIndex == 3) { + strcpy(stage, "Castle Basement"); + return; + } + } + if (courseNum == 0 && levelNum == 26) { + strcpy(stage, "Castle Courtyard"); + return; + } + + // If we are in in Course 0 we are in the castle which doesn't have a string + if (courseNum) { + void** courseNameTbl; + +#ifndef VERSION_EU + courseNameTbl = segmented_to_virtual(seg2_course_name_table); +#else + switch (gInGameLanguage) { + case LANGUAGE_ENGLISH: + courseNameTbl = segmented_to_virtual(course_name_table_eu_en); + break; + case LANGUAGE_FRENCH: + courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); + break; + case LANGUAGE_GERMAN: + courseNameTbl = segmented_to_virtual(course_name_table_eu_de); + break; + } +#endif + u8* courseName = segmented_to_virtual(courseNameTbl[courseNum - 1]); + + convert_string(&courseName[3], stage); + } else { + strcpy(stage, "Peach's Castle"); + } +} + +static void playerlist_update_row(u8 i, struct NetworkPlayer* np) { + u8 charIndex = np->modelIndex; + if (charIndex >= CT_MAX) { charIndex = 0; } + djuiImages[i]->texture = gCharacters[charIndex].hudHeadTexture; + + set_details(np->currCourseNum, np->currLevelNum, np->currAreaIndex); + djui_base_set_visible(&djuiRow[i]->base, np->connected); + + u8* rgb = get_player_color(np->paletteIndex, 0); + djui_base_set_color(&djuiTextNames[i]->base, rgb[0], rgb[1], rgb[2], 255); + djui_text_set_text(djuiTextNames[i], np->name); + + djui_text_set_text(djuiTextLocations[i], stage); +} + +void djui_panel_playerlist_on_render_pre(UNUSED struct DjuiBase* base, UNUSED bool* skipRender) { + int j = 0; + for (int i = 0; i < MAX_PLAYERS; i++) { + struct NetworkPlayer* np = &gNetworkPlayers[i]; + if (!np->connected) { continue; } + playerlist_update_row(j++, np); + } + + while (j < MAX_PLAYERS) { + djui_base_set_visible(&djuiRow[j]->base, false); + j++; + } +} + +void djui_panel_playerlist_create(UNUSED struct DjuiBase* caller) { + f32 bodyHeight = (MAX_PLAYERS * 32) + (MAX_PLAYERS - 1) * 4; + + struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\P\\#1be700\\L\\#00b3ff\\A\\#ffef00\\Y\\#ff0800\\E\\#1be700\\R\\#00b3ff\\S"); + gDjuiPlayerList = panel; + panel->base.on_render_pre = djui_panel_playerlist_on_render_pre; + djui_base_set_alignment(&panel->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + djui_base_set_size_type(&panel->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&panel->base, 580, bodyHeight + (32 + 16) + 32 + 32); + djui_base_set_visible(&panel->base, false); + struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); + djui_flow_layout_set_margin(body, 4); + + for (int i = 0; i < MAX_PLAYERS; i++) { + struct DjuiFlowLayout* row = djui_flow_layout_create(&body->base); + djui_base_set_size_type(&row->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&row->base, 1.0f, 32.0f); + int v = (i % 2) ? 16 : 32; + djui_base_set_color(&row->base, v, v, v, 128); + djui_flow_layout_set_flow_direction(row, DJUI_FLOW_DIR_RIGHT); + djui_flow_layout_set_margin(row, 8); + djui_base_set_visible(&row->base, false); + djuiRow[i] = row; + + extern ALIGNED8 const u8 texture_hud_char_mario_head[]; + struct DjuiImage* i1 = djui_image_create(&row->base, texture_hud_char_mario_head, 16, 16, 8); + djui_base_set_size(&i1->base, 32, 32); + djuiImages[i] = i1; + + int t = 220; + struct DjuiText* t2 = djui_text_create(&row->base, "name"); + djui_base_set_size_type(&t2->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&t2->base, 180, 32.0f); + djui_base_set_color(&t2->base, t, t, t, 255); + djuiTextNames[i] = t2; + + struct DjuiText* t3 = djui_text_create(&row->base, "location"); + djui_base_set_size_type(&t3->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&t3->base, 300, 32.0f); + djui_base_set_color(&t3->base, t, t, t, 255); + djui_text_set_alignment(t3, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djuiTextLocations[i] = t3; + } +} diff --git a/src/pc/djui/djui_panel_playerlist.h b/src/pc/djui/djui_panel_playerlist.h new file mode 100644 index 00000000..9608c060 --- /dev/null +++ b/src/pc/djui/djui_panel_playerlist.h @@ -0,0 +1,6 @@ +#pragma once +#include "djui.h" + +extern struct DjuiThreePanel* gDjuiPlayerList; + +void djui_panel_playerlist_create(UNUSED struct DjuiBase* caller); diff --git a/src/pc/djui/djui_text.c b/src/pc/djui/djui_text.c index 1d703cb3..3097e1b0 100644 --- a/src/pc/djui/djui_text.c +++ b/src/pc/djui/djui_text.c @@ -12,6 +12,11 @@ static u8 sSavedA = 0; //////////////// void djui_text_set_text(struct DjuiText* text, const char* message) { + // validate that the text needs to change + if (text->message != NULL && strcmp(text->message, message) == 0) { + return; + } + // deallocate old message if (text->message != NULL) { free(text->message);