From b27fa1dd7a6f9775e927ab7b45872a9a4b65fad4 Mon Sep 17 00:00:00 2001 From: Gibson Pilconis Date: Thu, 26 Mar 2026 22:54:05 -0400 Subject: [PATCH 1/6] Fix rumble and add PS4/PS5 controller rumble. (#1007) Previously `thread6_rumble_loop()` was only being called once in `thread5_game_loop()`. This commit removes that call and adds one to `game_loop_one_iteration()`, creating the desired outcome of `thread6_runnable_loop()` running once per frame. Additionally, PS4 and PS5 rumble support is added. PS4 and PS5 rumble requires a feature called 'extended reporting', which can be enabled using two SDL hints. Since extended reported, once enabled, breaks compatibility with DirectInput until the controller is restarted, an option is also added to the configuration menu to toggle this functionality. --- lang/Czech.ini | 1 + lang/Dutch.ini | 1 + lang/English.ini | 1 + lang/French.ini | 1 + lang/German.ini | 1 + lang/Italian.ini | 1 + lang/Japanese.ini | 1 + lang/Polish.ini | 1 + lang/Portuguese.ini | 1 + lang/Russian.ini | 1 + lang/Spanish.ini | 1 + src/game/game_init.c | 3 +-- src/pc/configfile.c | 2 ++ src/pc/configfile.h | 1 + src/pc/controller/controller_sdl2.c | 15 +++++++++++++++ src/pc/djui/djui_panel_controls.c | 2 ++ 16 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lang/Czech.ini b/lang/Czech.ini index ddf7ae547..81a2e89a3 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Ovládání" EXTRA_BINDS = "Extra Ovládání" BACKGROUND_GAMEPAD = "Ovladač v pozadí" DISABLE_GAMEPADS = "Zakažte gamepady" +EXTENDED_REPORTS = "Rozšířené zprávy" GAMEPAD = "Použít ovladač" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Síla vibrace" diff --git a/lang/Dutch.ini b/lang/Dutch.ini index a06530f8b..c4e32f77c 100644 --- a/lang/Dutch.ini +++ b/lang/Dutch.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Toetsen" EXTRA_BINDS = "Extra Toetsen" BACKGROUND_GAMEPAD = "Achtergrond Gamepad" DISABLE_GAMEPADS = "Gamepads uitschakelen" +EXTENDED_REPORTS = "Uitgebreide rapporten" GAMEPAD = "Gamepad" DEADZONE = "Doode-zone" RUMBLE_STRENGTH = "Rommel Kracht" diff --git a/lang/English.ini b/lang/English.ini index 14ec1d462..0560d9ac2 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Binds" EXTRA_BINDS = "Extra Binds" BACKGROUND_GAMEPAD = "Background Gamepad" DISABLE_GAMEPADS = "Disable Gamepads" +EXTENDED_REPORTS = "Extended Reports" GAMEPAD = "Gamepad" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Rumble Strength" diff --git a/lang/French.ini b/lang/French.ini index b0dfe3be2..645ed0c2a 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -100,6 +100,7 @@ N64_BINDS = "Touches N64" EXTRA_BINDS = "Touches Supplémentaires" BACKGROUND_GAMEPAD = "Manette en arrière plan" DISABLE_GAMEPADS = "Désactiver les manettes de jeu" +EXTENDED_REPORTS = "Rapports détaillés" GAMEPAD = "Manette" DEADZONE = "Zone Morte" RUMBLE_STRENGTH = "Vibrations" diff --git a/lang/German.ini b/lang/German.ini index e44e5c718..1fb8031d7 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -99,6 +99,7 @@ CONTROLS = "STEUERUNG" N64_BINDS = "N64-Einstellungen" EXTRA_BINDS = "Zusätzliche Einstellungen" BACKGROUND_GAMEPAD = "Hintergrund-Gamepad" +EXTENDED_REPORTS = "Erweiterte Berichte" DISABLE_GAMEPADS = "Gamepads deaktivieren" GAMEPAD = "Gamepad" DEADZONE = "Tote Zone" diff --git a/lang/Italian.ini b/lang/Italian.ini index e0827a2f6..dfe13d3bb 100644 --- a/lang/Italian.ini +++ b/lang/Italian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLLI" N64_BINDS = "Comandi N64" EXTRA_BINDS = "Comandi Extra" BACKGROUND_GAMEPAD = "Attivi in Background" +EXTENDED_REPORTS = "Rapporti estesi" DISABLE_GAMEPADS = "Disabilita i Gamepad" GAMEPAD = "Controller" DEADZONE = "Zona Morta" diff --git a/lang/Japanese.ini b/lang/Japanese.ini index a15f74b3a..05b20a216 100644 --- a/lang/Japanese.ini +++ b/lang/Japanese.ini @@ -99,6 +99,7 @@ CONTROLS = "CONTROLS" N64_BINDS = "ニンテンドウ64のボタン割り当て" EXTRA_BINDS = "追加のボタン割り当て" BACKGROUND_GAMEPAD = "バックグラウンドでのコントローラー認識" +EXTENDED_REPORTS = "拡張レポート" DISABLE_GAMEPADS = "コントローラーを無効化" GAMEPAD = "コントローラー" DEADZONE = "デッドゾーン" diff --git a/lang/Polish.ini b/lang/Polish.ini index 9781c549e..cb983027d 100644 --- a/lang/Polish.ini +++ b/lang/Polish.ini @@ -99,6 +99,7 @@ CONTROLS = "STEROWANIE" N64_BINDS = "Przypisania N64" EXTRA_BINDS = "Dodatkowe Przypisania" BACKGROUND_GAMEPAD = "Gamepad w Tle" +EXTENDED_REPORTS = "Rozszerzone raporty" DISABLE_GAMEPADS = "Wyłącz Gamepady" GAMEPAD = "Gamepad" DEADZONE = "Martwa Strefa" diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index e935499a6..d3b9e0c6b 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -99,6 +99,7 @@ CONTROLS = "CONTROLES" N64_BINDS = "N64" EXTRA_BINDS = "Outros" BACKGROUND_GAMEPAD = "Controle de fundo" +EXTENDED_REPORTS = "Relatórios detalhados" DISABLE_GAMEPADS = "Desativar controles" GAMEPAD = "Controle" DEADZONE = "Zona morta" diff --git a/lang/Russian.ini b/lang/Russian.ini index 52861a4cf..85d39ae37 100644 --- a/lang/Russian.ini +++ b/lang/Russian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLS" N64_BINDS = "Кнопки N64" EXTRA_BINDS = "Дополнительные кнопки" BACKGROUND_GAMEPAD = "Фоновый ввод" +EXTENDED_REPORTS = "Расширенные отчеты" DISABLE_GAMEPADS = "Отключить геймпады" GAMEPAD = "Геймпад" DEADZONE = "Mёртвая зона" diff --git a/lang/Spanish.ini b/lang/Spanish.ini index f083c8ba2..50660a637 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -100,6 +100,7 @@ N64_BINDS = "Botones de N64" EXTRA_BINDS = "Botones Adicionales" BACKGROUND_GAMEPAD = "Mando en segundo plano" DISABLE_GAMEPADS = "Desactivar mandos" +EXTENDED_REPORTS = "Informes ampliados" GAMEPAD = "Mando" DEADZONE = "Zona muerta" RUMBLE_STRENGTH = "Intensidad de vibración" diff --git a/src/game/game_init.c b/src/game/game_init.c index 2e13c7e57..058cc9697 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -594,8 +594,6 @@ void thread5_game_loop(UNUSED void *arg) { play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0); set_sound_mode(save_file_get_sound_mode()); - thread6_rumble_loop(NULL); - gGlobalTimer++; } @@ -609,6 +607,7 @@ void game_loop_one_iteration(void) { osContStartReadData(&gSIEventMesgQueue); } + thread6_rumble_loop(NULL); audio_game_loop_tick(); config_gfx_pool(); read_controller_inputs(); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index f2267a7db..4e52b50b6 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -127,6 +127,7 @@ unsigned int configStickDeadzone = 16; unsigned int configRumbleStrength = 50; unsigned int configGamepadNumber = 0; bool configBackgroundGamepad = true; +bool configExtendedReports = false; bool configDisableGamepads = false; bool configUseStandardKeyBindingsChat = false; bool configSmoothScrolling = false; @@ -270,6 +271,7 @@ static const struct ConfigOption options[] = { {.name = "rumble_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configRumbleStrength}, {.name = "gamepad_number", .type = CONFIG_TYPE_UINT, .uintValue = &configGamepadNumber}, {.name = "background_gamepad", .type = CONFIG_TYPE_UINT, .boolValue = &configBackgroundGamepad}, + {.name = "extended_reports", .type = CONFIG_TYPE_BOOL, .boolValue = &configExtendedReports}, #ifndef HANDHELD {.name = "disable_gamepads", .type = CONFIG_TYPE_BOOL, .boolValue = &configDisableGamepads}, #endif diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d537970e4..11cdbd5bc 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -93,6 +93,7 @@ extern unsigned int configStickDeadzone; extern unsigned int configRumbleStrength; extern unsigned int configGamepadNumber; extern bool configBackgroundGamepad; +extern bool configExtendedReports; extern bool configDisableGamepads; extern bool configUseStandardKeyBindingsChat; extern bool configSmoothScrolling; diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 251d8a85f..6d4fc30ed 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -40,6 +40,7 @@ static SDL_GameController *sdl_cntrl = NULL; static SDL_Joystick *sdl_joystick = NULL; static SDL_Haptic *sdl_haptic = NULL; +static bool sExtendedReports = false; static bool sBackgroundGamepad = false; static u32 num_joy_binds = 0; @@ -102,6 +103,13 @@ static void controller_sdl_bind(void) { } static void controller_sdl_init(void) { + // Allows extended reports on PS4 and PS5 controllers + if (configExtendedReports) { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); + } + sExtendedReports = configExtendedReports; + // Allows game to be controlled by gamepad when not in focus if (configBackgroundGamepad) { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); @@ -200,6 +208,13 @@ static void controller_sdl_read(OSContPad *pad) { // remember buttons that changed from 0 to 1 last_mouse = (mouse_prev ^ mouse) & mouse; + if (configExtendedReports != sExtendedReports) { + sExtendedReports = configExtendedReports; + char* hint = sExtendedReports ? "1" : "0"; + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, hint); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, hint); + } + if (configBackgroundGamepad != sBackgroundGamepad) { sBackgroundGamepad = configBackgroundGamepad; SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, sBackgroundGamepad ? "1" : "0"); diff --git a/src/pc/djui/djui_panel_controls.c b/src/pc/djui/djui_panel_controls.c index 8088580a9..e8b39396d 100644 --- a/src/pc/djui/djui_panel_controls.c +++ b/src/pc/djui/djui_panel_controls.c @@ -43,6 +43,8 @@ void djui_panel_controls_create(struct DjuiBase* caller) { djui_checkbox_create(body, DLANG(MISC, USE_STANDARD_KEY_BINDINGS_CHAT), &configUseStandardKeyBindingsChat, NULL); #ifdef HAVE_SDL2 + djui_checkbox_create(body, DLANG(CONTROLS, EXTENDED_REPORTS), &configExtendedReports, NULL); + int numJoys = SDL_NumJoysticks(); if (numJoys == 0) { numJoys = 1; } From dfb3b2523d7abd1e2a2e8339cfad667e5f71a059 Mon Sep 17 00:00:00 2001 From: Blockyyy <88585273+Blockyyy@users.noreply.github.com> Date: Fri, 27 Mar 2026 04:32:44 +0100 Subject: [PATCH 2/6] Fix lives (#1119) - Fix lives going below 0 right before a game over (also fixes a crash where if you exited with -1 lives and went into a subarea you would crash) - Restore how lives get removed upon death exit (like in vanilla) - Prevent bubbling from happening if you don't have enough lives (this fixes the annoying bug where you would get put back in a bubble and get popped immediately after over and over again) --- src/game/interaction.c | 2 +- src/game/level_update.c | 6 ++---- src/game/mario.c | 2 +- src/game/mario_actions_airborne.c | 2 +- src/game/mario_actions_cutscene.c | 21 +++++++++++++++------ src/game/mario_actions_submerged.c | 6 +++--- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/game/interaction.c b/src/game/interaction.c index 4304a23ae..599d6c65a 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -2445,7 +2445,7 @@ void check_death_barrier(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { switch (gCurrCourseNum) { case COURSE_COTMC: // (20) Cavern of the Metal Cap case COURSE_TOTWC: // (21) Tower of the Wing Cap diff --git a/src/game/level_update.c b/src/game/level_update.c index 1ad390601..ff6ae25b1 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -878,8 +878,7 @@ void verify_warp(struct MarioState *m, bool killMario) { return; } - m->numLives--; - if (m->numLives < 0) { + if (m->numLives <= 0) { sDelayedWarpOp = WARP_OP_GAME_OVER; } else { sSourceWarpNodeId = WARP_NODE_DEATH; @@ -934,8 +933,7 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) { break; case WARP_OP_DEATH: - m->numLives--; - if (m->numLives <= -1) { + if (m->numLives <= 0) { sDelayedWarpOp = WARP_OP_GAME_OVER; } sDelayedWarpTimer = 48; diff --git a/src/game/mario.c b/src/game/mario.c index 897bbc3fe..d929544f6 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -447,7 +447,7 @@ void mario_set_bubbled(struct MarioState* m) { gLocalBubbleCounter = 20; drop_and_set_mario_action(m, ACT_BUBBLED, 0); - if (m->numLives > -1) { + if (m->numLives > 0) { m->numLives--; } m->healCounter = 0; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 00b338447..6112358eb 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -1734,7 +1734,7 @@ s32 act_lava_boost(struct MarioState *m) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { m->health = 0xFF; mario_set_bubbled(m); } else { diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 9162cd84a..293079dc4 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -849,7 +849,7 @@ s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWa smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return animFrame; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -922,8 +922,7 @@ s32 act_quicksand_death(struct MarioState *m) { bool allowDeath = true; smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -947,7 +946,7 @@ s32 act_eaten_by_bubba(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { m->health = 0xFF; mario_set_bubbled(m); } else { @@ -1437,6 +1436,12 @@ s32 act_exit_land_save_dialog(struct MarioState *m) { return FALSE; } +static void lose_life_after_death_exit(struct MarioState *m) { + if (sDelayedWarpArg != WARP_ARG_EXIT_COURSE) { + m->numLives--; + } +} + s32 act_death_exit(struct MarioState *m) { if (!m) { return 0; } if (15 < m->actionTimer++ @@ -1447,6 +1452,7 @@ s32 act_death_exit(struct MarioState *m) { play_character_sound(m, CHAR_SOUND_OOOF2); #endif queue_rumble_data_mario(m, 5, 80); + lose_life_after_death_exit(m); // restore 7.75 units of health m->healCounter = 31; } @@ -1463,6 +1469,7 @@ s32 act_unused_death_exit(struct MarioState *m) { #else play_character_sound(m, CHAR_SOUND_OOOF2); #endif + lose_life_after_death_exit(m); // restore 7.75 units of health m->healCounter = 31; } @@ -1479,6 +1486,7 @@ s32 act_falling_death_exit(struct MarioState *m) { #else play_character_sound(m, CHAR_SOUND_OOOF2); #endif + lose_life_after_death_exit(m); queue_rumble_data_mario(m, 5, 80); // restore 7.75 units of health m->healCounter = 31; @@ -1526,6 +1534,7 @@ s32 act_special_death_exit(struct MarioState *m) { if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, CHAR_ANIM_BACKWARD_AIR_KB, -24.0f)) { queue_rumble_data_mario(m, 5, 80); + lose_life_after_death_exit(m); m->healCounter = 31; } // show Mario @@ -1829,7 +1838,7 @@ s32 act_squished(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1880,7 +1889,7 @@ s32 act_squished(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { // 0 units of health diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 61f3a0e15..e4195f64c 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -1003,7 +1003,7 @@ static s32 act_drowning(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1038,7 +1038,7 @@ static s32 act_water_death(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1164,7 +1164,7 @@ static s32 act_caught_in_whirlpool(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { reset_rumble_timers(m); return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); From 5288ac5b2d5455bdf0828bf552829301fb74f5e5 Mon Sep 17 00:00:00 2001 From: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:24:28 +0200 Subject: [PATCH 3/6] fix text interpolation with legacy font --- src/pc/djui/djui_hud_utils.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index 8ad0a5b15..d36938e79 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -533,8 +533,6 @@ static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 if (message == NULL) { return; } gDjuiHudUtilsZ += 0.001f; - if (djui_hud_text_font_is_legacy()) { scale *= 0.5f; } - const struct DjuiFont* font = djui_hud_get_text_font(); f32 fontScale = font->defaultFontScale * scale; @@ -654,13 +652,22 @@ static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 } void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { + if (message == NULL) { return; } + + if (djui_hud_text_font_is_legacy()) { + scale *= 0.5f; + } + djui_hud_print_text_internal(message, x, y, scale, NULL); } void djui_hud_print_text_interpolated(const char* message, f32 prevX, f32 prevY, f32 prevScale, f32 x, f32 y, f32 scale) { if (message == NULL) { return; } - if (djui_hud_text_font_is_legacy()) { prevScale *= 0.5f; } + if (djui_hud_text_font_is_legacy()) { + scale *= 0.5f; + prevScale *= 0.5f; + } struct InterpHud *interp = djui_hud_create_interp(); if (interp) { From 7604ef9297f9cc789ce04a4cf6bc08a991b186bd Mon Sep 17 00:00:00 2001 From: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:05:29 +0200 Subject: [PATCH 4/6] check surface type and special object preset type in dynos collision validation --- data/dynos_bin_col.cpp | 132 ++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 35 deletions(-) diff --git a/data/dynos_bin_col.cpp b/data/dynos_bin_col.cpp index cd5a58e50..331cbb130 100644 --- a/data/dynos_bin_col.cpp +++ b/data/dynos_bin_col.cpp @@ -3,7 +3,9 @@ extern "C" { #include "include/surface_terrains.h" #include "include/level_misc_macros.h" +#include "include/special_presets.h" #include "include/special_preset_names.h" +#include "src/engine/surface_load.h" } // Free data pointers, but keep nodes and tokens intact @@ -34,12 +36,32 @@ struct CollisionValidationData { u32 vtxCount; u32 triAlloc; u32 triCount; + s16 surfaceType; u32 specialAlloc; u32 specialCount; u32 waterBoxAlloc; u32 waterBoxCount; }; +static u8 GetSpecialObjectType(u8 preset) { + for (s32 i = 0; i < ARRAY_COUNT(SpecialObjectPresets); ++i) { + if (SpecialObjectPresets[i].preset_id == preset) { + return SpecialObjectPresets[i].type; + } + } + return SPTYPE_UNKNOWN; +} + +static const char *GetCorrectSpecialObjectCommand(u8 presetType) { + switch (presetType) { + case SPTYPE_NO_YROT_OR_PARAMS: return "SPECIAL_OBJECT"; + case SPTYPE_YROT_NO_PARAMS: return "SPECIAL_OBJECT_WITH_YAW"; + case SPTYPE_PARAMS_AND_YROT: return "SPECIAL_OBJECT_WITH_YAW_AND_PARAM"; + case SPTYPE_DEF_PARAM_AND_YROT: return "SPECIAL_OBJECT_WITH_YAW"; + default: return ""; + } +} + static void ValidateColSectionChange(GfxData* aGfxData, struct CollisionValidationData& aColValData, u8 section) { if (aColValData.section == COL_SECTION_END) { PrintDataError("Found new col section after COL_END"); @@ -67,51 +89,70 @@ static void ValidateColInit(GfxData* aGfxData, struct CollisionValidationData& a ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_VTX); } -static void ValidateColVertexInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { +static void ValidateColVertexInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertexCount) { if (strcmp(aColValData.lastSymbol, "COL_INIT") != 0) { PrintDataError("COL_VERTEX_INIT found outside of vertex section"); } - if (arg0 < 0) { - PrintDataError("COL_VERTEX_INIT with a negative count: %d", arg0); + if (vertexCount < 0) { + PrintDataError("COL_VERTEX_INIT with a negative count: %d", vertexCount); } - aColValData.vtxAlloc = arg0; + aColValData.vtxAlloc = vertexCount; aColValData.vtxCount = 0; } -static void ValidateColVertex(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2) { +static void ValidateColVertex(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 x, s16 y, s16 z) { if (aColValData.section != COL_SECTION_VTX) { PrintDataError("COL_VERTEX found outside of vertex section"); } aColValData.vtxCount++; } -static void ValidateColTriInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1) { - if (arg1 < 0) { - PrintDataError("COL_TRI_INIT with a negative count: %d", arg1); +static void ValidateColTriInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 surfaceType, s16 triangleCount) { + if (triangleCount < 0) { + PrintDataError("COL_TRI_INIT with a negative count: %d", triangleCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_TRI); - aColValData.triAlloc = arg1; + aColValData.triAlloc = triangleCount; aColValData.triCount = 0; + aColValData.surfaceType = surfaceType; } -static void ValidateColTri(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2) { +static void ValidateColTri(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertex0, s16 vertex1, s16 vertex2) { if (aColValData.section != COL_SECTION_TRI) { PrintDataError("COL_TRI found outside of triangle section"); } - if (arg0 < 0 || arg0 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for first param: %d", arg0); + if (surface_has_force(aColValData.surfaceType)) { + PrintDataError("COL_TRI cannot be used by surface types with a force parameter: %d (use COL_TRI_SPECIAL instead)", aColValData.surfaceType); } - if (arg1 < 0 || arg1 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for second param: %d", arg1); + if (vertex0 < 0 || vertex0 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for first param: %d", vertex0); } - if (arg2 < 0 || arg2 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for third param: %d", arg2); + if (vertex1 < 0 || vertex1 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for second param: %d", vertex1); + } + if (vertex2 < 0 || vertex2 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for third param: %d", vertex2); } aColValData.triCount++; } -static void ValidateColTriSpecial(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3) { - ValidateColTri(aGfxData, aColValData, arg0, arg1, arg2); +static void ValidateColTriSpecial(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertex0, s16 vertex1, s16 vertex2, s16 force) { + if (aColValData.section != COL_SECTION_TRI) { + PrintDataError("COL_TRI_SPECIAL found outside of triangle section"); + } + if (!surface_has_force(aColValData.surfaceType)) { + PrintDataError("COL_TRI_SPECIAL cannot be used by surface types with no force parameter: %d (use COL_TRI instead)", aColValData.surfaceType); + } + if (vertex0 < 0 || vertex0 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for first param: %d", vertex0); + } + if (vertex1 < 0 || vertex1 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for second param: %d", vertex1); + } + if (vertex2 < 0 || vertex2 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for third param: %d", vertex2); + } + aColValData.triCount++; } static void ValidateColStop(GfxData* aGfxData, struct CollisionValidationData& aColValData) { @@ -122,49 +163,70 @@ static void ValidateColEnd(GfxData* aGfxData, struct CollisionValidationData& aC ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_END); } -static void ValidateColSpecialInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { - if (arg0 < 0) { - PrintDataError("COL_SPECIAL_INIT with a negative count: %d", arg0); +static void ValidateColSpecialInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 specialCount) { + if (specialCount < 0) { + PrintDataError("COL_SPECIAL_INIT with a negative count: %d", specialCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_SPECIAL); - aColValData.specialAlloc = arg0; + aColValData.specialAlloc = specialCount; aColValData.specialCount = 0; } -static void ValidateColWaterBoxInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { - if (arg0 < 0) { - PrintDataError("COL_WATER_BOX_INIT with a negative count: %d", arg0); +static void ValidateColWaterBoxInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 waterBoxCount) { + if (waterBoxCount < 0) { + PrintDataError("COL_WATER_BOX_INIT with a negative count: %d", waterBoxCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_WATER_BOX); - aColValData.waterBoxAlloc = arg0; + aColValData.waterBoxAlloc = waterBoxCount; aColValData.waterBoxCount = 0; } -static void ValidateColWaterBox(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5) { +static void ValidateColWaterBox(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 id, s16 x1, s16 z1, s16 x2, s16 z2, s16 y) { if (aColValData.section != COL_SECTION_WATER_BOX) { PrintDataError("COL_WATER_BOX found outside of water box section"); } aColValData.waterBoxCount++; } -static void ValidateColSpecialObject(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3) { +static void ValidateColSpecialObject(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ) { if (aColValData.section != COL_SECTION_SPECIAL) { PrintDataError("SPECIAL_OBJECT found outside of special section"); } - aColValData.specialCount++; -} - -static void ValidateColSpecialObjectWithYaw(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4) { - if (aColValData.section != COL_SECTION_SPECIAL) { - PrintDataError("SPECIAL_OBJECT_WITH_YAW found outside of special section"); + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT has invalid preset: %d", preset); + } + if (presetType != SPTYPE_NO_YROT_OR_PARAMS) { + PrintDataError("SPECIAL_OBJECT cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); } aColValData.specialCount++; } -static void ValidateColSpecialObjectWithYawAndParam(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5) { +static void ValidateColSpecialObjectWithYaw(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ, s16 yaw) { + if (aColValData.section != COL_SECTION_SPECIAL) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW found outside of special section"); + } + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW has invalid preset: %d", preset); + } + if (presetType != SPTYPE_YROT_NO_PARAMS && presetType != SPTYPE_DEF_PARAM_AND_YROT) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); + } + aColValData.specialCount++; +} + +static void ValidateColSpecialObjectWithYawAndParam(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ, s16 yaw, s16 param) { if (aColValData.section != COL_SECTION_SPECIAL) { PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM found outside of special section"); } + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM has invalid preset: %d", preset); + } + if (presetType != SPTYPE_PARAMS_AND_YROT) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); + } aColValData.specialCount++; } From ba54cbd1d0b8ec9474f5cfd3bebae758f69c2e2b Mon Sep 17 00:00:00 2001 From: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:23:31 +0200 Subject: [PATCH 5/6] path separators --- data/dynos_mgr_pack.cpp | 2 +- src/pc/lua/smlua.c | 2 +- src/pc/lua/smlua_require.c | 2 +- src/pc/lua/smlua_utils.c | 2 +- src/pc/lua/utils/smlua_misc_utils.c | 4 ++-- src/pc/mods/mods_utils.c | 26 ++++++++++++-------------- src/pc/platform.c | 6 +++--- src/pc/platform.h | 8 ++++++++ 8 files changed, 29 insertions(+), 23 deletions(-) diff --git a/data/dynos_mgr_pack.cpp b/data/dynos_mgr_pack.cpp index 9bd54ad92..e60ccf410 100644 --- a/data/dynos_mgr_pack.cpp +++ b/data/dynos_mgr_pack.cpp @@ -151,7 +151,7 @@ PackData* DynOS_Pack_Add(const SysPath& aPath) { const char* displayName = aPath.c_str(); const char* ctoken = displayName; while (*ctoken != '\0') { - if (*ctoken == '/' || *ctoken == '\\') { + if (*ctoken == *PATH_SEPARATOR || *ctoken == *PATH_SEPARATOR_ALT) { if (*(ctoken + 1) != '\0') { displayName = (ctoken + 1); } diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c index cd2502050..d8f2619c7 100644 --- a/src/pc/lua/smlua.c +++ b/src/pc/lua/smlua.c @@ -359,7 +359,7 @@ void smlua_init(void) { } // skip loading scripts in subdirectories - if (strchr(file->relativePath, '/') != NULL || strchr(file->relativePath, '\\') != NULL) { + if (strchr(file->relativePath, *PATH_SEPARATOR) != NULL || strchr(file->relativePath, *PATH_SEPARATOR_ALT) != NULL) { continue; } diff --git a/src/pc/lua/smlua_require.c b/src/pc/lua/smlua_require.c index c331a0570..c99db7513 100644 --- a/src/pc/lua/smlua_require.c +++ b/src/pc/lua/smlua_require.c @@ -121,7 +121,7 @@ static int smlua_custom_require(lua_State* L) { return 0; } - if (path_ends_with(moduleName, "/") || path_ends_with(moduleName, "\\")) { + if (path_ends_with(moduleName, PATH_SEPARATOR) || path_ends_with(moduleName, PATH_SEPARATOR_ALT)) { LOG_LUA_LINE("cannot require a directory"); return 0; } diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c index a680fe2b0..3ccda2a7f 100644 --- a/src/pc/lua/smlua_utils.c +++ b/src/pc/lua/smlua_utils.c @@ -849,7 +849,7 @@ void smlua_logline(void) { if (strlen(src) < SYS_MAX_PATH) { int slashCount = 0; for (const char* p = src + strlen(src); p > src; --p) { - if (*p == '/' || *p == '\\') { + if (*p == *PATH_SEPARATOR || *p == *PATH_SEPARATOR_ALT) { if (++slashCount == 2) { folderStart = p + 1; break; diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index 9e90a45fb..faa10c1ae 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -622,8 +622,8 @@ LuaTable get_mod_files(struct Mod* mod, OPTIONAL const char* subDirectory) { normalize_path(normalizedSubDir); size_t subDirLen = strlen(normalizedSubDir); - if (subDirLen > 0 && subDirLen + 1 < SYS_MAX_PATH && normalizedSubDir[subDirLen - 1] != '/') { - strcat(normalizedSubDir, "/"); + if (subDirLen > 0 && subDirLen + 1 < SYS_MAX_PATH && normalizedSubDir[subDirLen - 1] != *PATH_SEPARATOR) { + strcat(normalizedSubDir, PATH_SEPARATOR); subDirLen = strlen(normalizedSubDir); } diff --git a/src/pc/mods/mods_utils.c b/src/pc/mods/mods_utils.c index 6e8c2bcd9..e09df3089 100644 --- a/src/pc/mods/mods_utils.c +++ b/src/pc/mods/mods_utils.c @@ -144,7 +144,7 @@ bool mod_file_create_directories(struct Mod* mod, struct ModFile* modFile) { char* p = path; u16 index = 0; while (*p != '\0') { - if (*p == '/' || *p == '\\') { + if (*p == *PATH_SEPARATOR || *p == *PATH_SEPARATOR_ALT) { if (snprintf(tmpPath, index + 1, "%s", path) < 0) { } if (!fs_sys_dir_exists(tmpPath)) { fs_sys_mkdir(tmpPath); @@ -210,11 +210,9 @@ void normalize_path(char* path) { // replace slashes char* p = path; while (*p) { -#if defined(_WIN32) - if (*p == '/') { *p = '\\'; } -#else - if (*p == '\\') { *p = '/'; } -#endif + if (*p == *PATH_SEPARATOR_ALT) { + *p = *PATH_SEPARATOR; + } p++; } } @@ -227,7 +225,7 @@ char* path_basename(char* path) { char* base = path; while (*path != '\0') { if (*(path + 1) != '\0') { - if (*path == '\\' || *path == '/') { + if (*path == *PATH_SEPARATOR || *path == *PATH_SEPARATOR_ALT) { base = path + 1; } } @@ -251,7 +249,7 @@ void path_get_folder(char* path, char* outpath) { int path_depth(const char* path) { int depth = 0; for (; *path; path++) { - if (*path == '/' || *path == '\\') { + if (*path == *PATH_SEPARATOR || *path == *PATH_SEPARATOR_ALT) { depth++; } } @@ -262,7 +260,7 @@ void resolve_relative_path(const char* base, const char* path, char* output) { char combined[SYS_MAX_PATH] = ""; // If path is absolute, copy as is. Otherwise, combine base and relative path - if (path[0] == '/' || path[0] == '\\') { + if (path[0] == *PATH_SEPARATOR || path[0] == *PATH_SEPARATOR_ALT) { snprintf(combined, sizeof(combined), "%s", path); } else { snprintf(combined, sizeof(combined), "%s/%s", base, path); @@ -272,7 +270,7 @@ void resolve_relative_path(const char* base, const char* path, char* output) { int tokenCount = 0; // Tokenize path by separators - char* token = strtok(combined, "/\\"); + char* token = strtok(combined, PATH_SEPARATOR PATH_SEPARATOR_ALT); while (token && tokenCount < 64) { if (strcmp(token, "..") == 0) { // Pop last token to go up a directory @@ -283,7 +281,7 @@ void resolve_relative_path(const char* base, const char* path, char* output) { tokens[tokenCount++] = token; } - token = strtok(NULL, "/\\"); + token = strtok(NULL, PATH_SEPARATOR PATH_SEPARATOR_ALT); } output[0] = '\0'; @@ -291,7 +289,7 @@ void resolve_relative_path(const char* base, const char* path, char* output) { // Build output path from tokens for (int i = 0; i < tokenCount; i++) { if (i > 0) { - strncat(output, "/", SYS_MAX_PATH - strlen(output) - 1); + strncat(output, PATH_SEPARATOR, SYS_MAX_PATH - strlen(output) - 1); } strncat(output, tokens[i], SYS_MAX_PATH - strlen(output) - 1); } @@ -308,8 +306,8 @@ bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath) { if (!fs_sys_filename_is_portable(dir->d_name)) { return false; } // skip anything that contains \ or / - if (strchr(dir->d_name, '/') != NULL) { return false; } - if (strchr(dir->d_name, '\\') != NULL) { return false; } + if (strchr(dir->d_name, *PATH_SEPARATOR) != NULL) { return false; } + if (strchr(dir->d_name, *PATH_SEPARATOR_ALT) != NULL) { return false; } // skip anything that starts with . if (dir->d_name[0] == '.') { return false; } diff --git a/src/pc/platform.c b/src/pc/platform.c index 185b85bd9..7daeb178e 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -56,8 +56,8 @@ const char *sys_file_extension(const char *fpath) { } const char *sys_file_name(const char *fpath) { - const char *sep1 = strrchr(fpath, '/'); - const char *sep2 = strrchr(fpath, '\\'); + const char *sep1 = strrchr(fpath, *PATH_SEPARATOR); + const char *sep2 = strrchr(fpath, *PATH_SEPARATOR_ALT); const char *sep = sep1 > sep2 ? sep1 : sep2; if (!sep) return fpath; return sep + 1; @@ -325,7 +325,7 @@ const char *sys_user_path(void) { // strip the trailing separator const unsigned int len = strlen(path); - if (path[len-1] == '/' || path[len-1] == '\\') { path[len-1] = 0; } + if (path[len-1] == *PATH_SEPARATOR || path[len-1] == *PATH_SEPARATOR_ALT) { path[len-1] = 0; } return path; } diff --git a/src/pc/platform.h b/src/pc/platform.h index c05094ad4..e043c6ab5 100644 --- a/src/pc/platform.h +++ b/src/pc/platform.h @@ -7,6 +7,14 @@ /* platform-specific functions and whatnot */ +#ifdef _WIN32 +#define PATH_SEPARATOR "\\" +#define PATH_SEPARATOR_ALT "/" +#else +#define PATH_SEPARATOR "/" +#define PATH_SEPARATOR_ALT "\\" +#endif + #define SYS_MAX_PATH 4096 // crossplatform impls of misc stuff From 8afe062be1ff6d172f04fbffdc620daf5a06b98b Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:37:16 +0200 Subject: [PATCH 6/6] Fix nametags color and rendering order (#1150) * Fix nametags color and rendering order * make growing_array_init reuse array and buffer if they were already allocated * use prim color for text color * isaac review * update text color descriptions * small optimization --- autogen/convert_functions.py | 1 + autogen/lua_definitions/functions.lua | 32 ++++-- docs/lua/functions-3.md | 74 +++++++++++- docs/lua/functions-6.md | 20 ---- docs/lua/functions.md | 4 +- src/game/area.c | 3 + src/game/level_update.c | 2 + src/game/memory.c | 44 +++++-- src/game/object_helpers.c | 15 --- src/game/object_helpers.h | 1 - src/game/object_list_processor.c | 2 + src/pc/crash_handler.c | 2 +- src/pc/djui/djui.c | 1 + src/pc/djui/djui_gfx.c | 8 +- src/pc/djui/djui_hud_utils.c | 106 ++++++++++++----- src/pc/djui/djui_hud_utils.h | 14 ++- src/pc/djui/djui_inputbox.c | 1 + src/pc/djui/djui_text.c | 7 +- src/pc/lua/smlua_functions_autogen.c | 75 ++++++++---- src/pc/nametags.c | 158 ++++++++++++++++---------- 20 files changed, 395 insertions(+), 175 deletions(-) diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 9a9f7d53a..19d9ad16b 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -118,6 +118,7 @@ override_disallowed_functions = { "src/game/mario.h": [ " init_mario" ], "src/pc/djui/djui_console.h": [ " djui_console_create", "djui_console_message_create", "djui_console_message_dequeue" ], "src/pc/djui/djui_chat_message.h": [ "create_from" ], + "src/pc/djui/djui_hud_utils.h": [ "djui_hud_clear_interp_data" ], "src/game/interaction.h": [ "process_interaction", "_handle_" ], "src/game/sound_init.h": [ "_loop_", "thread4_", "set_sound_mode" ], "src/pc/network/network_utils.h": [ "network_get_player_text_color[^_]" ], diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 08b0b1587..00d52fad7 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -3837,7 +3837,7 @@ function djui_hud_set_font(fontType) end --- @return DjuiColor ---- Gets the current DJUI HUD color +--- Gets the current DJUI HUD global color function djui_hud_get_color() -- ... end @@ -3846,16 +3846,36 @@ end --- @param g integer --- @param b integer --- @param a integer ---- Sets the current DJUI HUD color +--- Sets the current DJUI HUD global color function djui_hud_set_color(r, g, b, a) -- ... end ---- Resets the current DJUI HUD color +--- Resets the current DJUI HUD global color function djui_hud_reset_color() -- ... end +--- @return DjuiColor +--- Gets the current DJUI HUD text default color. This color is overridden by color codes +function djui_hud_get_text_color() + -- ... +end + +--- @param r integer +--- @param g integer +--- @param b integer +--- @param a integer +--- Sets the current DJUI HUD text default color. This color is overridden by color codes +function djui_hud_set_text_color(r, g, b, a) + -- ... +end + +--- Resets the current DJUI HUD text default color. This color is overridden by color codes +function djui_hud_reset_text_color() + -- ... +end + --- @return integer rotation --- @return number pivotX --- @return number pivotY @@ -9077,12 +9097,6 @@ function cur_obj_check_anim_frame_in_range(startFrame, rangeLength) -- ... end ---- @param a0 Pointer_integer ---- @return integer -function cur_obj_check_frame_prior_current_frame(a0) - -- ... -end - --- @param m MarioState --- @return integer function mario_is_in_air_action(m) diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index 8e4f8c230..572b46f94 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -2858,7 +2858,7 @@ Sets the current DJUI HUD font ## [djui_hud_get_color](#djui_hud_get_color) ### Description -Gets the current DJUI HUD color +Gets the current DJUI HUD global color ### Lua Example `local djuiColorValue = djui_hud_get_color()` @@ -2879,7 +2879,7 @@ Gets the current DJUI HUD color ## [djui_hud_set_color](#djui_hud_set_color) ### Description -Sets the current DJUI HUD color +Sets the current DJUI HUD global color ### Lua Example `djui_hud_set_color(r, g, b, a)` @@ -2905,7 +2905,7 @@ Sets the current DJUI HUD color ## [djui_hud_reset_color](#djui_hud_reset_color) ### Description -Resets the current DJUI HUD color +Resets the current DJUI HUD global color ### Lua Example `djui_hud_reset_color()` @@ -2923,6 +2923,74 @@ Resets the current DJUI HUD color
+## [djui_hud_get_text_color](#djui_hud_get_text_color) + +### Description +Gets the current DJUI HUD text default color. This color is overridden by color codes + +### Lua Example +`local djuiColorValue = djui_hud_get_text_color()` + +### Parameters +- None + +### Returns +- [DjuiColor](structs.md#DjuiColor) + +### C Prototype +`struct DjuiColor* djui_hud_get_text_color(void);` + +[:arrow_up_small:](#) + +
+ +## [djui_hud_set_text_color](#djui_hud_set_text_color) + +### Description +Sets the current DJUI HUD text default color. This color is overridden by color codes + +### Lua Example +`djui_hud_set_text_color(r, g, b, a)` + +### Parameters +| Field | Type | +| ----- | ---- | +| r | `integer` | +| g | `integer` | +| b | `integer` | +| a | `integer` | + +### Returns +- None + +### C Prototype +`void djui_hud_set_text_color(u8 r, u8 g, u8 b, u8 a);` + +[:arrow_up_small:](#) + +
+ +## [djui_hud_reset_text_color](#djui_hud_reset_text_color) + +### Description +Resets the current DJUI HUD text default color. This color is overridden by color codes + +### Lua Example +`djui_hud_reset_text_color()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void djui_hud_reset_text_color(void);` + +[:arrow_up_small:](#) + +
+ ## [djui_hud_get_rotation](#djui_hud_get_rotation) ### Description diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md index a639b7db9..d45ae38a8 100644 --- a/docs/lua/functions-6.md +++ b/docs/lua/functions-6.md @@ -1615,26 +1615,6 @@ Multiplies a vector by the transpose of a matrix of the form: `| ? ? ? 0 |` `| ?
-## [cur_obj_check_frame_prior_current_frame](#cur_obj_check_frame_prior_current_frame) - -### Lua Example -`local integerValue = cur_obj_check_frame_prior_current_frame(a0)` - -### Parameters -| Field | Type | -| ----- | ---- | -| a0 | `Pointer` <`integer`> | - -### Returns -- `integer` - -### C Prototype -`s32 cur_obj_check_frame_prior_current_frame(s16 *a0);` - -[:arrow_up_small:](#) - -
- ## [mario_is_in_air_action](#mario_is_in_air_action) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index b463ac63e..cd60e96f9 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -760,6 +760,9 @@ - [djui_hud_get_color](functions-3.md#djui_hud_get_color) - [djui_hud_set_color](functions-3.md#djui_hud_set_color) - [djui_hud_reset_color](functions-3.md#djui_hud_reset_color) + - [djui_hud_get_text_color](functions-3.md#djui_hud_get_text_color) + - [djui_hud_set_text_color](functions-3.md#djui_hud_set_text_color) + - [djui_hud_reset_text_color](functions-3.md#djui_hud_reset_text_color) - [djui_hud_get_rotation](functions-3.md#djui_hud_get_rotation) - [djui_hud_set_rotation](functions-3.md#djui_hud_set_rotation) - [djui_hud_set_rotation_interpolated](functions-3.md#djui_hud_set_rotation_interpolated) @@ -1609,7 +1612,6 @@ - [cur_obj_check_if_at_animation_end](functions-6.md#cur_obj_check_if_at_animation_end) - [cur_obj_check_anim_frame](functions-6.md#cur_obj_check_anim_frame) - [cur_obj_check_anim_frame_in_range](functions-6.md#cur_obj_check_anim_frame_in_range) - - [cur_obj_check_frame_prior_current_frame](functions-6.md#cur_obj_check_frame_prior_current_frame) - [mario_is_in_air_action](functions-6.md#mario_is_in_air_action) - [mario_is_dive_sliding](functions-6.md#mario_is_dive_sliding) - [cur_obj_set_y_vel_and_animation](functions-6.md#cur_obj_set_y_vel_and_animation) diff --git a/src/game/area.c b/src/game/area.c index 36448abd5..c22fa22eb 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -26,6 +26,7 @@ #include "pc/network/network.h" #include "pc/lua/smlua_hooks.h" #include "pc/djui/djui.h" +#include "pc/djui/djui_hud_utils.h" #include "pc/djui/djui_panel_pause.h" #include "pc/nametags.h" #include "engine/lighting_engine.h" @@ -254,6 +255,7 @@ void clear_areas(void) { le_clear(); geo_clear_interp_data(); + djui_hud_clear_interp_data(); } void clear_area_graph_nodes(void) { @@ -313,6 +315,7 @@ void unload_area(void) { le_clear(); geo_clear_interp_data(); + djui_hud_clear_interp_data(); } void load_mario_area(void) { diff --git a/src/game/level_update.c b/src/game/level_update.c index ff6ae25b1..76348da45 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -43,6 +43,7 @@ #include "pc/configfile.h" #include "pc/network/network.h" #include "pc/djui/djui.h" +#include "pc/djui/djui_hud_utils.h" // used for getting gMainMenuSounds #include "pc/djui/djui_panel_menu_options.h" #include "pc/lua/smlua_hooks.h" @@ -1764,6 +1765,7 @@ s32 update_level(void) { s32 init_level(void) { sync_objects_clear(); geo_clear_interp_data(); + djui_hud_clear_interp_data(); reset_dialog_render_state(); s32 val4 = 0; diff --git a/src/game/memory.c b/src/game/memory.c index feca24fd1..ee123c0ff 100644 --- a/src/game/memory.c +++ b/src/game/memory.c @@ -185,10 +185,42 @@ void growing_pool_free_pool(struct GrowingPool *pool) { // growing array // /////////////////// +static void growing_array_free_elements(struct GrowingArray *array) { + if (array) { + if (array->buffer) { + for (u32 i = 0; i != array->capacity; ++i) { + if (array->buffer[i]) { + array->free(array->buffer[i]); + } + } + memset(array->buffer, 0, sizeof(void *) * array->capacity); + } + array->count = 0; + } +} + struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity, GrowingArrayAllocFunc alloc, GrowingArrayFreeFunc free) { - growing_array_free(&array); - array = calloc(1, sizeof(struct GrowingArray)); - array->buffer = calloc(capacity, sizeof(void *)); + growing_array_free_elements(array); + + // reuse buffer if array was already allocated + if (array) { + if (!array->buffer || array->capacity != capacity) { + void **buffer = realloc(array->buffer, sizeof(void *) * capacity); + + // if realloc fails, destroy the array and create a new one + if (!buffer) { + growing_array_free(&array); + return growing_array_init(NULL, capacity, alloc, free); + } + + memset(buffer, 0, sizeof(void *) * capacity); + array->buffer = buffer; + } + } else { + array = malloc(sizeof(struct GrowingArray)); + array->buffer = calloc(capacity, sizeof(void *)); + } + array->capacity = capacity; array->count = 0; array->alloc = alloc; @@ -248,11 +280,7 @@ void growing_array_move(struct GrowingArray *array, u32 from, u32 to, u32 count) void growing_array_free(struct GrowingArray **array) { if (*array) { - for (u32 i = 0; i != (*array)->capacity; ++i) { - if ((*array)->buffer[i]) { - (*array)->free((*array)->buffer[i]); - } - } + growing_array_free_elements(*array); free((*array)->buffer); free(*array); *array = NULL; diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index f0bf4d162..ff35fe280 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -1333,21 +1333,6 @@ s32 cur_obj_check_anim_frame_in_range(s32 startFrame, s32 rangeLength) { } } -s32 cur_obj_check_frame_prior_current_frame(s16 *a0) { - if (!o) { return 0; } - s16 sp6 = o->header.gfx.animInfo.animFrame; - - while (*a0 != -1) { - if (*a0 == sp6) { - return TRUE; - } - - a0++; - } - - return FALSE; -} - s32 mario_is_in_air_action(struct MarioState* m) { if (!m) { return 0; } if (m->action & ACT_FLAG_AIR) { diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index fc65f764a..d004ecbeb 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -158,7 +158,6 @@ s32 cur_obj_check_if_near_animation_end(void); s32 cur_obj_check_if_at_animation_end(void); s32 cur_obj_check_anim_frame(s32 frame); s32 cur_obj_check_anim_frame_in_range(s32 startFrame, s32 rangeLength); -s32 cur_obj_check_frame_prior_current_frame(s16 *a0); s32 mario_is_in_air_action(struct MarioState* m); s32 mario_is_dive_sliding(struct MarioState* m); void cur_obj_set_y_vel_and_animation(f32 sp18, s32 sp1C); diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 1752ae7b4..98fd6174c 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -25,6 +25,7 @@ #include "engine/math_util.h" #include "pc/network/network.h" #include "pc/lua/smlua.h" +#include "pc/djui/djui_hud_utils.h" /** * Flags controlling what debug info is displayed. @@ -605,6 +606,7 @@ void clear_objects(void) { clear_dynamic_surfaces(); geo_clear_interp_data(); + djui_hud_clear_interp_data(); } /** diff --git a/src/pc/crash_handler.c b/src/pc/crash_handler.c index 470f4e2e6..a493bbf39 100644 --- a/src/pc/crash_handler.c +++ b/src/pc/crash_handler.c @@ -217,12 +217,12 @@ static void crash_handler_produce_one_frame_callback(void) { if (font->textBeginDisplayList != NULL) { gSPDisplayList(gDisplayListHead++, font->textBeginDisplayList); } + gDPSetPrimColor(gDisplayListHead++, 0, 0, 255, 255, 255, 255); for (CrashHandlerText* text = sCrashHandlerText; text->s[0] != 0; ++text) { s32 x = GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(text->x * aspectScale); s32 y = SCREEN_HEIGHT - 8 - text->y * aspectScale; gDPPipeSync(gDisplayListHead++); - gDPSetEnvColor(gDisplayListHead++, text->r, text->g, text->b, 0xFF); create_dl_translation_matrix(DJUI_MTX_PUSH, x, y, 0); // translate scale diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c index 3bd9fc8b1..43218a151 100644 --- a/src/pc/djui/djui.c +++ b/src/pc/djui/djui.c @@ -171,6 +171,7 @@ void djui_reset_hud_params(void) { djui_hud_set_rotation(0, ROTATION_PIVOT_X_LEFT, ROTATION_PIVOT_Y_TOP); djui_hud_set_text_alignment(TEXT_HALIGN_LEFT, TEXT_VALIGN_TOP); djui_hud_reset_color(); + djui_hud_reset_text_color(); djui_hud_set_filter(FILTER_NEAREST); djui_hud_reset_viewport(); djui_hud_reset_scissor(); diff --git a/src/pc/djui/djui_gfx.c b/src/pc/djui/djui_gfx.c index 4319782c3..eccfa5c95 100644 --- a/src/pc/djui/djui_gfx.c +++ b/src/pc/djui/djui_gfx.c @@ -166,7 +166,8 @@ void djui_gfx_render_texture_tile(const Texture* texture, u32 w, u32 h, u8 fmt, void djui_gfx_render_texture_font_begin() { gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); - gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_MODULATERGBA_PRIM2); + gDPSetCycleType(gDisplayListHead++, G_CYC_2CYCLE); gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetTextureFilter(gDisplayListHead++, djui_hud_get_filter() ? G_TF_BILERP : G_TF_POINT); gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); @@ -193,12 +194,14 @@ void djui_gfx_render_texture_font(const Texture* texture, u32 w, u32 h, u8 fmt, void djui_gfx_render_texture_font_end() { gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BACK); } void djui_gfx_render_texture_tile_font_begin() { gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BOTH); - gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_FADEA); + gDPSetCombineMode(gDisplayListHead++, G_CC_FADEA, G_CC_MODULATERGBA_PRIM2); + gDPSetCycleType(gDisplayListHead++, G_CYC_2CYCLE); gDPSetRenderMode(gDisplayListHead++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetTextureFilter(gDisplayListHead++, G_TF_POINT); gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); @@ -243,6 +246,7 @@ void djui_gfx_render_texture_tile_font(const Texture* texture, u32 w, u32 h, u8 void djui_gfx_render_texture_tile_font_end() { gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); gSPSetGeometryMode(gDisplayListHead++, G_LIGHTING | G_CULL_BACK); } diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index d36938e79..2e5d675d2 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -35,6 +35,7 @@ struct HudUtilsState { enum HudUtilsFilter filter; enum DjuiFontType font; struct DjuiColor color; + struct DjuiColor textColor; struct { InterpFieldF32 degrees; InterpFieldF32 pivotX; @@ -51,6 +52,7 @@ static struct HudUtilsState sHudUtilsState = { .filter = FILTER_NEAREST, .font = FONT_NORMAL, .color = { 255, 255, 255, 255 }, + .textColor = { 255, 255, 255, 255 }, .rotation = { .degrees = INTERP_INIT(0), .pivotX = INTERP_INIT(ROTATION_PIVOT_X_LEFT), @@ -63,6 +65,7 @@ static struct HudUtilsState sHudUtilsState = { }; static struct DjuiColor sRefColor = { 255, 255, 255, 255 }; +static struct DjuiColor sRefTextColor = { 255, 255, 255, 255 }; f32 gDjuiHudUtilsZ = 0; bool gDjuiHudLockMouse = false; @@ -144,8 +147,6 @@ static void djui_hud_translate_positions(f32 *outX, f32 *outY, f32 *outW, f32 *o // interp // //////////// -#define MAX_INTERP_HUD 512 - enum InterpHudType { INTERP_HUD_TRANSLATION, INTERP_HUD_ROTATION, @@ -170,9 +171,16 @@ struct InterpHud { struct GrowingArray *gfx; }; -static struct InterpHud sInterpHuds[MAX_INTERP_HUD] = { 0 }; -static u16 sInterpHudCount = 0; -static u8 sColorAltered = FALSE; +static struct GrowingArray *sInterpHuds = NULL; +static u32 sInterpHudCount = 0; + +static void interp_hud_free(void *ptr) { + struct InterpHud *interp = ptr; + if (interp) { + growing_array_free(&interp->gfx); + free(interp); + } +} void patch_djui_hud_before(void) { sInterpHudCount = 0; @@ -183,8 +191,8 @@ void patch_djui_hud(f32 delta) { Gfx* savedHeadPos = gDisplayListHead; struct HudUtilsState savedState = sHudUtilsState; - for (u16 i = 0; i < sInterpHudCount; i++) { - struct InterpHud* interp = &sInterpHuds[i]; + for (u32 i = 0; i < sInterpHudCount; i++) { + struct InterpHud *interp = sInterpHuds->buffer[i]; f32 x = delta_interpolate_f32(interp->posX.prev, interp->posX.curr, delta); f32 y = delta_interpolate_f32(interp->posY.prev, interp->posY.curr, delta); @@ -260,22 +268,28 @@ void patch_djui_hud(f32 delta) { gDjuiHudUtilsZ = savedZ; } -struct InterpHud *djui_hud_create_interp() { - if (sInterpHudCount >= MAX_INTERP_HUD) { return NULL; } +static struct InterpHud *djui_hud_create_interp() { + struct InterpHud *interp = ( + sInterpHudCount < sInterpHuds->count ? + sInterpHuds->buffer[sInterpHudCount] : + growing_array_alloc(sInterpHuds, sizeof(struct InterpHud)) + ); - struct InterpHud *interp = &sInterpHuds[sInterpHudCount++]; - interp->z = gDjuiHudUtilsZ; - interp->state = sHudUtilsState; - if (!interp->gfx) { - interp->gfx = growing_array_init(NULL, 8, malloc, free); - } else { - interp->gfx->count = 0; + if (interp) { + interp->z = gDjuiHudUtilsZ; + interp->state = sHudUtilsState; + if (!interp->gfx) { + interp->gfx = growing_array_init(NULL, 8, malloc, free); + } else { + interp->gfx->count = 0; + } + sInterpHudCount++; } return interp; } -InterpHudGfx *djui_hud_create_interp_gfx(struct InterpHud *interp, enum InterpHudType type) { +static InterpHudGfx *djui_hud_create_interp_gfx(struct InterpHud *interp, enum InterpHudType type) { if (!interp) { return NULL; } InterpHudGfx *gfx = growing_array_alloc(interp->gfx, sizeof(InterpHudGfx)); @@ -284,6 +298,11 @@ InterpHudGfx *djui_hud_create_interp_gfx(struct InterpHud *interp, enum InterpHu return gfx; } +void djui_hud_clear_interp_data() { + sInterpHuds = growing_array_init(sInterpHuds, 16, malloc, interp_hud_free); + sInterpHudCount = 0; +} + //////////// // others // //////////// @@ -328,19 +347,37 @@ void djui_hud_set_color(u8 r, u8 g, u8 b, u8 a) { sHudUtilsState.color.g = g; sHudUtilsState.color.b = b; sHudUtilsState.color.a = a; - sColorAltered = TRUE; gDPSetEnvColor(gDisplayListHead++, r, g, b, a); } void djui_hud_reset_color(void) { - if (sColorAltered) { - sHudUtilsState.color.r = 255; - sHudUtilsState.color.g = 255; - sHudUtilsState.color.b = 255; - sHudUtilsState.color.a = 255; - sColorAltered = FALSE; - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - } + sHudUtilsState.color.r = 255; + sHudUtilsState.color.g = 255; + sHudUtilsState.color.b = 255; + sHudUtilsState.color.a = 255; + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); +} + +struct DjuiColor* djui_hud_get_text_color(void) { + sRefTextColor.r = sHudUtilsState.textColor.r; + sRefTextColor.g = sHudUtilsState.textColor.g; + sRefTextColor.b = sHudUtilsState.textColor.b; + sRefTextColor.a = sHudUtilsState.textColor.a; + return &sRefTextColor; +} + +void djui_hud_set_text_color(u8 r, u8 g, u8 b, u8 a) { + sHudUtilsState.textColor.r = r; + sHudUtilsState.textColor.g = g; + sHudUtilsState.textColor.b = b; + sHudUtilsState.textColor.a = a; +} + +void djui_hud_reset_text_color(void) { + sHudUtilsState.textColor.r = 255; + sHudUtilsState.textColor.g = 255; + sHudUtilsState.textColor.b = 255; + sHudUtilsState.textColor.a = 255; } void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY) { @@ -580,13 +617,26 @@ static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 f32 lineWidth = 0; f32 textHeight = font->lineHeight; + // apply text color + gDPSetPrimColor(gDisplayListHead++, 0, 0, + sHudUtilsState.textColor.r, + sHudUtilsState.textColor.g, + sHudUtilsState.textColor.b, + sHudUtilsState.textColor.a + ); + font->render_begin(); while (*c != '\0') { // check color code struct DjuiColor parsedColor; - if (djui_text_parse_color(c, end, false, &sHudUtilsState.color, &c, &parsedColor)) { - gDPSetEnvColor(gDisplayListHead++, parsedColor.r, parsedColor.g, parsedColor.b, parsedColor.a); + if (djui_text_parse_color(c, end, false, &sHudUtilsState.textColor, &c, &parsedColor)) { + gDPSetPrimColor(gDisplayListHead++, 0, 0, + parsedColor.r, + parsedColor.g, + parsedColor.b, + parsedColor.a + ); continue; } diff --git a/src/pc/djui/djui_hud_utils.h b/src/pc/djui/djui_hud_utils.h index 353ee3511..569b18a9b 100644 --- a/src/pc/djui/djui_hud_utils.h +++ b/src/pc/djui/djui_hud_utils.h @@ -62,6 +62,8 @@ extern struct GlobalTextures gGlobalTextures; extern f32 gDjuiHudUtilsZ; extern bool gDjuiHudLockMouse; +void djui_hud_clear_interp_data(); + /* |description|Gets the current DJUI HUD resolution|descriptionEnd| */ u8 djui_hud_get_resolution(void); /* |description|Sets the current DJUI HUD resolution|descriptionEnd| */ @@ -74,12 +76,18 @@ void djui_hud_set_filter(enum HudUtilsFilter filterType); s8 djui_hud_get_font(void); /* |description|Sets the current DJUI HUD font|descriptionEnd| */ void djui_hud_set_font(s8 fontType); -/* |description|Gets the current DJUI HUD color|descriptionEnd| */ +/* |description|Gets the current DJUI HUD global color|descriptionEnd| */ struct DjuiColor* djui_hud_get_color(void); -/* |description|Sets the current DJUI HUD color|descriptionEnd| */ +/* |description|Sets the current DJUI HUD global color|descriptionEnd| */ void djui_hud_set_color(u8 r, u8 g, u8 b, u8 a); -/* |description|Resets the current DJUI HUD color|descriptionEnd| */ +/* |description|Resets the current DJUI HUD global color|descriptionEnd| */ void djui_hud_reset_color(void); +/* |description|Gets the current DJUI HUD text default color. This color is overridden by color codes|descriptionEnd| */ +struct DjuiColor* djui_hud_get_text_color(void); +/* |description|Sets the current DJUI HUD text default color. This color is overridden by color codes|descriptionEnd| */ +void djui_hud_set_text_color(u8 r, u8 g, u8 b, u8 a); +/* |description|Resets the current DJUI HUD text default color. This color is overridden by color codes|descriptionEnd| */ +void djui_hud_reset_text_color(void); /* |description|Gets the current DJUI HUD rotation|descriptionEnd| */ void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY); /* |description|Sets the current DJUI HUD rotation|descriptionEnd| */ diff --git a/src/pc/djui/djui_inputbox.c b/src/pc/djui/djui_inputbox.c index c0a911130..da7fa59b0 100644 --- a/src/pc/djui/djui_inputbox.c +++ b/src/pc/djui/djui_inputbox.c @@ -577,6 +577,7 @@ static bool djui_inputbox_render(struct DjuiBase* base) { } // set color + gDPSetPrimColor(gDisplayListHead++, 0, 0, 255, 255, 255, 255); gDPSetEnvColor(gDisplayListHead++, inputbox->textColor.r, inputbox->textColor.g, inputbox->textColor.b, inputbox->textColor.a); // make selection well formed diff --git a/src/pc/djui/djui_text.c b/src/pc/djui/djui_text.c index 26fd4ea9c..eb5643ac8 100644 --- a/src/pc/djui/djui_text.c +++ b/src/pc/djui/djui_text.c @@ -382,8 +382,10 @@ static void djui_text_render_line(struct DjuiText* text, char* c1, char* c2, f32 for (char* c = c1; c < c2;) { struct DjuiColor parsedColor; if (djui_text_parse_color(c, c2, true, &sDjuiTextDefaultColor, &c, &parsedColor)) { - gDPSetEnvColor(gDisplayListHead++, parsedColor.r, parsedColor.g, parsedColor.b, parsedColor.a); - sDjuiTextCurrentColor = parsedColor; + sDjuiTextCurrentColor.r = parsedColor.r; + sDjuiTextCurrentColor.g = parsedColor.g; + sDjuiTextCurrentColor.b = parsedColor.b; + gDPSetEnvColor(gDisplayListHead++, sDjuiTextCurrentColor.r, sDjuiTextCurrentColor.g, sDjuiTextCurrentColor.b, sDjuiTextCurrentColor.a); continue; } @@ -450,6 +452,7 @@ static bool djui_text_render(struct DjuiBase* base) { create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f); // set color + gDPSetPrimColor(gDisplayListHead++, 0, 0, 255, 255, 255, 255); gDPSetEnvColor(gDisplayListHead++, base->color.r, base->color.g, base->color.b, base->color.a); sDjuiTextCurrentColor = base->color; diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 54bb0672b..47dcd1e4c 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -12318,6 +12318,59 @@ int smlua_func_djui_hud_reset_color(UNUSED lua_State* L) { return 1; } +int smlua_func_djui_hud_get_text_color(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_get_text_color", 0, top); + return 0; + } + + + smlua_push_object(L, LOT_DJUICOLOR, djui_hud_get_text_color(), NULL); + + return 1; +} + +int smlua_func_djui_hud_set_text_color(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 4) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_set_text_color", 4, top); + return 0; + } + + u8 r = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "djui_hud_set_text_color"); return 0; } + u8 g = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "djui_hud_set_text_color"); return 0; } + u8 b = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "djui_hud_set_text_color"); return 0; } + u8 a = smlua_to_integer(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "djui_hud_set_text_color"); return 0; } + + djui_hud_set_text_color(r, g, b, a); + + return 1; +} + +int smlua_func_djui_hud_reset_text_color(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "djui_hud_reset_text_color", 0, top); + return 0; + } + + + djui_hud_reset_text_color(); + + return 1; +} + int smlua_func_djui_hud_get_rotation(lua_State* L) { if (L == NULL) { return 0; } @@ -26858,24 +26911,6 @@ int smlua_func_cur_obj_check_anim_frame_in_range(lua_State* L) { return 1; } -int smlua_func_cur_obj_check_frame_prior_current_frame(lua_State* L) { - if (L == NULL) { return 0; } - - int top = lua_gettop(L); - if (top != 1) { - LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "cur_obj_check_frame_prior_current_frame", 1, top); - return 0; - } - - s16 * a0 = (s16 *)smlua_to_cpointer(L, 1, LVT_S16_P); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "cur_obj_check_frame_prior_current_frame"); return 0; } - - extern s32 cur_obj_check_frame_prior_current_frame(s16 *a0); - lua_pushinteger(L, cur_obj_check_frame_prior_current_frame(a0)); - - return 1; -} - int smlua_func_mario_is_in_air_action(lua_State* L) { if (L == NULL) { return 0; } @@ -37494,6 +37529,9 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "djui_hud_get_color", smlua_func_djui_hud_get_color); smlua_bind_function(L, "djui_hud_set_color", smlua_func_djui_hud_set_color); smlua_bind_function(L, "djui_hud_reset_color", smlua_func_djui_hud_reset_color); + smlua_bind_function(L, "djui_hud_get_text_color", smlua_func_djui_hud_get_text_color); + smlua_bind_function(L, "djui_hud_set_text_color", smlua_func_djui_hud_set_text_color); + smlua_bind_function(L, "djui_hud_reset_text_color", smlua_func_djui_hud_reset_text_color); smlua_bind_function(L, "djui_hud_get_rotation", smlua_func_djui_hud_get_rotation); smlua_bind_function(L, "djui_hud_set_rotation", smlua_func_djui_hud_set_rotation); smlua_bind_function(L, "djui_hud_set_rotation_interpolated", smlua_func_djui_hud_set_rotation_interpolated); @@ -38277,7 +38315,6 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "cur_obj_check_if_at_animation_end", smlua_func_cur_obj_check_if_at_animation_end); smlua_bind_function(L, "cur_obj_check_anim_frame", smlua_func_cur_obj_check_anim_frame); smlua_bind_function(L, "cur_obj_check_anim_frame_in_range", smlua_func_cur_obj_check_anim_frame_in_range); - smlua_bind_function(L, "cur_obj_check_frame_prior_current_frame", smlua_func_cur_obj_check_frame_prior_current_frame); smlua_bind_function(L, "mario_is_in_air_action", smlua_func_mario_is_in_air_action); smlua_bind_function(L, "mario_is_dive_sliding", smlua_func_mario_is_dive_sliding); smlua_bind_function(L, "cur_obj_set_y_vel_and_animation", smlua_func_cur_obj_set_y_vel_and_animation); diff --git a/src/pc/nametags.c b/src/pc/nametags.c index 37fe27bf8..c39a1a249 100644 --- a/src/pc/nametags.c +++ b/src/pc/nametags.c @@ -18,34 +18,26 @@ struct StateExtras { }; static struct StateExtras sStateExtras[MAX_PLAYERS]; -void name_without_hex(char* input) { - s32 i, j; - bool inSlash = false; - for (i = j = 0; input[i] != '\0'; i++) { - if (input[i] == '\\') { - inSlash = !inSlash; - } else if (!inSlash) { - input[j++] = input[i]; // it just works - } - } - - input[j] = '\0'; -} - void djui_hud_print_outlined_text_interpolated(const char* text, f32 prevX, f32 prevY, f32 prevScale, f32 x, f32 y, f32 scale, u8 r, u8 g, u8 b, u8 a, f32 outlineDarkness) { f32 offset = 1 * (scale * 2); f32 prevOffset = 1 * (prevScale * 2); + djui_hud_set_text_color(r, g, b, 255); + // render outline - djui_hud_set_color(r * outlineDarkness, g * outlineDarkness, b * outlineDarkness, a); + djui_hud_set_color(255 * outlineDarkness, 255 * outlineDarkness, 255 * outlineDarkness, a); djui_hud_print_text_interpolated(text, prevX - prevOffset, prevY, prevScale, x - offset, y, scale); djui_hud_print_text_interpolated(text, prevX + prevOffset, prevY, prevScale, x + offset, y, scale); djui_hud_print_text_interpolated(text, prevX, prevY - prevOffset, prevScale, x, y - offset, scale); djui_hud_print_text_interpolated(text, prevX, prevY + prevOffset, prevScale, x, y + offset, scale); + // render text - djui_hud_set_color(r, g, b, a); + djui_hud_set_color(255, 255, 255, a); djui_hud_print_text_interpolated(text, prevX, prevY, prevScale, x, y, scale); + + // reset colors djui_hud_set_color(255, 255, 255, 255); + djui_hud_set_text_color(255, 255, 255, 255); } void nametags_render(void) { @@ -59,7 +51,21 @@ void nametags_render(void) { djui_hud_set_resolution(RESOLUTION_N64); djui_hud_set_font(FONT_SPECIAL); - for (u8 i = gNametagsSettings.showSelfTag ? 0 : 1; i < MAX_PLAYERS; i++) { + struct NametagInfo { + Vec3f pos; + f32 scale; + char name[MAX_CONFIG_STRING]; + }; + struct NametagInfo nametags[MAX_PLAYERS] = {0}; + s32 sortedNametagIndices[MAX_PLAYERS] = {0}; + s32 numNametags = 0; + + extern bool gDjuiHudToWorldCalcViewport; + gDjuiHudToWorldCalcViewport = false; + + // sort nametags by their distance to the camera + // insertion sort is quick enough for such small array + for (s32 i = gNametagsSettings.showSelfTag ? 0 : 1; i < MAX_PLAYERS; i++) { struct MarioState* m = &gMarioStates[i]; if (!is_player_active(m)) { continue; } struct NetworkPlayer* np = &gNetworkPlayers[i]; @@ -84,8 +90,6 @@ void nametags_render(void) { vec3f_copy(pos, m->marioBodyState->headPos); pos[1] += 100; - extern bool gDjuiHudToWorldCalcViewport; - gDjuiHudToWorldCalcViewport = false; if ((i != 0 || (i == 0 && m->action != ACT_FIRST_PERSON)) && djui_hud_world_pos_to_screen_pos(pos, out)) { @@ -96,61 +100,89 @@ void nametags_render(void) { snprintf(name, MAX_CONFIG_STRING, "%s", hookedString); } else { snprintf(name, MAX_CONFIG_STRING, "%s", np->name); - name_without_hex(name); } if (!djui_hud_world_pos_to_screen_pos(pos, out)) { continue; } - u8* color = network_get_player_text_color(m->playerIndex); f32 scale = -300 / out[2] * djui_hud_get_fov_coeff(); - f32 measure = djui_hud_measure_text(name) * scale * 0.5f; - out[1] -= 16 * scale; - u8 alpha = (i == 0 ? 255 : MIN(np->fadeOpacity << 3, 255)) * clamp(FADE_SCALE - scale, 0.f, 1.f); - - struct StateExtras* e = &sStateExtras[i]; - if (!e->inited) { - vec3f_copy(e->prevPos, out); - e->prevScale = scale; - e->inited = true; + s32 j = 0; + for (; j < numNametags; ++j) { + const struct NametagInfo *nametag = &nametags[sortedNametagIndices[j]]; + if (scale < nametag->scale) { + memmove(sortedNametagIndices + j + 1, sortedNametagIndices + j, sizeof(s32) * (numNametags - j)); + break; + } } + sortedNametagIndices[j] = i; - // Apply viewport for credits - extern Vp *gViewportOverride; - extern Vp *gViewportClip; - extern Vp gViewportFullscreen; - Vp *viewport = gViewportOverride == NULL ? gViewportClip : gViewportOverride; - if (viewport) { - make_viewport_clip_rect(viewport); - gSPViewport(gDisplayListHead++, viewport); - } + struct NametagInfo *nametag = &nametags[i]; + vec3f_copy(nametag->pos, out); + nametag->scale = scale; + memcpy(nametag->name, name, sizeof(name)); - djui_hud_print_outlined_text_interpolated(name, - e->prevPos[0] - measure, e->prevPos[1], e->prevScale, - out[0] - measure, out[1], scale, - color[0], color[1], color[2], alpha, 0.25); - - if (i != 0 && gNametagsSettings.showHealth) { - djui_hud_set_color(255, 255, 255, alpha); - f32 healthScale = 90 * scale; - f32 prevHealthScale = 90 * e->prevScale; - hud_render_power_meter_interpolated(m->health, - e->prevPos[0] - (prevHealthScale * 0.5f), e->prevPos[1] - 72 * scale, prevHealthScale, prevHealthScale, - out[0] - ( healthScale * 0.5f), out[1] - 72 * scale, healthScale, healthScale - ); - } - - // Reset viewport - if (viewport) { - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - BORDER_HEIGHT); - gSPViewport(gDisplayListHead++, &gViewportFullscreen); - } - - vec3f_copy(e->prevPos, out); - e->prevScale = scale; + numNametags++; } - gDjuiHudToWorldCalcViewport = true; + } + + gDjuiHudToWorldCalcViewport = true; + + // render nametags + for (s32 k = 0; k < numNametags; ++k) { + s32 playerIndex = sortedNametagIndices[k]; + struct NametagInfo *nametag = &nametags[playerIndex]; + struct MarioState *m = &gMarioStates[playerIndex]; + struct NetworkPlayer *np = &gNetworkPlayers[playerIndex]; + + u8* color = network_get_player_text_color(m->playerIndex); + f32 measure = djui_hud_measure_text(nametag->name) * nametag->scale * 0.5f; + nametag->pos[1] -= 16 * nametag->scale; + + u8 alpha = (playerIndex == 0 ? 255 : MIN(np->fadeOpacity << 3, 255)) * clamp(FADE_SCALE - nametag->scale, 0.f, 1.f); + + struct StateExtras* e = &sStateExtras[playerIndex]; + if (!e->inited) { + vec3f_copy(e->prevPos, nametag->pos); + e->prevScale = nametag->scale; + e->inited = true; + } + + // Apply viewport for credits + extern Vp *gViewportOverride; + extern Vp *gViewportClip; + extern Vp gViewportFullscreen; + Vp *viewport = gViewportOverride == NULL ? gViewportClip : gViewportOverride; + if (viewport) { + make_viewport_clip_rect(viewport); + gSPViewport(gDisplayListHead++, viewport); + } + + // render name + djui_hud_print_outlined_text_interpolated(nametag->name, + e->prevPos[0] - measure, e->prevPos[1], e->prevScale, + nametag->pos[0] - measure, nametag->pos[1], nametag->scale, + color[0], color[1], color[2], alpha, 0.25); + + // render power meter + if (playerIndex != 0 && gNametagsSettings.showHealth) { + djui_hud_set_color(255, 255, 255, alpha); + f32 healthScale = 90 * nametag->scale; + f32 prevHealthScale = 90 * e->prevScale; + hud_render_power_meter_interpolated(m->health, + e->prevPos[0] - (prevHealthScale * 0.5f), e->prevPos[1] - 72 * nametag->scale, prevHealthScale, prevHealthScale, + nametag->pos[0] - ( healthScale * 0.5f), nametag->pos[1] - 72 * nametag->scale, healthScale, healthScale + ); + } + + // Reset viewport + if (viewport) { + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - BORDER_HEIGHT); + gSPViewport(gDisplayListHead++, &gViewportFullscreen); + } + + vec3f_copy(e->prevPos, nametag->pos); + e->prevScale = nametag->scale; } }