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;