From dc3ca7c76d1384081f26d1b570f6158203c0ae02 Mon Sep 17 00:00:00 2001 From: TheGag96 Date: Sun, 7 Aug 2022 17:13:19 -0500 Subject: [PATCH] Arbitrary shirt, pants, glove colors + settings menu (#145) * Support for more granular player colors You can now configure RGB values for shirt, pants, gloves, and shoes. Due to some limitations, configuring shoes does nothing at the moment. * Remove paletteIndex and friends Restructured and filled in some remaining code to account for that. * Add Edit Palette panel to Player panel * Change PlayerPalette contents to an enum-indexed array, remove shoes This gets rid of all the hokey code doing switch cases on the different parts. * Fix goof with player model selection box Should actually have affect now even if a custom palette is being used. * Fix gap in player color display list commands The extra space was leftover from when I was trying to get shoes working. Forgot to clean it up. * Standardize PlayerParts enum, including for lua constants autogen * djui_panel_player.c: Properly hook sending palette changes on unpause Editing the palette and then unpausing should send out the packet to everyone with the new palette changes (and update the palette preset selection box), but since we weren't hooking that situation before, it would stay changed only for you. You would have had to press the Back button for it to work right. * Allow Lua mods to continue using `paletteIndex`, `overridePaletteIndex` This lets mod code like this still work unchanged: if s.team == 2 then np.overridePaletteIndex = 7 elseif s.team == 1 then np.overridePaletteIndex = 15 else np.overridePaletteIndex = np.paletteIndex end It's essentially faked, and would work strangely if the value of either variable was inspected more closely directly. This should at least handle the typical use case, though. Every frame, `overridePaletteIndex` is checked to see if it was modified from its previous value. If so, `overridePalette` is set to the preset corresponding to the index. `paletteIndex` contains a special value that when used to assign to `overridePaletteIndex`, it copies `palette` into `overridePalette` to restore the real colors, which of course may not follow the presets at all. * characters.h: Pack `PlayerPalette` to eliminate size differences between computers * mario_misc.c: Remove remaining "TODO GAG" --- actors/luigi/model.inc.c | 24 +- actors/mario/model.inc.c | 44 ++-- actors/toad_player/model.inc.c | 4 +- actors/waluigi/model.inc.c | 3 +- actors/wario/model.inc.c | 3 +- autogen/convert_structs.py | 6 +- autogen/lua_definitions/constants.lua | 19 +- autogen/lua_definitions/manual.lua | 6 + autogen/lua_definitions/structs.lua | 5 + docs/lua/constants.md | 12 +- docs/lua/structs.md | 13 ++ mods/arena/arena-player.lua | 6 +- mods/football.lua | 6 +- src/game/characters.c | 49 ++++ src/game/characters.h | 18 +- src/game/mario_misc.c | 134 +++-------- src/game/mario_misc.h | 4 - src/game/object_list_processor.c | 19 ++ src/pc/configfile.c | 19 +- src/pc/configfile.h | 4 +- src/pc/djui/djui_panel_player.c | 215 +++++++++++++++++- src/pc/djui/djui_selectionbox.c | 8 +- src/pc/djui/djui_selectionbox.h | 3 +- src/pc/djui/djui_slider.c | 2 +- src/pc/djui/djui_slider.h | 1 + src/pc/lua/smlua_cobject_autogen.c | 65 +++--- src/pc/lua/smlua_cobject_autogen.h | 1 + src/pc/lua/smlua_constants_autogen.c | 7 +- src/pc/network/discord/activity.c | 2 +- src/pc/network/network.c | 2 +- src/pc/network/network_player.c | 19 +- src/pc/network/network_player.h | 12 +- src/pc/network/network_utils.c | 3 +- src/pc/network/packets/packet_join.c | 14 +- .../network/packets/packet_network_players.c | 11 +- .../network/packets/packet_player_settings.c | 11 +- src/pc/pc_main.c | 1 - 37 files changed, 544 insertions(+), 231 deletions(-) diff --git a/actors/luigi/model.inc.c b/actors/luigi/model.inc.c index 79ecfcee..57a77375 100644 --- a/actors/luigi/model.inc.c +++ b/actors/luigi/model.inc.c @@ -473,8 +473,8 @@ const Gfx luigi_left_hand_closed_shared_dl[] = { }; const Gfx luigi_left_hand_closed[] = { - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_left_hand_closed_shared_dl), gsSPEndDisplayList(), }; @@ -711,8 +711,8 @@ const Gfx luigi_right_hand_closed_dl[] = { }; const Gfx luigi_right_hand_closed[] = { - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_right_hand_closed_dl), gsSPEndDisplayList(), }; @@ -3049,8 +3049,8 @@ const Gfx luigi_left_hand_open_shared_dl[] = { }; const Gfx luigi_left_hand_open[] = { - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_left_hand_open_shared_dl), gsSPEndDisplayList(), }; @@ -3191,8 +3191,8 @@ const Gfx luigi_right_hand_open_dl[] = { }; const Gfx luigi_right_hand_open[] = { - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_right_hand_open_dl), gsSPEndDisplayList(), }; @@ -3446,8 +3446,8 @@ const Gfx luigi_right_hand_cap_bottom_dl[] = { const Gfx luigi_right_hand_cap_dl[] = { gsSPDisplayList(luigi_right_hand_cap_top_dl), - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_right_hand_cap_hand_position_dl), gsSPLight(&luigi_brown2_lights_group.l, 1), gsSPLight(&luigi_brown2_lights_group.a, 2), @@ -3751,8 +3751,8 @@ const Gfx luigi_right_hand_peace_shared_dl[] = { }; const Gfx luigi_right_hand_peace[] = { - gsSPLight(&luigi_white_lights_group.l, 1), - gsSPLight(&luigi_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(luigi_right_hand_peace_shared_dl), gsSPEndDisplayList(), }; diff --git a/actors/mario/model.inc.c b/actors/mario/model.inc.c index 1611c1e6..7c963fd9 100644 --- a/actors/mario/model.inc.c +++ b/actors/mario/model.inc.c @@ -651,8 +651,8 @@ const Gfx mario_left_hand_closed_shared_dl[] = { // 0x0400D8F0 - 0x0400D910 const Gfx mario_left_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_left_hand_closed_shared_dl), gsSPEndDisplayList(), }; @@ -897,8 +897,8 @@ const Gfx mario_right_hand_closed_dl[] = { // 0x0400E458 - 0x0400E478 const Gfx mario_right_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_right_hand_closed_dl), gsSPEndDisplayList(), }; @@ -3444,8 +3444,8 @@ const Gfx mario_medium_poly_left_hand_closed_shared_dl[] = { // 0x04014DC0 - 0x04014DE0 const Gfx mario_medium_poly_left_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPLight(&mario_white_lights_group.l, 7), + gsSPLight(&mario_white_lights_group.a, 8), gsSPDisplayList(mario_medium_poly_left_hand_closed_shared_dl), gsSPEndDisplayList(), }; @@ -3483,8 +3483,8 @@ const Gfx mario_medium_poly_right_arm_shared_dl[] = { // 0x04014F40 - 0x04014F60 const Gfx mario_medium_poly_right_arm[] = { - gsSPCopyLightEXT(1, 5), // gsSPLight(&mario_red_lights_group.l, 1), - gsSPCopyLightEXT(2, 6), // gsSPLight(&mario_red_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // gsSPLight(&mario_red_lights_group.l, 1), + gsSPCopyLightEXT(2, 8), // gsSPLight(&mario_red_lights_group.a, 2), gsSPDisplayList(mario_medium_poly_right_arm_shared_dl), gsSPEndDisplayList(), }; @@ -3606,8 +3606,8 @@ const Gfx mario_medium_poly_right_hand_closed_dl[] = { // 0x040154E0 - 0x04015500 const Gfx mario_medium_poly_right_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_medium_poly_right_hand_closed_dl), gsSPEndDisplayList(), }; @@ -4253,8 +4253,8 @@ const Gfx mario_low_poly_left_hand_closed_shared_dl[] = { // 0x04016E80 - 0x04016EA0 const Gfx mario_low_poly_left_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_low_poly_left_hand_closed_shared_dl), gsSPEndDisplayList(), }; @@ -4344,8 +4344,8 @@ const Gfx mario_low_poly_right_hand_closed_dl[] = { // 0x040171C0 - 0x040171E0 const Gfx mario_low_poly_right_hand_closed[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_low_poly_right_hand_closed_dl), gsSPEndDisplayList(), }; @@ -5599,8 +5599,8 @@ const Gfx mario_left_hand_open_shared_dl[] = { // 0x04019CA0 - 0x04019CC0 const Gfx mario_left_hand_open[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_left_hand_open_shared_dl), gsSPEndDisplayList(), }; @@ -5758,8 +5758,8 @@ const Gfx mario_right_hand_open_dl[] = { // 0x0401A428 - 0x0401A448 const Gfx mario_right_hand_open[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_right_hand_open_dl), gsSPEndDisplayList(), }; @@ -6008,8 +6008,8 @@ const Gfx mario_right_hand_cap_bottom_dl[] = { // 0x0401AF20 - 0x0401AF60 const Gfx mario_right_hand_cap_dl[] = { gsSPDisplayList(mario_right_hand_cap_top_dl), - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_right_hand_cap_hand_position_dl), gsSPLight(&mario_brown2_lights_group.l, 1), gsSPLight(&mario_brown2_lights_group.a, 2), @@ -6401,8 +6401,8 @@ const Gfx mario_right_hand_peace_shared_dl[] = { // 0x0401BF30 - 0x0401BF50 const Gfx mario_right_hand_peace[] = { - gsSPLight(&mario_white_lights_group.l, 1), - gsSPLight(&mario_white_lights_group.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(mario_right_hand_peace_shared_dl), gsSPEndDisplayList(), }; diff --git a/actors/toad_player/model.inc.c b/actors/toad_player/model.inc.c index e77ae8d1..650202ff 100644 --- a/actors/toad_player/model.inc.c +++ b/actors/toad_player/model.inc.c @@ -807,8 +807,8 @@ const Gfx toad_player_dl_cap[] = { gsDPPipeSync(), gsDPSetCombineMode(G_CC_SHADEFADEA, G_CC_SHADEFADEA), - gsSPLight(&toad_player_lights_cap.l, 1), - gsSPLight(&toad_player_lights_cap.a, 2), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPDisplayList(toad_player_dl_cap_inner), diff --git a/actors/waluigi/model.inc.c b/actors/waluigi/model.inc.c index 6ef26af7..28904216 100644 --- a/actors/waluigi/model.inc.c +++ b/actors/waluigi/model.inc.c @@ -3687,7 +3687,8 @@ Gfx mat_waluigi_gloves_v3[] = { gsDPPipeSync(), gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, 0, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0, G_TX_WRAP | G_TX_NOMIRROR, 5, 0), gsDPSetTileSize(0, 0, 0, 124, 124), - gsSPSetLights1(waluigi_gloves_v3_lights), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPEndDisplayList(), }; diff --git a/actors/wario/model.inc.c b/actors/wario/model.inc.c index 63bdbddb..b88e31ea 100644 --- a/actors/wario/model.inc.c +++ b/actors/wario/model.inc.c @@ -3670,7 +3670,8 @@ Gfx mat_wario_gloves[] = { gsDPPipeSync(), gsDPSetCombineLERP(0, 0, 0, SHADE, 0, 0, 0, ENVIRONMENT, 0, 0, 0, SHADE, 0, 0, 0, ENVIRONMENT), gsSPTexture(65535, 65535, 0, 0, 1), - gsSPSetLights1(wario_white_lights_group), + gsSPCopyLightEXT(1, 7), // glove light, set in mario_misc.c + gsSPCopyLightEXT(2, 8), gsSPEndDisplayList(), }; diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py index ef36c8e4..ca82153e 100644 --- a/autogen/convert_structs.py +++ b/autogen/convert_structs.py @@ -64,7 +64,11 @@ override_field_types = { } override_field_mutable = { - "NetworkPlayer": [ "overrideModelIndex", "overridePaletteIndex" ], + "NetworkPlayer": [ + "overrideModelIndex", + "overridePalette", + "overridePaletteIndex", + ], } override_field_invisible = { diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 502c2da6..27060d20 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -2523,7 +2523,7 @@ SHAKE_SHOCK = 10 SHAKE_SMALL_DAMAGE = 3 --- @type integer -PALETTE_MAX = 32 +PALETTE_PRESET_MAX = 32 --- @class CharacterSound @@ -2682,6 +2682,20 @@ CT_WARIO = 4 --- @type CharacterType CT_MAX = 5 +--- @class PlayerParts + +--- @type PlayerParts +SHIRT = 0 + +--- @type PlayerParts +PANTS = 1 + +--- @type PlayerParts +GLOVES = 2 + +--- @type PlayerParts +PLAYER_PART_MAX = 3 + --- @class DialogId --- @type DialogId @@ -4460,6 +4474,9 @@ UNKNOWN_LOCAL_INDEX = (-1) --- @type integer UNKNOWN_NETWORK_INDEX = (-1) +--- @type integer +USE_REAL_PALETTE_VAR = 0xFF + --- @class NetworkPlayerType --- @type NetworkPlayerType diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index 023337d9..d5e4e35a 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -37,6 +37,12 @@ gLevelValues = {} --- @type BehaviorValues gBehaviorValues = {} +--- @type BehaviorValues +gBehaviorValues = {} + +--- @type PlayerPalette[] +gPalettePresets = {} + ----------- -- hooks -- ----------- diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 362e7bc0..eac25574 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -786,7 +786,10 @@ --- @field public name string --- @field public onRxSeqId integer --- @field public overrideModelIndex integer +--- @field public overridePalette PlayerPalette --- @field public overridePaletteIndex integer +--- @field public overridePaletteIndexLp integer +--- @field public palette PlayerPalette --- @field public paletteIndex integer --- @field public type integer @@ -1602,6 +1605,8 @@ --- @field public prevFloorType integer --- @field public waterHeight number +--- @class PlayerPalette + --- @class RayIntersectionInfo --- @field public hitPos Vec3f --- @field public surface Surface diff --git a/docs/lua/constants.md b/docs/lua/constants.md index b42f7175..a5188c0b 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -8,6 +8,7 @@ - [characters.h](#charactersh) - [enum CharacterSound](#enum-CharacterSound) - [enum CharacterType](#enum-CharacterType) + - [enum PlayerParts](#enum-PlayerParts) - [dialog_ids.h](#dialog_idsh) - [enum DialogId](#enum-DialogId) - [djui_hud_utils.h](#djui_hud_utilsh) @@ -809,7 +810,7 @@
## [characters.h](#characters.h) -- PALETTE_MAX +- PALETTE_PRESET_MAX ### [enum CharacterSound](#CharacterSound) | Identifier | Value | @@ -870,6 +871,14 @@ | CT_WARIO | 4 | | CT_MAX | 5 | +### [enum PlayerParts](#PlayerParts) +| Identifier | Value | +| :--------- | :---- | +| SHIRT | 0 | +| PANTS | 1 | +| GLOVES | 2 | +| PLAYER_PART_MAX | 3 | + [:arrow_up_small:](#)
@@ -1565,6 +1574,7 @@ - UNKNOWN_GLOBAL_INDEX - UNKNOWN_LOCAL_INDEX - UNKNOWN_NETWORK_INDEX +- USE_REAL_PALETTE_VAR ### [enum NetworkPlayerType](#NetworkPlayerType) | Identifier | Value | diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 3647c654..f76135da 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -48,6 +48,7 @@ - [ParallelTrackingPoint](#ParallelTrackingPoint) - [PlayerCameraState](#PlayerCameraState) - [PlayerGeometry](#PlayerGeometry) +- [PlayerPalette](#PlayerPalette) - [RayIntersectionInfo](#RayIntersectionInfo) - [SPTask](#SPTask) - [ServerSettings](#ServerSettings) @@ -1123,7 +1124,10 @@ | name | `string` | read-only | | onRxSeqId | `integer` | read-only | | overrideModelIndex | `integer` | | +| overridePalette | [PlayerPalette](structs.md#PlayerPalette) | | | overridePaletteIndex | `integer` | | +| overridePaletteIndexLp | `integer` | read-only | +| palette | [PlayerPalette](structs.md#PlayerPalette) | read-only | | paletteIndex | `integer` | read-only | | type | `integer` | read-only | @@ -2007,6 +2011,15 @@
+## [PlayerPalette](#PlayerPalette) + +| Field | Type | Access | +| ----- | ---- | ------ | + +[:arrow_up_small:](#) + +
+ ## [RayIntersectionInfo](#RayIntersectionInfo) | Field | Type | Access | diff --git a/mods/arena/arena-player.lua b/mods/arena/arena-player.lua index 9ace5ce9..815d0ef4 100644 --- a/mods/arena/arena-player.lua +++ b/mods/arena/arena-player.lua @@ -372,11 +372,11 @@ function mario_update(m) -- update palette if s.team == 2 then - np.overridePaletteIndex = 7 + np.overridePalette = gPalettePresets[7] elseif s.team == 1 then - np.overridePaletteIndex = 15 + np.overridePalette = gPalettePresets[15] else - np.overridePaletteIndex = np.paletteIndex + np.overridePalette = np.palette end -- set metal diff --git a/mods/football.lua b/mods/football.lua index 29aca154..61030f8d 100644 --- a/mods/football.lua +++ b/mods/football.lua @@ -1262,13 +1262,13 @@ function mario_update(m) local s = gPlayerSyncTable[m.playerIndex] local np = gNetworkPlayers[m.playerIndex] if s.team == 2 then - np.overridePaletteIndex = 7 + np.overridePalette = gPalettePresets[7] m.marioBodyState.modelState = 0 elseif s.team == 1 then - np.overridePaletteIndex = 15 + np.overridePalette = gPalettePresets[15] m.marioBodyState.modelState = 0 else - np.overridePaletteIndex = np.paletteIndex + np.overridePalette = np.palette m.marioBodyState.modelState = MODEL_STATE_NOISE_ALPHA end m.health = 0x880 diff --git a/src/game/characters.c b/src/game/characters.c index 96025af9..66bd6d74 100644 --- a/src/game/characters.c +++ b/src/game/characters.c @@ -355,6 +355,55 @@ struct Character gCharacters[CT_MAX] = { }, }; +const struct PlayerPalette DEFAULT_MARIO_PALETTE = {{{0xff, 0x00, 0x00}, {0x00, 0x00, 0xff}, {0xff, 0xff, 0xff}}}; + +const struct PlayerPalette gPalettePresets[PALETTE_PRESET_MAX] = { + //shirt //pants //gloves + + // default mario + {{{0xff, 0x00, 0x00}, {0x00, 0x00, 0xff}, {0xff, 0xff, 0xff}}}, + // default luigi + {{{0x00, 0x98, 0x00}, {0x00, 0x00, 0xfe}, {0xff, 0xff, 0xff}}}, + // fake waluigi + {{{0x6d, 0x3c, 0x9a}, {0x2c, 0x26, 0x3f}, {0xff, 0xff, 0xff}}}, + // fake wario + {{{0xf9, 0xeb, 0x30}, {0x7f, 0x20, 0x7a}, {0xff, 0xff, 0xff}}}, + + {{{0x7b, 0x00, 0xde}, {0xff, 0x00, 0x00}, {0xff, 0xff, 0xff}}}, + {{{0x95, 0x43, 0x01}, {0xc6, 0xb1, 0x32}, {0xff, 0xff, 0xff}}}, + {{{0x4c, 0x5f, 0x20}, {0x07, 0x09, 0x07}, {0xff, 0xff, 0xff}}}, + {{{0x00, 0x2f, 0xc8}, {0xbf, 0xde, 0xff}, {0xff, 0xff, 0xff}}}, + {{{0x11, 0x11, 0x11}, {0xf8, 0x3b, 0x05}, {0xff, 0xff, 0xff}}}, + {{{0xc1, 0x2c, 0x72}, {0x34, 0x16, 0x0d}, {0xff, 0xff, 0xff}}}, + {{{0xff, 0x96, 0xc8}, {0xff, 0x00, 0x00}, {0xff, 0xff, 0xff}}}, + {{{0x4c, 0xff, 0x4c}, {0x81, 0x00, 0x00}, {0xff, 0xff, 0xff}}}, + {{{0xa9, 0x78, 0xfc}, {0x61, 0x3d, 0x2e}, {0xff, 0xff, 0xff}}}, + + {{{0x84, 0x60, 0x00}, {0x00, 0x46, 0x5c}, {0xff, 0xff, 0xff}}}, + {{{0x5a, 0x94, 0xff}, {0x4f, 0x31, 0x8b}, {0xff, 0xff, 0xff}}}, + {{{0x68, 0x0a, 0x17}, {0x23, 0x11, 0x03}, {0xff, 0xff, 0xff}}}, + {{{0x95, 0xd0, 0x8f}, {0x53, 0x39, 0x3d}, {0xff, 0xff, 0xff}}}, + + {{{0x37, 0x32, 0x42}, {0xe6, 0xe3, 0xff}, {0xff, 0xff, 0xff}}}, + {{{0xff, 0x8a, 0x00}, {0x00, 0x51, 0x10}, {0xff, 0xff, 0xff}}}, + {{{0x65, 0xfa, 0xff}, {0x4c, 0x1e, 0x3f}, {0xff, 0xff, 0xff}}}, + + {{{0xe6, 0xe6, 0xe6}, {0xb2, 0x28, 0x18}, {0xff, 0xff, 0xff}}}, + {{{0xe6, 0xe6, 0xe6}, {0x00, 0x98, 0x00}, {0xff, 0xff, 0xff}}}, + {{{0xe6, 0xe6, 0xe6}, {0x6d, 0x3c, 0x9a}, {0xff, 0xff, 0xff}}}, + {{{0xe6, 0xe6, 0xe6}, {0xf9, 0xeb, 0x30}, {0xff, 0xff, 0xff}}}, + + {{{0xe7, 0xe7, 0x21}, {0x17, 0x18, 0x15}, {0xff, 0xff, 0xff}}}, + {{{0xaa, 0x27, 0x31}, {0xf7, 0x9a, 0x47}, {0xff, 0xff, 0xff}}}, + {{{0x55, 0x92, 0xb2}, {0xf7, 0xc2, 0x45}, {0xff, 0xff, 0xff}}}, + {{{0x10, 0x1b, 0x2e}, {0xeb, 0x8a, 0x4b}, {0xff, 0xff, 0xff}}}, + {{{0x3b, 0x8f, 0xf7}, {0xd6, 0x35, 0x4d}, {0xff, 0xff, 0xff}}}, + {{{0xff, 0x8e, 0xb2}, {0xd6, 0x35, 0x4d}, {0xff, 0xff, 0xff}}}, + + {{{0x47, 0xc5, 0xff}, {0xb2, 0x28, 0x18}, {0xff, 0xff, 0xff}}}, + {{{0x47, 0xc5, 0xff}, {0x00, 0x98, 0x00}, {0xff, 0xff, 0xff}}}, +}; + enum AnimType { ANIM_TYPE_NONE, ANIM_TYPE_LOWY, diff --git a/src/game/characters.h b/src/game/characters.h index 8aaf1d5c..7757113b 100644 --- a/src/game/characters.h +++ b/src/game/characters.h @@ -2,9 +2,25 @@ #define CHARACTERS_H #include "PR/ultratypes.h" #include "types.h" +#include "pc/configfile.h" // NOTE: do not include any additional headers -#define PALETTE_MAX 32 +#define PALETTE_PRESET_MAX 32 + +enum PlayerParts { + SHIRT, PANTS, GLOVES, PLAYER_PART_MAX + //SHOES (can't implement due to light limit) +}; + +#pragma pack(1) +struct PlayerPalette { + //rgb + u8 parts[PLAYER_PART_MAX][3]; +}; +#pragma pack() + +extern const struct PlayerPalette DEFAULT_MARIO_PALETTE; +extern const struct PlayerPalette gPalettePresets[PALETTE_PRESET_MAX]; enum CharacterType { CT_MARIO, diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index 854cd3c2..a00e5522 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -54,8 +54,7 @@ enum UnlockDoorStarStates { }; struct PlayerColor { - Lights1 shirt; - Lights1 pants; + Lights1 parts[PLAYER_PART_MAX]; }; /** @@ -80,58 +79,14 @@ struct MarioBodyState gBodyStates[MAX_PLAYERS]; struct GraphNodeObject gMirrorMario[MAX_PLAYERS]; // copy of Mario's geo node for drawing mirror Mario // ambient color is always half the diffuse color, so we can pull a macro -#define DEFINE_PLAYER_COLOR(sr, sg, sb, pr, pg, pb) \ - { \ - gdSPDefLights1((sr >> 1), (sg >> 1), (sb >> 1), sr, sg, sb, 0x28, 0x28, 0x28), \ - gdSPDefLights1((pr >> 1), (pg >> 1), (pb >> 1), pr, pg, pb, 0x28, 0x28, 0x28), \ - } +#define PALETTE_TO_LIGHTS(palette) \ + {{ \ + gdSPDefLights1((palette.parts[SHIRT][0] >> 1), (palette.parts[SHIRT][1] >> 1), (palette.parts[SHIRT][2] >> 1), palette.parts[SHIRT][0], palette.parts[SHIRT][1], palette.parts[SHIRT][2], 0x28, 0x28, 0x28), \ + gdSPDefLights1((palette.parts[PANTS][0] >> 1), (palette.parts[PANTS][1] >> 1), (palette.parts[PANTS][2] >> 1), palette.parts[PANTS][0], palette.parts[PANTS][1], palette.parts[PANTS][2], 0x28, 0x28, 0x28), \ + gdSPDefLights1((palette.parts[GLOVES][0] >> 1), (palette.parts[GLOVES][1] >> 1), (palette.parts[GLOVES][2] >> 1), palette.parts[GLOVES][0], palette.parts[GLOVES][1], palette.parts[GLOVES][2], 0x28, 0x28, 0x28), \ + }} -struct PlayerColor gPlayerColors[PALETTE_MAX] = { - // default mario - DEFINE_PLAYER_COLOR(0xff, 0x00, 0x00, /**/ 0x00, 0x00, 0xff), - // default luigi - DEFINE_PLAYER_COLOR(0x00, 0x98, 0x00, /**/ 0x00, 0x00, 0xfe), - // fake waluigi - DEFINE_PLAYER_COLOR(0x6d, 0x3c, 0x9a, /**/ 0x2c, 0x26, 0x3f), - // fake wario - DEFINE_PLAYER_COLOR(0xf9, 0xeb, 0x30, /**/ 0x7f, 0x20, 0x7a), - - DEFINE_PLAYER_COLOR(0x7b, 0x00, 0xde, /**/ 0xff, 0x00, 0x00), - DEFINE_PLAYER_COLOR(0x95, 0x43, 0x01, /**/ 0xc6, 0xb1, 0x32), - DEFINE_PLAYER_COLOR(0x4c, 0x5f, 0x20, /**/ 0x07, 0x09, 0x07), - DEFINE_PLAYER_COLOR(0x00, 0x2f, 0xc8, /**/ 0xbf, 0xde, 0xff), - DEFINE_PLAYER_COLOR(0x11, 0x11, 0x11, /**/ 0xf8, 0x3b, 0x05), - DEFINE_PLAYER_COLOR(0xc1, 0x2c, 0x72, /**/ 0x34, 0x16, 0x0d), - DEFINE_PLAYER_COLOR(0xff, 0x96, 0xc8, /**/ 0xff, 0x00, 0x00), - 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), - DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0x00, 0x98, 0x00), - DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0x6d, 0x3c, 0x9a), - DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0xf9, 0xeb, 0x30), - - DEFINE_PLAYER_COLOR(0xe7, 0xe7, 0x21, /**/ 0x17, 0x18, 0x15), - DEFINE_PLAYER_COLOR(0xaa, 0x27, 0x31, /**/ 0xf7, 0x9a, 0x47), - DEFINE_PLAYER_COLOR(0x55, 0x92, 0xb2, /**/ 0xf7, 0xc2, 0x45), - DEFINE_PLAYER_COLOR(0x10, 0x1b, 0x2e, /**/ 0xeb, 0x8a, 0x4b), - DEFINE_PLAYER_COLOR(0x3b, 0x8f, 0xf7, /**/ 0xd6, 0x35, 0x4d), - DEFINE_PLAYER_COLOR(0xff, 0x8e, 0xb2, /**/ 0xd6, 0x35, 0x4d), - - DEFINE_PLAYER_COLOR(0x47, 0xc5, 0xff, /**/ 0xb2, 0x28, 0x18), - DEFINE_PLAYER_COLOR(0x47, 0xc5, 0xff, /**/ 0x00, 0x98, 0x00), -}; - -const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerColors); +struct PlayerColor gNetworkPlayerColors[MAX_PLAYERS]; // This whole file is weirdly organized. It has to be the same file due // to rodata boundaries and function aligns, which means the programmer @@ -139,36 +94,6 @@ const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerColors); // (message NPC related things, the Mario head geo, and Mario geo // functions) -/** - * Set the Light1 struct from player colors. - * The 4th component is the shade factor (difference between ambient and diffuse), - * usually set to 1. - */ -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 (paletteIndex >= gNumPlayerColors) paletteIndex = gNumPlayerColors - 1; - const u8 pAmb[3] = { pants[0] >> pants[3], pants[1] >> pants[3], pants[2] >> pants[3] }; - const u8 sAmb[3] = { shirt[0] >> shirt[3], shirt[1] >> shirt[3], shirt[2] >> shirt[3] }; - gPlayerColors[paletteIndex].pants = - (Lights1) gdSPDefLights1(pAmb[0], pAmb[1], pAmb[2], pants[0], pants[1], pants[2], 0x28, 0x28, 0x28); - gPlayerColors[paletteIndex].shirt = - (Lights1) gdSPDefLights1(sAmb[0], sAmb[1], sAmb[2], shirt[0], shirt[1], shirt[2], 0x28, 0x28, 0x28); -} - -/** - * Return the specified color for player globalIndex. - * 0 = shirt, 1 = pants - * Returns RGB, not RGBA! - */ -u8 *get_player_color(u8 paletteIndex, const s32 which) { - // choose the last color in the table for extra players - if (paletteIndex >= gNumPlayerColors) paletteIndex = gNumPlayerColors - 1; - if (which == 0) - return gPlayerColors[paletteIndex].shirt.l[0].l.col; - else - return gPlayerColors[paletteIndex].pants.l[0].l.col; -} - /** * Geo node script that draws Mario's head on the title screen. */ @@ -827,21 +752,26 @@ 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].overridePaletteIndex; + + struct PlayerColor color = PALETTE_TO_LIGHTS(gNetworkPlayers[index].overridePalette); + + gNetworkPlayerColors[index] = color; + struct MarioBodyState* bodyState = &gBodyStates[index]; if (callContext == GEO_CONTEXT_RENDER) { // extra players get last color - if (colorIndex >= gNumPlayerColors) colorIndex = gNumPlayerColors - 1; - gfx = alloc_display_list(5 * sizeof(*gfx)); + gfx = alloc_display_list(7 * sizeof(*gfx)); if (gfx == NULL) { return NULL; } - // put the player colors into lights 3, 4, 5, 6 + // put the player colors into lights 3, 4, 5, 6, 7, 8 // they will be later copied to lights 1, 2 with gsSPCopyLightEXT - gSPLight(gfx + 0, &gPlayerColors[colorIndex].pants.l, 3); - gSPLight(gfx + 1, &gPlayerColors[colorIndex].pants.a, 4); - gSPLight(gfx + 2, &gPlayerColors[colorIndex].shirt.l, 5); - gSPLight(gfx + 3, &gPlayerColors[colorIndex].shirt.a, 6); - gSPEndDisplayList(gfx + 4); + gSPLight(gfx + 0, &gNetworkPlayerColors[index].parts[PANTS].l, 3); + gSPLight(gfx + 1, &gNetworkPlayerColors[index].parts[PANTS].a, 4); + gSPLight(gfx + 2, &gNetworkPlayerColors[index].parts[SHIRT].l, 5); + gSPLight(gfx + 3, &gNetworkPlayerColors[index].parts[SHIRT].a, 6); + gSPLight(gfx + 4, &gNetworkPlayerColors[index].parts[GLOVES].l, 7); + gSPLight(gfx + 5, &gNetworkPlayerColors[index].parts[GLOVES].a, 8); + gSPEndDisplayList(gfx + 6); u32 layer = LAYER_OPAQUE; if (asGenerated->parameter == 0) { // put on transparent layer if vanish effect, opaque otherwise @@ -861,24 +791,30 @@ Gfx* geo_mario_set_player_colors(s32 callContext, struct GraphNode* node, UNUSED Gfx* geo_mario_cap_display_list(s32 callContext, struct GraphNode* node, UNUSED Mat4* c) { if (callContext != GEO_CONTEXT_RENDER) { return NULL; } u8 globalIndex = geo_get_processing_object_index(); - u8 colorIndex = gNetworkPlayers[globalIndex].overridePaletteIndex; + + struct PlayerColor color = PALETTE_TO_LIGHTS(gNetworkPlayers[globalIndex].overridePalette); + + gNetworkPlayerColors[globalIndex] = color; + u8 charIndex = gNetworkPlayers[globalIndex].overrideModelIndex; if (charIndex >= CT_MAX) { charIndex = 0; } struct Character* character = &gCharacters[charIndex]; - u8 dpLength = 5; + u8 dpLength = 7; if (character->capEnemyGfx != NULL) { dpLength++; } if (character->capEnemyDecalGfx != NULL) { dpLength++; } Gfx* gfx = alloc_display_list(dpLength * sizeof(*gfx)); if (gfx == NULL) { return NULL; } Gfx* onGfx = gfx; - // put the player colors into lights 3, 4, 5, 6 + // put the player colors into lights 3, 4, 5, 6, 7, 8 // they will be later copied to lights 1, 2 with gsSPCopyLightEXT - gSPLight(onGfx++, &gPlayerColors[colorIndex].pants.l, 3); - gSPLight(onGfx++, &gPlayerColors[colorIndex].pants.a, 4); - gSPLight(onGfx++, &gPlayerColors[colorIndex].shirt.l, 5); - gSPLight(onGfx++, &gPlayerColors[colorIndex].shirt.a, 6); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[PANTS].l, 3); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[PANTS].a, 4); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[SHIRT].l, 5); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[SHIRT].a, 6); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[GLOVES].l, 7); + gSPLight(onGfx++, &gNetworkPlayerColors[globalIndex].parts[GLOVES].a, 8); if (character->capEnemyGfx != NULL) { gSPDisplayList(onGfx++, character->capEnemyGfx); } if (character->capEnemyDecalGfx != NULL) { gSPDisplayList(onGfx++, character->capEnemyDecalGfx); } gSPEndDisplayList(onGfx++); diff --git a/src/game/mario_misc.h b/src/game/mario_misc.h index ce1ce654..63d673ed 100644 --- a/src/game/mario_misc.h +++ b/src/game/mario_misc.h @@ -8,10 +8,6 @@ extern struct GraphNodeObject gMirrorMario[MAX_PLAYERS]; extern struct MarioBodyState gBodyStates[MAX_PLAYERS]; -extern const size_t gNumPlayerColors; - -void set_player_colors(u8 paletteIndex, const u8 shirt[4], const u8 pants[4]); -u8 *get_player_color(u8 paletteIndex, const s32 which); Gfx *geo_draw_mario_head_goddard(s32 callContext, struct GraphNode *node, Mat4 *c); void bhv_toad_message_loop(void); diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 202fb2c1..5e29f3c7 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -296,6 +296,25 @@ void bhv_mario_update(void) { particleFlags |= gMarioState->particleFlags; gCurrentObject->oMarioParticleFlags = particleFlags; + // This code is meant to preserve old Lua mods' ability to set overridePaletteIndex and paletteIndex and still work + // as they expected. USE_REAL_PALETTE_VAR is meant to help support cases where mods will do: + // np.overridePaletteIndex = np.paletteIndex + // to undo the palette override and have it still go back to the new REAL palette stored in `palette`. + { + struct NetworkPlayer *np = &gNetworkPlayers[gMarioState->playerIndex]; + + if (np->overridePaletteIndex != np->overridePaletteIndexLp) { + np->overridePaletteIndexLp = np->overridePaletteIndex; + + if (np->overridePaletteIndex == USE_REAL_PALETTE_VAR) { + np->overridePalette = np->palette; + } + else { + np->overridePalette = gPalettePresets[np->overridePaletteIndex]; + } + } + } + // Mario code updates MarioState's versions of position etc, so we need // to sync it with the Mario object copy_mario_state_to_object(gMarioState); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 342272e3..e26ee32b 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -18,7 +18,6 @@ #include "pc/crash_handler.h" #include "pc/network/moderator_list.h" - #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) enum ConfigOptionType { @@ -28,6 +27,7 @@ enum ConfigOptionType { CONFIG_TYPE_BIND, CONFIG_TYPE_STRING, CONFIG_TYPE_U64, + CONFIG_TYPE_COLOR, }; struct ConfigOption { @@ -39,6 +39,7 @@ struct ConfigOption { float* floatValue; char* stringValue; u64* u64Value; + u8 (*colorValue)[3]; }; int maxStringLength; }; @@ -132,7 +133,7 @@ unsigned int configStayInLevelAfterStar = 0; unsigned int configNetworkSystem = 0; char configPlayerName[MAX_PLAYER_STRING] = ""; unsigned int configPlayerModel = 0; -unsigned int configPlayerPalette = 0; +struct PlayerPalette configPlayerPalette = {{{0xff, 0x00, 0x00}, {0x00, 0x00, 0xff}, {0xff, 0xff, 0xff}}}; bool configUncappedFramerate = true; unsigned int configFrameLimit = 60; unsigned int configDrawDistance = 5; @@ -219,7 +220,9 @@ static const struct ConfigOption options[] = { {.name = "coop_player_knockback_strength", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerKnockbackStrength}, {.name = "coop_player_model", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerModel}, {.name = "coop_player_name", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configPlayerName, .maxStringLength = MAX_PLAYER_STRING}, - {.name = "coop_player_palette", .type = CONFIG_TYPE_UINT , .uintValue = &configPlayerPalette}, + {.name = "coop_player_palette_shirt", .type = CONFIG_TYPE_COLOR , .colorValue = &configPlayerPalette.parts[SHIRT]}, + {.name = "coop_player_palette_pants", .type = CONFIG_TYPE_COLOR , .colorValue = &configPlayerPalette.parts[PANTS]}, + {.name = "coop_player_palette_gloves", .type = CONFIG_TYPE_COLOR , .colorValue = &configPlayerPalette.parts[GLOVES]}, {.name = "coop_stay_in_level_after_star", .type = CONFIG_TYPE_UINT , .uintValue = &configStayInLevelAfterStar}, {.name = "share_lives", .type = CONFIG_TYPE_BOOL , .boolValue = &configShareLives}, {.name = "disable_popups", .type = CONFIG_TYPE_BOOL , .boolValue = &configDisablePopups}, @@ -405,6 +408,7 @@ const char *configfile_name(void) { void configfile_load(const char *filename) { fs_file_t *file; char *line; + unsigned int temp; printf("Loading configuration from '%s'\n", filename); @@ -480,6 +484,12 @@ void configfile_load(const char *filename) { case CONFIG_TYPE_U64: sscanf(tokens[1], "%llu", option->u64Value); break; + case CONFIG_TYPE_COLOR: + for (int i = 0; i < 3 && i < numTokens - 1; ++i) { + sscanf(tokens[i + 1], "%x", &temp); + (*option->colorValue)[i] = temp; + } + break; default: assert(0); // bad type } @@ -541,6 +551,9 @@ void configfile_save(const char *filename) { case CONFIG_TYPE_U64: fprintf(file, "%s %llu\n", option->name, *option->u64Value); break; + case CONFIG_TYPE_COLOR: + fprintf(file, "%s %02x %02x %02x\n", option->name, (*option->colorValue)[0], (*option->colorValue)[1], (*option->colorValue)[2]); + break; default: assert(0); // unknown type } diff --git a/src/pc/configfile.h b/src/pc/configfile.h index 4d7f2966..b01ec11c 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -2,6 +2,8 @@ #define CONFIGFILE_H #include +#include "PR/ultratypes.h" +#include "game/characters.h" #define CONFIGFILE_DEFAULT "sm64config.txt" @@ -88,7 +90,7 @@ extern unsigned int configStayInLevelAfterStar; extern unsigned int configNetworkSystem; extern char configPlayerName[]; extern unsigned int configPlayerModel; -extern unsigned int configPlayerPalette; +extern struct PlayerPalette configPlayerPalette; extern bool configUncappedFramerate; extern unsigned int configFrameLimit; extern unsigned int configDrawDistance; diff --git a/src/pc/djui/djui_panel_player.c b/src/pc/djui/djui_panel_player.c index 2814dc6f..0d279b1f 100644 --- a/src/pc/djui/djui_panel_player.c +++ b/src/pc/djui/djui_panel_player.c @@ -1,10 +1,185 @@ #include +#include #include "djui.h" #include "pc/configfile.h" #include "pc/network/network_player.h" #include "game/level_update.h" #include "game/area.h" +#define PALETTE_CUSTOM PALETTE_PRESET_MAX + +static unsigned int sPalettePresetIndex = PALETTE_CUSTOM; +static unsigned int sCurrentPlayerPart = SHIRT; +static unsigned int sSliderChannels[3] = {0}; + +static struct DjuiSelectionbox* sPalettePresetSelection; + +static struct DjuiSelectionbox* sPartSelection; +static struct DjuiInputbox* sHexColorTextBox; +static struct DjuiSlider *sSliderR, *sSliderG, *sSliderB; + +static void djui_panel_player_edit_palette_update_hex_code_box() { + char buf[7]; + static const char digitToChar[] = "0123456789abcdef"; + + for (size_t i = 0; i < 3; i++) { + buf[2*i] = digitToChar[configPlayerPalette.parts[sCurrentPlayerPart][i] >> 4]; + buf[2*i+1] = digitToChar[configPlayerPalette.parts[sCurrentPlayerPart][i] & 0xF]; + } + + buf[6] = '\0'; + + djui_inputbox_set_text(sHexColorTextBox, buf); +} + +static void djui_panel_player_edit_palette_update_palette_display() { + if (memcmp(&gNetworkPlayers[0].overridePalette, &gNetworkPlayers[0].palette, sizeof(struct PlayerPalette)) == 0) { + gNetworkPlayers[0].overridePalette = configPlayerPalette; + } + + gNetworkPlayers[0].palette = configPlayerPalette; +} + +static void djui_panel_player_edit_palette_update_sliders() { + for (int i = 0; i < 3; i++) sSliderChannels[i] = configPlayerPalette.parts[sCurrentPlayerPart][i]; + + djui_slider_update_value(&sSliderR->base); + djui_slider_update_value(&sSliderG->base); + djui_slider_update_value(&sSliderB->base); +} + +static void djui_panel_player_edit_palette_part_changed(UNUSED struct DjuiBase* caller) { + djui_panel_player_edit_palette_update_sliders(); + djui_panel_player_edit_palette_update_hex_code_box(); +} + +static int char_to_hex_digit(char c) { + return (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10; +} + +static void djui_panel_player_edit_palette_hex_code_changed(struct DjuiBase* caller) { + struct DjuiInputbox* input = (struct DjuiInputbox*) caller; + + for (int i = 0; i < 6; i++) { + char c = input->buffer[i]; + if (c == '\0') return; // all 6 characters must be filled + + if (c >= 'A' && c <= 'Z') { + input->buffer[i] = c - 'A' + 'a'; // convert all characters to lowercase + } + + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) return; + } + + for (int i = 0; i < 3; i++) { + configPlayerPalette.parts[sCurrentPlayerPart][i] = (char_to_hex_digit(input->buffer[2*i]) << 4) | + char_to_hex_digit(input->buffer[2*i] + 1); + } + + djui_panel_player_edit_palette_update_sliders(); + djui_panel_player_edit_palette_update_palette_display(); + sPalettePresetIndex = PALETTE_CUSTOM; +} + +static void djui_panel_player_edit_palette_slider_changed(UNUSED struct DjuiBase* caller, size_t index) { + configPlayerPalette.parts[sCurrentPlayerPart][index] = sSliderChannels[index]; + + djui_panel_player_edit_palette_update_hex_code_box(); + djui_panel_player_edit_palette_update_palette_display(); + sPalettePresetIndex = PALETTE_CUSTOM; +} + +static void djui_panel_player_edit_palette_red_changed(UNUSED struct DjuiBase* caller) { + djui_panel_player_edit_palette_slider_changed(caller, 0); +} + +static void djui_panel_player_edit_palette_green_changed(UNUSED struct DjuiBase* caller) { + djui_panel_player_edit_palette_slider_changed(caller, 1); +} + +static void djui_panel_player_edit_palette_blue_changed(UNUSED struct DjuiBase* caller) { + djui_panel_player_edit_palette_slider_changed(caller, 2); +} + +static void (*sSavedDestroy)(struct DjuiBase*); +void djui_panel_player_edit_palette_destroy(struct DjuiBase* caller) { + if (gNetworkType != NT_NONE) { + network_send_player_settings(); + } + + djui_selectionbox_update_value(&sPalettePresetSelection->base); // since editing palette values can change it + + (*sSavedDestroy)(caller); +} + +static void djui_panel_player_edit_palette_create(struct DjuiBase* caller) { + char* sPartStrings[PLAYER_PART_MAX] = { "Shirt", "Pants", "Gloves" }; + + f32 bodyHeight = 32 * 5 + 64 * 1 + 16 * 5; + + struct DjuiBase* defaultBase = NULL; + struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\P\\#1be700\\A\\#00b3ff\\L\\#ffef00\\E\\#ff0800\\T\\#1be700\\T\\#00b3ff\\E"); + + // A bit of a gross hack to send out palette changes and update the palette preset selection box on unpause AND + // pressing the Back button + sSavedDestroy = panel->base.destroy; + panel->base.destroy = djui_panel_player_edit_palette_destroy; + + struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); + + { + sCurrentPlayerPart = SHIRT; + sPartSelection = djui_selectionbox_create(&body->base, "Part", sPartStrings, PLAYER_PART_MAX, &sCurrentPlayerPart); + djui_base_set_size_type(&sPartSelection->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sPartSelection->base, 1.0f, 32); + djui_interactable_hook_value_change(&sPartSelection->base, djui_panel_player_edit_palette_part_changed); + + 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, "Hex Code"); + 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); + + sHexColorTextBox = djui_inputbox_create(&rect1->base, 7); + djui_base_set_size_type(&sHexColorTextBox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sHexColorTextBox->base, 0.5f, 32); + djui_base_set_alignment(&sHexColorTextBox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djui_panel_player_edit_palette_update_hex_code_box(); + djui_interactable_hook_value_change(&sHexColorTextBox->base, djui_panel_player_edit_palette_hex_code_changed); + } + + for (int i = 0; i < 3; i++) sSliderChannels[i] = configPlayerPalette.parts[SHIRT][i]; + + sSliderR = djui_slider_create(&body->base, "Red", &sSliderChannels[0], 0, 255); + djui_base_set_size_type(&sSliderR->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_interactable_hook_value_change(&sSliderR->base, djui_panel_player_edit_palette_red_changed); + djui_base_set_size(&sSliderR->base, 1.0f, 32); + + sSliderG = djui_slider_create(&body->base, "Green", &sSliderChannels[1], 0, 255); + djui_base_set_size_type(&sSliderG->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_interactable_hook_value_change(&sSliderG->base, djui_panel_player_edit_palette_green_changed); + djui_base_set_size(&sSliderG->base, 1.0f, 32); + + sSliderB = djui_slider_create(&body->base, "Blue", &sSliderChannels[2], 0, 255); + djui_base_set_size_type(&sSliderB->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_interactable_hook_value_change(&sSliderB->base, djui_panel_player_edit_palette_blue_changed); + djui_base_set_size(&sSliderB->base, 1.0f, 32); + + 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); +} + static bool djui_panel_player_name_valid(char* buffer) { if (buffer[0] == '\0') { return false; } while (*buffer != '\0') { @@ -39,12 +214,16 @@ static void djui_panel_player_name_on_focus_end(struct DjuiBase* caller) { } } -void djui_panel_player_value_changed(UNUSED struct DjuiBase* caller) { +static void djui_panel_player_value_changed(UNUSED struct DjuiBase* caller) { + if (sPalettePresetIndex != PALETTE_CUSTOM) { + configPlayerPalette = gPalettePresets[sPalettePresetIndex]; + djui_panel_player_edit_palette_update_palette_display(); + } + if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; } - if (gNetworkPlayers[0].overrideModelIndex == gNetworkPlayers[0].modelIndex) { gNetworkPlayers[0].overrideModelIndex = configPlayerModel; } - if (gNetworkPlayers[0].overridePaletteIndex == gNetworkPlayers[0].paletteIndex) { gNetworkPlayers[0].overridePaletteIndex = configPlayerPalette; } - gNetworkPlayers[0].modelIndex = configPlayerModel; - gNetworkPlayers[0].paletteIndex = configPlayerPalette; + if (gNetworkPlayers[0].overrideModelIndex == gNetworkPlayers[0].modelIndex) { gNetworkPlayers[0].overrideModelIndex = configPlayerModel; } + + gNetworkPlayers[0].modelIndex = configPlayerModel; network_player_update_model(0); if (gNetworkType != NT_NONE) { @@ -53,7 +232,7 @@ void djui_panel_player_value_changed(UNUSED struct DjuiBase* caller) { } void djui_panel_player_create(struct DjuiBase* caller) { - f32 bodyHeight = 32 * 3 + 64 * 1 + 16 * 4; + f32 bodyHeight = 32 * 3 + 64 * 2 + 16 * 5; struct DjuiBase* defaultBase = NULL; struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\P\\#1be700\\L\\#00b3ff\\A\\#ffef00\\Y\\#ff0800\\E\\#1be700\\R"); @@ -93,7 +272,7 @@ void djui_panel_player_create(struct DjuiBase* caller) { djui_base_set_size(&selectionbox1->base, 1.0f, 32); djui_interactable_hook_value_change(&selectionbox1->base, djui_panel_player_value_changed); - char* paletteChoices[PALETTE_MAX] = { + char* paletteChoices[PALETTE_PRESET_MAX+1] = { "Mario", "Luigi", "Waluigi", @@ -126,11 +305,25 @@ void djui_panel_player_create(struct DjuiBase* caller) { "Bubblegum", "Ice Mario", "Ice Luigi", + "Custom", }; - struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "Palette", paletteChoices, PALETTE_MAX, &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); + + for (int i = 0; i < PALETTE_PRESET_MAX; i++) { + if (memcmp(&gNetworkPlayers[0].palette, &gPalettePresets[i], sizeof(struct PlayerPalette)) == 0) { + sPalettePresetIndex = i; + break; + } + } + + sPalettePresetSelection = djui_selectionbox_create(&body->base, "Palette Preset", paletteChoices, PALETTE_PRESET_MAX+1, &sPalettePresetIndex); + djui_base_set_size_type(&sPalettePresetSelection->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sPalettePresetSelection->base, 1.0f, 32); + djui_interactable_hook_value_change(&sPalettePresetSelection->base, djui_panel_player_value_changed); + + struct DjuiButton* editPaletteButton = djui_button_create(&body->base, "Edit Palette"); + djui_base_set_size_type(&editPaletteButton->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&editPaletteButton->base, 1.0f, 64); + djui_interactable_hook_click(&editPaletteButton->base, djui_panel_player_edit_palette_create); struct DjuiButton* button6 = djui_button_create(&body->base, "Back"); djui_base_set_size_type(&button6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); diff --git a/src/pc/djui/djui_selectionbox.c b/src/pc/djui/djui_selectionbox.c index de5dbadc..1991400f 100644 --- a/src/pc/djui/djui_selectionbox.c +++ b/src/pc/djui/djui_selectionbox.c @@ -45,18 +45,24 @@ static void djui_selectionbox_get_cursor_hover_location(struct DjuiBase* base, f *y = (rectBase->elem.y + rectBase->elem.height * 3.0f / 4.0f); } +void djui_selectionbox_update_value(struct DjuiBase* base) { + struct DjuiSelectionbox* selectionbox = (struct DjuiSelectionbox*)base; + djui_text_set_text(selectionbox->rectText, selectionbox->choices[*selectionbox->value]); +} + static void djui_selectionbox_on_cursor_down_begin(struct DjuiBase* base, UNUSED bool inputCursor) { struct DjuiSelectionbox* selectionbox = (struct DjuiSelectionbox*)base; f32 x = selectionbox->rect->base.elem.x; if (gCursorX >= x) { *selectionbox->value = (*selectionbox->value + 1) % selectionbox->choiceCount; - djui_text_set_text(selectionbox->rectText, selectionbox->choices[*selectionbox->value]); + djui_selectionbox_update_value(base); if (base != NULL && base->interactable != NULL && base->interactable->on_value_change != NULL) { base->interactable->on_value_change(base); } } } + static void djui_selectionbox_destroy(struct DjuiBase* base) { struct DjuiSelectionbox* selectionbox = (struct DjuiSelectionbox*)base; for (int i = 0; i < selectionbox->choiceCount; i++) { diff --git a/src/pc/djui/djui_selectionbox.h b/src/pc/djui/djui_selectionbox.h index 736c40a8..56fdcc7b 100644 --- a/src/pc/djui/djui_selectionbox.h +++ b/src/pc/djui/djui_selectionbox.h @@ -12,4 +12,5 @@ struct DjuiSelectionbox { u8 choiceCount; }; -struct DjuiSelectionbox* djui_selectionbox_create(struct DjuiBase* parent, const char* message, char* choices[], u8 choiceCount, unsigned int* value); \ No newline at end of file +struct DjuiSelectionbox* djui_selectionbox_create(struct DjuiBase* parent, const char* message, char* choices[], u8 choiceCount, unsigned int* value); +void djui_selectionbox_update_value(struct DjuiBase* base); \ No newline at end of file diff --git a/src/pc/djui/djui_slider.c b/src/pc/djui/djui_slider.c index aec15567..d9b8a653 100644 --- a/src/pc/djui/djui_slider.c +++ b/src/pc/djui/djui_slider.c @@ -28,7 +28,7 @@ static void djui_slider_update_style(struct DjuiBase* base) { } } -static void djui_slider_update_value(struct DjuiBase* base) { +void djui_slider_update_value(struct DjuiBase* base) { struct DjuiSlider* slider = (struct DjuiSlider*)base; u32 min = slider->min; u32 max = slider->max; diff --git a/src/pc/djui/djui_slider.h b/src/pc/djui/djui_slider.h index 1c80eaee..f5da2799 100644 --- a/src/pc/djui/djui_slider.h +++ b/src/pc/djui/djui_slider.h @@ -12,3 +12,4 @@ struct DjuiSlider { }; struct DjuiSlider* djui_slider_create(struct DjuiBase* parent, const char* message, unsigned int* value, unsigned int min, unsigned int max); +void djui_slider_update_value(struct DjuiBase* base); diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index ee878116..9b4fd60e 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -891,35 +891,38 @@ static struct LuaObjectField sModeTransitionInfoFields[LUA_MODE_TRANSITION_INFO_ { "transitionStart", LVT_COBJECT, offsetof(struct ModeTransitionInfo, transitionStart), true, LOT_LINEARTRANSITIONPOINT }, }; -#define LUA_NETWORK_PLAYER_FIELD_COUNT 25 +#define LUA_NETWORK_PLAYER_FIELD_COUNT 28 static struct LuaObjectField sNetworkPlayerFields[LUA_NETWORK_PLAYER_FIELD_COUNT] = { - { "connected", LVT_BOOL, offsetof(struct NetworkPlayer, connected), true, LOT_NONE }, - { "currActNum", LVT_S16, offsetof(struct NetworkPlayer, currActNum), true, LOT_NONE }, - { "currAreaIndex", LVT_S16, offsetof(struct NetworkPlayer, currAreaIndex), true, LOT_NONE }, - { "currAreaSyncValid", LVT_BOOL, offsetof(struct NetworkPlayer, currAreaSyncValid), true, LOT_NONE }, - { "currCourseNum", LVT_S16, offsetof(struct NetworkPlayer, currCourseNum), true, LOT_NONE }, - { "currLevelAreaSeqId", LVT_U16, offsetof(struct NetworkPlayer, currLevelAreaSeqId), true, LOT_NONE }, - { "currLevelNum", LVT_S16, offsetof(struct NetworkPlayer, currLevelNum), true, LOT_NONE }, - { "currLevelSyncValid", LVT_BOOL, offsetof(struct NetworkPlayer, currLevelSyncValid), true, LOT_NONE }, - { "description", LVT_STRING, offsetof(struct NetworkPlayer, description), true, LOT_NONE }, - { "descriptionA", LVT_U8, offsetof(struct NetworkPlayer, descriptionA), true, LOT_NONE }, - { "descriptionB", LVT_U8, offsetof(struct NetworkPlayer, descriptionB), true, LOT_NONE }, - { "descriptionG", LVT_U8, offsetof(struct NetworkPlayer, descriptionG), true, LOT_NONE }, - { "descriptionR", LVT_U8, offsetof(struct NetworkPlayer, descriptionR), true, LOT_NONE }, - { "fadeOpacity", LVT_U8, offsetof(struct NetworkPlayer, fadeOpacity), true, LOT_NONE }, - { "globalIndex", LVT_U8, offsetof(struct NetworkPlayer, globalIndex), true, LOT_NONE }, - { "lastReceived", LVT_F32, offsetof(struct NetworkPlayer, lastReceived), true, LOT_NONE }, - { "lastSent", LVT_F32, offsetof(struct NetworkPlayer, lastSent), true, LOT_NONE }, - { "localIndex", LVT_U8, offsetof(struct NetworkPlayer, localIndex), true, LOT_NONE }, - { "modelIndex", LVT_U8, offsetof(struct NetworkPlayer, modelIndex), true, LOT_NONE }, - { "name", LVT_STRING, offsetof(struct NetworkPlayer, name), true, LOT_NONE }, - { "onRxSeqId", LVT_U8, offsetof(struct NetworkPlayer, onRxSeqId), true, LOT_NONE }, - { "overrideModelIndex", LVT_U8, offsetof(struct NetworkPlayer, overrideModelIndex), false, LOT_NONE }, - { "overridePaletteIndex", LVT_U8, offsetof(struct NetworkPlayer, overridePaletteIndex), false, LOT_NONE }, - { "paletteIndex", LVT_U8, offsetof(struct NetworkPlayer, paletteIndex), true, LOT_NONE }, -// { "rxPacketHash", LOT_???, offsetof(struct NetworkPlayer, rxPacketHash), true, LOT_??? }, <--- UNIMPLEMENTED -// { "rxSeqIds", LOT_???, offsetof(struct NetworkPlayer, rxSeqIds), true, LOT_??? }, <--- UNIMPLEMENTED - { "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE }, + { "connected", LVT_BOOL, offsetof(struct NetworkPlayer, connected), true, LOT_NONE }, + { "currActNum", LVT_S16, offsetof(struct NetworkPlayer, currActNum), true, LOT_NONE }, + { "currAreaIndex", LVT_S16, offsetof(struct NetworkPlayer, currAreaIndex), true, LOT_NONE }, + { "currAreaSyncValid", LVT_BOOL, offsetof(struct NetworkPlayer, currAreaSyncValid), true, LOT_NONE }, + { "currCourseNum", LVT_S16, offsetof(struct NetworkPlayer, currCourseNum), true, LOT_NONE }, + { "currLevelAreaSeqId", LVT_U16, offsetof(struct NetworkPlayer, currLevelAreaSeqId), true, LOT_NONE }, + { "currLevelNum", LVT_S16, offsetof(struct NetworkPlayer, currLevelNum), true, LOT_NONE }, + { "currLevelSyncValid", LVT_BOOL, offsetof(struct NetworkPlayer, currLevelSyncValid), true, LOT_NONE }, + { "description", LVT_STRING, offsetof(struct NetworkPlayer, description), true, LOT_NONE }, + { "descriptionA", LVT_U8, offsetof(struct NetworkPlayer, descriptionA), true, LOT_NONE }, + { "descriptionB", LVT_U8, offsetof(struct NetworkPlayer, descriptionB), true, LOT_NONE }, + { "descriptionG", LVT_U8, offsetof(struct NetworkPlayer, descriptionG), true, LOT_NONE }, + { "descriptionR", LVT_U8, offsetof(struct NetworkPlayer, descriptionR), true, LOT_NONE }, + { "fadeOpacity", LVT_U8, offsetof(struct NetworkPlayer, fadeOpacity), true, LOT_NONE }, + { "globalIndex", LVT_U8, offsetof(struct NetworkPlayer, globalIndex), true, LOT_NONE }, + { "lastReceived", LVT_F32, offsetof(struct NetworkPlayer, lastReceived), true, LOT_NONE }, + { "lastSent", LVT_F32, offsetof(struct NetworkPlayer, lastSent), true, LOT_NONE }, + { "localIndex", LVT_U8, offsetof(struct NetworkPlayer, localIndex), true, LOT_NONE }, + { "modelIndex", LVT_U8, offsetof(struct NetworkPlayer, modelIndex), true, LOT_NONE }, + { "name", LVT_STRING, offsetof(struct NetworkPlayer, name), true, LOT_NONE }, + { "onRxSeqId", LVT_U8, offsetof(struct NetworkPlayer, onRxSeqId), true, LOT_NONE }, + { "overrideModelIndex", LVT_U8, offsetof(struct NetworkPlayer, overrideModelIndex), false, LOT_NONE }, + { "overridePalette", LVT_COBJECT, offsetof(struct NetworkPlayer, overridePalette), false, LOT_PLAYERPALETTE }, + { "overridePaletteIndex", LVT_U8, offsetof(struct NetworkPlayer, overridePaletteIndex), false, LOT_NONE }, + { "overridePaletteIndexLp", LVT_U8, offsetof(struct NetworkPlayer, overridePaletteIndexLp), true, LOT_NONE }, + { "palette", LVT_COBJECT, offsetof(struct NetworkPlayer, palette), true, LOT_PLAYERPALETTE }, + { "paletteIndex", LVT_U8, offsetof(struct NetworkPlayer, paletteIndex), true, LOT_NONE }, +// { "rxPacketHash", LOT_???, offsetof(struct NetworkPlayer, rxPacketHash), true, LOT_??? }, <--- UNIMPLEMENTED +// { "rxSeqIds", LOT_???, offsetof(struct NetworkPlayer, rxSeqIds), true, LOT_??? }, <--- UNIMPLEMENTED + { "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE }, }; #define LUA_OBJECT_FIELD_COUNT 755 @@ -1762,6 +1765,11 @@ static struct LuaObjectField sPlayerGeometryFields[LUA_PLAYER_GEOMETRY_FIELD_COU { "waterHeight", LVT_F32, offsetof(struct PlayerGeometry, waterHeight), false, LOT_NONE }, }; +#define LUA_PLAYER_PALETTE_FIELD_COUNT 0 +static struct LuaObjectField sPlayerPaletteFields[LUA_PLAYER_PALETTE_FIELD_COUNT] = { +// { "parts", LOT_???, offsetof(struct PlayerPalette, parts), false, LOT_??? }, <--- UNIMPLEMENTED +}; + #define LUA_RAY_INTERSECTION_INFO_FIELD_COUNT 2 static struct LuaObjectField sRayIntersectionInfoFields[LUA_RAY_INTERSECTION_INFO_FIELD_COUNT] = { { "hitPos", LVT_COBJECT, offsetof(struct RayIntersectionInfo, hitPos), true, LOT_VEC3F }, @@ -2026,6 +2034,7 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] { LOT_PARALLELTRACKINGPOINT, sParallelTrackingPointFields, LUA_PARALLEL_TRACKING_POINT_FIELD_COUNT }, { LOT_PLAYERCAMERASTATE, sPlayerCameraStateFields, LUA_PLAYER_CAMERA_STATE_FIELD_COUNT }, { LOT_PLAYERGEOMETRY, sPlayerGeometryFields, LUA_PLAYER_GEOMETRY_FIELD_COUNT }, + { LOT_PLAYERPALETTE, sPlayerPaletteFields, LUA_PLAYER_PALETTE_FIELD_COUNT }, { LOT_RAYINTERSECTIONINFO, sRayIntersectionInfoFields, LUA_RAY_INTERSECTION_INFO_FIELD_COUNT }, { LOT_SERVERSETTINGS, sServerSettingsFields, LUA_SERVER_SETTINGS_FIELD_COUNT }, { LOT_SOUNDSTATE, sSoundStateFields, LUA_SOUND_STATE_FIELD_COUNT }, diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h index 2c93b94f..dca97d9d 100644 --- a/src/pc/lua/smlua_cobject_autogen.h +++ b/src/pc/lua/smlua_cobject_autogen.h @@ -51,6 +51,7 @@ enum LuaObjectAutogenType { LOT_PARALLELTRACKINGPOINT, LOT_PLAYERCAMERASTATE, LOT_PLAYERGEOMETRY, + LOT_PLAYERPALETTE, LOT_RAYINTERSECTIONINFO, LOT_SERVERSETTINGS, LOT_SOUNDSTATE, diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index fe87bdbb..09cf7839 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1035,7 +1035,11 @@ char gSmluaConstants[] = "" "CAM_EVENT_START_ENDING = 11\n" "CAM_EVENT_START_END_WAVING = 12\n" "CAM_EVENT_START_CREDITS = 13\n" -"PALETTE_MAX = 32\n" +"PALETTE_PRESET_MAX = 32\n" +"SHIRT = 0\n" +"PANTS = 1\n" +"GLOVES = 2\n" +"PLAYER_PART_MAX = 3\n" "CT_MARIO = 0\n" "CT_LUIGI = 1\n" "CT_TOAD = 2\n" @@ -1671,6 +1675,7 @@ char gSmluaConstants[] = "" "UNKNOWN_NETWORK_INDEX = (-1)\n" "NETWORK_PLAYER_TIMEOUT = 10\n" "MAX_RX_SEQ_IDS = 64\n" +"USE_REAL_PALETTE_VAR = 0xFF\n" "NPT_UNKNOWN = 0\n" "NPT_LOCAL = 1\n" "NPT_SERVER = 2\n" diff --git a/src/pc/network/discord/activity.c b/src/pc/network/discord/activity.c index 0a6987f2..d7d2aa48 100644 --- a/src/pc/network/discord/activity.c +++ b/src/pc/network/discord/activity.c @@ -41,7 +41,7 @@ static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult res if (gNetworkType == NT_CLIENT) { if (gNetworkPlayerServer == NULL) { - network_player_connected(NPT_SERVER, 0, 0, 0, "Player"); + network_player_connected(NPT_SERVER, 0, 0, &DEFAULT_MARIO_PALETTE, "Player"); } ns_discord_save_id(gNetworkPlayerServer->localIndex, lobby->owner_id); network_send_mod_list_request(); diff --git a/src/pc/network/network.c b/src/pc/network/network.c index dc100d45..4cdaad71 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -127,7 +127,7 @@ bool network_init(enum NetworkType inNetworkType) { dynos_behavior_hook_all_custom_behaviors(); - network_player_connected(NPT_LOCAL, 0, configPlayerModel, configPlayerPalette, configPlayerName); + network_player_connected(NPT_LOCAL, 0, configPlayerModel, &configPlayerPalette, configPlayerName); extern u8* gOverrideEeprom; gOverrideEeprom = NULL; diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index 3d3ac487..450190af 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -19,9 +19,9 @@ static char sDefaultPlayerName[] = "Player"; void network_player_init(void) { gNetworkPlayers[0].modelIndex = (configPlayerModel < CT_MAX) ? configPlayerModel : 0; - gNetworkPlayers[0].paletteIndex = configPlayerPalette; + gNetworkPlayers[0].palette = configPlayerPalette; gNetworkPlayers[0].overrideModelIndex = gNetworkPlayers[0].modelIndex; - gNetworkPlayers[0].overridePaletteIndex = gNetworkPlayers[0].paletteIndex; + gNetworkPlayers[0].overridePalette = gNetworkPlayers[0].palette; } void network_player_update_model(u8 localIndex) { @@ -169,7 +169,7 @@ void network_player_update(void) { } } -u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, u8 paletteIndex, char *name) { +u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, const struct PlayerPalette* palette, char *name) { // translate globalIndex to localIndex u8 localIndex = globalIndex; if (gNetworkType == NT_SERVER) { @@ -198,9 +198,9 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode if ((type != NPT_LOCAL) && (gNetworkType == NT_SERVER || type == NPT_SERVER)) { gNetworkSystem->save_id(localIndex, 0); } if (np->modelIndex == np->overrideModelIndex) { np->overrideModelIndex = modelIndex; } - if (np->paletteIndex == np->overridePaletteIndex) { np->overridePaletteIndex = paletteIndex; } + if (memcmp(&np->palette, &np->overridePalette, sizeof(struct PlayerPalette)) == 0) { np->overridePalette = *palette; } np->modelIndex = modelIndex; - np->paletteIndex = paletteIndex; + np->palette = *palette; network_player_update_model(localIndex); snprintf(np->name, MAX_PLAYER_STRING, "%s", name); @@ -228,9 +228,14 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode // update visuals np->fadeOpacity = 0; np->modelIndex = modelIndex; - np->paletteIndex = paletteIndex; + np->palette = *palette; np->overrideModelIndex = modelIndex; - np->overridePaletteIndex = paletteIndex; + np->overridePalette = *palette; + + np->paletteIndex = USE_REAL_PALETTE_VAR; + np->overridePaletteIndex = USE_REAL_PALETTE_VAR; + np->overridePaletteIndexLp = USE_REAL_PALETTE_VAR; + snprintf(np->name, MAX_PLAYER_STRING, "%s", name); network_player_update_model(localIndex); diff --git a/src/pc/network/network_player.h b/src/pc/network/network_player.h index 31d6da74..cb444c00 100644 --- a/src/pc/network/network_player.h +++ b/src/pc/network/network_player.h @@ -10,6 +10,7 @@ #define UNKNOWN_NETWORK_INDEX ((u64)-1) #define NETWORK_PLAYER_TIMEOUT 10 #define MAX_RX_SEQ_IDS 64 +#define USE_REAL_PALETTE_VAR 0xFF enum NetworkPlayerType { NPT_UNKNOWN, @@ -36,7 +37,7 @@ struct NetworkPlayer { u8 fadeOpacity; u8 onRxSeqId; u8 modelIndex; - u8 paletteIndex; + struct PlayerPalette palette; char name[MAX_PLAYER_STRING+1]; char description[MAX_DESCRIPTION_STRING+1]; @@ -46,10 +47,15 @@ struct NetworkPlayer { u8 descriptionA; u8 overrideModelIndex; - u8 overridePaletteIndex; + struct PlayerPalette overridePalette; u16 rxSeqIds[MAX_RX_SEQ_IDS]; u32 rxPacketHash[MAX_RX_SEQ_IDS]; + + // legacy fields to allow mods not to break + u8 paletteIndex; + u8 overridePaletteIndex; + u8 overridePaletteIndexLp; }; extern struct NetworkPlayer gNetworkPlayers[]; @@ -69,7 +75,7 @@ struct NetworkPlayer* get_network_player_smallest_global(void); void network_player_update(void); -u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, u8 paletteIndex, char* name); +u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 modelIndex, const struct PlayerPalette* playerPalette, char* name); u8 network_player_disconnected(u8 globalIndex); void network_player_update_course_level(struct NetworkPlayer* np, s16 courseNum, s16 actNum, s16 levelNum, s16 areaIndex); diff --git a/src/pc/network/network_utils.c b/src/pc/network/network_utils.c index 5da54f03..8e05fce7 100644 --- a/src/pc/network/network_utils.c +++ b/src/pc/network/network_utils.c @@ -30,10 +30,9 @@ u8* network_get_player_text_color(u8 localIndex) { if (localIndex >= MAX_PLAYERS) { localIndex = 0; } struct NetworkPlayer* np = &gNetworkPlayers[localIndex]; - u8* rgb = get_player_color(np->overridePaletteIndex, 0); static u8 sTextRgb[3] = { 0 }; for (int i = 0; i < 3; i++) { - sTextRgb[i] = 127 + rgb[i] / 2; + sTextRgb[i] = 127 + np->overridePalette.parts[SHIRT][i] / 2; } return sTextRgb; diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index cb2594d7..413a9635 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -26,7 +26,7 @@ extern u8* gOverrideEeprom; static u8 eeprom[512] = { 0 }; static u8 sJoinRequestPlayerModel; -static u8 sJoinRequestPlayerPalette; +static struct PlayerPalette sJoinRequestPlayerPalette; static char sJoinRequestPlayerName[MAX_PLAYER_STRING]; void network_send_join_request(void) { @@ -39,7 +39,7 @@ void network_send_join_request(void) { packet_init(&p, PACKET_JOIN_REQUEST, true, PLMT_NONE); packet_write(&p, &configPlayerModel, sizeof(u8)); - packet_write(&p, &configPlayerPalette, sizeof(u8)); + packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette)); packet_write(&p, &configPlayerName, sizeof(u8) * MAX_PLAYER_STRING); network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p); @@ -52,11 +52,11 @@ void network_receive_join_request(struct Packet* p) { if (p->dataLength > 5) { packet_read(p, &sJoinRequestPlayerModel, sizeof(u8)); - packet_read(p, &sJoinRequestPlayerPalette, sizeof(u8)); + packet_read(p, &sJoinRequestPlayerPalette, sizeof(struct PlayerPalette)); packet_read(p, &sJoinRequestPlayerName, sizeof(u8) * MAX_PLAYER_STRING); } else { sJoinRequestPlayerModel = 0; - sJoinRequestPlayerPalette = 0; + sJoinRequestPlayerPalette = DEFAULT_MARIO_PALETTE; snprintf(sJoinRequestPlayerName, MAX_PLAYER_STRING, "%s", "Player"); } @@ -83,7 +83,7 @@ void network_send_join(struct Packet* joinRequestPacket) { LOG_INFO("chose globalIndex: %d", globalIndex); // do connection event - network_player_connected(NPT_CLIENT, globalIndex, sJoinRequestPlayerModel, sJoinRequestPlayerPalette, sJoinRequestPlayerName); + network_player_connected(NPT_CLIENT, globalIndex, sJoinRequestPlayerModel, &sJoinRequestPlayerPalette, sJoinRequestPlayerName); fs_file_t* fp = fs_open(SAVE_FILENAME); if (fp != NULL) { @@ -220,8 +220,8 @@ void network_receive_join(struct Packet* p) { } string_linked_list_free(&head); - network_player_connected(NPT_SERVER, 0, 0, 0, "Player"); - network_player_connected(NPT_LOCAL, myGlobalIndex, configPlayerModel, configPlayerPalette, configPlayerName); + network_player_connected(NPT_SERVER, 0, 0, &DEFAULT_MARIO_PALETTE, "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 607e1fce..4fc25d2e 100644 --- a/src/pc/network/packets/packet_network_players.c +++ b/src/pc/network/packets/packet_network_players.c @@ -33,7 +33,7 @@ static void network_send_to_network_players(u8 sendToLocalIndex) { 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].palette, sizeof(struct PlayerPalette)); packet_write(&p, &gNetworkPlayers[i].name, sizeof(u8) * MAX_PLAYER_STRING); LOG_INFO("send network player [%d == %d]", gNetworkPlayers[i].globalIndex, npType); } @@ -89,7 +89,8 @@ void network_receive_network_players(struct Packet *p) { s16 courseNum, actNum, levelNum, areaIndex; u8 levelSyncValid, areaSyncValid; s64 networkId; - u8 modelIndex, paletteIndex; + u8 modelIndex; + struct PlayerPalette palette; char playerName[MAX_PLAYER_STRING] = { 0 }; packet_read(p, &npType, sizeof(u8)); @@ -103,10 +104,10 @@ void network_receive_network_players(struct Packet *p) { 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, &palette, sizeof(struct PlayerPalette)); packet_read(p, &playerName, sizeof(u8) * MAX_PLAYER_STRING); - u8 localIndex = network_player_connected(npType, globalIndex, modelIndex, paletteIndex, playerName); + u8 localIndex = network_player_connected(npType, globalIndex, modelIndex, &palette, playerName); LOG_INFO("received network player [%d == %d] (%d)", globalIndex, npType, localIndex); if (localIndex != UNKNOWN_GLOBAL_INDEX) { struct NetworkPlayer *np = &gNetworkPlayers[localIndex]; @@ -121,7 +122,7 @@ void network_receive_network_players(struct Packet *p) { } } else { np->modelIndex = (modelIndex < CT_MAX) ? modelIndex : 0; - np->paletteIndex = paletteIndex; + np->palette = palette; network_player_update_model(localIndex); } } diff --git a/src/pc/network/packets/packet_player_settings.c b/src/pc/network/packets/packet_player_settings.c index 02d6f011..b572868a 100644 --- a/src/pc/network/packets/packet_player_settings.c +++ b/src/pc/network/packets/packet_player_settings.c @@ -13,7 +13,7 @@ void network_send_player_settings(void) { packet_write(&p, &gNetworkPlayers[0].globalIndex, sizeof(u8)); packet_write(&p, playerName, MAX_PLAYER_STRING * sizeof(u8)); packet_write(&p, &configPlayerModel, sizeof(u8)); - packet_write(&p, &configPlayerPalette, sizeof(u8)); + packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette)); if (gNetworkPlayerLocal != NULL) { if (snprintf(gNetworkPlayerLocal->name, MAX_PLAYER_STRING, "%s", playerName) < 0) { @@ -28,12 +28,12 @@ void network_receive_player_settings(struct Packet* p) { u8 globalId; char playerName[MAX_PLAYER_STRING+1] = { 0 }; u8 playerModel; - u8 playerPalette; + struct PlayerPalette playerPalette; packet_read(p, &globalId, sizeof(u8)); packet_read(p, &playerName, MAX_PLAYER_STRING * sizeof(u8)); packet_read(p, &playerModel, sizeof(u8)); - packet_read(p, &playerPalette, sizeof(u8)); + packet_read(p, &playerPalette, sizeof(struct PlayerPalette)); if (globalId == gNetworkPlayers[0].globalIndex || globalId > MAX_PLAYERS) { LOG_ERROR("Received player settings from improper player."); @@ -48,7 +48,6 @@ void network_receive_player_settings(struct Packet* p) { // sanity check if (playerModel >= CT_MAX) { playerModel = CT_MARIO; } - if (playerPalette >= PALETTE_MAX) { playerPalette = 0; } struct NetworkPlayer* np = network_player_from_global_index(globalId); if (snprintf(np->name, MAX_PLAYER_STRING, "%s", playerName) < 0) { @@ -56,10 +55,10 @@ void network_receive_player_settings(struct Packet* p) { } if (np->modelIndex == np->overrideModelIndex) { np->overrideModelIndex = playerModel; } - if (np->paletteIndex == np->overridePaletteIndex) { np->overridePaletteIndex = playerPalette; } + if (memcmp(&np->palette, &np->overridePalette, sizeof(struct PlayerPalette)) == 0) { np->overridePalette = playerPalette; } np->modelIndex = playerModel; - np->paletteIndex = playerPalette; + np->palette = playerPalette; network_player_update_model(np->localIndex); } diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index e88f4397..ea25839b 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -316,7 +316,6 @@ void main_func(void) { dynos_pack_init(); if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; } - if (configPlayerPalette >= PALETTE_MAX) { configPlayerPalette = 0; } if (gCLIOpts.FullScreen == 1) configWindow.fullscreen = true;