diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 7a775769..1123b142 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -7688,6 +7688,14 @@ function obj_set_billboard(obj)
-- ...
end
+--- @param obj Object
+--- @param index integer
+--- @param modelID integer
+--- @return nil
+function obj_set_character_model(obj, index, modelID)
+ -- ...
+end
+
--- @param obj Object
--- @return nil
function obj_set_cylboard(obj)
diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md
index c2e6fda6..84741cfe 100644
--- a/docs/lua/functions-4.md
+++ b/docs/lua/functions-4.md
@@ -5961,6 +5961,28 @@
+## [obj_set_character_model](#obj_set_character_model)
+
+### Lua Example
+`obj_set_character_model(obj, index, modelID)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| obj | [Object](structs.md#Object) |
+| index | `integer` |
+| modelID | `integer` |
+
+### Returns
+- None
+
+### C Prototype
+`void obj_set_character_model(struct Object* obj, u16 index, s32 modelID);`
+
+[:arrow_up_small:](#)
+
+
+
## [obj_set_cylboard](#obj_set_cylboard)
### Lua Example
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 959a6cfc..f095b482 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -1443,6 +1443,7 @@
- [obj_set_angle](functions-4.md#obj_set_angle)
- [obj_set_behavior](functions-4.md#obj_set_behavior)
- [obj_set_billboard](functions-4.md#obj_set_billboard)
+ - [obj_set_character_model](functions-4.md#obj_set_character_model)
- [obj_set_cylboard](functions-4.md#obj_set_cylboard)
- [obj_set_face_angle](functions-4.md#obj_set_face_angle)
- [obj_set_face_angle_to_move_angle](functions-4.md#obj_set_face_angle_to_move_angle)
diff --git a/lang/Czech.ini b/lang/Czech.ini
index fb097b7e..0e32ec03 100644
--- a/lang/Czech.ini
+++ b/lang/Czech.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Výběr souboru"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Globální modely hráčů"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/Dutch.ini b/lang/Dutch.ini
index 9d037640..469d96a0 100644
--- a/lang/Dutch.ini
+++ b/lang/Dutch.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Bestand selecteren"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Globale spelermodellen"
[HOST_MESSAGE]
INFO_TITLE = "INFORMATIE"
diff --git a/lang/English.ini b/lang/English.ini
index 2da41c11..29ce7bd5 100644
--- a/lang/English.ini
+++ b/lang/English.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "File Select"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Global Player Models"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/French.ini b/lang/French.ini
index 8f1a2395..9e30ec14 100644
--- a/lang/French.ini
+++ b/lang/French.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Sélection de fichier"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Modèles de joueurs mondiaux"
[HOST_MESSAGE]
INFO_TITLE = "INFORMATIONS"
diff --git a/lang/German.ini b/lang/German.ini
index 4417d479..c767964b 100644
--- a/lang/German.ini
+++ b/lang/German.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Dateiauswahl"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Globale Spielermodelle"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/Italian.ini b/lang/Italian.ini
index 0038fc9d..31b17c6c 100644
--- a/lang/Italian.ini
+++ b/lang/Italian.ini
@@ -152,6 +152,7 @@ FILE_SELECT_THEME = "Selezione file"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Modelli di giocatore globali"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/Polish.ini b/lang/Polish.ini
index 37783358..8ee7ad52 100644
--- a/lang/Polish.ini
+++ b/lang/Polish.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Wybór Pliku"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Globalne modele graczy"
[HOST_MESSAGE]
INFO_TITLE = "INFORMACJE"
diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini
index 1542f260..635a4738 100644
--- a/lang/Portuguese.ini
+++ b/lang/Portuguese.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Escolha de arquivo"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Modelos de jogador global"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/Russian.ini b/lang/Russian.ini
index 8b8446d1..0dab0f23 100644
--- a/lang/Russian.ini
+++ b/lang/Russian.ini
@@ -153,6 +153,7 @@ FILE_SELECT_THEME = "Выбор файла"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Глобальные модели игроков"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/lang/Spanish.ini b/lang/Spanish.ini
index 2c5f700f..4fcae9da 100644
--- a/lang/Spanish.ini
+++ b/lang/Spanish.ini
@@ -154,6 +154,7 @@ FILE_SELECT_THEME = "Selección de archivo"
[DYNOS]
DYNOS = "DYNOS"
+GLOBAL_PLAYER_MODELS = "Modelos de jugador global"
[HOST_MESSAGE]
INFO_TITLE = "INFO"
diff --git a/src/game/mario.c b/src/game/mario.c
index 08575e68..bed16a32 100644
--- a/src/game/mario.c
+++ b/src/game/mario.c
@@ -2369,7 +2369,7 @@ void init_single_mario(struct MarioState* m) {
u8 modelIndex = gNetworkPlayers[playerIndex].overrideModelIndex;
if (modelIndex >= CT_MAX) { modelIndex = 0; }
m->character = &gCharacters[modelIndex];
- obj_set_model(m->marioObj, m->character->modelId);
+ obj_set_character_model(m->marioObj, m->playerIndex, m->character->modelId);
}
void init_mario(void) {
diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c
index a6654c92..49012137 100644
--- a/src/game/object_helpers.c
+++ b/src/game/object_helpers.c
@@ -1441,6 +1441,14 @@ void obj_set_model(struct Object* obj, s32 modelID) {
smlua_call_event_hooks_object_model_param(HOOK_OBJECT_SET_MODEL, obj, modelID);
}
+void obj_set_character_model(struct Object* obj, u16 index, s32 modelID) {
+ obj->header.gfx.sharedChild = dynos_model_get_geo(modelID);
+ if (configGlobalPlayerModels || index == 0) {
+ dynos_actor_override((void*)&obj->header.gfx.sharedChild);
+ }
+ smlua_call_event_hooks_object_model_param(HOOK_OBJECT_SET_MODEL, obj, modelID);
+}
+
void mario_set_flag(s32 flag) {
gMarioStates[0].flags |= flag;
}
diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h
index 5934ce93..1804de3d 100644
--- a/src/game/object_helpers.h
+++ b/src/game/object_helpers.h
@@ -175,6 +175,7 @@ void cur_obj_get_thrown_or_placed(f32 forwardVel, f32 velY, s32 thrownAction);
void cur_obj_get_dropped(void);
void cur_obj_set_model(s32 modelID);
void obj_set_model(struct Object* obj, s32 modelID);
+void obj_set_character_model(struct Object* obj, u16 index, s32 modelID);
void mario_set_flag(s32 flag);
s32 cur_obj_clear_interact_status_flag(s32 flag);
void obj_mark_for_deletion(struct Object *obj);
diff --git a/src/pc/configfile.c b/src/pc/configfile.c
index 71eca46f..b35d1eb1 100644
--- a/src/pc/configfile.c
+++ b/src/pc/configfile.c
@@ -165,6 +165,7 @@ unsigned int configDjuiTheme = DJUI_THEME_DARK;
bool configDjuiThemeCenter = true;
unsigned int configDjuiScale = 0;
bool configCoopCompatibility = false;
+bool configGlobalPlayerModels = true;
char configLastVersion[MAX_CONFIG_STRING] = SM64COOPDX_VERSION;
static const struct ConfigOption options[] = {
@@ -262,6 +263,7 @@ static const struct ConfigOption options[] = {
{.name = "coop_custom_palette_cap", .type = CONFIG_TYPE_COLOR , .colorValue = &configCustomPalette.parts[CAP]},
{.name = "coop_stay_in_level_after_star", .type = CONFIG_TYPE_UINT , .uintValue = &configStayInLevelAfterStar},
{.name = "coop_compatibility", .type = CONFIG_TYPE_BOOL , .boolValue = &configCoopCompatibility},
+ {.name = "coopdx_global_player_models", .type = CONFIG_TYPE_BOOL , .boolValue = &configGlobalPlayerModels},
{.name = "disable_popups", .type = CONFIG_TYPE_BOOL , .boolValue = &configDisablePopups},
{.name = "lua_profiler", .type = CONFIG_TYPE_BOOL , .boolValue = &configLuaProfiler},
#ifdef DEVELOPMENT
diff --git a/src/pc/configfile.h b/src/pc/configfile.h
index c9cef7eb..85b05885 100644
--- a/src/pc/configfile.h
+++ b/src/pc/configfile.h
@@ -122,6 +122,7 @@ extern unsigned int configDjuiTheme;
extern bool configDjuiThemeCenter;
extern unsigned int configDjuiScale;
extern bool configCoopCompatibility;
+extern bool configGlobalPlayerModels;
extern char configLastVersion[];
void enable_queued_mods();
diff --git a/src/pc/djui/djui_panel_dynos.c b/src/pc/djui/djui_panel_dynos.c
index 084df260..4fe037a7 100644
--- a/src/pc/djui/djui_panel_dynos.c
+++ b/src/pc/djui/djui_panel_dynos.c
@@ -14,6 +14,12 @@ static void djui_panel_dynos_apply(struct DjuiBase* caller) {
dynos_pack_set_enabled(caller->tag, caller->bTag);
}
+static void djui_panel_dynos_global_player_models(UNUSED struct DjuiBase* caller) {
+ for (s32 i = 0; i < MAX_PLAYERS; i++) {
+ network_player_update_model(i);
+ }
+}
+
static void djui_panel_dynos_refresh(UNUSED struct DjuiBase* base) {
dynos_gfx_init();
dynos_packs_init();
@@ -46,6 +52,12 @@ void djui_panel_dynos_create(struct DjuiBase* caller) {
}
djui_paginated_calculate_height(paginated);
+ struct DjuiRect* space = djui_rect_create(body);
+ djui_base_set_size_type(&space->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&space->base, (DJUI_DEFAULT_PANEL_WIDTH * (configDjuiThemeCenter ? DJUI_THEME_CENTERED_WIDTH : 1)) - 32, 1);
+ djui_base_set_color(&space->base, 220, 220, 220, 255);
+
+ djui_checkbox_create(body, DLANG(DYNOS, GLOBAL_PLAYER_MODELS), &configGlobalPlayerModels, djui_panel_dynos_global_player_models);
if (gNetworkType == NT_NONE) {
struct DjuiRect* rect1 = djui_rect_container_create(body, 64);
{
@@ -56,7 +68,7 @@ void djui_panel_dynos_create(struct DjuiBase* caller) {
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
}
- panel->bodySize.value = paginated->base.height.value + 16 + 64;
+ panel->bodySize.value = paginated->base.height.value + 16 + 64 + 64;
}
djui_panel_add(caller, panel, NULL);
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index d2a91fc7..c8807bf0 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -25818,6 +25818,28 @@ int smlua_func_obj_set_billboard(lua_State* L) {
return 1;
}
+int smlua_func_obj_set_character_model(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 3) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "obj_set_character_model", 3, top);
+ return 0;
+ }
+
+ struct Object* obj = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "obj_set_character_model"); return 0; }
+ u16 index = smlua_to_integer(L, 2);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "obj_set_character_model"); return 0; }
+ s32 modelID = smlua_to_integer(L, 3);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "obj_set_character_model"); return 0; }
+
+ extern void obj_set_character_model(struct Object* obj, u16 index, s32 modelID);
+ obj_set_character_model(obj, index, modelID);
+
+ return 1;
+}
+
/*
int smlua_func_obj_set_collision_data(lua_State* L) {
if (L == NULL) { return 0; }
@@ -32855,6 +32877,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "obj_set_angle", smlua_func_obj_set_angle);
smlua_bind_function(L, "obj_set_behavior", smlua_func_obj_set_behavior);
smlua_bind_function(L, "obj_set_billboard", smlua_func_obj_set_billboard);
+ smlua_bind_function(L, "obj_set_character_model", smlua_func_obj_set_character_model);
//smlua_bind_function(L, "obj_set_collision_data", smlua_func_obj_set_collision_data); <--- UNIMPLEMENTED
smlua_bind_function(L, "obj_set_cylboard", smlua_func_obj_set_cylboard);
smlua_bind_function(L, "obj_set_face_angle", smlua_func_obj_set_face_angle);
diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c
index 1b29b843..fcc696b6 100644
--- a/src/pc/network/network_player.c
+++ b/src/pc/network/network_player.c
@@ -39,7 +39,7 @@ void network_player_update_model(u8 localIndex) {
m->character = &gCharacters[index];
if (m->marioObj == NULL) { return; }
- obj_set_model(m->marioObj, m->character->modelId);
+ obj_set_character_model(m->marioObj, m->playerIndex, m->character->modelId);
}
bool network_player_any_connected(void) {