Add in-game player list

This commit is contained in:
MysterD 2022-01-15 20:44:11 -08:00
parent 0d2358d4c6
commit b5762c2446
13 changed files with 288 additions and 16 deletions

View file

@ -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"
};

View file

@ -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,

View file

@ -16,6 +16,7 @@ enum CharacterType {
struct Character {
char* name;
char hudHead;
const u8* hudHeadTexture;
u32 cameraHudHead;
u32 modelId;
u32 capModelId;

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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();

View file

@ -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"

View file

@ -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;

View file

@ -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;
@ -32,6 +32,7 @@ void djui_panel_controls_create(struct DjuiBase* caller) {
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;
}

View file

@ -0,0 +1,218 @@
#include <string.h>
#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;
}
}

View file

@ -0,0 +1,6 @@
#pragma once
#include "djui.h"
extern struct DjuiThreePanel* gDjuiPlayerList;
void djui_panel_playerlist_create(UNUSED struct DjuiBase* caller);

View file

@ -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);