Add a mod menu where mods can put DJUI elements (#56)

* Add a menu where mods can put their options at

* Document mod menu hook functions

* Add HOOK_ON_LANGUAGE_CHANGED

* Add new Cheats mod

* Make player menu disable singleplayer pause

* fix some git merge conflicts that weren't resolved (#55)

and added -latomic to build flags to fix compile warnings while compiling miniaudio

* Remove legacy 'deluxe' field from built-in mods

* Lots of improvements to memory safety

* Abbreviated hex color parsing

Co-Authored-By: Mechstreme <84944335+mechstreme@users.noreply.github.com>

---------

Co-authored-by: Isaac0-dev <62234577+Isaac0-dev@users.noreply.github.com>
Co-authored-by: Mechstreme <84944335+mechstreme@users.noreply.github.com>
This commit is contained in:
Agent X 2024-06-01 20:52:43 -04:00 committed by GitHub
parent cb3b7ebef9
commit 2b2dceb333
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 1281 additions and 1244 deletions

View File

@ -1227,7 +1227,8 @@ def def_function(function):
if rtype.startswith('Pointer_') and rtype not in def_pointers:
def_pointers.append(rtype)
s += '--- @return %s\n' % rtype
if rtype != "nil":
s += '--- @return %s\n' % rtype
s += "function %s(%s)\n -- ...\nend\n\n" % (fid, param_str)
return s

View File

@ -9228,7 +9228,27 @@ HOOK_ON_SEQ_LOAD = 42
HOOK_ON_ATTACK_OBJECT = 43
--- @type LuaHookedEventType
HOOK_MAX = 44
HOOK_ON_LANGUAGE_CHANGED = 44
--- @type LuaHookedEventType
HOOK_MAX = 45
--- @class LuaModMenuElementType
--- @type LuaModMenuElementType
MOD_MENU_ELEMENT_BUTTON = 0
--- @type LuaModMenuElementType
MOD_MENU_ELEMENT_CHECKBOX = 1
--- @type LuaModMenuElementType
MOD_MENU_ELEMENT_SLIDER = 2
--- @type LuaModMenuElementType
MOD_MENU_ELEMENT_INPUTBOX = 3
--- @type LuaModMenuElementType
MOD_MENU_ELEMENT_MAX = 4
--- @class HudDisplayFlags

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,13 @@
-------------
--- @type MarioState[]
--- Array of MarioStates, from 0 to MAX_PLAYERS - 1
--- Array of `MarioState`s, from 0 to `MAX_PLAYERS` - 1
--- - Uses the local index, which is different between every player
--- - Index 0 always refers to the local player
gMarioStates = {}
--- @type NetworkPlayer[]
--- Array of NetworkPlayers, from 0 to MAX_PLAYERS - 1
--- Array of `NetworkPlayer`s, from 0 to `MAX_PLAYERS` - 1
--- - Uses the local index, which is different between every player
--- - Index 0 always refers to the local player
gNetworkPlayers = {}
@ -21,45 +21,56 @@ gNetworkPlayers = {}
gActiveMods = {}
--- @type Character[]
--- Array of every character, from 0 to `CT_MAX` - 1
--- - The contents or order of the characters can never change
gCharacters = {}
--- @type Controller[]
--- Array of every controller, from 0 to `MAX_PLAYERS` - 1
--- - Uses the local index, which is different between every player
--- - Index 0 always refers to the local player
gControllers = {}
--- @type GlobalTextures
--- Struct containing HUD glyph textures
gTextures = {}
--- @type GlobalObjectAnimations
--- Struct containing every object animation
gObjectAnimations = {}
--- @type GlobalObjectCollisionData
--- Struct containing all object collision data
gGlobalObjectCollisionData = {}
--- @type PaintingValues
--- Struct containing all paintings and their fields
gPaintingValues = {}
--- @alias SyncTable table
--- @type SyncTable
--- Any keys added and modified to this table will be synced among everyone.
--- - This shouldn't be used to sync player-specific values; Use gPlayerSyncTable for that
--- - This shouldn't be used to sync player-specific values; Use `gPlayerSyncTable` for that
--- - Note: Does not support tables as keys
gGlobalSyncTable = {}
--- @type SyncTable[]
--- An array of sync tables. Any change to any sync tables will be synced to everyone else.
--- Array of sync tables. Any change to any sync tables will be synced to everyone else.
--- - This array takes in a local index, however it automatically translates to the global index
--- - Note: Does not support tables as keys
gPlayerSyncTable = {}
--- @type LevelValues
--- Struct containing fields that modify specific gameplay or level properties
gLevelValues = {}
--- @type BehaviorValues
--- Struct containing fields that modify specific object behavior properties
gBehaviorValues = {}
--- @type FirstPersonCamera
--- The struct that contains the values for the first person camera
--- Struct that contains the fields for the first person camera
gFirstPersonCamera = {}
--- @type LakituState
@ -68,9 +79,12 @@ gFirstPersonCamera = {}
gLakituState = {}
--- @type ServerSettings
--- Struct containing the settings for the server
--- - enablePlayersInLevelDisplay and enablePlayerList are not synced
gServerSettings = {}
--- @type NametagsSettings
--- Struct containing the settings for Nametags
gNametagsSettings = {}
-----------
@ -92,7 +106,6 @@ end
--- @param command string The command to run. Should be easy to type
--- @param description string Should describe what the command does and how to use it
--- @param func fun(msg:string): boolean Run upon activating the command. Return `true` to confirm the command has succeeded
--- @return nil
function hook_chat_command(command, description, func)
-- ...
end
@ -132,6 +145,48 @@ function hook_on_sync_table_change(syncTable, field, tag, func)
-- ...
end
--- @param name string The text to show on the button
--- @param func fun(index:integer) The function that is called when the button is pressed
--- Hooks a DJUI button into the mod menu. If you want to unpause the game when the button is pressed, return `true` from `func` to do so
function hook_mod_menu_button(name, func)
-- ...
end
--- @param name string The text to show on the left
--- @param defaultValue boolean The default state of the checkbox
--- @param func fun(index:integer, value:boolean) The function that is called when the checkbox is changed
--- Hooks a DJUI checkbox into the mod menu
function hook_mod_menu_checkbox(name, defaultValue, func)
-- ...
end
--- @param name string The text to show on the left
--- @param defaultValue integer The default value of the slider
--- @param min integer The lowest the slider can go
--- @param max integer The highest the slider can go
--- @param func fun(index:integer, value:integer) The function that is called when the value of the slider changes
--- Hooks a DJUI slider into the mod menu
function hook_mod_menu_slider(name, defaultValue, min, max, func)
-- ...
end
--- @param name string The text to show on the left
--- @param defaultValue string The default text in the inputbox
--- @param stringLength integer The max length of the inputbox
--- @param func fun(index:integer, value:string) The function that is called when the value of the inputbox changes
--- Hooks a DJUI inputbox into the mod menu
function hook_mod_menu_inputbox(name, defaultValue, stringLength, func)
-- ...
end
--- @param index integer The index of the element in the order in which they were hooked
--- @param name string The name to change to
--- Updates a mod menu element's text
--- - NOTE: `index` is zero-indexed
function update_mod_menu_element_name(index, name)
-- ...
end
---------------
-- functions --
---------------
@ -156,7 +211,6 @@ function atan2s(y, x)
end
--- @param objFieldTable table<any, "u32"|"s32"|"f32">
--- @return nil
--- Keys must start with `o` and values must be `"u32"`, `"s32"`, or `"f32"`
function define_custom_obj_fields(objFieldTable)
-- ...
@ -165,7 +219,6 @@ end
--- @param object Object Object to sync
--- @param standardSync boolean Automatically syncs common fields and syncs with distance. If `false`, all syncing must be done with `network_send_object`.
--- @param fieldTable table<string> The fields to sync
--- @return nil
--- All synced fields must start with `o` and there should not be any keys, just values
function network_init_object(object, standardSync, fieldTable)
-- ...
@ -173,7 +226,6 @@ end
--- @param object Object Object to sync
--- @param reliable boolean Whether or not the game should try to resend the packet in case it gets lost, good for important packets
--- @return nil
--- Sends a sync packet to sync up the object with everyone else
function network_send_object(object, reliable)
-- ...
@ -181,7 +233,6 @@ end
--- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets
--- @param dataTable table Table of values to be included in the packet
--- @return nil
--- `dataTable` can only contain strings, integers, numbers, booleans, and nil
function network_send(reliable, dataTable)
-- ...
@ -190,13 +241,12 @@ end
--- @param toLocalIndex integer The local index to send the packet to
--- @param reliable boolean Whether or not the game should try to resend the packet in case its lost, good for important packets
--- @param dataTable table Table of values to be included in the packet
--- @return nil
--- `dataTable` can only contain strings, integers, numbers, booleans, and nil
function network_send_to(toLocalIndex, reliable, dataTable)
-- ...
end
--- @param textureName string
--- @param textureName string The texture name
--- @return TextureInfo
--- Gets the `TextureInfo` of a texture by name
--- - Note: This also works with vanilla textures
@ -204,78 +254,72 @@ function get_texture_info(textureName)
-- ...
end
--- @param texInfo TextureInfo
--- @param x number
--- @param y number
--- @param scaleW number
--- @param scaleH number
--- @return nil
--- @param texInfo TextureInfo The texture
--- @param x number Where the texture is horizontally (left anchored)
--- @param y number Where the texture is vertically (top anchored)
--- @param scaleW number The scaled width of the texture
--- @param scaleH number The scaled height of the texture
--- Renders a texture to the screen
function djui_hud_render_texture(texInfo, x, y, scaleW, scaleH)
-- ...
end
--- @param texInfo TextureInfo
--- @param x number
--- @param y number
--- @param scaleW number
--- @param scaleH number
--- @param tileX number
--- @param tileY number
--- @param tileW number
--- @param tileH number
--- @return nil
--- @param texInfo TextureInfo The texture
--- @param x number Where the texture is horizontally (left anchored)
--- @param y number Where the texture is vertically (top anchored)
--- @param scaleW number The scaled width of the texture
--- @param scaleH number The scaled height of the texture
--- @param tileX number Where the tile is horizontally (left anchored)
--- @param tileY number Where the tile is vertically (top anchored)
--- @param tileW number The width of the tile
--- @param tileH number The height of the tile
--- Renders a tile of a texture to the screen
function djui_hud_render_texture_tile(texInfo, x, y, scaleW, scaleH, tileX, tileY, tileW, tileH)
-- ...
end
--- @param texInfo TextureInfo
--- @param prevX number
--- @param prevY number
--- @param prevScaleW number
--- @param prevScaleH number
--- @param x number
--- @param y number
--- @param scaleW number
--- @param scaleH number
--- @return nil
--- @param texInfo TextureInfo The texture
--- @param prevX number Where the texture previously was horizontally (left anchored)
--- @param prevY number Where the texture previously was vertically (top anchored)
--- @param prevScaleW number The previous scaled width of the texture
--- @param prevScaleH number The previous scaled height of the texture
--- @param x number Where the texture is horizontally (left anchored)
--- @param y number Where the texture is vertically (top anchored)
--- @param scaleW number The scaled width of the texture
--- @param scaleH number The scaled height of the texture
--- Renders an interpolated texture to the screen
function djui_hud_render_texture_interpolated(texInfo, prevX, prevY, prevScaleW, prevScaleH, x, y, scaleW, scaleH)
-- ...
end
--- @param texInfo TextureInfo
--- @param prevX number
--- @param prevY number
--- @param prevScaleW number
--- @param prevScaleH number
--- @param x number
--- @param y number
--- @param scaleW number
--- @param scaleH number
--- @param tileX number
--- @param tileY number
--- @param tileW number
--- @param tileH number
--- @return nil
--- @param texInfo TextureInfo The texture
--- @param prevX number Where the texture previously was horizontally (left anchored)
--- @param prevY number Where the texture previously was vertically (top anchored)
--- @param prevScaleW number The previous scaled width of the texture
--- @param prevScaleH number The previous scaled height of the texture
--- @param x number Where the texture is horizontally (left anchored)
--- @param y number Where the texture is vertically (top anchored)
--- @param scaleW number The scaled width of the texture
--- @param scaleH number The scaled height of the texture
--- @param tileX number Where the tile is horizontally (left anchored)
--- @param tileY number Where the tile is vertically (top anchored)
--- @param tileW number The width of the tile
--- @param tileH number The height of the tile
--- Renders an interpolated tile of a texture to the screen
function djui_hud_render_texture_tile_interpolated(texInfo, prevX, prevY, prevScaleW, prevScaleH, x, y, scaleW, scaleH, tileX, tileY, tileW, tileH)
-- ...
end
--- @param textureName string
--- @param overrideTexInfo TextureInfo
--- @return nil
--- @param textureName string The name of the texture
--- @param overrideTexInfo TextureInfo The texture to override with
--- Overrides a texture with a custom `TextureInfo`
--- * textureName must be the codename of a vanilla texture, you can find these in files such as `texture.inc.c`s
--- * overrideTexInfo can be any TextureInfo
--- - `textureName` must be the codename of a vanilla texture, you can find these in files such as `texture.inc.c`s
--- - `overrideTexInfo` can be any TextureInfo
function texture_override_set(textureName, overrideTexInfo)
-- ...
end
--- @param textureName string
--- @return nil
--- @param textureName string The name of the texture
--- Resets an overridden texture
function texture_override_reset(textureName)
-- ...
@ -287,7 +331,6 @@ end
--- @param levelNum LevelNum | integer
--- @param func fun(areaIndex:number, bhvData:bhvData, macroBhvIds:BehaviorId[], macroBhvArgs:integer[])
--- @return nil
--- When `func` is called, arguments are filled depending on the level command:
--- - `AREA` command: only `areaIndex` is filled. It's a number.
--- - `OBJECT` command: only `bhvData` is filled. `bhvData` is a table with two fields: `behavior` and `behaviorArg`.
@ -296,15 +339,14 @@ function level_script_parse(levelNum, func)
-- ...
end
--- @param name string
--- @param flags integer
--- @param animYTransDivisor integer
--- @param startFrame integer
--- @param loopStart integer
--- @param loopEnd integer
--- @param values table
--- @param index table
--- @return nil
--- @param name string The name of the animation
--- @param flags integer The flags of the animation (`ANIM_FLAG_*`)
--- @param animYTransDivisor integer The vertical animation translation divisor
--- @param startFrame integer What frame the animation starts on
--- @param loopStart integer When the loop starts
--- @param loopEnd integer When the loop ends
--- @param values table The table containing animation values
--- @param index table The table containing animation indices
--- Registers an animation that can be used in objects if `smlua_anim_util_set_animation` is called
function smlua_anim_util_register_animation(name, flags, animYTransDivisor, startFrame, loopStart, loopEnd, values, index)
-- ...
@ -312,7 +354,6 @@ end
--- @param message string The message to log
--- @param level? ConsoleMessageLevel Optional; Determines whether the message should appear as info, a warning or an error.
--- @return nil
--- Logs a message to the in-game console
function log_to_console(message, level)
-- ...
@ -320,7 +361,6 @@ end
--- @param index integer The index of the scroll target, should match up with the behavior param of RM_Scroll_Texture or editor_Scroll_Texture
--- @param name string The name of the vertex buffer that should be used while scrolling the texture
--- @return nil
--- Registers a vertex buffer to be used for a scrolling texture. Should be used with RM_Scroll_Texture or editor_Scroll_Texture
function add_scroll_target(index, name)
-- ...

View File

@ -63,6 +63,7 @@
- [smlua_hooks.h](#smlua_hooksh)
- [enum LuaActionHookType](#enum-LuaActionHookType)
- [enum LuaHookedEventType](#enum-LuaHookedEventType)
- [enum LuaModMenuElementType](#enum-LuaModMenuElementType)
- [smlua_misc_utils.h](#smlua_misc_utilsh)
- [enum HudDisplayFlags](#enum-HudDisplayFlags)
- [enum HudDisplayValue](#enum-HudDisplayValue)
@ -3293,7 +3294,17 @@
| HOOK_ON_PLAY_SOUND | 41 |
| HOOK_ON_SEQ_LOAD | 42 |
| HOOK_ON_ATTACK_OBJECT | 43 |
| HOOK_MAX | 44 |
| HOOK_ON_LANGUAGE_CHANGED | 44 |
| HOOK_MAX | 45 |
### [enum LuaModMenuElementType](#LuaModMenuElementType)
| Identifier | Value |
| :--------- | :---- |
| MOD_MENU_ELEMENT_BUTTON | 0 |
| MOD_MENU_ELEMENT_CHECKBOX | 1 |
| MOD_MENU_ELEMENT_SLIDER | 2 |
| MOD_MENU_ELEMENT_INPUTBOX | 3 |
| MOD_MENU_ELEMENT_MAX | 4 |
[:arrow_up_small:](#)

View File

@ -8401,6 +8401,26 @@
<br />
## [get_mario_vanilla_animation](#get_mario_vanilla_animation)
### Lua Example
`local AnimationValue = get_mario_vanilla_animation(index)`
### Parameters
| Field | Type |
| ----- | ---- |
| index | `integer` |
### Returns
[Animation](structs.md#Animation)
### C Prototype
`struct Animation *get_mario_vanilla_animation(u16 index);`
[:arrow_up_small:](#)
<br />
## [smlua_anim_util_get_current_animation_name](#smlua_anim_util_get_current_animation_name)
### Lua Example

View File

@ -9,6 +9,10 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
- [hook_event](#hook_event)
- [hook_mario_action](#hook_mario_action)
- [hook_on_sync_table_change](#hook_on_sync_table_change)
- [hook_mod_menu_button](#hook_mod_menu_button)
- [hook_mod_menu_checkbox](#hook_mod_menu_checkbox)
- [hook_mod_menu_slider](#hook_mod_menu_slider)
- [hook_mod_menu_inputbox](#hook_mod_menu_inputbox)
<br />
@ -63,17 +67,17 @@ id_bhvExample = hook_behavior(nil, OBJ_LIST_DEFAULT, true, bhv_example_init, bhv
```lua
function on_test_command(msg)
if msg == 'on' then
djui_chat_message_create('Test: enabled')
if msg == "on" then
djui_chat_message_create("Test: enabled")
return true
elseif msg == 'off' then
djui_chat_message_create('Test: disabled')
elseif msg == "off" then
djui_chat_message_create("Test: disabled")
return true
end
return false
end
hook_chat_command('test', "[on|off] turn test on or off", on_hide_and_seek_command)
hook_chat_command("test", "[on|off] turn test on or off", on_hide_and_seek_command)
```
[:arrow_up_small:](#)
@ -131,6 +135,7 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh
| HOOK_ON_PLAY_SOUND | Called when a sound is going to play, return a `SOUND_*` constant or `NO_SOUND` to override the sound | `integer` soundBits, `Vec3f` pos |
| HOOK_ON_SEQ_LOAD | Called when a sequence is going to play, return a `SEQ_*` constant to override the sequence. `SEQ_SOUND_PLAYER` (0) is silence. | `integer` player, `integer` seqID |
| HOOK_ON_ATTACK_OBJECT | Called when a player attacks an object. May be double-fired in some cases, you'll need to write special code for this | [MarioState](structs.md#MarioState) attacker, [Object](structs.md#Object) victim, `integer` interactionId |
| HOOK_ON_LANGUAGE_CHANGED | Called when the language is changed | `string` language |
### Parameters
@ -144,7 +149,7 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh
The following example will print out a message 16 times per frame (once for every possible player).
```lua
function mario_update(m)
print('Mario update was called for player index ', m.playerIndex)
print("Mario update was called for player index ", m.playerIndex)
end
hook_event(HOOK_MARIO_UPDATE, mario_update)
@ -261,14 +266,140 @@ hook_mario_action(ACT_WALL_SLIDE, { every_frame = act_wall_slide, gravity = act_
```lua
function on_testing_field_changed(tag, oldVal, newVal)
print('testingField changed:', tag, ',', oldVal, '->', newVal)
print("testingField changed:", tag, ",", oldVal, "->", newVal)
end
hook_on_sync_table_change(gGlobalSyncTable, 'testingField', 'tag', on_testing_field_changed)
-- now when testingField is set, either locally or over the network, on_testing_field_changed() will be called
gGlobalSyncTable.testingField = 'hello'
hook_on_sync_table_change(gGlobalSyncTable, "testingField", "tag", on_testing_field_changed)
-- now when testingField is set, either locally or over the network on_testing_field_changed() will be called
gGlobalSyncTable.testingField = "hello"
```
[:arrow_up_small:](#)
<br />
## [hook_mod_menu_button](#hook_mod_menu_button)
`hook_mod_menu_button()` allows Lua to add buttons to their designated mod menu submenu.
### Parameters
| Field | Type |
| ----- | ---- |
| message | `string` |
| func | `Lua Function` (`integer` index) |
### Lua Example
```lua
local menu1Open = false
local menu2Open = false
--- @param index integer
local function on_open_menu(index)
if index == 0 then
menu1Open = true
menu2Open = false
else
menu1Open = false
menu2Open = true
end
end
-- you can always do separate functions too!
hook_mod_menu_button("Open Menu 1", on_open_menu)
hook_mod_menu_button("Open Menu 2", on_open_menu)
```
[:arrow_up_small:](#)
<br />
## [hook_mod_menu_checkbox](#hook_mod_menu_checkbox)
`hook_mod_menu_checkbox()` allows Lua to add checkboxes to their designated mod menu submenu.
### Parameters
| Field | Type |
| ----- | ---- |
| message | `string` |
| defaultValue | `boolean` |
| func | `Lua Function` (`integer` index, `boolean` value) |
### Lua Example
```lua
local flyMode = false
local noclipMode = false
--- @param index integer
--- @param value boolean
local function on_set_player_mode(index, value)
if index == 0 then
flyMode = value
else
noclipMode = value
end
end
-- you can always do separate functions too!
hook_mod_menu_checkbox("Fly Mode", false, on_set_player_mode)
hook_mod_menu_checkbox("Noclip Mode", false, on_set_player_mode)
```
[:arrow_up_small:](#)
<br />
## [hook_mod_menu_slider](#hook_mod_menu_slider)
`hook_mod_menu_slider()` allows Lua to add sliders to their designated mod menu submenu.
### Parameters
| Field | Type |
| ----- | ---- |
| message | `string` |
| defaultValue | `integer` |
| min | `integer` |
| max | `integer` |
| func | `Lua Function` (`integer` index, `integer` value) |
### Lua Example
```lua
local timeScale = 0.0
local function on_set_time_scale(index, value)
timeScale = value
end
hook_mod_menu_slider("Time Scale", 1, 0, 10, on_set_time_scale)
```
[:arrow_up_small:](#)
<br />
## [hook_mod_menu_inputbox](#hook_mod_menu_inputbox)
`hook_mod_menu_inputbox()` allows Lua to add textboxes to their designated mod menu submenu.
### Parameters
| Field | Type |
| ----- | ---- |
| message | `string` |
| defaultValue | `string` |
| stringLength | `integer` |
| func | `Lua Function` (`integer` index, `string` value) |
### Lua Example
```lua
--- @param index integer
--- @param value string
local function on_set_network_player_description(index, value)
network_player_set_description(gNetworkPlayers[0], value, 255, 255, 255, 255)
end
hook_mod_menu_inputbox("Network Player Description", on_set_network_player_description)
```

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Nastavení serveru"
RESUME = "Pokračovat"
STOP_HOSTING = "Vypnout server"
DISCONNECT = "Odpojit se"
MOD_MENU = "Menu modů"
MOD_MENU_TITLE = "MENU MODŮ"
[PLAYER]
PLAYER_TITLE = "HRAC"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Server Instellingen"
RESUME = "Verder gaan"
STOP_HOSTING = "Stop Met Organizeren"
DISCONNECT = "Verbinding Verbreken"
MOD_MENU = "Modusmenu"
MOD_MENU_TITLE = "MODUSMENU"
[PLAYER]
PLAYER_TITLE = "Speler"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Server Settings"
RESUME = "Resume"
STOP_HOSTING = "Stop Hosting"
DISCONNECT = "Disconnect"
MOD_MENU = "Mod Menu"
MOD_MENU_TITLE = "MOD MENU"
[PLAYER]
PLAYER_TITLE = "PLAYER"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Paramètres du serveur"
RESUME = "Reprendre"
STOP_HOSTING = "Arrêter d'héberger"
DISCONNECT = "Se déconnecter"
MOD_MENU = "Menu des mods"
MOD_MENU_TITLE = "MENU DES MODS"
[PLAYER]
PLAYER_TITLE = "JOUEUR"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Servereinstellungen"
RESUME = "Zurück zum Spiel"
STOP_HOSTING = "Hosting beenden"
DISCONNECT = "Verbindung trennen"
MOD_MENU = "Mod-Menü"
MOD_MENU_TITLE = "MOD-MENÜ"
[PLAYER]
PLAYER_TITLE = "SPIELER"

View File

@ -303,6 +303,8 @@ SERVER_SETTINGS = "Impostazioni Server"
RESUME = "Riprendi"
STOP_HOSTING = "Interrompi la connessione"
DISCONNECT = "Disconnettiti"
MOD_MENU = "Menu delle mod"
MOD_MENU_TITLE = "MENU DELLE MOD"
[PLAYER]
PLAYER_TITLE = "GICATORE"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Ustawienia Serwera"
RESUME = "Kontynuuj"
STOP_HOSTING = "Przerwij Hostowanie"
DISCONNECT = "Rozlacz"
MOD_MENU = "Menu modyfikacji"
MOD_MENU_TITLE = "MENU MODYFIKACJI"
[PLAYER]
PLAYER_TITLE = "GRACZ"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Configurações de servidor"
RESUME = "Resumo"
STOP_HOSTING = "Parar a partida"
DISCONNECT = "Desconectar"
MOD_MENU = "Menu de mods"
MOD_MENU_TITLE = "MENU DE MODS"
[PLAYER]
PLAYER_TITLE = "JOGADOR"

View File

@ -304,6 +304,8 @@ SERVER_SETTINGS = "Настройки сервера"
RESUME = "Продолжить"
STOP_HOSTING = "Остановить хостинг"
DISCONNECT = "Отключиться"
MOD_MENU = "Меню модов"
MOD_MENU_TITLE = "МЕНЮ МОДОВ"
[PLAYER]
PLAYER_TITLE = "PLAYER"

View File

@ -305,6 +305,8 @@ SERVER_SETTINGS = "Ajustes de la partida"
RESUME = "Continuar"
STOP_HOSTING = "Finalizar partida"
DISCONNECT = "Desconectarse"
MOD_MENU = "Menú de mods"
MOD_MENU_TITLE = "MENÚ DE MODS"
[PLAYER]
PLAYER_TITLE = "JUGADOR"

366
mods/cheats.lua Normal file
View File

@ -0,0 +1,366 @@
-- name: Cheats
-- incompatible: cheats
-- description: Cheats\nA mod that adds a bunch of cheats to the mod menu, accessible through the pause menu.
-- pausable: true
-- localize functions to improve performance
local math_floor,smlua_text_utils_get_language,table_insert,approach_s32,set_mario_action,get_network_area_timer = math.floor,smlua_text_utils_get_language,table.insert,approach_s32,set_mario_action,get_network_area_timer
--- @class Cheat
--- @field public codename string
--- @field public names table
--- @field public hook LuaHookedEventType
--- @field public func function
--- @field public allowHazardSurfaces boolean
--- @type Cheat[]
local sCheats = {}
--- @param m MarioState
--- Checks if `m` is active
local function active_player(m)
local np = gNetworkPlayers[m.playerIndex]
if m.playerIndex == 0 then
return true
end
if not np.connected then
return false
end
if np.currCourseNum ~= gNetworkPlayers[0].currCourseNum then
return false
end
if np.currActNum ~= gNetworkPlayers[0].currActNum then
return false
end
if np.currLevelNum ~= gNetworkPlayers[0].currLevelNum then
return false
end
if np.currAreaIndex ~= gNetworkPlayers[0].currAreaIndex then
return false
end
return true
end
--- @param num integer
--- Limits an integer in the s16 range
local function s16(num)
num = math_floor(num) & 0xFFFF
if num >= 32768 then return num - 65536 end
return num
end
local function lang_string(strings)
local table = strings[smlua_text_utils_get_language()]
if table == nil then return strings["English"] end
return table
end
--- @param codename string
--- @param names table
--- @param hook LuaHookedEventType
--- @param func function
--- @param allowHazardSurfaces boolean
--- Registers a cheat
---
--- Supported hooks:
--- - `HOOK_MARIO_UPDATE`
--- - `HOOK_BEFORE_MARIO_UPDATE`
--- - `HOOK_BEFORE_PHYS_STEP`
local function register_cheat(codename, names, hook, func, allowHazardSurfaces)
table_insert(sCheats, {
codename = codename,
names = names,
hook = hook,
func = func,
allowHazardSurfaces = allowHazardSurfaces
})
for i = 0, MAX_PLAYERS - 1 do
gPlayerSyncTable[i][codename] = false
end
end
--- @param m MarioState
local function moon_jump_update(m)
if m.controller.buttonDown & L_TRIG ~= 0 then
m.faceAngle.y = m.intendedYaw - approach_s32(s16(m.intendedYaw - m.faceAngle.y), 0, 0x800, 0x800)
m.vel.y = 40
if m.action == ACT_FORWARD_GROUND_KB or
m.action == ACT_BACKWARD_GROUND_KB or
m.action == ACT_SOFT_FORWARD_GROUND_KB or
m.action == ACT_HARD_BACKWARD_GROUND_KB or
m.action == ACT_FORWARD_AIR_KB or
m.action == ACT_BACKWARD_AIR_KB or
m.action == ACT_HARD_FORWARD_AIR_KB or
m.action == ACT_HARD_BACKWARD_AIR_KB or
m.action == ACT_AIR_HIT_WALL then
set_mario_action(m, ACT_FREEFALL, 0)
end
end
end
--- @param m MarioState
local function god_mode_update(m)
m.health = 0x880
m.healCounter = 0
m.hurtCounter = 0
m.peakHeight = m.pos.y
end
--- @param m MarioState
local function infinite_lives_update(m)
m.numLives = 100
end
--- @param m MarioState
local function super_speed_update(m)
if m.action ~= ACT_BUBBLED and m.action ~= ACT_WATER_JUMP and m.action ~= ACT_HOLD_WATER_JUMP then
m.vel.x = m.vel.x * 4
m.vel.z = m.vel.z * 4
end
end
--- @param m MarioState
local function responsive_controls_update(m)
if m.action == ACT_WALKING or
m.action == ACT_HOLD_WALKING or
m.action == ACT_HOLD_HEAVY_WALKING or
m.action == ACT_FINISH_TURNING_AROUND or
m.action == ACT_CRAWLING then
m.faceAngle.y = m.intendedYaw
end
end
--- @param m MarioState
local function rapid_fire_update(m)
if (m.controller.buttonDown & A_BUTTON) ~= 0 and get_network_area_timer() % 2 == 0 then
m.controller.buttonPressed = m.controller.buttonPressed | A_BUTTON
end
end
local function blj_anywhere_update(m)
if m.action == ACT_LONG_JUMP and
m.controller.buttonDown & Z_TRIG ~= 0 and
m.forwardVel < -15 then
m.vel.y = -30
end
end
local function always_triple_jump_update(m, action)
if m.forwardVel < 20 and m.action == ACT_DOUBLE_JUMP_LAND and action == ACT_JUMP then
return ACT_TRIPLE_JUMP
end
end
register_cheat(
"moonJump",
{
["Czech"] = "Nekonečný Skok",
["Dutch"] = "Maan Sprong",
["English"] = "Moon Jump",
["French"] = "Saut Antigravité",
["German"] = "Mond-Sprung",
["Italian"] = "Salto della Luna",
["Polish"] = "Skok Ksiezycowy",
["Portuguese"] = "Pulo da Lua",
["Russian"] = "Супер прыжок",
["Spanish"] = "Salto Lunar"
},
HOOK_MARIO_UPDATE,
moon_jump_update,
true
)
register_cheat(
"godMode",
{
["Czech"] = "Nenech Se Zranit",
["Dutch"] = "God Modus",
["English"] = "God Mode",
["French"] = "Mode Invincible",
["German"] = "Gott Modus",
["Italian"] = "Modalità Dio",
["Polish"] = "Tryb Boga",
["Portuguese"] = "Modo Deus",
["Russian"] = "Режим бога",
["Spanish"] = "Modo Dios",
},
HOOK_MARIO_UPDATE,
god_mode_update,
false
)
register_cheat(
"infiniteLives",
{
["Czech"] = "Nekonečné Životy",
["Dutch"] = "Oneindige Levens",
["English"] = "Infinite Lives",
["French"] = "Vies Infinies",
["German"] = "Unbegrenzte Leben",
["Italian"] = "Vite Infinite",
["Polish"] = "Nieskonczone Zycia",
["Portuguese"] = "Vidas Infinitas",
["Russian"] = "Бесконечные жизни",
["Spanish"] = "Vidas Infinitas",
},
HOOK_MARIO_UPDATE,
infinite_lives_update,
true
)
register_cheat(
"superSpeed",
{
["Czech"] = "Super Rychlost",
["Dutch"] = "Super Snelheid",
["English"] = "Super Speed",
["French"] = "Super Vitesse",
["German"] = "Supergeschwindigkeit",
["Italian"] = "Super Velocità",
["Polish"] = "Super Szybkosc",
["Portuguese"] = "Super Velocidade",
["Russian"] = "Супер cкорость",
["Spanish"] = "Super Velocidad",
},
HOOK_BEFORE_PHYS_STEP,
super_speed_update,
true
)
register_cheat(
"responsiveControls",
{
["Czech"] = "Citlivé Ovládání",
["Dutch"] = "Snel Reagerende Controles",
["English"] = "Responsive Controls",
["French"] = "Contrôles Réactifs",
["German"] = "Reaktionsschnelle Steuerung",
["Italian"] = "Controlli Reattivi",
["Polish"] = "Responsywne Sterowanie",
["Portuguese"] = "Controle Responsivos",
["Russian"] = "Отзывчивое управление",
["Spanish"] = "Controles Responsivos",
},
HOOK_MARIO_UPDATE,
responsive_controls_update,
true
)
register_cheat(
"rapidFire",
{
["Czech"] = "Rychle Mačkat Tlačítko",
["Dutch"] = "Snel Vuur",
["English"] = "Rapid Fire",
["French"] = "Tir Rapide",
["German"] = "Schnellfeuer",
["Italian"] = "Fuoco Rapido",
["Polish"] = "Szybkostrzelnosc",
["Portuguese"] = "Fogo Rápido",
["Russian"] = "Быстрый огонь",
["Spanish"] = "Pulsación Rápida",
},
HOOK_BEFORE_MARIO_UPDATE,
rapid_fire_update,
true
)
register_cheat(
"bljAnywhere",
{
["Czech"] = "BLJ Všude",
["Dutch"] = "BLJ Overal",
["English"] = "BLJ Anywhere",
["French"] = "BLJ N'importe Où",
["German"] = "Überall Rückwertsweitspringen",
["Italian"] = "BLJ Ovunque",
["Polish"] = "BLJ Gdziekolwiek",
["Portuguese"] = "BLJ Em Qualquer Lugar",
["Russian"] = "BLJ в любом месте",
["Spanish"] = "BLJ Donde Sea",
},
HOOK_BEFORE_MARIO_UPDATE,
blj_anywhere_update,
true
)
register_cheat(
"alwaysTripleJump",
{
["Czech"] = "Vždy Trojitý Skok",
["Dutch"] = "Altijd Drievoudige Sprong",
["English"] = "Always Triple Jump",
["French"] = "Triple Sauts Infinis",
["German"] = "Immer Dreisprung",
["Italian"] = "Sempre Salto Triplo",
["Polish"] = "Potrojny Skok Zawsze",
["Portuguese"] = "Sempre Fazer Triple Jump",
["Russian"] = "Всегда тройной прыжок",
["Spanish"] = "Siempre Hacer Salto Triple",
},
HOOK_BEFORE_SET_MARIO_ACTION,
always_triple_jump_update,
true
)
--- @param hookType LuaHookedEventType
local function generate_mario_hook_function(hookType)
--- @param m MarioState
return function(m)
if not active_player(m) then return end
for _, cheat in ipairs(sCheats) do
if cheat.hook == hookType and gPlayerSyncTable[m.playerIndex][cheat.codename] then
cheat.func(m)
end
end
end
end
local function before_set_mario_action(m, action)
if not active_player(m) then return end
for _, cheat in ipairs(sCheats) do
if cheat.hook == HOOK_BEFORE_SET_MARIO_ACTION and gPlayerSyncTable[m.playerIndex][cheat.codename] then
return cheat.func(m, action)
end
end
end
--- @param m MarioState
local function allow_hazard_surface(m)
for _, cheat in ipairs(sCheats) do
if gPlayerSyncTable[m.playerIndex][cheat.codename] and not cheat.allowHazardSurfaces then return false end
end
return true
end
local function on_language_changed()
for i, cheat in ipairs(sCheats) do
update_mod_menu_element_name(i - 1, lang_string(cheat.names))
end
end
--- @param index integer
--- @param value boolean
local function update_cheat(index, value)
for i, cheat in ipairs(sCheats) do
if i - 1 == index then
gPlayerSyncTable[0][cheat.codename] = value
end
end
end
hook_event(HOOK_MARIO_UPDATE, generate_mario_hook_function(HOOK_MARIO_UPDATE))
hook_event(HOOK_BEFORE_MARIO_UPDATE, generate_mario_hook_function(HOOK_BEFORE_MARIO_UPDATE))
hook_event(HOOK_BEFORE_PHYS_STEP, generate_mario_hook_function(HOOK_BEFORE_PHYS_STEP))
hook_event(HOOK_BEFORE_SET_MARIO_ACTION, before_set_mario_action)
hook_event(HOOK_ALLOW_HAZARD_SURFACE, allow_hazard_surface)
hook_event(HOOK_ON_LANGUAGE_CHANGED, on_language_changed)
for _, cheat in ipairs(sCheats) do
hook_mod_menu_checkbox(lang_string(cheat.names), false, update_cheat)
end

View File

@ -139,6 +139,7 @@ s8 gLastDialogResponse = 0;
u8 gMenuHoldKeyIndex = 0;
u8 gMenuHoldKeyTimer = 0;
s32 gDialogResponse = 0;
bool gForceUnpause = false;
#if defined(VERSION_JP) || defined(VERSION_SH) || defined(VERSION_EU)
#ifdef VERSION_EU
@ -3103,16 +3104,19 @@ s16 render_pause_courses_and_castle(void) {
}
#ifdef VERSION_EU
if (gPlayer1Controller->buttonPressed & (A_BUTTON | Z_TRIG | START_BUTTON))
if (gPlayer1Controller->buttonPressed & (A_BUTTON | Z_TRIG | START_BUTTON)
|| gForceUnpause)
#else
if (gPlayer1Controller->buttonPressed & A_BUTTON
|| gPlayer1Controller->buttonPressed & START_BUTTON)
|| gPlayer1Controller->buttonPressed & START_BUTTON
|| gForceUnpause)
#endif
{
level_set_transition(0, NULL);
play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource);
gDialogBoxState = DIALOG_STATE_OPENING;
gMenuMode = -1;
gForceUnpause = false;
if (gDialogLineNum == 2 || gDialogLineNum == 3) {
num = gDialogLineNum;
@ -3138,16 +3142,19 @@ s16 render_pause_courses_and_castle(void) {
}
#ifdef VERSION_EU
if (gPlayer1Controller->buttonPressed & (A_BUTTON | Z_TRIG | START_BUTTON))
if (gPlayer1Controller->buttonPressed & (A_BUTTON | Z_TRIG | START_BUTTON)
|| gForceUnpause)
#else
if (gPlayer1Controller->buttonPressed & A_BUTTON
|| gPlayer1Controller->buttonPressed & START_BUTTON)
|| gPlayer1Controller->buttonPressed & START_BUTTON
|| gForceUnpause)
#endif
{
level_set_transition(0, NULL);
play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource);
gMenuMode = -1;
gDialogBoxState = DIALOG_STATE_OPENING;
gForceUnpause = false;
return 1;
}

View File

@ -129,6 +129,7 @@ extern u8 gDialogTextColorR;
extern u8 gDialogTextColorG;
extern u8 gDialogTextColorB;
extern u8 gDialogTextColorA;
extern bool gForceUnpause;
void create_dl_identity_matrix(void);
void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z);

View File

@ -60,7 +60,7 @@ void player_palettes_read(const char* palettesPath, bool appendPalettes) {
if (appendPalettes) {
snprintf(lpath, SYS_MAX_PATH, "%s/palettes", palettesPath);
} else {
strncpy(lpath, palettesPath, SYS_MAX_PATH);
snprintf(lpath, SYS_MAX_PATH, "%s", palettesPath);
}
// open directory
@ -104,7 +104,7 @@ void player_palettes_read(const char* palettesPath, bool appendPalettes) {
// free
ini_free(sPalette);
sPalette = NULL;
strncpy(gPresetPalettes[gPresetPaletteCount].name, path, 4096);
snprintf(gPresetPalettes[gPresetPaletteCount].name, 64, "%s", path);
gPresetPalettes[gPresetPaletteCount].palette = palette;
gPresetPaletteCount++;
#ifdef DEVELOPMENT

View File

@ -18,7 +18,7 @@ struct PlayerPalette {
#pragma pack()
struct PresetPalette {
char name[4096];
char name[64];
struct PlayerPalette palette;
};

View File

@ -79,7 +79,7 @@ bool parse_cli_opts(int argc, char* argv[]) {
gCLIOpts.networkPort = 7777;
}
} else if (!strcmp(argv[i], "--playername") && (i + 1) < argc) {
arg_string("--playername", argv[++i], gCLIOpts.playerName, MAX_PLAYER_STRING);
arg_string("--playername", argv[++i], gCLIOpts.playerName, MAX_CONFIG_STRING);
} else if (!strcmp(argv[i], "--help")) {
print_help();
return false;

View File

@ -24,7 +24,7 @@ struct CLIOptions {
enum NetworkType network;
unsigned int networkPort;
char joinIp[IP_MAX_LEN];
char playerName[MAX_PLAYER_STRING];
char playerName[MAX_CONFIG_STRING];
bool hideLoadingScreen;
};

View File

@ -144,7 +144,7 @@ bool configDebugError = false;
bool configCtxProfiler = false;
#endif
// player settings
char configPlayerName[MAX_PLAYER_STRING] = "";
char configPlayerName[MAX_CONFIG_STRING] = "";
unsigned int configPlayerModel = 0;
struct PlayerPalette configPlayerPalette = { { { 0x00, 0x00, 0xff }, { 0xff, 0x00, 0x00 }, { 0xff, 0xff, 0xff }, { 0x72, 0x1c, 0x0e }, { 0x73, 0x06, 0x00 }, { 0xfe, 0xc1, 0x79 }, { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x00 } } };
// coop settings
@ -257,7 +257,7 @@ static const struct ConfigOption options[] = {
{.name = "ctx_profiler", .type = CONFIG_TYPE_BOOL, .boolValue = &configCtxProfiler},
#endif
// player settings
{.name = "coop_player_name", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configPlayerName, .maxStringLength = MAX_PLAYER_STRING},
{.name = "coop_player_name", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configPlayerName, .maxStringLength = MAX_CONFIG_STRING},
{.name = "coop_player_model", .type = CONFIG_TYPE_UINT, .uintValue = &configPlayerModel},
{.name = "coop_player_palette_pants", .type = CONFIG_TYPE_COLOR, .colorValue = &configPlayerPalette.parts[PANTS]},
{.name = "coop_player_palette_shirt", .type = CONFIG_TYPE_COLOR, .colorValue = &configPlayerPalette.parts[SHIRT]},
@ -446,7 +446,7 @@ static void save_name_read(char** tokens, int numTokens) {
}
}
strncpy(configSaveNames[index], fullSaveName, MAX_SAVE_NAME_STRING);
snprintf(configSaveNames[index], MAX_SAVE_NAME_STRING, "%s", fullSaveName);
}
static void save_name_write(FILE* file) {
@ -679,7 +679,7 @@ NEXT_OPTION:
if (configDjuiTheme >= DJUI_THEME_MAX) { configDjuiTheme = 0; }
if (!strcmp(configLastVersion, "")) { strncpy(configLastVersion, get_version(), MAX_CONFIG_STRING); }
if (!strcmp(configLastVersion, "")) { snprintf(configLastVersion, MAX_CONFIG_STRING, "%s", get_version()); }
#ifndef COOPNET
configNetworkSystem = NS_SOCKET;

View File

@ -11,7 +11,6 @@
#define MAX_BINDS 3
#define MAX_VOLUME 127
#define MAX_CONFIG_STRING 64
#define MAX_PLAYER_STRING 60
#define MAX_SAVE_NAME_STRING 32
#define DEFAULT_PORT 7777
@ -96,7 +95,7 @@ extern bool configDebugError;
extern bool configCtxProfiler;
#endif
// player settings
extern char configPlayerName[MAX_PLAYER_STRING];
extern char configPlayerName[MAX_CONFIG_STRING];
extern unsigned int configPlayerModel;
extern struct PlayerPalette configPlayerPalette;
// coop settings

View File

@ -94,7 +94,7 @@ static void on_current_user_update(UNUSED void* data) {
if (configPlayerName[0] == '\0' && strlen(user.username) > 0) {
char* cname = configPlayerName;
char* dname = user.username;
for (int i = 0; i < MAX_PLAYER_STRING - 1; i++) {
for (int i = 0; i < MAX_CONFIG_STRING - 1; i++) {
if (*dname >= '!' && *dname <= '~') {
*cname = *dname;
cname++;

View File

@ -93,10 +93,10 @@ static void discord_populate_details(char* buffer, int bufferLength) {
void discord_activity_update(void) {
sCurActivity.type = DiscordActivityType_Playing;
strncpy(sCurActivity.assets.large_image, "characters", 128);
strncpy(sCurActivity.assets.large_text, "sm64coopdx Characters", 128);
strncpy(sCurActivity.assets.small_image, "icon", 128);
strncpy(sCurActivity.assets.small_text, "sm64coopdx Icon", 128);
snprintf(sCurActivity.assets.large_image, 128, "characters");
snprintf(sCurActivity.assets.large_text, 128, "sm64coopdx Characters");
snprintf(sCurActivity.assets.small_image, 128, "icon");
snprintf(sCurActivity.assets.small_text, 128, "sm64coopdx Icon");
if (gNetworkType != NT_NONE && gNetworkSystem) {
gNetworkSystem->get_lobby_id(sCurActivity.party.id, 128);

View File

@ -103,7 +103,7 @@ void djui_init_late(void) {
djui_panel_language_create(NULL);
}
if (strcmp(configLastVersion, get_version())) {
strncpy(configLastVersion, get_version(), MAX_CONFIG_STRING);
snprintf(configLastVersion, MAX_CONFIG_STRING, "%s", get_version());
djui_panel_changelog_create(NULL);
}

View File

@ -51,20 +51,20 @@ extern ALIGNED8 const u8 texture_hud_char_apostrophe[];
extern ALIGNED8 const u8 texture_hud_char_double_quote[];
struct GlobalTextures gGlobalTextures = {
.camera = { .texture = (u8*)texture_hud_char_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_camera" },
.lakitu = { .texture = (u8*)texture_hud_char_lakitu, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_lakitu" },
.no_camera = { .texture = (u8*)texture_hud_char_no_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_no_camera" },
.arrow_up = { .texture = (u8*)texture_hud_char_arrow_up, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_up" },
.arrow_down = { .texture = (u8*)texture_hud_char_arrow_down, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_down" },
.coin = { .texture = (u8*)texture_hud_char_coin, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_coin" },
.star = { .texture = (u8*)texture_hud_char_star, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_star" },
.apostrophe = { .texture = (u8*)texture_hud_char_apostrophe, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_apostrophe" },
.camera = { .texture = (u8*)texture_hud_char_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_camera" },
.lakitu = { .texture = (u8*)texture_hud_char_lakitu, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_lakitu" },
.no_camera = { .texture = (u8*)texture_hud_char_no_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_no_camera" },
.arrow_up = { .texture = (u8*)texture_hud_char_arrow_up, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_up" },
.arrow_down = { .texture = (u8*)texture_hud_char_arrow_down, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_down" },
.coin = { .texture = (u8*)texture_hud_char_coin, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_coin" },
.star = { .texture = (u8*)texture_hud_char_star, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_star" },
.apostrophe = { .texture = (u8*)texture_hud_char_apostrophe, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_apostrophe" },
.double_quote = { .texture = (u8*)texture_hud_char_double_quote, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_double_quote" },
.mario_head = { .texture = (u8*)texture_hud_char_mario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_mario_head" },
.luigi_head = { .texture = (u8*)texture_hud_char_luigi_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_luigi_head" },
.toad_head = { .texture = (u8*)texture_hud_char_toad_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_toad_head" },
.mario_head = { .texture = (u8*)texture_hud_char_mario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_mario_head" },
.luigi_head = { .texture = (u8*)texture_hud_char_luigi_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_luigi_head" },
.toad_head = { .texture = (u8*)texture_hud_char_toad_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_toad_head" },
.waluigi_head = { .texture = (u8*)texture_hud_char_waluigi_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_waluigi_head" },
.wario_head = { .texture = (u8*)texture_hud_char_wario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_wario_head" }
.wario_head = { .texture = (u8*)texture_hud_char_wario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_wario_head" }
};
static void djui_hud_position_translate(f32* x, f32* y) {

View File

@ -16,7 +16,7 @@ static char* sSaveLetters[] = { "A", "B", "C", "D" };
static void djui_panel_host_save_update_button(struct DjuiButton* button, int slot);
static void djui_panel_host_save_save_name_change(UNUSED struct DjuiBase* caller) {
strncpy(configSaveNames[sButtonTag], sSaveNameInputBox->buffer, MAX_SAVE_NAME_STRING);
snprintf(configSaveNames[sButtonTag], MAX_SAVE_NAME_STRING, "%s", sSaveNameInputBox->buffer);
if (strlen(sSaveNameInputBox->buffer) >= 64) {
djui_inputbox_set_text(sSaveNameInputBox, configSaveNames[sButtonTag]);
}
@ -47,7 +47,7 @@ static void djui_panel_edit_create(struct DjuiBase* caller) {
djui_base_set_size(&sSaveNameInputBox->base, 0.45f, 32);
djui_base_set_alignment(&sSaveNameInputBox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
char saveName[MAX_SAVE_NAME_STRING] = { 0 };
strncpy(saveName, configSaveNames[sButtonTag], MAX_SAVE_NAME_STRING);
snprintf(saveName, MAX_SAVE_NAME_STRING, "%s", configSaveNames[sButtonTag]);
djui_inputbox_set_text(sSaveNameInputBox, saveName);
djui_interactable_hook_value_change(&sSaveNameInputBox->base, djui_panel_host_save_save_name_change);
}

View File

@ -9,6 +9,7 @@
#include "pc/utils/misc.h"
#include "pc/configfile.h"
#include "pc/os/os.h"
#include "pc/lua/smlua_hooks.h"
extern bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
static bool sTrue = true;
@ -34,6 +35,7 @@ static void select_language(struct DjuiBase* caller) {
if (strcmp(configLanguage, checkbox->text->message)) {
snprintf(configLanguage, MAX_CONFIG_STRING, "%s", checkbox->text->message);
sLanguageChanged = true;
smlua_call_event_hooks_string_param(HOOK_ON_LANGUAGE_CHANGED, configLanguage);
}
checkbox->value = &sTrue;

View File

@ -83,7 +83,7 @@ struct DjuiThreePanel* djui_panel_menu_create(char* headerText) {
djui_base_set_location(&header->base, 0, DJUI_PANEL_HEADER_OFFSET);
djui_text_set_alignment(header, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM);
djui_text_set_font(header, hudFontHeader ? gDjuiFonts[2] : gDjuiFonts[1]);
djui_text_set_font_scale(header, gDjuiFonts[1]->defaultFontScale * (hudFontHeader ? 0.7f : 1.0f));
djui_text_set_font_scale(header, gDjuiFonts[1]->defaultFontScale * (hudFontHeader ? 0.7f : 1.0f) * (strlen(headerText) > 15 ? 0.9f : 1.0f));
struct DjuiFlowLayout* body = djui_flow_layout_create(&panel->base);
djui_base_set_alignment(&body->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);

View File

@ -0,0 +1,106 @@
#include "djui.h"
#include "djui_panel.h"
#include "djui_panel_menu.h"
#include "pc/lua/smlua_hooks.h"
static char* sDjuiPanelModMenuModName = NULL;
static char* to_uppercase(char* str) {
char* buffer = strdup(str);
int i = 0;
while (buffer[i] != '\0') {
buffer[i] = toupper(buffer[i]);
i++;
}
return buffer;
}
// generic
void djui_panel_mod_menu_mod_element(struct DjuiBase* caller) {
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[caller->tag];
smlua_call_mod_menu_element_hook(hooked, caller->tag);
}
static void djui_panel_mod_menu_mod_inputbox(struct DjuiBase* caller) {
struct DjuiInputbox* inputbox = (struct DjuiInputbox*)caller;
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[caller->tag];
snprintf(hooked->stringValue, 256, "%s", inputbox->buffer);
smlua_call_mod_menu_element_hook(hooked, caller->tag);
}
static void djui_panel_mod_menu_mod_create_element(struct DjuiBase* parent, int i) {
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[i];
switch (hooked->element) {
case MOD_MENU_ELEMENT_BUTTON:
struct DjuiButton* button = djui_button_create(parent, hooked->name, DJUI_BUTTON_STYLE_NORMAL, djui_panel_mod_menu_mod_element);
button->base.tag = i;
break;
case MOD_MENU_ELEMENT_CHECKBOX:
struct DjuiCheckbox* checkbox = djui_checkbox_create(parent, hooked->name, &hooked->boolValue, djui_panel_mod_menu_mod_element);
checkbox->base.tag = i;
break;
case MOD_MENU_ELEMENT_SLIDER:
struct DjuiSlider* slider = djui_slider_create(parent, hooked->name, &hooked->uintValue, hooked->sliderMin, hooked->sliderMax, djui_panel_mod_menu_mod_element);
slider->base.tag = i;
break;
case MOD_MENU_ELEMENT_INPUTBOX:
struct DjuiRect* rect = djui_rect_container_create(parent, 32);
{
struct DjuiText* text1 = djui_text_create(&rect->base, hooked->name);
djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_color(&text1->base, 220, 220, 220, 255);
djui_base_set_size(&text1->base, 0.585f, 64);
djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
djui_text_set_drop_shadow(text1, 64, 64, 64, 100);
struct DjuiInputbox* inputbox = djui_inputbox_create(&rect->base, hooked->length);
djui_base_set_size_type(&inputbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&inputbox->base, 0.45f, 32);
djui_base_set_alignment(&inputbox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
djui_inputbox_set_text(inputbox, hooked->stringValue);
djui_interactable_hook_value_change(&inputbox->base, djui_panel_mod_menu_mod_inputbox);
inputbox->base.tag = i;
}
break;
case MOD_MENU_ELEMENT_MAX:
}
}
void djui_panel_mod_menu_mod_create(struct DjuiBase* caller) {
struct DjuiThreePanel* panel = djui_panel_menu_create(to_uppercase(sDjuiPanelModMenuModName));
struct DjuiBase* body = djui_three_panel_get_body(panel);
{
struct DjuiPaginated* paginated = djui_paginated_create(body, 8);
struct DjuiBase* layoutBase = &paginated->layout->base;
for (int i = 0; i < gHookedModMenuElementsCount; i++) {
djui_panel_mod_menu_mod_create_element(layoutBase, i);
}
djui_paginated_calculate_height(paginated);
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
}
djui_panel_add(caller, panel, NULL);
}
void djui_panel_mod_menu_create(struct DjuiBase* caller) {
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(PAUSE, MOD_MENU_TITLE));
struct DjuiBase* body = djui_three_panel_get_body(panel);
{
struct DjuiPaginated* paginated = djui_paginated_create(body, 8);
struct DjuiBase* layoutBase = &paginated->layout->base;
struct Mod* lastMod = NULL;
for (int i = 0; i < gHookedModMenuElementsCount; i++) {
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[i];
if (lastMod == hooked->mod) { continue; }
lastMod = hooked->mod;
sDjuiPanelModMenuModName = lastMod->name;
djui_button_create(layoutBase, hooked->mod->name, DJUI_BUTTON_STYLE_NORMAL, djui_panel_mod_menu_mod_create);
}
djui_paginated_calculate_height(paginated);
djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back);
}
djui_panel_add(caller, panel, NULL);
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "djui.h"
void djui_panel_mod_menu_mod_element(struct DjuiBase* caller);
void djui_panel_mod_menu_mod_create(struct DjuiBase* caller);
void djui_panel_mod_menu_create(struct DjuiBase* caller);

View File

@ -1,3 +1,4 @@
#include "sm64.h"
#include "djui.h"
#include "djui_panel.h"
#include "djui_panel_player.h"
@ -6,11 +7,12 @@
#include "djui_panel_host.h"
#include "djui_panel_menu.h"
#include "djui_panel_confirm.h"
#include "djui_panel_mod_menu.h"
#include "pc/pc_main.h"
#include "pc/network/network.h"
#include "pc/lua/smlua_hooks.h"
#include "game/object_helpers.h"
#include "behavior_table.h"
#include "sm64.h"
bool gDjuiPanelPauseCreated = false;
@ -56,7 +58,6 @@ void djui_panel_pause_create(struct DjuiBase* caller) {
struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(PAUSE, PAUSE_TITLE));
struct DjuiBase* body = djui_three_panel_get_body(panel);
{
struct DjuiRect* rect1 = djui_rect_container_create(body, 64);
{
djui_button_left_create(&rect1->base, DLANG(PAUSE, PLAYER), DJUI_BUTTON_STYLE_NORMAL, djui_panel_player_create);
@ -70,6 +71,20 @@ void djui_panel_pause_create(struct DjuiBase* caller) {
djui_button_create(body, DLANG(PAUSE, SERVER_SETTINGS), DJUI_BUTTON_STYLE_NORMAL, djui_panel_host_create);
}
if (gHookedModMenuElementsCount == 1 && gHookedModMenuElements[0].element == MOD_MENU_ELEMENT_BUTTON) {
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[0];
char buffer[256] = { 0 };
if (strlen(hooked->name) > 0) {
snprintf(buffer, 256, "%s - %s", hooked->mod->name, hooked->name);
} else {
snprintf(buffer, 256, "%s", hooked->mod->name);
}
struct DjuiButton* button = djui_button_create(body, buffer, DJUI_BUTTON_STYLE_NORMAL, djui_panel_mod_menu_mod_element);
button->base.tag = 0;
} else if (gHookedModMenuElementsCount > 0) {
djui_button_create(body, DLANG(PAUSE, MOD_MENU), DJUI_BUTTON_STYLE_NORMAL, djui_panel_mod_menu_create);
}
djui_button_create(body, DLANG(PAUSE, RESUME), DJUI_BUTTON_STYLE_NORMAL, djui_panel_pause_resume);
if (gNetworkType == NT_SERVER) {

View File

@ -26,8 +26,8 @@ static struct DjuiInputbox* sPalettePresetNameTextBox = NULL;
void djui_panel_player_create(struct DjuiBase* caller);
////////////////////////
// edit palette panel //
////////////////////////
// edit palette panel //
////////////////////////
static unsigned int djui_panel_player_edit_palette_get_palette_index(struct PlayerPalette palette) {
@ -290,8 +290,8 @@ static void djui_panel_player_edit_palette_create(struct DjuiBase* caller) {
}
//////////////////
// player panel //
//////////////////
// player panel //
//////////////////
static bool djui_panel_player_name_valid(char* buffer) {
@ -319,7 +319,7 @@ static void djui_panel_player_name_on_focus_end(struct DjuiBase* caller) {
if (!djui_panel_player_name_valid(inputbox1->buffer)) {
djui_inputbox_set_text(inputbox1, DLANG(PLAYER, PLAYER));
}
snprintf(configPlayerName, MAX_PLAYER_STRING, "%s", inputbox1->buffer);
snprintf(configPlayerName, MAX_CONFIG_STRING, "%s", inputbox1->buffer);
djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
if (gNetworkType != NT_NONE) {
@ -384,7 +384,7 @@ void djui_panel_player_create(struct DjuiBase* caller) {
djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
djui_text_set_drop_shadow(text1, 64, 64, 64, 100);
struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, MAX_PLAYER_STRING);
struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, MAX_CONFIG_STRING);
djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&inputbox1->base, 0.45f, 32);
djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);

View File

@ -232,7 +232,23 @@ static char* djui_text_render_line_parse_escape(char* c1, char* c2) {
}
if (parsingColor) {
if (colorPieces == 6) {
if (colorPieces == 3) {
u32 r = (color >> 8) & 0xF;
u32 g = (color >> 4) & 0xF;
u32 b = (color >> 0) & 0xF;
sSavedR = (r << 4) | r;
sSavedG = (g << 4) | g;
sSavedB = (b << 4) | b;
} else if (colorPieces == 4) {
u32 r = (color >> 12) & 0xF;
u32 g = (color >> 8) & 0xF;
u32 b = (color >> 4) & 0xF;
u32 a = (color >> 0) & 0xF;
sSavedR = (r << 4) | r;
sSavedG = (g << 4) | g;
sSavedB = (b << 4) | b;
sSavedA = (a << 4) | a;
} else if (colorPieces == 6) {
sSavedR = ((color >> 16) & 0xFF);
sSavedG = ((color >> 8) & 0xFF);
sSavedB = ((color >> 0) & 0xFF);

View File

@ -3291,10 +3291,16 @@ char gSmluaConstants[] = ""
"HOOK_ON_PLAY_SOUND = 41\n"
"HOOK_ON_SEQ_LOAD = 42\n"
"HOOK_ON_ATTACK_OBJECT = 43\n"
"HOOK_MAX = 44\n"
"HOOK_ON_LANGUAGE_CHANGED = 44\n"
"HOOK_MAX = 45\n"
"ACTION_HOOK_EVERY_FRAME = 0\n"
"ACTION_HOOK_GRAVITY = 1\n"
"ACTION_HOOK_MAX = 2\n"
"MOD_MENU_ELEMENT_BUTTON = 0\n"
"MOD_MENU_ELEMENT_CHECKBOX = 1\n"
"MOD_MENU_ELEMENT_SLIDER = 2\n"
"MOD_MENU_ELEMENT_INPUTBOX = 3\n"
"MOD_MENU_ELEMENT_MAX = 4\n"
"HUD_DISPLAY_LIVES = 0\n"
"HUD_DISPLAY_COINS = 1\n"
"HUD_DISPLAY_STARS = 2\n"

View File

@ -15,6 +15,7 @@
#include "pc/network/socket/socket.h"
#include "pc/chat_commands.h"
#include "pc/pc_main.h"
#include "pc/djui/djui_panel.h"
#include "../mods/mods.h"
#include "game/print.h"
@ -1116,6 +1117,25 @@ const char *smlua_call_event_hooks_int_ret_bool_and_string(enum LuaHookedEventTy
return NULL;
}
void smlua_call_event_hooks_string_param(enum LuaHookedEventType hookType, const char* string) {
lua_State* L = gLuaState;
if (L == NULL) { return; }
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
for (int i = 0; i < hook->count; i++) {
// push the callback onto the stack
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
// push string
lua_pushstring(L, string);
// call the callback
if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) {
LOG_LUA("Failed to call the callback: %u", hookType);
continue;
}
}
}
////////////////////
// hooked actions //
////////////////////
@ -1213,7 +1233,6 @@ int smlua_hook_mario_action(lua_State* L) {
hooked->action = action;
hooked->interactionType = interactionType;
hooked->mod = gLuaActiveMod;
if (!gSmLuaConvertSuccess) { return 0; }
sHookedMarioActionsCount++;
return 1;
@ -1633,7 +1652,6 @@ int smlua_hook_chat_command(lua_State* L) {
hooked->description = strdup(description);
hooked->reference = ref;
hooked->mod = gLuaActiveMod;
if (!gSmLuaConvertSuccess) { return 0; }
sHookedChatCommandsCount++;
return 1;
@ -1966,6 +1984,292 @@ int smlua_hook_on_sync_table_change(lua_State* L) {
return 1;
}
////////////////////////////
// hooked mod menu button //
////////////////////////////
#define MAX_HOOKED_MOD_MENU_ELEMENTS 256
struct LuaHookedModMenuElement gHookedModMenuElements[MAX_HOOKED_MOD_MENU_ELEMENTS] = { 0 };
int gHookedModMenuElementsCount = 0;
int smlua_hook_mod_menu_button(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 2)) { return 0; }
if (gLuaLoadingMod == NULL) {
LOG_LUA_LINE("hook_mod_menu_button() can only be called on load.");
return 0;
}
if (gHookedModMenuElementsCount >= MAX_HOOKED_MOD_MENU_ELEMENTS) {
LOG_LUA_LINE("Hooked mod menu element exceeded maximum references!");
return 0;
}
const char* name = smlua_to_string(L, 1);
if (name == NULL || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ref == -1) {
LOG_LUA_LINE("Hook mod menu element: tried to hook undefined function '%s'", gLuaActiveMod->name);
return 0;
}
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[gHookedModMenuElementsCount];
hooked->element = MOD_MENU_ELEMENT_BUTTON;
snprintf(hooked->name, 64, "%s", name);
hooked->boolValue = false;
hooked->uintValue = 0;
hooked->stringValue[0] = '\0';
hooked->length = 0;
hooked->sliderMin = 0;
hooked->sliderMax = 0;
hooked->reference = ref;
hooked->mod = gLuaActiveMod;
gHookedModMenuElementsCount++;
return 1;
}
int smlua_hook_mod_menu_checkbox(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 3)) { return 0; }
if (gLuaLoadingMod == NULL) {
LOG_LUA_LINE("hook_mod_menu_checkbox() can only be called on load.");
return 0;
}
if (gHookedModMenuElementsCount >= MAX_HOOKED_MOD_MENU_ELEMENTS) {
LOG_LUA_LINE("Hooked mod menu element exceeded maximum references!");
return 0;
}
const char* name = smlua_to_string(L, 1);
if (name == NULL || strlen(name) == 0 || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
bool defaultValue = smlua_to_boolean(L, 2);
if (!gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ref == -1) {
LOG_LUA_LINE("Hook mod menu element: tried to hook undefined function '%s'", gLuaActiveMod->name);
return 0;
}
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[gHookedModMenuElementsCount];
hooked->element = MOD_MENU_ELEMENT_CHECKBOX;
snprintf(hooked->name, 64, "%s", name);
hooked->boolValue = defaultValue;
hooked->uintValue = 0;
hooked->stringValue[0] = '\0';
hooked->length = 0;
hooked->sliderMin = 0;
hooked->sliderMax = 0;
hooked->reference = ref;
hooked->mod = gLuaActiveMod;
gHookedModMenuElementsCount++;
return 1;
}
int smlua_hook_mod_menu_slider(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 5)) { return 0; }
if (gLuaLoadingMod == NULL) {
LOG_LUA_LINE("hook_mod_menu_slider() can only be called on load.");
return 0;
}
if (gHookedModMenuElementsCount >= MAX_HOOKED_MOD_MENU_ELEMENTS) {
LOG_LUA_LINE("Hooked mod menu element exceeded maximum references!");
return 0;
}
const char* name = smlua_to_string(L, 1);
if (name == NULL || strlen(name) == 0 || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
u32 defaultValue = smlua_to_integer(L, 2);
if (!gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
u32 sliderMin = smlua_to_integer(L, 3);
if (!gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
u32 sliderMax = smlua_to_integer(L, 4);
if (!gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ref == -1) {
LOG_LUA_LINE("Hook mod menu element: tried to hook undefined function '%s'", gLuaActiveMod->name);
return 0;
}
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[gHookedModMenuElementsCount];
hooked->element = MOD_MENU_ELEMENT_SLIDER;
snprintf(hooked->name, 64, "%s", name);
hooked->boolValue = false;
hooked->uintValue = defaultValue;
hooked->stringValue[0] = '\0';
hooked->length = 0;
hooked->sliderMin = sliderMin;
hooked->sliderMax = sliderMax;
hooked->reference = ref;
hooked->mod = gLuaActiveMod;
gHookedModMenuElementsCount++;
return 1;
}
int smlua_hook_mod_menu_inputbox(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 4)) { return 0; }
if (gLuaLoadingMod == NULL) {
LOG_LUA_LINE("hook_mod_menu_inputbox() can only be called on load.");
return 0;
}
if (gHookedModMenuElementsCount >= MAX_HOOKED_MOD_MENU_ELEMENTS) {
LOG_LUA_LINE("Hooked mod menu element exceeded maximum references!");
return 0;
}
const char* name = smlua_to_string(L, 1);
if (name == NULL || strlen(name) == 0 || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
const char* defaultValue = smlua_to_string(L, 2);
if (defaultValue == NULL || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
u32 length = smlua_to_integer(L, 3);
length = MIN(length, 256);
if (!gSmLuaConvertSuccess) {
LOG_LUA_LINE("Hook mod menu element: tried to hook invalid element");
return 0;
}
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ref == -1) {
LOG_LUA_LINE("Hook mod menu element: tried to hook undefined function '%s'", gLuaActiveMod->name);
return 0;
}
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[gHookedModMenuElementsCount];
hooked->element = MOD_MENU_ELEMENT_INPUTBOX;
snprintf(hooked->name, 64, "%s", name);
hooked->boolValue = false;
hooked->uintValue = 0;
snprintf(hooked->stringValue, 256, "%s", defaultValue);
hooked->length = length;
hooked->sliderMin = 0;
hooked->sliderMax = 0;
hooked->reference = ref;
hooked->mod = gLuaActiveMod;
gHookedModMenuElementsCount++;
return 1;
}
int smlua_update_mod_menu_element_name(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 2)) { return 0; }
int index = smlua_to_integer(L, 1);
if (index >= gHookedModMenuElementsCount || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Update mod menu element: tried to update invalid element");
return 0;
}
const char* name = smlua_to_string(L, 2);
if (name == NULL || !gSmLuaConvertSuccess) {
LOG_LUA_LINE("Update mod menu element: tried to update invalid name");
return 0;
}
if (gHookedModMenuElements[index].element != MOD_MENU_ELEMENT_BUTTON && strlen(name) == 0) {
LOG_LUA_LINE("Update mod menu element: tried to update invalid name");
return 0;
}
snprintf(gHookedModMenuElements[index].name, 64, "%s", name);
return 1;
}
void smlua_call_mod_menu_element_hook(struct LuaHookedModMenuElement* hooked, int index) {
lua_State* L = gLuaState;
if (L == NULL) { return; }
// push the callback onto the stack
lua_rawgeti(L, LUA_REGISTRYINDEX, hooked->reference);
// push parameter
u8 params = 2;
lua_pushinteger(L, index);
switch (hooked->element) {
case MOD_MENU_ELEMENT_BUTTON:
params = 1;
break;
case MOD_MENU_ELEMENT_CHECKBOX:
lua_pushboolean(L, hooked->boolValue);
break;
case MOD_MENU_ELEMENT_SLIDER:
lua_pushinteger(L, hooked->uintValue);
break;
case MOD_MENU_ELEMENT_INPUTBOX:
lua_pushstring(L, hooked->stringValue);
break;
case MOD_MENU_ELEMENT_MAX:
}
// call the callback
if (0 != smlua_call_hook(L, params, 1, 0, hooked->mod)) {
LOG_LUA("Failed to call the mod menu element callback: %s", hooked->name);
return;
}
// output the return value
bool returnValue = false;
if (lua_type(L, -1) == LUA_TBOOLEAN) {
returnValue = smlua_to_boolean(L, -1);
}
lua_pop(L, 1);
if (!gSmLuaConvertSuccess || !returnValue || hooked->element != MOD_MENU_ELEMENT_BUTTON) { return; }
gForceUnpause = true;
djui_panel_shutdown();
}
//////////
// misc //
//////////
@ -2001,6 +2305,21 @@ void smlua_clear_hooks(void) {
}
sHookedChatCommandsCount = 0;
for (int i = 0; i < gHookedModMenuElementsCount; i++) {
struct LuaHookedModMenuElement* hooked = &gHookedModMenuElements[i];
hooked->element = MOD_MENU_ELEMENT_BUTTON;
hooked->name[0] = '\0';
hooked->boolValue = false;
hooked->uintValue = 0;
hooked->stringValue[0] = '\0';
hooked->length = 0;
hooked->sliderMin = 0;
hooked->sliderMax = 0;
hooked->reference = 0;
hooked->mod = NULL;
}
gHookedModMenuElementsCount = 0;
for (int i = 0; i < sHookedBehaviorsCount; i++) {
struct LuaHookedBehavior* hooked = &sHookedBehaviors[i];
@ -2040,5 +2359,10 @@ void smlua_bind_hooks(void) {
smlua_bind_function(L, "hook_chat_command", smlua_hook_chat_command);
smlua_bind_function(L, "hook_on_sync_table_change", smlua_hook_on_sync_table_change);
smlua_bind_function(L, "hook_behavior", smlua_hook_behavior);
smlua_bind_function(L, "hook_mod_menu_button", smlua_hook_mod_menu_button);
smlua_bind_function(L, "hook_mod_menu_checkbox", smlua_hook_mod_menu_checkbox);
smlua_bind_function(L, "hook_mod_menu_slider", smlua_hook_mod_menu_slider);
smlua_bind_function(L, "hook_mod_menu_inputbox", smlua_hook_mod_menu_inputbox);
smlua_bind_function(L, "update_chat_command_description", smlua_update_chat_command_description);
smlua_bind_function(L, "update_mod_menu_element_name", smlua_update_mod_menu_element_name);
}

View File

@ -55,6 +55,7 @@ enum LuaHookedEventType {
HOOK_ON_PLAY_SOUND,
HOOK_ON_SEQ_LOAD,
HOOK_ON_ATTACK_OBJECT,
HOOK_ON_LANGUAGE_CHANGED,
HOOK_MAX,
};
@ -103,6 +104,7 @@ static const char* LuaHookedEventTypeName[] = {
"HOOK_ON_PLAY_SOUND",
"HOOK_ON_SEQ_LOAD",
"HOOK_ON_ATTACK_OBJECT",
"HOOK_ON_LANGUAGE_CHANGED",
"HOOK_MAX"
};
@ -118,7 +120,31 @@ static const char* LuaActionHookTypeArgName[] = {
"max (dummy)",
};
enum LuaModMenuElementType {
MOD_MENU_ELEMENT_BUTTON,
MOD_MENU_ELEMENT_CHECKBOX,
MOD_MENU_ELEMENT_SLIDER,
MOD_MENU_ELEMENT_INPUTBOX,
MOD_MENU_ELEMENT_MAX
};
struct LuaHookedModMenuElement {
enum LuaModMenuElementType element;
char name[64];
// use a union here?
bool boolValue;
u32 uintValue;
char stringValue[256];
u32 length;
u32 sliderMin;
u32 sliderMax;
int reference;
struct Mod* mod;
};
extern u32 gLuaMarioActionIndex[];
extern struct LuaHookedModMenuElement gHookedModMenuElements[];
extern int gHookedModMenuElementsCount;
int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName);
@ -154,6 +180,7 @@ bool smlua_call_event_hooks_mario_param_and_int_and_int_ret_int(enum LuaHookedEv
void smlua_call_event_hooks_graph_node_object_and_int_param(enum LuaHookedEventType hookType, struct GraphNodeObject* node, s32 param);
void smlua_call_event_hooks_on_seq_load(enum LuaHookedEventType hookType, u32 player, u32 seqId, s32 loadAsync, u8* returnValue);
const char *smlua_call_event_hooks_int_ret_bool_and_string(enum LuaHookedEventType hookType, s32 param, bool* returnValue);
void smlua_call_event_hooks_string_param(enum LuaHookedEventType hookType, const char* string);
enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior);
const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior);
@ -174,6 +201,8 @@ char** smlua_get_chat_subcommands_list(const char* maincommand);
bool smlua_maincommand_exists(const char* maincommand);
bool smlua_subcommand_exists(const char* maincommand, const char* subcommand);
void smlua_call_mod_menu_element_hook(struct LuaHookedModMenuElement* hooked, int index);
void smlua_clear_hooks(void);
void smlua_bind_hooks(void);

View File

@ -91,8 +91,8 @@ void nametags_render(void) {
scale = clampf(1 - scale, 0, NAMETAG_MAX_SCALE);
}
char name[MAX_PLAYER_STRING + 1];
strncpy(name, np->name, MAX_PLAYER_STRING + 1);
char name[MAX_CONFIG_STRING];
snprintf(name, MAX_CONFIG_STRING, "%s", np->name);
name_without_hex(name);
Color color = {
np->palette.parts[CAP][0],

View File

@ -259,7 +259,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->palette = *palette;
network_player_update_model(localIndex);
snprintf(np->name, MAX_PLAYER_STRING, "%s", name);
snprintf(np->name, MAX_CONFIG_STRING, "%s", name);
return localIndex;
}
@ -289,7 +289,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
np->overrideModelIndex = modelIndex;
np->overridePalette = *palette;
snprintf(np->name, MAX_PLAYER_STRING, "%s", name);
snprintf(np->name, MAX_CONFIG_STRING, "%s", name);
network_player_update_model(localIndex);
// clear networking fields

View File

@ -44,9 +44,9 @@ struct NetworkPlayer {
u8 gag;
u32 ping;
struct PlayerPalette palette;
char name[MAX_PLAYER_STRING+1];
char name[MAX_CONFIG_STRING];
char description[MAX_DESCRIPTION_STRING+1];
char description[MAX_DESCRIPTION_STRING];
u8 descriptionR;
u8 descriptionG;
u8 descriptionB;
@ -58,7 +58,7 @@ struct NetworkPlayer {
u16 rxSeqIds[MAX_RX_SEQ_IDS];
u32 rxPacketHash[MAX_RX_SEQ_IDS];
// legacy fields to allow mods not to break (they don't do anything anymore)
// legacy fields to allow mods not to fully break (they don't do anything anymore)
u8 paletteIndex;
u8 overridePaletteIndex;
u8 overridePaletteIndexLp;

View File

@ -1,5 +1,6 @@
#include <stdio.h>
#include "network_utils.h"
#include "game/level_update.h"
#include "game/mario_misc.h"
#include "pc/mods/mods.h"
@ -53,5 +54,5 @@ const char* network_get_player_text_color_string(u8 localIndex) {
extern s16 gMenuMode;
bool network_check_singleplayer_pause(void) {
return gMenuMode != -1 && network_player_connected_count() == 1 && mods_get_all_pausable();
return gMenuMode != -1 && network_player_connected_count() == 1 && mods_get_all_pausable() && !gInPlayerMenu;
}

View File

@ -31,7 +31,7 @@ static u8 eeprom[512] = { 0 };
static u8 sJoinRequestPlayerModel;
static struct PlayerPalette sJoinRequestPlayerPalette;
static char sJoinRequestPlayerName[MAX_PLAYER_STRING];
static char sJoinRequestPlayerName[MAX_CONFIG_STRING];
bool gCurrentlyJoining = false;
void network_send_join_request(void) {
@ -48,7 +48,7 @@ void network_send_join_request(void) {
packet_write(&p, &configPlayerModel, sizeof(u8));
packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette));
packet_write(&p, &configPlayerName, sizeof(u8) * MAX_PLAYER_STRING);
packet_write(&p, &configPlayerName, sizeof(u8) * MAX_CONFIG_STRING);
network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p);
LOG_INFO("sending join request");
@ -63,11 +63,11 @@ void network_receive_join_request(struct Packet* p) {
packet_read(p, &version, sizeof(u8) * MAX_VERSION_LENGTH);
packet_read(p, &sJoinRequestPlayerModel, sizeof(u8));
packet_read(p, &sJoinRequestPlayerPalette, sizeof(struct PlayerPalette));
packet_read(p, &sJoinRequestPlayerName, sizeof(u8) * MAX_PLAYER_STRING);
packet_read(p, &sJoinRequestPlayerName, sizeof(u8) * MAX_CONFIG_STRING);
} else {
sJoinRequestPlayerModel = 0;
sJoinRequestPlayerPalette = DEFAULT_MARIO_PALETTE;
snprintf(sJoinRequestPlayerName, MAX_PLAYER_STRING, "%s", "Player");
snprintf(sJoinRequestPlayerName, MAX_CONFIG_STRING, "%s", "Player");
}
network_send_join(p);

View File

@ -34,7 +34,7 @@ static void network_send_to_network_players(u8 sendToLocalIndex) {
packet_write(&p, &networkId, sizeof(s64));
packet_write(&p, &gNetworkPlayers[i].modelIndex, sizeof(u8));
packet_write(&p, &gNetworkPlayers[i].palette, sizeof(struct PlayerPalette));
packet_write(&p, &gNetworkPlayers[i].name, sizeof(u8) * MAX_PLAYER_STRING);
packet_write(&p, &gNetworkPlayers[i].name, sizeof(u8) * MAX_CONFIG_STRING);
LOG_INFO("send network player [%d == %d]", gNetworkPlayers[i].globalIndex, npType);
}
@ -91,7 +91,7 @@ void network_receive_network_players(struct Packet *p) {
s64 networkId;
u8 modelIndex;
struct PlayerPalette palette;
char playerName[MAX_PLAYER_STRING] = { 0 };
char playerName[MAX_CONFIG_STRING] = { 0 };
packet_read(p, &npType, sizeof(u8));
packet_read(p, &globalIndex, sizeof(u8));
@ -105,7 +105,7 @@ void network_receive_network_players(struct Packet *p) {
packet_read(p, &networkId, sizeof(s64));
packet_read(p, &modelIndex, sizeof(u8));
packet_read(p, &palette, sizeof(struct PlayerPalette));
packet_read(p, &playerName, sizeof(u8) * MAX_PLAYER_STRING);
packet_read(p, &playerName, sizeof(u8) * MAX_CONFIG_STRING);
u8 localIndex = network_player_connected(npType, globalIndex, modelIndex, &palette, playerName);
LOG_INFO("received network player [%d == %d] (%d)", globalIndex, npType, localIndex);

View File

@ -3,20 +3,20 @@
#include "pc/debuglog.h"
void network_send_player_settings(void) {
char playerName[MAX_PLAYER_STRING+1] = { 0 };
if (snprintf(playerName, MAX_PLAYER_STRING, "%s", configPlayerName) < 0) {
char playerName[MAX_CONFIG_STRING] = { 0 };
if (snprintf(playerName, MAX_CONFIG_STRING, "%s", configPlayerName) < 0) {
LOG_INFO("truncating player name");
}
struct Packet p = { 0 };
packet_init(&p, PACKET_PLAYER_SETTINGS, true, PLMT_NONE);
packet_write(&p, &gNetworkPlayers[0].globalIndex, sizeof(u8));
packet_write(&p, playerName, MAX_PLAYER_STRING * sizeof(u8));
packet_write(&p, playerName, MAX_CONFIG_STRING * sizeof(u8));
packet_write(&p, &configPlayerModel, sizeof(u8));
packet_write(&p, &configPlayerPalette, sizeof(struct PlayerPalette));
if (gNetworkPlayerLocal != NULL) {
if (snprintf(gNetworkPlayerLocal->name, MAX_PLAYER_STRING, "%s", playerName) < 0) {
if (snprintf(gNetworkPlayerLocal->name, MAX_CONFIG_STRING, "%s", playerName) < 0) {
LOG_INFO("truncating player name");
}
}
@ -26,12 +26,12 @@ void network_send_player_settings(void) {
void network_receive_player_settings(struct Packet* p) {
u8 globalId;
char playerName[MAX_PLAYER_STRING+1] = { 0 };
char playerName[MAX_CONFIG_STRING] = { 0 };
u8 playerModel;
struct PlayerPalette playerPalette;
packet_read(p, &globalId, sizeof(u8));
packet_read(p, &playerName, MAX_PLAYER_STRING * sizeof(u8));
packet_read(p, &playerName, MAX_CONFIG_STRING * sizeof(u8));
packet_read(p, &playerModel, sizeof(u8));
packet_read(p, &playerPalette, sizeof(struct PlayerPalette));
@ -51,7 +51,7 @@ void network_receive_player_settings(struct Packet* p) {
struct NetworkPlayer* np = network_player_from_global_index(globalId);
if (!np) { LOG_ERROR("Failed to retrieve network player."); return; }
if (snprintf(np->name, MAX_PLAYER_STRING, "%s", playerName) < 0) {
if (snprintf(np->name, MAX_CONFIG_STRING, "%s", playerName) < 0) {
LOG_INFO("truncating player name");
}

View File

@ -88,7 +88,7 @@ static const char *sys_old_user_path(void) {
char *sdlPath = SDL_GetPrefPath("", "sm64ex-coop");
if (sdlPath) {
const unsigned int len = strlen(sdlPath);
strncpy(path, sdlPath, sizeof(path));
snprintf(path, sizeof(path), "%s", sdlPath);
path[sizeof(path)-1] = 0;
SDL_free(sdlPath);
@ -113,7 +113,7 @@ const char *sys_user_path(void) {
}
const unsigned int len = strlen(sdlPath);
strncpy(path, sdlPath, sizeof(path));
snprintf(path, sizeof(path), "%s", sdlPath);
path[sizeof(path)-1] = 0;
SDL_free(sdlPath);
@ -131,7 +131,7 @@ const char *sys_exe_path(void) {
if (sdlPath && sdlPath[0]) {
// use the SDL path if it exists
const unsigned int len = strlen(sdlPath);
strncpy(path, sdlPath, sizeof(path));
snprintf(path, sizeof(path), "%s", sdlPath);
path[sizeof(path)-1] = 0;
SDL_free(sdlPath);
if (path[len-1] == '/' || path[len-1] == '\\')

View File

@ -75,7 +75,7 @@ void get_version_remote(void) {
}
buffer[bytesRead] = '\0';
strncpy(sRemoteVersion, buffer, 8);
snprintf(sRemoteVersion, 8, "%s", buffer);
// close handles
InternetCloseHandle(hUrl);
@ -107,7 +107,7 @@ void get_version_remote(void) {
if (!buffer) { return; }
strncpy(sRemoteVersion, buffer, 8);
snprintf(sRemoteVersion, 8, "%s", buffer);
// Clean up
curl_easy_cleanup(curl);