Allowed Lua mods to override player models and palettes

This commit is contained in:
MysterD 2022-03-10 18:23:25 -08:00
parent 85c54baa96
commit 497a250476
14 changed files with 106 additions and 65 deletions

View file

@ -62,6 +62,10 @@ override_field_types = {
"Object": { "oAnimations": "ObjectAnimPointer*"}
}
override_field_mutable = {
"NetworkPlayer": [ "overrideModelIndex", "overridePaletteIndex" ],
}
override_field_immutable = {
"MarioState": [ "playerIndex" ],
"Character": [ "*" ],
@ -212,6 +216,10 @@ def get_struct_field_info(struct, field):
if fid in override_field_immutable[sid] or '*' in override_field_immutable[sid]:
fimmutable = 'true'
if sid in override_field_mutable:
if fid in override_field_mutable[sid] or '*' in override_field_mutable[sid]:
fimmutable = 'false'
return fid, ftype, fimmutable, lvt, lot
def build_struct(struct):

View file

@ -837,6 +837,8 @@
| modelIndex | `integer` | read-only |
| name | `string` | read-only |
| onRxSeqId | `integer` | read-only |
| overrideModelIndex | `integer` | |
| overridePaletteIndex | `integer` | |
| paletteIndex | `integer` | read-only |
| type | `integer` | read-only |

View file

@ -514,7 +514,9 @@ function bhv_ball_loop(obj)
local colNormals = {}
if info.surface ~= nil then
table.insert(colNormals, { x = info.surface.normal.x, y = info.surface.normal.y, z = info.surface.normal.z })
bhv_ball_particle_bounce(obj)
if vMag > 5 then
bhv_ball_particle_bounce(obj)
end
else
table.insert(colNormals, nil)
end
@ -673,8 +675,8 @@ gGlobalSyncTable.gameState = GAME_STATE_WAIT
gGlobalSyncTable.displayText = ' '
gGlobalSyncTable.displayFont = FONT_HUD
gGlobalSyncTable.displayColor = 0xFFFFFF
gGlobalSyncTable.scoreNormal = 0
gGlobalSyncTable.scoreMetal = 0
gGlobalSyncTable.scoreRed = 0
gGlobalSyncTable.scoreBlue = 0
function gamemode_initialize()
-- prevent warp doors from working
@ -872,24 +874,24 @@ function gamemode_active()
end
if scoringTeam == 1 then
gGlobalSyncTable.scoreNormal = gGlobalSyncTable.scoreNormal + 1
gGlobalSyncTable.scoreRed = gGlobalSyncTable.scoreRed + 1
gGlobalSyncTable.displayFont = FONT_NORMAL
gGlobalSyncTable.displayColor = 0x99FF99
if gGlobalSyncTable.scoreNormal >= sMaxScore then
gGlobalSyncTable.displayText = 'normal team wins!'
gGlobalSyncTable.displayColor = 0xFF9999
if gGlobalSyncTable.scoreRed >= sMaxScore then
gGlobalSyncTable.displayText = 'red team wins!'
gameOver = true
else
gGlobalSyncTable.displayText = 'normal team scored' .. displayName
gGlobalSyncTable.displayText = 'red team scored' .. displayName
end
else
gGlobalSyncTable.scoreMetal = gGlobalSyncTable.scoreMetal + 1
gGlobalSyncTable.scoreBlue = gGlobalSyncTable.scoreBlue + 1
gGlobalSyncTable.displayFont = FONT_NORMAL
gGlobalSyncTable.displayColor = 0x9999FF
if gGlobalSyncTable.scoreMetal >= sMaxScore then
gGlobalSyncTable.displayText = 'metal team wins!'
if gGlobalSyncTable.scoreBlue >= sMaxScore then
gGlobalSyncTable.displayText = 'blue team wins!'
gameOver = true
else
gGlobalSyncTable.displayText = 'metal team scored' .. displayName
gGlobalSyncTable.displayText = 'blue team scored' .. displayName
end
end
@ -962,7 +964,7 @@ function gamemode_oob()
gGlobalSyncTable.displayFont = FONT_NORMAL
gGlobalSyncTable.displayText = 'out of bounds'
gGlobalSyncTable.displayColor = 0xFF9999
gGlobalSyncTable.displayColor = 0xFFFFFF
-- start the round
if sStateTimer <= 0 then
@ -984,8 +986,8 @@ function gamemode_over()
if sStateTimer <= 0 then
-- shuffle teams
gamemode_shuffle()
gGlobalSyncTable.scoreMetal = 0
gGlobalSyncTable.scoreNormal = 0
gGlobalSyncTable.scoreRed = 0
gGlobalSyncTable.scoreBlue = 0
gGlobalSyncTable.gameState = GAME_STATE_WAIT
sStateTimer = sWaitTimeout
end
@ -1072,15 +1074,15 @@ function hud_score_render()
end
-- render
djui_hud_set_color(100, 255, 100, 180);
djui_hud_set_color(255, 100, 100, 180);
djui_hud_render_rect(x - xOffset, y, width, height + 4);
djui_hud_set_color(100, 100, 255, 180);
djui_hud_render_rect(x + xOffset, y, width, height + 4);
djui_hud_set_color(255, 255, 255, 255);
djui_hud_print_text(tostring(gGlobalSyncTable.scoreNormal), x - xOffset + textOffset, y + 2, 1);
djui_hud_print_text(tostring(gGlobalSyncTable.scoreMetal), x + xOffset + textOffset, y + 2, 1);
djui_hud_print_text(tostring(gGlobalSyncTable.scoreRed), x - xOffset + textOffset, y + 2, 1);
djui_hud_print_text(tostring(gGlobalSyncTable.scoreBlue), x + xOffset + textOffset, y + 2, 1);
end
function on_hud_render()
@ -1140,8 +1142,8 @@ function on_football_reset_command(msg)
elseif msg == 'game' then
djui_chat_message_create('Resetting the game.')
gamemode_shuffle()
gGlobalSyncTable.scoreMetal = 0
gGlobalSyncTable.scoreNormal = 0
gGlobalSyncTable.scoreRed = 0
gGlobalSyncTable.scoreBlue = 0
gGlobalSyncTable.displayText = ' '
gGlobalSyncTable.gameState = GAME_STATE_WAIT
sStateTimer = sWaitTimeout
@ -1238,21 +1240,24 @@ function mario_update(m)
-- set metal state and health
local s = gPlayerSyncTable[m.playerIndex]
local np = gNetworkPlayers[m.playerIndex]
if s.team == 2 then
m.marioBodyState.modelState = MODEL_STATE_METAL
np.overridePaletteIndex = 7
m.marioBodyState.modelState = 0
elseif s.team == 1 then
np.overridePaletteIndex = 15
m.marioBodyState.modelState = 0
else
np.overridePaletteIndex = np.paletteIndex
m.marioBodyState.modelState = MODEL_STATE_NOISE_ALPHA
end
m.health = 0x880
-- update description
local np = gNetworkPlayers[m.playerIndex]
if s.team == 1 then
network_player_set_description(np, "normal", 64, 255, 64, 255)
network_player_set_description(np, "red", 255, 64, 64, 255)
elseif s.team == 2 then
network_player_set_description(np, "metal", 64, 64, 255, 255)
network_player_set_description(np, "blue", 64, 64, 255, 255)
else
network_player_set_description(np, "unknown", 64, 64, 64, 255)
end

View file

@ -422,7 +422,7 @@ void bhv_klepto_update(void) {
if (network_owns_object(o) && kleptoHoldingCap) {
struct NetworkPlayer* np = network_player_from_global_index(o->globalPlayerIndex);
if (np == NULL) { np = gNetworkPlayerLocal; }
u8 modelIndex = (np->modelIndex < CT_MAX) ? np->modelIndex : 0;
u8 modelIndex = (np->overrideModelIndex < CT_MAX) ? np->overrideModelIndex : 0;
u32 capModel = gCharacters[modelIndex].capModelId;
save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO);

View file

@ -2181,8 +2181,8 @@ static void init_single_mario(struct MarioState* m) {
gNetworkPlayers[playerIndex].fadeOpacity = 0;
}
// set mario/luigi model
u8 modelIndex = gNetworkPlayers[playerIndex].modelIndex;
// set character model
u8 modelIndex = gNetworkPlayers[playerIndex].overrideModelIndex;
if (modelIndex >= CT_MAX) { modelIndex = 0; }
m->character = &gCharacters[modelIndex];
m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId];

View file

@ -488,7 +488,7 @@ Gfx* geo_mario_tilt_torso(s32 callContext, struct GraphNode* node, Mat4* mtx) {
struct MarioBodyState* bodyState = &gBodyStates[plrIdx];
s32 action = bodyState->action;
u8 charIndex = gNetworkPlayers[plrIdx].modelIndex;
u8 charIndex = gNetworkPlayers[plrIdx].overrideModelIndex;
if (charIndex >= CT_MAX) { charIndex = 0; }
struct Character* character = &gCharacters[charIndex];
@ -807,7 +807,7 @@ Gfx* geo_mario_set_player_colors(s32 callContext, struct GraphNode* node, UNUSED
struct GraphNodeGenerated* asGenerated = (struct GraphNodeGenerated*) node;
Gfx* gfx = NULL;
u8 index = geo_get_processing_object_index();
u8 colorIndex = gNetworkPlayers[index].paletteIndex;
u8 colorIndex = gNetworkPlayers[index].overridePaletteIndex;
struct MarioBodyState* bodyState = &gBodyStates[index];
if (callContext == GEO_CONTEXT_RENDER) {
@ -838,8 +838,8 @@ 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].paletteIndex;
u8 charIndex = gNetworkPlayers[globalIndex].modelIndex;
u8 colorIndex = gNetworkPlayers[globalIndex].overridePaletteIndex;
u8 charIndex = gNetworkPlayers[globalIndex].overrideModelIndex;
if (charIndex >= CT_MAX) { charIndex = 0; }
struct Character* character = &gCharacters[charIndex];

View file

@ -41,7 +41,9 @@ static void djui_panel_player_name_on_focus_end(struct DjuiBase* caller) {
void djui_panel_player_value_changed(UNUSED struct DjuiBase* caller) {
if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; }
gNetworkPlayers[0].modelIndex = configPlayerModel;
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;
network_player_update_model(0);

View file

@ -17,7 +17,7 @@ static struct DjuiText* djuiTextDescriptions[MAX_PLAYERS] = { 0 };
static struct DjuiText* djuiTextLocations[MAX_PLAYERS] = { 0 };
static void playerlist_update_row(u8 i, struct NetworkPlayer* np) {
u8 charIndex = np->modelIndex;
u8 charIndex = np->overrideModelIndex;
if (charIndex >= CT_MAX) { charIndex = 0; }
djuiImages[i]->texture = gCharacters[charIndex].hudHeadTexture.texture;

View file

@ -646,33 +646,35 @@ static struct LuaObjectField sModeTransitionInfoFields[LUA_MODE_TRANSITION_INFO_
{ "transitionStart", LVT_COBJECT, offsetof(struct ModeTransitionInfo, transitionStart), true, LOT_LINEARTRANSITIONPOINT },
};
#define LUA_NETWORK_PLAYER_FIELD_COUNT 23
#define LUA_NETWORK_PLAYER_FIELD_COUNT 25
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 },
{ "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 },
{ "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 },
};
#define LUA_OBJECT_FIELD_COUNT 750

View file

@ -17,12 +17,19 @@ static char sDefaultPlayerName[] = "Player";
void network_player_init(void) {
gNetworkPlayers[0].modelIndex = (configPlayerModel < CT_MAX) ? configPlayerModel : 0;
gNetworkPlayers[0].paletteIndex = configPlayerPalette;
gNetworkPlayers[0].overrideModelIndex = gNetworkPlayers[0].modelIndex;
gNetworkPlayers[0].overridePaletteIndex = gNetworkPlayers[0].paletteIndex;
}
void network_player_update_model(u8 localIndex) {
struct MarioState* m = &gMarioStates[localIndex];
if (m == NULL) { return; }
m->character = &gCharacters[gNetworkPlayers[localIndex].modelIndex];
struct NetworkPlayer* np = &gNetworkPlayers[localIndex];
u8 index = np->overrideModelIndex;
if (index >= CT_MAX) { index = 0; }
m->character = &gCharacters[index];
if (m->marioObj == NULL) { return; }
m->marioObj->header.gfx.sharedChild = gLoadedGraphNodes[m->character->modelId];
}
@ -116,9 +123,13 @@ void network_player_update(void) {
if (!network_player_any_connected()) { return; }
if (gNetworkType == NT_SERVER) {
for (int i = 1; i < MAX_PLAYERS; i++) {
for (int i = 0; i < MAX_PLAYERS; i++) {
struct NetworkPlayer* np = &gNetworkPlayers[i];
if (!np->connected) { continue; }
if (!np->connected && i > 0) { continue; }
network_player_update_model(i);
if (i == 0) { continue; }
float elapsed = (clock_elapsed() - np->lastReceived);
#ifndef DEVELOPMENT
if (elapsed > NETWORK_PLAYER_TIMEOUT) {
@ -179,6 +190,8 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->lastSent = clock_elapsed();
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; }
np->modelIndex = modelIndex;
np->paletteIndex = paletteIndex;
network_player_update_model(localIndex);
@ -208,6 +221,8 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->fadeOpacity = 0;
np->modelIndex = modelIndex;
np->paletteIndex = paletteIndex;
np->overrideModelIndex = modelIndex;
np->overridePaletteIndex = paletteIndex;
snprintf(np->name, MAX_PLAYER_STRING, "%s", name);
network_player_update_model(localIndex);

View file

@ -44,6 +44,9 @@ struct NetworkPlayer {
u8 descriptionB;
u8 descriptionA;
u8 overrideModelIndex;
u8 overridePaletteIndex;
u16 rxSeqIds[MAX_RX_SEQ_IDS];
u32 rxPacketHash[MAX_RX_SEQ_IDS];
};

View file

@ -30,7 +30,7 @@ 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->paletteIndex, 0);
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;

View file

@ -114,7 +114,7 @@ void network_receive_network_players(struct Packet* p) {
gNetworkSystem->save_id(localIndex, networkId);
}
} else {
np->modelIndex = (modelIndex < CT_MAX) ? modelIndex : 0;
np->modelIndex = (modelIndex < CT_MAX) ? modelIndex : 0;
np->paletteIndex = paletteIndex;
network_player_update_model(localIndex);
}

View file

@ -48,7 +48,11 @@ void network_receive_player_settings(struct Packet* p) {
struct NetworkPlayer* np = network_player_from_global_index(globalId);
snprintf(np->name, MAX_PLAYER_STRING, "%s", playerName);
np->modelIndex = playerModel;
if (np->modelIndex == np->overrideModelIndex) { np->overrideModelIndex = playerModel; }
if (np->paletteIndex == np->overridePaletteIndex) { np->overridePaletteIndex = playerPalette; }
np->modelIndex = playerModel;
np->paletteIndex = playerPalette;
network_player_update_model(np->localIndex);