diff --git a/mods/character-select-coop/a-utils.lua b/mods/character-select-coop/a-utils.lua index a33e45fd..cd6dfcd9 100644 --- a/mods/character-select-coop/a-utils.lua +++ b/mods/character-select-coop/a-utils.lua @@ -1,5 +1,10 @@ -MOD_VERSION = "1.7" -IS_COOPDX = get_coop_compatibility_enabled ~= nil +if VERSION_NUMBER < 37 then + djui_popup_create("\n\\#FFAAAA\\Character Select requires\n CoopDX v1 or higher use!\n\nYou can find CoopDX here:\n\\#6666FF\\https://sm64coopdx.com", 5) + incompatibleClient = true + return 0 +end + +MOD_VERSION = "1.9" ommActive = false for i in pairs(gActiveMods) do @@ -102,4 +107,18 @@ end allowMenu = {} -renderInMenuTable = {} \ No newline at end of file +renderInMenuTable = { + front = {}, + back = {}, +} + +queueStorageFailsafe = false + +charBeingSet = false + +stopPalettes = false +for i in pairs(gActiveMods) do + if (gActiveMods[i].incompatible ~= nil and gActiveMods[i].incompatible:find("gamemode")) and not (gActiveMods[i].name:find("Personal Star Counter EX+")) then + stopPalettes = true + end +end \ No newline at end of file diff --git a/mods/character-select-coop/main.lua b/mods/character-select-coop/main.lua index 9ae516bc..221583b2 100644 --- a/mods/character-select-coop/main.lua +++ b/mods/character-select-coop/main.lua @@ -1,10 +1,12 @@ -- name: Character Select --- description:\\#ffff33\\---- Character Select Coop v1.7 ----\n\n\\#dcdcdc\\A Library / API made to make adding and using Custom Characters as simple as possible!\nUse\\#ffff33\\ /char-select\\#dcdcdc\\ to get started!\n\nCreated by:\\#008800\\ Squishy6094\n\\#dcdcdc\\Concepts by:\\#4496f5\\ AngelicMiracles\n\n\\#AAAAFF\\Updates and Packs can be found on\nCharacter Select's Github:\n\\#6666FF\\Squishy6094/character-select-coop +-- description:\\#ffff33\\---- Character Select Coop v1.9 ----\n\n\\#dcdcdc\\A Library / API made to make adding and using Custom Characters as simple as possible!\nUse\\#ffff33\\ /char-select\\#dcdcdc\\ to get started!\n\nCreated by:\\#008800\\ Squishy6094\n\\#dcdcdc\\Concepts by:\\#4496f5\\ AngelicMiracles\n\n\\#AAAAFF\\Updates can be found on\nCharacter Select's Github:\n\\#6666FF\\Squishy6094/character-select-coop + +if incompatibleClient then return 0 end -- localize functions to improve performance local mod_storage_load,tonumber,djui_popup_create,mod_storage_save,tostring,djui_chat_message_create,camera_freeze,hud_hide,vec3f_copy,set_mario_action,set_mario_animation,camera_unfreeze,hud_show,obj_has_behavior_id,network_local_index_from_global,obj_has_model_extended,obj_set_model_extended,get_id_from_behavior,nearest_player_to_object,math_random,djui_hud_set_resolution,djui_hud_set_font,djui_hud_get_screen_width,maxf,djui_hud_set_color,djui_hud_render_rect,djui_hud_measure_text,djui_hud_print_text,math_ceil,math_abs,math_sin,min,math_min,minf,djui_hud_set_rotation,djui_hud_is_pause_menu_created,is_game_paused,hud_is_hidden,obj_get_first_with_behavior_id,hud_get_value,hud_set_value,play_sound,string_lower = mod_storage_load,tonumber,djui_popup_create,mod_storage_save,tostring,djui_chat_message_create,camera_freeze,hud_hide,vec3f_copy,set_mario_action,set_mario_animation,camera_unfreeze,hud_show,obj_has_behavior_id,network_local_index_from_global,obj_has_model_extended,obj_set_model_extended,get_id_from_behavior,nearest_player_to_object,math.random,djui_hud_set_resolution,djui_hud_set_font,djui_hud_get_screen_width,maxf,djui_hud_set_color,djui_hud_render_rect,djui_hud_measure_text,djui_hud_print_text,math.ceil,math.abs,math.sin,min,math.min,minf,djui_hud_set_rotation,djui_hud_is_pause_menu_created,is_game_paused,hud_is_hidden,obj_get_first_with_behavior_id,hud_get_value,hud_set_value,play_sound,string.lower -local menu = false +menu = false menuAndTransition = false options = false currChar = 1 @@ -26,7 +28,7 @@ local TEXT_PREF_LOAD = "Default" --[[ Note: Do NOT add characters via the characterTable below, - We highly reccomend you create your own mod and use the + We highly recommend you create your own mod and use the API to add characters, this ensures your pack is easy to use for anyone and low on file space! ]] @@ -34,11 +36,12 @@ characterTable = { [1] = { name = "Default", saveName = "Default", - description = {"The vanilla cast for sm64ex-coop!", "", "These Characters are swappable", "via the default Options Menu"}, + description = {"The vanilla cast for sm64coopdx!", "", "These Characters are swappable", "via the default Options Menu"}, credit = "Nintendo / Coop Team", color = {r = 255, g = 50, b = 50}, model = nil, - forceChar = nil, + offset = 0, + forceChar = nil, -- Legacy Functionality lifeIcon = gTextures.mario_head, starIcon = gTextures.star, camScale = 1.0 @@ -47,6 +50,7 @@ characterTable = { characterCaps = {} characterCelebrationStar = {} +characterColorPresets = {} optionTableRef = { openInputs = 1, @@ -54,10 +58,11 @@ optionTableRef = { menuColor = 3, anims = 4, inputLatency = 5, - localModels = 6, - localVoices = 7, - debugInfo = 8, - resetSaveData = 9 + autoPalette = 6, + localModels = 7, + localVoices = 8, + debugInfo = 9, + resetSaveData = 10 } optionTable = { @@ -65,9 +70,9 @@ optionTable = { name = "Open Binds", toggle = tonumber(mod_storage_load("MenuInput")), toggleSaveName = "MenuInput", - toggleDefault = 0, + toggleDefault = 1, toggleMax = 2, - toggleNames = {"None", ommActive and "D-pad Down + R" or "D-pad Down", "Z (Pause Menu)"}, + toggleNames = {"None", "Z (Pause Menu)", ommActive and "D-pad Down + R" or "D-pad Down"}, description = {"Sets a Bind to Open the Menu", "rather than using the command."} }, [optionTableRef.notification] = { @@ -106,6 +111,14 @@ optionTable = { toggleNames = {"Slow", "Normal", "Fast"}, description = {"Sets how fast you scroll", "throughout the Menu"} }, + [optionTableRef.autoPalette] = { + name = "Auto-Apply Palette", + toggle = tonumber(mod_storage_load("autoPalette")), + toggleSaveName = "autoPalette", + toggleDefault = 1, + toggleMax = 1, + description = {"If On, Automatically", "sets your palette to the", "Character's Preset if avalible"} + }, [optionTableRef.localModels] = { name = "Locally Display Models", toggle = tonumber(mod_storage_load("localModels")), @@ -140,7 +153,7 @@ optionTable = { }, } -local optionTableCount = #optionTable +local defaultOptionCount = #optionTable local latencyValueTable = {12, 6, 3} @@ -164,7 +177,7 @@ local defaultNames = { [CT_WALUIGI] = "Waluigi", [CT_WARIO] = "Wario" } -local defaultPlayerColors = { +local defaultMenuColors = { [CT_MARIO] = menuColorTable[1], [CT_LUIGI] = menuColorTable[4], [CT_TOAD] = menuColorTable[5], @@ -192,6 +205,13 @@ local defaultCamScales = { [CT_WALUIGI] = 1.1, [CT_WARIO] = 1 } +local defaultModels = { + [CT_MARIO] = E_MODEL_MARIO, + [CT_LUIGI] = E_MODEL_LUIGI, + [CT_TOAD] = E_MODEL_TOAD_PLAYER, + [CT_WALUIGI] = E_MODEL_WALUIGI, + [CT_WARIO] = E_MODEL_WARIO +} ---@param m MarioState local function nullify_inputs(m) @@ -218,7 +238,7 @@ local function nullify_inputs(m) c.stickY = 0 end -local prefCharColor = defaultPlayerColors[CT_MARIO] +local prefCharColor = defaultMenuColors[CT_MARIO] local function load_preferred_char() local savedChar = mod_storage_load("PrefChar") @@ -267,10 +287,14 @@ local function mod_storage_save_pref_char(charTable) prefCharColor = charTable.color end -local function failsafe_options() +function failsafe_options() for i = 1, #optionTable do if optionTable[i].toggle == nil or optionTable[i].toggle == "" then - optionTable[i].toggle = optionTable[i].toggleDefault + local load = optionTable[i].toggleSaveName and mod_storage_load(optionTable[i].toggleSaveName) or nil + if load == "" then + load = nil + end + optionTable[i].toggle = load and tonumber(load) or optionTable[i].toggleDefault if optionTable[i].toggleSaveName ~= nil then mod_storage_save(optionTable[i].toggleSaveName, tostring(optionTable[i].toggle)) end @@ -279,7 +303,7 @@ local function failsafe_options() optionTable[i].toggleNames = {"Off", "On"} end end - if optionTable[optionTableRef.openInputs].toggle > 0 and ommActive then + if optionTable[optionTableRef.openInputs].toggle == 2 and ommActive then djui_popup_create('Character Select:\nYour Open bind has changed to:\nD-pad Down + R\nDue to OMM Rebirth being active!', 4) end end @@ -313,10 +337,15 @@ local function boot_note() else djui_chat_message_create("Character Select is active!\nYou can use \\#ffff33\\/char-select \\#ffffff\\to open the menu!") end +end - if not IS_COOPDX then - djui_chat_message_create("\\#FFAAAA\\Note: Although not required, we highly reccomend\nusing SM64CoopDX when playing Character Select!\n\\#6666FF\\https://sm64coopdx.com") +local function menu_is_allowed() + for _, func in pairs(allowMenu) do + if not func() then + return false + end end + return true end ------------------- @@ -361,12 +390,15 @@ local faceAngle = 0 --- @param m MarioState local function mario_update(m) - if stallFrame == 1 then + if stallFrame == 1 or queueStorageFailsafe then failsafe_options() - TEXT_PREF_LOAD = load_preferred_char() - if optionTable[optionTableRef.notification].toggle == 1 then - boot_note() + if not queueStorageFailsafe then + TEXT_PREF_LOAD = load_preferred_char() + if optionTable[optionTableRef.notification].toggle == 1 then + boot_note() + end end + queueStorageFailsafe = false end if stallFrame < 2 then @@ -374,31 +406,30 @@ local function mario_update(m) end if m.playerIndex == 0 and stallFrame > 1 then - local modelIndex = gNetworkPlayers[m.playerIndex].modelIndex + local modelIndex = gNetworkPlayers[0].modelIndex characterTable[1].forceChar = modelIndex characterTable[1].name = defaultNames[modelIndex] + characterTable[1].color = defaultMenuColors[modelIndex] if currChar == 1 then - characterTable[1].color = defaultPlayerColors[modelIndex] characterTable[1].lifeIcon = defaultIcons[modelIndex] characterTable[1].camScale = defaultCamScales[modelIndex] + gNetworkPlayers[0].overrideModelIndex = modelIndex + else + gNetworkPlayers[0].overrideModelIndex = CT_MARIO end if optionTable[optionTableRef.localModels].toggle > 0 then - gPlayerSyncTable[m.playerIndex].modelId = characterTable[currChar].model + gPlayerSyncTable[0].modelId = characterTable[currChar].model if characterTable[currChar].forceChar ~= nil then - gNetworkPlayers[m.playerIndex].overrideModelIndex = characterTable[currChar].forceChar + gNetworkPlayers[0].overrideModelIndex = characterTable[currChar].forceChar end + m.marioObj.hookRender = 1 else - gPlayerSyncTable[m.playerIndex].modelId = nil - gNetworkPlayers[m.playerIndex].overrideModelIndex = characterTable[1].forceChar + gPlayerSyncTable[0].modelId = nil + gNetworkPlayers[0].overrideModelIndex = characterTable[1].forceChar + currChar = 1 end if menuAndTransition then - for _, func in pairs(allowMenu) do - if not func() then - menu = false - return - end - end camera_freeze() hud_hide() if _G.PersonalStarCounter then @@ -434,12 +465,41 @@ local function mario_update(m) end noLoop = true end - end - --Reset Save Data Check - if optionTable[optionTableRef.resetSaveData].toggle > 0 then - reset_options(false) - optionTable[optionTableRef.resetSaveData].toggle = 0 + --Reset Save Data Check + if optionTable[optionTableRef.resetSaveData].toggle > 0 then + reset_options(false) + optionTable[optionTableRef.resetSaveData].toggle = 0 + end + charBeingSet = false + for i = 1, #optionTable do + optionTable[i].optionBeingSet = false + end + end +end + +local altOffsetActs = { + [ACT_CROUCH_SLIDE] = true, + [ACT_READING_AUTOMATIC_DIALOG] = true, + [ACT_DEATH_EXIT_LAND] = true, + [ACT_SLIDE_KICK] = false, + [ACT_SLIDE_KICK_SLIDE] = false, + [ACT_GROUND_POUND] = true, +} + +-- Vertical Offsets +local function on_object_render(obj) + if get_id_from_behavior(obj.behavior) ~= id_bhvMario then + return + end + + for i=0, MAX_PLAYERS-1 do + local m = gMarioStates[i] + if characterTable[currChar].offset ~= 0 then + if altOffsetActs[m.action] ~= false then + m.marioObj.header.gfx.pos.y = (altOffsetActs[m.action] and m.pos.y or m.marioObj.header.gfx.pos.y) + characterTable[currChar].offset + end + end end end @@ -452,6 +512,7 @@ local sCapBhvs = { --- @param o Object --- @param model integer function set_model(o, model) + if optionTable[optionTableRef.localModels].toggle == 0 then return end if obj_has_behavior_id(o, id_bhvMario) ~= 0 then local i = network_local_index_from_global(o.globalPlayerIndex) if gPlayerSyncTable[i].modelId ~= nil and obj_has_model_extended(o, gPlayerSyncTable[i].modelId) == 0 then @@ -490,7 +551,7 @@ function set_model(o, model) elseif model == c.capMetalWingModelId then capModel = capModels.metalWing end - if capModel ~= E_MODEL_NONE then + if capModel ~= E_MODEL_NONE and capModel ~= E_MODEL_ERROR_MODEL and capModel ~= nil then obj_set_model_extended(o, capModel) end end @@ -499,6 +560,7 @@ end hook_event(HOOK_MARIO_UPDATE, mario_update) hook_event(HOOK_OBJECT_SET_MODEL, set_model) +hook_event(HOOK_ON_OBJECT_RENDER, on_object_render) ------------------ -- Menu Handler -- @@ -516,19 +578,20 @@ local inputStallTimerDirectional = 0 local inputStallToDirectional = 12 local inputStallToButton = 10 --- Font Consistency between sm64ex-coop and sm64coopdx -local FONT_CS_NORMAL = IS_COOPDX and FONT_ALIASED or FONT_NORMAL - --Basic Menu Text local TEXT_OPTIONS_HEADER = "Menu Options" -local TEXT_VERSION = "Version: " .. MOD_VERSION .. (IS_COOPDX and " | sm64coopdx" or " | sm64ex-coop") +local TEXT_OPTIONS_HEADER_API = "API Options" +local TEXT_VERSION = "Version: " .. MOD_VERSION .. " | sm64coopdx" local TEXT_RATIO_UNSUPPORTED = "Your Current Aspect-Ratio isn't Supported!" local TEXT_DESCRIPTION = "Character Description:" local TEXT_PREF_SAVE = "Press A to Set as Preferred Character" +local TEXT_PREF_SAVE_AND_PALETTE = "A - Set Preference | Y - Toggle Palette" local TEXT_PAUSE_Z_OPEN = "Z Button - Character Select" +local TEXT_PAUSE_UNAVALIBLE = "Character Select is Unavalible" local TEXT_PAUSE_CURR_CHAR = "Current Character: " -if math_random(100) == 64 then -- Easter Egg if you get lucky loading the mod >v< - TEXT_PAUSE_Z_OPEN = "Z - DynOS" -- Referencing the original sm64ex DynOS options +if math_random(100) == 64 then + -- Easter Egg if you get lucky loading the mod Referencing the original sm64ex DynOS options by PeachyPeach >v< + TEXT_PAUSE_Z_OPEN = "Z - DynOS" TEXT_PAUSE_CURR_CHAR = "Model: " end @@ -536,8 +599,11 @@ end local TEXT_DEBUGGING = "Character Debug" local TEXT_DESCRIPTION_SHORT = "Description:" local TEXT_LIFE_ICON = "Life Icon:" +local TEXT_STAR_ICON = "Star Icon:" local TEXT_FORCED_CHAR = "Forced: " +local TEXT_OFFSET = "Offset: " local TEXT_TABLE_POS = "Table Position: " +local TEXT_PALETTE = "Palette: " --Options Text local TEXT_OPTIONS_OPEN = "Press START to open Options" @@ -546,30 +612,38 @@ local TEXT_OPTIONS_SELECT = "A - Select | B - Exit " local TEXT_LOCAL_MODEL_OFF = "Locally Display Models is Off" local TEXT_LOCAL_MODEL_OFF_OPTIONS = "You can turn it back on in the Options Menu" -local menuColor = characterTable[currChar].color +menuColor = characterTable[currChar].color -local MATH_DIVIDE_THREE_HUNDRED_TWENTY = 1/320 -local MATH_DIVIDE_THIRTY_TWO = 1/32 +local MATH_DIVIDE_320 = 1/320 +local MATH_DIVIDE_32 = 1/32 +local MATH_DIVIDE_30 = 1/30 +local MATH_DIVIDE_16 = 1/16 + +function update_menu_color() + if optionTable[optionTableRef.menuColor].toggle > 1 then + menuColor = menuColorTable[optionTable[optionTableRef.menuColor].toggle - 1] + elseif optionTable[optionTableRef.menuColor].toggle == 1 then + optionTable[optionTableRef.menuColor].toggleNames[2] = string_underscore_to_space(TEXT_PREF_LOAD) .. " (Pref)" + menuColor = prefCharColor + else + menuColor = characterTable[currChar].color + end + return menuColor +end local function on_hud_render() + local FONT_USER = djui_menu_get_font() djui_hud_set_resolution(RESOLUTION_N64) - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_set_font(FONT_ALIASED) local width = djui_hud_get_screen_width() + 1.4 local height = 240 local widthHalf = width * 0.5 local heightHalf = height * 0.5 - local widthScale = maxf(width, 321.4) * MATH_DIVIDE_THREE_HUNDRED_TWENTY + local widthScale = maxf(width, 321.4) * MATH_DIVIDE_320 if menuAndTransition then - if optionTable[optionTableRef.menuColor].toggle > 1 then - menuColor = menuColorTable[optionTable[optionTableRef.menuColor].toggle - 1] - elseif optionTable[optionTableRef.menuColor].toggle == 1 then - optionTable[optionTableRef.menuColor].toggleNames[2] = string_underscore_to_space(TEXT_PREF_LOAD) .. " (Pref)" - menuColor = prefCharColor - else - menuColor = characterTable[currChar].color - end + update_menu_color() if optionTable[optionTableRef.localModels].toggle == 0 then djui_hud_set_color(0, 0, 0, 200) @@ -581,18 +655,37 @@ local function on_hud_render() local x = 135 * widthScale * 0.8 - --Character Description - djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(width - x, 0, x, height) + -- Render All Black Squares Behind Below API + -- Description djui_hud_set_color(0, 0, 0, 255) djui_hud_render_rect(width - x + 2, 2, x - 4, height - 4) + -- Buttons + djui_hud_set_color(0, 0, 0, 255) + djui_hud_render_rect(2, 2, x - 4, height - 4) + -- Header + djui_hud_set_color(0, 0, 0, 255) + djui_hud_render_rect(2, 2, width - 4, 46) + + + -- API Rendering (Below Text) + if #renderInMenuTable.back > 0 then + for i = 1, #renderInMenuTable.back do + renderInMenuTable.back[i]() + end + end + + --Character Description djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_render_rect(width - x, 50, 2, height - 50) + djui_hud_render_rect(width - x, height - 2, x, 2) + djui_hud_render_rect(width - 2, 50, 2, height - 50) + djui_hud_set_font(FONT_ALIASED) local character = characterTable[currChar] if optionTable[optionTableRef.debugInfo].toggle == 0 then -- Actual Description local TEXT_NAME = string_underscore_to_space(character.name) local TEXT_CREDIT = "Credit: " .. character.credit local TEXT_DESCRIPTION_TABLE = character.description + local TEXT_PRESET = "Preset Character Palette: "..(gPlayerSyncTable[0].presetPalette and "On" or "Off") local TEXT_PREF = "Preferred Character:" local TEXT_PREF_LOAD = string_underscore_to_space(TEXT_PREF_LOAD) if djui_hud_measure_text(TEXT_PREF_LOAD) / widthScale > 110 then @@ -606,8 +699,10 @@ local function on_hud_render() local textX = x * 0.5 djui_hud_print_text(TEXT_NAME, width - textX - djui_hud_measure_text(TEXT_NAME) * 0.3, 55, 0.6) djui_hud_set_font(FONT_TINY) - djui_hud_print_text(TEXT_CREDIT, width - textX - djui_hud_measure_text(TEXT_CREDIT) * 0.3, 74, 0.6) - djui_hud_set_font(FONT_CS_NORMAL) + local creditScale = 0.6 + creditScale = math.min(creditScale, 100/djui_hud_measure_text(TEXT_CREDIT)) + djui_hud_print_text(TEXT_CREDIT, width - textX - djui_hud_measure_text(TEXT_CREDIT) * creditScale *0.5, 74, creditScale) + djui_hud_set_font(FONT_ALIASED) djui_hud_print_text(TEXT_DESCRIPTION, width - textX - djui_hud_measure_text(TEXT_DESCRIPTION) * 0.2, 85, 0.4) if widthScale < 1.65 then for i = 1, #TEXT_DESCRIPTION_TABLE do @@ -626,72 +721,117 @@ local function on_hud_render() end end + local modelId = gPlayerSyncTable[0].modelId and gPlayerSyncTable[0].modelId or defaultModels[gMarioStates[0].character.type] djui_hud_print_text(TEXT_PREF, width - textX - djui_hud_measure_text(TEXT_PREF) * 0.15, height - 22, 0.3) - djui_hud_set_font(FONT_TINY) - djui_hud_print_text(TEXT_PREF_SAVE, width - textX - djui_hud_measure_text(TEXT_PREF_SAVE) * 0.25, height - 13, 0.5) + if characterColorPresets[modelId] and not stopPalettes then + djui_hud_print_text(TEXT_PRESET, width - textX - djui_hud_measure_text(TEXT_PRESET) * 0.15, height - 31, 0.3) + djui_hud_set_font(FONT_TINY) + djui_hud_print_text(TEXT_PREF_SAVE_AND_PALETTE, width - textX - djui_hud_measure_text(TEXT_PREF_SAVE_AND_PALETTE) * 0.25, height - 13, 0.5) + else + djui_hud_set_font(FONT_TINY) + djui_hud_print_text(TEXT_PREF_SAVE, width - textX - djui_hud_measure_text(TEXT_PREF_SAVE) * 0.25, height - 13, 0.5) + end else -- Debugging Info local TEXT_NAME = "Name: " .. character.name local TEXT_SAVE_NAME = "Save Name: " .. character.saveName local TEXT_CREDIT = "Credit: " .. character.credit local TEXT_DESCRIPTION_TABLE = character.description local TEXT_COLOR = "Color: R-" .. character.color.r ..", G-" ..character.color.g ..", B-"..character.color.b - local TEX_ICON = character.lifeIcon + local TEX_LIFE_ICON = character.lifeIcon + local TEX_STAR_ICON = character.starIcon local TEXT_ICON_DEFAULT = "?" local TEXT_SCALE = "Camera Scale: " .. character.camScale + local TEXT_PRESET = "Preset Palette: "..(gPlayerSyncTable[0].presetPalette and "On" or "Off") local TEXT_PREF = "Preferred: " .. TEXT_PREF_LOAD local TEXT_PREF_COLOR = "Pref Color: R-" .. prefCharColor.r .. ", G-" .. prefCharColor.g .. ", B-" .. prefCharColor.b local textX = x * 0.5 djui_hud_print_text(TEXT_DEBUGGING, width - textX - djui_hud_measure_text(TEXT_DEBUGGING) * 0.3, 55, 0.6) djui_hud_set_font(FONT_TINY) - djui_hud_print_text(TEXT_NAME, width - x + 8, 72, 0.6) - djui_hud_print_text(TEXT_SAVE_NAME, width - x + 8, 81, 0.6) - djui_hud_print_text(TEXT_CREDIT, width - x + 8, 90, 0.6) - djui_hud_print_text(TEXT_DESCRIPTION_SHORT, width - x + 8, 99, 0.6) - local removeLine = 0 - for i = 1, #TEXT_DESCRIPTION_TABLE do - if TEXT_DESCRIPTION_TABLE[i] ~= "" then - djui_hud_set_font(FONT_CS_NORMAL) - local TEXT_DESCRIPTION_LINE = TEXT_DESCRIPTION_TABLE[i] - if (djui_hud_measure_text(TEXT_DESCRIPTION_TABLE[i]) * 0.3 > 100) then - TEXT_DESCRIPTION_LINE = "(!) " .. TEXT_DESCRIPTION_LINE + local y = 72 + djui_hud_print_text(TEXT_NAME, width - x + 8, y, 0.5) + y = y + 7 + djui_hud_print_text(TEXT_SAVE_NAME, width - x + 8, y, 0.5) + y = y + 7 + djui_hud_print_text(TEXT_CREDIT, width - x + 8, y, 0.5) + y = y + 7 + if TEXT_DESCRIPTION_TABLE[1] ~= "No description has been provided" then + djui_hud_print_text(TEXT_DESCRIPTION_SHORT, width - x + 8, y, 0.5) + y = y + 2 + local removeLine = 0 + for i = 1, #TEXT_DESCRIPTION_TABLE do + if TEXT_DESCRIPTION_TABLE[i] ~= "" then + djui_hud_set_font(FONT_ALIASED) + local TEXT_DESCRIPTION_LINE = TEXT_DESCRIPTION_TABLE[i] + if (djui_hud_measure_text(TEXT_DESCRIPTION_TABLE[i]) * 0.3 > 100) then + TEXT_DESCRIPTION_LINE = "(!) " .. TEXT_DESCRIPTION_LINE + else + TEXT_DESCRIPTION_LINE = " " .. TEXT_DESCRIPTION_LINE + end + djui_hud_set_font(FONT_TINY) + djui_hud_print_text(TEXT_DESCRIPTION_LINE, width - x + 5, y + (i-removeLine) * 5, 0.4) else - TEXT_DESCRIPTION_LINE = " " .. TEXT_DESCRIPTION_LINE + removeLine = removeLine + 1 end - djui_hud_set_font(FONT_TINY) - djui_hud_print_text(TEXT_DESCRIPTION_LINE, width - x + 5, 101 + (i-removeLine) * 7, 0.5) - else - removeLine = removeLine + 1 end + local descriptionOffset = (#TEXT_DESCRIPTION_TABLE - removeLine) * 5 + y = y + 5 + descriptionOffset end - local descriptionOffset = (#TEXT_DESCRIPTION_TABLE - removeLine) * 7 - djui_hud_print_text(TEXT_COLOR, width - x + 8, 109 + descriptionOffset, 0.6) - if TEX_ICON ~= nil then - djui_hud_print_text(TEXT_LIFE_ICON .. " (" .. TEX_ICON.width .. "x" .. TEX_ICON.height .. ")", width - x + 8, 118 + descriptionOffset, 0.6) + djui_hud_set_color(character.color.r, character.color.g, character.color.b, 255) + djui_hud_print_text(TEXT_COLOR, width - x + 8, y, 0.5) + djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) + y = y + 7 + if TEX_LIFE_ICON ~= nil then + djui_hud_print_text(TEXT_LIFE_ICON .. " (" .. TEX_LIFE_ICON.width .. "x" .. TEX_LIFE_ICON.height .. ")", width - x + 8, y, 0.5) djui_hud_set_color(255, 255, 255, 255) - djui_hud_render_texture(TEX_ICON, width - x + 38, 119 + descriptionOffset, 0.5 / (TEX_ICON.width / 16), 0.5 / (TEX_ICON.height / 16)) + djui_hud_render_texture(TEX_LIFE_ICON, width - x + 33, y + 1, 0.4 / (TEX_LIFE_ICON.width * MATH_DIVIDE_16), 0.4 / (TEX_LIFE_ICON.height * MATH_DIVIDE_16)) else - djui_hud_print_text(TEXT_LIFE_ICON .. " (?x?)", width - x + 8, 118 + descriptionOffset, 0.6) + djui_hud_print_text(TEXT_LIFE_ICON .. " (?x?)", width - x + 8, y, 0.5) djui_hud_set_font(FONT_HUD) djui_hud_set_color(255, 255, 255, 255) - djui_hud_print_text(TEXT_ICON_DEFAULT, width - x + 38, 119 + descriptionOffset, 0.5) + djui_hud_print_text(TEXT_ICON_DEFAULT, width - x + 33, y + 1, 0.5) djui_hud_set_font(FONT_TINY) end + y = y + 7 djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_print_text(TEXT_FORCED_CHAR .. defaultForceChar[character.forceChar], width - x + 8, 127 + descriptionOffset, 0.6) - djui_hud_print_text(TEXT_TABLE_POS .. currChar, width - x + 8, 136 + descriptionOffset, 0.6) - djui_hud_print_text(TEXT_SCALE, width - x + 8, 145 + descriptionOffset, 0.6) + djui_hud_print_text(TEXT_STAR_ICON .. " (" .. TEX_STAR_ICON.width .. "x" .. TEX_STAR_ICON.height .. ")", width - x + 8, y, 0.5) + djui_hud_set_color(255, 255, 255, 255) + djui_hud_render_texture(TEX_STAR_ICON, width - x + 35, y + 1, 0.4 / (TEX_STAR_ICON.width * MATH_DIVIDE_16), 0.4 / (TEX_STAR_ICON.height * MATH_DIVIDE_16)) + y = y + 7 + djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) + djui_hud_print_text(TEXT_FORCED_CHAR .. defaultForceChar[character.forceChar], width - x + 8, y, 0.5) + y = y + 7 + djui_hud_print_text(TEXT_OFFSET .. character.offset, width - x + 8, y, 0.5) + y = y + 7 + djui_hud_print_text(TEXT_TABLE_POS .. currChar, width - x + 8, y, 0.5) + y = y + 7 + djui_hud_print_text(TEXT_SCALE, width - x + 8, y, 0.5) + local modelId = gPlayerSyncTable[0].modelId and gPlayerSyncTable[0].modelId or defaultModels[gMarioStates[0].character.type] + if characterColorPresets[modelId] ~= nil then + y = y + 7 + djui_hud_print_text(TEXT_PALETTE, width - x + 8, y, 0.5) + local x = x - djui_hud_measure_text(TEXT_PALETTE)*0.5 + for i = 0, #characterColorPresets[modelId] do + djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) + djui_hud_render_rect(width - x + 6.5 + (6.5 * i), y + 1.5, 6, 6) + djui_hud_set_color(characterColorPresets[modelId][i].r, characterColorPresets[modelId][i].g, characterColorPresets[modelId][i].g, 255) + djui_hud_render_rect(width - x + 7 + (6.5 * i), y + 2, 5, 5) + end + end - - djui_hud_print_text(TEXT_PREF, width - x + 8, height - 27, 0.6) - djui_hud_print_text(TEXT_PREF_COLOR, width - x + 8, height - 18, 0.6) + djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) + djui_hud_print_text(TEXT_PRESET, width - x + 8, height - 29, 0.5) + djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) + djui_hud_print_text(TEXT_PREF, width - x + 8, height - 22, 0.5) + djui_hud_set_color(prefCharColor.r, prefCharColor.g, prefCharColor.b, 255) + djui_hud_print_text(TEXT_PREF_COLOR, width - x + 8, height - 15, 0.5) end --Character Buttons djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(0, 0, x, height) - djui_hud_set_color(0, 0, 0, 255) - djui_hud_render_rect(2, 2, x - 4, height - 4) + djui_hud_render_rect(0, 50, 2, height - 50) + djui_hud_render_rect(x - 2, 50, 2, height - 50) + djui_hud_render_rect(0, height - 2, x, 2) if optionTable[optionTableRef.anims].toggle > 0 then buttonAnimTimer = buttonAnimTimer + 1 @@ -699,53 +839,59 @@ local function on_hud_render() if optionTable[optionTableRef.anims].toggle == 0 then buttonScroll = 0 elseif math_abs(buttonScroll) > 0.1 then - buttonScroll = buttonScroll * 0.03 * inputStallToDirectional + buttonScroll = buttonScroll * 0.05 * inputStallToDirectional end local buttonColor = {} + local buttonX = 20 * widthScale + local buttonAnimX = buttonX + math_sin(buttonAnimTimer * 0.05) * 2.5 + 5 for i = -1, 4 do if characterTable[i + currChar] ~= nil then buttonColor = characterTable[i + currChar].color djui_hud_set_color(buttonColor.r, buttonColor.g, buttonColor.b, 255) - local buttonX = 20 * widthScale + local x = buttonX if i == 0 then if optionTable[optionTableRef.anims].toggle > 0 then - buttonX = buttonX + math_sin(buttonAnimTimer * 0.05) * 2.5 + 5 + x = buttonAnimX else - buttonX = buttonX + 10 + x = buttonX + 10 end end local y = (i + 3) * 30 + buttonScroll - djui_hud_render_rect(buttonX, y, 70, 20) - djui_hud_set_color(0, 0, 0, 255) - djui_hud_render_rect(buttonX + 1, y + 1, 68, 18) + djui_hud_render_rect(x, y, 1, 20) + djui_hud_render_rect(x, y, 70, 1) + djui_hud_render_rect(x + 69, y, 1, 20) + djui_hud_render_rect(x, y + 19, 70, 1) + djui_hud_set_color(0, 0, 0, 200) + djui_hud_render_rect(x + 1, y + 1, 68, 18) djui_hud_set_font(FONT_TINY) djui_hud_set_color(buttonColor.r, buttonColor.g, buttonColor.b, 255) - djui_hud_print_text(string_underscore_to_space(characterTable[currChar + i].name), buttonX + 5, y + 5, 0.6) + djui_hud_print_text(characterTable[currChar + i].name, x + 5, y + 5, 0.6) end end - djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(0, height-2, x, 2) -- Scroll Bar + local MATH_DIVIDE_CHARACTERS = 1/#characterTable + local MATH_7_WIDTHSCALE = 7 * widthScale djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(7 * widthScale, 55, 7, 170) - djui_hud_set_color(0, 0, 0, 255) - djui_hud_render_rect(7 * widthScale + 1, 56, 5, 168) - djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(7 * widthScale + 2, 57 + 166 * ((currChar - 1) / #characterTable) - (buttonScroll * 0.03333333333) * (166 / #characterTable), 3, 166 / #characterTable) + djui_hud_render_rect(MATH_7_WIDTHSCALE, 55, 1, 170) + djui_hud_render_rect(MATH_7_WIDTHSCALE, 55, 7, 1) + djui_hud_render_rect(MATH_7_WIDTHSCALE + 6, 55, 1, 170) + djui_hud_render_rect(MATH_7_WIDTHSCALE, 224, 7, 1) + djui_hud_render_rect(MATH_7_WIDTHSCALE + 2, 57 + 166 * ((currChar - 1) * MATH_DIVIDE_CHARACTERS) - (buttonScroll * MATH_DIVIDE_30) * (166 * MATH_DIVIDE_CHARACTERS), 3, 166 * MATH_DIVIDE_CHARACTERS) djui_hud_set_font(FONT_TINY) local TEXT_CHAR_COUNT = currChar .. "/" .. #characterTable djui_hud_print_text(TEXT_CHAR_COUNT, (11 - djui_hud_measure_text(TEXT_CHAR_COUNT) * 0.2) * widthScale, height - 12, 0.4) --Character Select Header djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) - djui_hud_render_rect(0, 0, width, 50) - djui_hud_set_color(0, 0, 0, 255) - djui_hud_render_rect(2, 2, width - 4, 46) + djui_hud_render_rect(0, 0, width, 2) + djui_hud_render_rect(0, 0, 2, 50) + djui_hud_render_rect(0, 48, width, 2) + djui_hud_render_rect(width - 2, 0, 2, 50) djui_hud_set_color(menuColor.r * 0.5 + 127, menuColor.g * 0.5 + 127, menuColor.b * 0.5 + 127, 255) if TEX_OVERRIDE_HEADER then -- Render Override Header - djui_hud_render_texture(TEX_OVERRIDE_HEADER, widthHalf - 128, 10, 1/(TEX_OVERRIDE_HEADER.height*MATH_DIVIDE_THIRTY_TWO), 1/(TEX_OVERRIDE_HEADER.height*MATH_DIVIDE_THIRTY_TWO)) + djui_hud_render_texture(TEX_OVERRIDE_HEADER, widthHalf - 128, 10, 1 / (TEX_OVERRIDE_HEADER.height*MATH_DIVIDE_32), 1 / (TEX_OVERRIDE_HEADER.height*MATH_DIVIDE_32)) else djui_hud_render_texture(TEX_HEADER, widthHalf - 128, 10, 1, 1) end @@ -758,14 +904,15 @@ local function on_hud_render() djui_hud_print_text(TEXT_RATIO_UNSUPPORTED, 5, 39, 0.5) end - -- API Rendering - if #renderInMenuTable > 0 then - for i = 1, #renderInMenuTable do - renderInMenuTable[i]() + -- API Rendering (Above Text) + if #renderInMenuTable.front > 0 then + for i = 1, #renderInMenuTable.front do + renderInMenuTable.front[i]() end end --Options display + local optionTableCount = #optionTable if options or optionAnimTimer > optionAnimTimerCap then djui_hud_set_color(menuColor.r * 0.25, menuColor.g * 0.25, menuColor.b * 0.25, 205 + maxf(-200, optionAnimTimer)) djui_hud_render_rect(0, 0, width, height) @@ -773,9 +920,13 @@ local function on_hud_render() djui_hud_render_rect(width * 0.5 - 50 * widthScale, minf(55 - optionAnimTimer, height - 25 * widthScale), 100 * widthScale, 200) djui_hud_set_color(0, 0, 0, 255) djui_hud_render_rect(width * 0.5 - 50 * widthScale + 2, minf(55 - optionAnimTimer + 2, height - 25 * widthScale + 2), 100 * widthScale - 4, 196) - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_set_font(FONT_ALIASED) djui_hud_set_color(menuColor.r * 0.5 + 127, menuColor.g * 0.5 + 127, menuColor.b * 0.5 + 127, 255) - djui_hud_print_text(TEXT_OPTIONS_HEADER, widthHalf - djui_hud_measure_text(TEXT_OPTIONS_HEADER) * 0.3 * minf(widthScale, 1.5), 65 + optionAnimTimer * -1, 0.6 * minf(widthScale, 1.5)) + local text = TEXT_OPTIONS_HEADER + if currOption > defaultOptionCount then + text = TEXT_OPTIONS_HEADER_API + end + djui_hud_print_text(text, widthHalf - djui_hud_measure_text(text) * 0.3 * minf(widthScale, 1.5), 65 + optionAnimTimer * -1, 0.6 * minf(widthScale, 1.5)) local widthScaleLimited = minf(widthScale, 1.5) -- Up Arrow @@ -806,11 +957,13 @@ local function on_hud_render() local scale = 0.5 local yOffset = 100 - optionAnimTimer + (i - currOption + 2) * 9 * widthScaleLimited if i == currOption then - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_set_font(FONT_ALIASED) scale = 0.3 yOffset = yOffset - 1 - if optionTable[i].toggleNames[optionTable[i].toggle + 1] ~= "" then - toggleName = toggleName .. " - " .. optionTable[i].toggleNames[optionTable[i].toggle + 1] + local currToggleName = optionTable[i].toggleNames[optionTable[i].toggle + 1] + currToggleName = currToggleName and currToggleName or "???" + if currToggleName ~= "" then + toggleName = toggleName .. " - " .. currToggleName else toggleName = toggleName end @@ -828,7 +981,7 @@ local function on_hud_render() if optionTable[currOption].description ~= nil then djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) for i = 1, #optionTable[currOption].description do - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_set_font(FONT_ALIASED) local line = optionTable[currOption].description[i] djui_hud_print_text(line, widthHalf - djui_hud_measure_text(line) * 0.15, 180 - optionAnimTimer + 15 * widthScaleLimited + 8 * i - 8 * #optionTable[currOption].description, 0.3) end @@ -848,7 +1001,7 @@ local function on_hud_render() djui_hud_render_rect(widthHalf - 50 * widthScale + 2, height - 25 * widthScaleLimited + 2, 100 * widthScale - 4, 22 * widthScaleLimited) djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255) djui_hud_render_rect(widthHalf - 50 * widthScale, height - 2, 100 * widthScale, 2) - djui_hud_set_font(FONT_CS_NORMAL) + djui_hud_set_font(FONT_ALIASED) djui_hud_print_text(TEXT_OPTIONS_OPEN, widthHalf - djui_hud_measure_text(TEXT_OPTIONS_OPEN) * 0.175 * widthScaleLimited, height - 23 * widthScaleLimited + optionAnimTimer + 202, 0.35 * widthScaleLimited) djui_hud_set_font(FONT_TINY) djui_hud_print_text(TEXT_MENU_CLOSE, widthHalf - djui_hud_measure_text(TEXT_MENU_CLOSE) * 0.25 * widthScaleLimited, height - 13 * widthScaleLimited + optionAnimTimer + 202, 0.5 * widthScaleLimited) @@ -859,7 +1012,7 @@ local function on_hud_render() if options then if optionTable[optionTableRef.anims].toggle > 0 then if optionAnimTimer < -1 then - optionAnimTimer = optionAnimTimer / 1.1 + optionAnimTimer = optionAnimTimer * 0.9 end else optionAnimTimer = -1 @@ -910,16 +1063,13 @@ local function on_hud_render() if is_game_paused() and not djui_hud_is_pause_menu_created() and gMarioStates[0].action ~= ACT_EXIT_LAND_SAVE_DIALOG then local currCharY = 0 djui_hud_set_resolution(RESOLUTION_DJUI) - djui_hud_set_font(FONT_NORMAL) - if optionTable[optionTableRef.openInputs].toggle == 2 then + djui_hud_set_font(FONT_USER) + if optionTable[optionTableRef.openInputs].toggle == 1 then currCharY = 27 - width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_PAUSE_Z_OPEN) - if not IS_COOPDX then -- Done to match DX not having dropshadow on the "R Button - Options" thingy - djui_hud_set_color(0, 0, 0, 255) - djui_hud_print_text(TEXT_PAUSE_Z_OPEN, width - 19, 17, 1) - end + local text = menu_is_allowed() and TEXT_PAUSE_Z_OPEN or TEXT_PAUSE_UNAVALIBLE + width = djui_hud_get_screen_width() - djui_hud_measure_text(text) djui_hud_set_color(255, 255, 255, 255) - djui_hud_print_text(TEXT_PAUSE_Z_OPEN, width - 20, 16, 1) + djui_hud_print_text(text, width - 20, 16, 1) end if optionTable[optionTableRef.localModels].toggle == 1 then @@ -927,20 +1077,12 @@ local function on_hud_render() local TEXT_PAUSE_CURR_CHAR_WITH_NAME = TEXT_PAUSE_CURR_CHAR .. charName width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_PAUSE_CURR_CHAR_WITH_NAME) local charColor = characterTable[currChar].color - if not IS_COOPDX then - djui_hud_set_color(0, 0, 0, 255) - djui_hud_print_text(TEXT_PAUSE_CURR_CHAR_WITH_NAME, width - 19, 17 + currCharY, 1) - end djui_hud_set_color(255, 255, 255, 255) djui_hud_print_text(TEXT_PAUSE_CURR_CHAR, width - 20, 16 + currCharY, 1) djui_hud_set_color(charColor.r, charColor.g, charColor.b, 255) djui_hud_print_text(charName, djui_hud_get_screen_width() - djui_hud_measure_text(charName) - 20, 16 + currCharY, 1) else width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_LOCAL_MODEL_OFF) - if not IS_COOPDX then - djui_hud_set_color(0, 0, 0, 255) - djui_hud_print_text(TEXT_LOCAL_MODEL_OFF, width - 19, 17 + currCharY, 1) - end djui_hud_set_color(255, 255, 255, 255) djui_hud_print_text(TEXT_LOCAL_MODEL_OFF, width - 20, 16 + currCharY, 1) end @@ -957,18 +1099,19 @@ local function before_mario_update(m) end -- Menu Inputs - if not menu and (m.controller.buttonDown & D_JPAD) ~= 0 and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and optionTable[optionTableRef.openInputs].toggle == 1 then - if ommActive then - if (m.controller.buttonDown & R_TRIG) ~= 0 then - menu = true - end - else + if is_game_paused() and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and (m.controller.buttonPressed & Z_TRIG) ~= 0 and optionTable[optionTableRef.openInputs].toggle == 1 then + menu = true + end + if not menu and (m.controller.buttonDown & D_JPAD) ~= 0 and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and optionTable[optionTableRef.openInputs].toggle == 2 then + if (m.controller.buttonDown & R_TRIG) ~= 0 or not ommActive then menu = true end inputStallTimerDirectional = inputStallToDirectional end - if is_game_paused() and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and (m.controller.buttonPressed & Z_TRIG) ~= 0 and optionTable[optionTableRef.openInputs].toggle == 2 then - menu = true + + if not menu_is_allowed() then + menu = false + return end local cameraToObject = gMarioStates[0].marioObj.header.gfx.cameraToObject @@ -985,7 +1128,7 @@ local function before_mario_update(m) if menuAndTransition and not options then if menu then - if inputStallTimerDirectional == 0 then + if inputStallTimerDirectional == 0 and optionTable[optionTableRef.localModels].toggle ~= 0 and not charBeingSet then if (m.controller.buttonPressed & D_JPAD) ~= 0 or (m.controller.buttonPressed & D_CBUTTONS) ~= 0 or m.controller.stickY < -60 then currChar = currChar + 1 if (m.controller.buttonPressed & D_CBUTTONS) == 0 then @@ -993,8 +1136,13 @@ local function before_mario_update(m) else inputStallTimerDirectional = 3 -- C-Scrolling end - buttonScroll = buttonScrollCap + if currChar > #characterTable then + buttonScroll = -buttonScrollCap * #characterTable + else + buttonScroll = buttonScrollCap + end play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject) + gPlayerSyncTable[0].presetPalette = false end if (m.controller.buttonPressed & U_JPAD) ~= 0 or (m.controller.buttonPressed & U_CBUTTONS) ~= 0 or m.controller.stickY > 60 then currChar = currChar - 1 @@ -1003,8 +1151,13 @@ local function before_mario_update(m) else inputStallTimerDirectional = 3 -- C-Scrolling end - buttonScroll = -buttonScrollCap + if currChar < 1 then + buttonScroll = buttonScrollCap * (#characterTable - 1) + else + buttonScroll = -buttonScrollCap + end play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject) + gPlayerSyncTable[0].presetPalette = false end end @@ -1024,6 +1177,17 @@ local function before_mario_update(m) if (m.controller.buttonPressed & START_BUTTON) ~= 0 then options = true end + local modelId = gPlayerSyncTable[0].modelId and gPlayerSyncTable[0].modelId or defaultModels[m.character.type] + if (m.controller.buttonPressed & Y_BUTTON) ~= 0 then + if characterColorPresets[modelId] and optionTable[optionTableRef.localModels].toggle > 0 and not stopPalettes then + play_sound(SOUND_MENU_CLICK_FILE_SELECT, cameraToObject) + gPlayerSyncTable[0].presetPalette = not gPlayerSyncTable[0].presetPalette + inputStallTimerButton = inputStallToButton + else + play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject) + inputStallTimerButton = inputStallToButton + end + end end end -- Wraping Menu @@ -1066,7 +1230,7 @@ local function before_mario_update(m) end if inputStallTimerButton == 0 then - if (m.controller.buttonPressed & A_BUTTON) ~= 0 then + if (m.controller.buttonPressed & A_BUTTON) ~= 0 and not optionTable[currOption].optionBeingSet then optionTable[currOption].toggle = optionTable[currOption].toggle + 1 if optionTable[currOption].toggle > optionTable[currOption].toggleMax then optionTable[currOption].toggle = 0 end if optionTable[currOption].toggleSaveName ~= nil then @@ -1120,6 +1284,12 @@ local function chat_command(msg) return true end + -- Stop Character checks if API disallows it + if not menu_is_allowed() or charBeingSet then + djui_chat_message_create("Character Cannot be Changed") + return true + end + -- Name Check for i = 1, #characterTable do if msg == string_lower(characterTable[i].name) or msg == string_underscore_to_space(string_lower(characterTable[i].saveName)) then diff --git a/mods/character-select-coop/n-hud.lua b/mods/character-select-coop/n-hud.lua index 610ee59a..b03b3ece 100644 --- a/mods/character-select-coop/n-hud.lua +++ b/mods/character-select-coop/n-hud.lua @@ -2,17 +2,83 @@ -- Custom HUD Rendering by Agent X and xLuigiGamerx -- ------------------------------------------------------ +if incompatibleClient then return 0 end + +local defaultIcons = { + [CT_MARIO] = gTextures.mario_head, + [CT_LUIGI] = gTextures.luigi_head, + [CT_TOAD] = gTextures.toad_head, + [CT_WALUIGI] = gTextures.waluigi_head, + [CT_WARIO] = gTextures.wario_head +} + +local MATH_DIVIDE_16 = 1/16 + +-- Localize Functions to improve performence +local texture_override_set,texture_override_reset,djui_hud_print_text,djui_hud_render_texture,tostring,hud_set_value,hud_get_value,hud_get_flash,djui_hud_get_screen_width,djui_hud_render_rect,math_ceil = texture_override_set,texture_override_reset,djui_hud_print_text,djui_hud_render_texture,tostring,hud_set_value,hud_get_value,hud_get_flash,djui_hud_get_screen_width,djui_hud_render_rect,math.ceil + + --- @param localIndex integer --- @return TextureInfo|nil ---- This assumes multiple characters will not have the same model +--- This assumes multiple characters will not have the same model, +--- Icons can only be seen by users who have the character avalible to them. +--- This function can return nil. if this is the case, render `djui_hud_print_text("?", x, y, 1)` function life_icon_from_local_index(localIndex) for i = 1, #characterTable do + if i == 1 and characterTable[i].model == gPlayerSyncTable[localIndex].modelId then + return defaultIcons[gMarioStates[localIndex].character.type] + end if characterTable[i].model == gPlayerSyncTable[localIndex].modelId then return characterTable[i].lifeIcon end end end +--- @param localIndex integer +--- @return TextureInfo +--- This assumes multiple characters will not have the same model, +--- Icons can only be seen by users who have the character avalible to them +function star_icon_from_local_index(localIndex) + for i = 1, #characterTable do + if characterTable[i].model == gPlayerSyncTable[localIndex].modelId then + return characterTable[i].starIcon + end + end + return gTextures.star +end + +local pieTextureNames = { + "one_segments", + "two_segments", + "three_segments", + "four_segments", + "five_segments", + "six_segments", + "seven_segments", + "full", +} + +local function render_hud_health() + local textureTable = characterTable[currChar].healthTexture + if textureTable then -- sets health HUD to custom textures + if textureTable.label.left and textureTable.label.right then -- if left and right label textures exist. BOTH have to exist to be set! + texture_override_set("texture_power_meter_left_side", textureTable.label.left) + texture_override_set("texture_power_meter_right_side", textureTable.label.right) + end + + for i = 1, 8 do + texture_override_set("texture_power_meter_" .. pieTextureNames[i], textureTable.pie[i]) + end + else -- resets the health HUD + texture_override_reset("texture_power_meter_left_side") + texture_override_reset("texture_power_meter_right_side") + + for i = 1, 8 do + texture_override_reset("texture_power_meter_" .. pieTextureNames[i]) + end + end +end + local function render_hud_mario_lives() hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) & ~HUD_DISPLAY_FLAG_LIVES) @@ -23,7 +89,7 @@ local function render_hud_mario_lives() if lifeIcon == nil then djui_hud_print_text("?", x, y, 1) else - djui_hud_render_texture(lifeIcon, x, y, 1 / (lifeIcon.width * 0.0625), 1 / (lifeIcon.height * 0.0625)) -- 0.0625 is 1/16 + djui_hud_render_texture(lifeIcon, x, y, 1 / (lifeIcon.width * MATH_DIVIDE_16), 1 / (lifeIcon.height * MATH_DIVIDE_16)) end djui_hud_print_text("@", x + 16, y, 1) djui_hud_print_text(tostring(hud_get_value(HUD_DISPLAY_LIVES)):gsub("-", "M"), x + 32, y, 1) @@ -32,10 +98,7 @@ end local function render_hud_stars() hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) & ~HUD_DISPLAY_FLAG_STAR_COUNT) - -- the flashing when you exit a painting, uses sm64coopdx exclusive functionality - -- v0.1.5 - --[[ - if IS_COOPDX then + if IS_COOPDX and hud_get_flash ~= nil then -- prevent star count from flashing outside of castle if gNetworkPlayers[0].currCourseNum ~= COURSE_NONE then hud_set_flash(0) end @@ -43,17 +106,19 @@ local function render_hud_stars() return end end - ]] - local x = djui_hud_get_screen_width() - 78 - local y = 15 -- SCREEN_HEIGHT - 209 - 16 + local x = math_ceil(djui_hud_get_screen_width() - 76) + if x % 2 ~= 0 then + x = x - 1 + end + local y = math_ceil(240 - 209 - 16) local starIcon = characterTable[currChar].starIcon and characterTable[currChar].starIcon or gTextures.star local showX = 0 local hudDisplayStars = hud_get_value(HUD_DISPLAY_STARS) if hudDisplayStars < 100 then showX = 1 end - djui_hud_render_texture(starIcon, x, y, 1 / (starIcon.width * 0.0625), 1 / (starIcon.height * 0.0625)) -- 0.0625 is 1/16 + djui_hud_render_texture(starIcon, x, y, 1 / (starIcon.width*MATH_DIVIDE_16), 1 / (starIcon.height*MATH_DIVIDE_16)) if showX == 1 then djui_hud_print_text("@", x + 16, y, 1) end @@ -79,7 +144,7 @@ local function render_hud_camera_status() if lifeIcon == nil then djui_hud_print_text("?", x + 16, y, 1) else - djui_hud_render_texture(lifeIcon, x + 16, y, 1 / (lifeIcon.width * 0.0625), 1 / (lifeIcon.height * 0.0625)) -- 0.0625 is 1/16 + djui_hud_render_texture(lifeIcon, x + 16, y, 1 / (lifeIcon.width * MATH_DIVIDE_16), 1 / (lifeIcon.height * MATH_DIVIDE_16)) end end, [CAM_STATUS_LAKITU] = function() @@ -103,20 +168,21 @@ end local function render_act_select_hud() local course = gNetworkPlayers[0].currCourseNum if gServerSettings.enablePlayersInLevelDisplay == 0 or course == 0 or obj_get_first_with_behavior_id(id_bhvActSelector) == nil then return end - local stars = save_file_get_star_flags(get_current_save_file_num() - 1, course - 1) local maxStar = 0 + local wasLastActBeat = true -- True by default to account for 0 stars collected not needing an offset for i = 5, 0, -1 do if stars & 2 ^ i ~= 0 then maxStar = i + wasLastActBeat = stars & 2^(i-1) ~= 0 break end end for a = 1, maxStar + 1 do - local x = (38 - maxStar * 17 + a * 34) + djui_hud_get_screen_width() * 0.5 - 75 - for j = 0, MAX_PLAYERS - 1 do + local x = (139 - (maxStar - (wasLastActBeat and 1 or 0) - (maxStar < 5 and 0 or 1) - (maxStar < 1 and 1 or 0)) * 17 + (a - (wasLastActBeat and 1 or 0)) * 34) + (djui_hud_get_screen_width()/2) - 176 + for j = 1, MAX_PLAYERS - 1 do -- 0 is not needed due to the due to the fact that you are never supposed to see yourself in the act local np = gNetworkPlayers[j] if np and np.connected and np.currCourseNum == course and np.currActNum == a then djui_hud_render_rect(x - 4, 17, 16, 16) @@ -124,8 +190,9 @@ local function render_act_select_hud() if displayHead == nil then djui_hud_print_text("?", x - 4, 17, 1) else - djui_hud_render_texture(displayHead, x - 4, 17, 1 / (displayHead.width * 0.0625), 1 / (displayHead.height * 0.0625)) -- 0.0625 is 1/16 + djui_hud_render_texture(displayHead, x - 4, 17, 1 / (displayHead.width/16), 1 / (displayHead.height * MATH_DIVIDE_16)) end + break end end end @@ -136,17 +203,26 @@ local function on_hud_render_behind() djui_hud_set_font(FONT_HUD) djui_hud_set_color(255, 255, 255, 255) - if obj_get_first_with_behavior_id(id_bhvActSelector) ~= nil then - render_act_select_hud() - return - elseif gNetworkPlayers[0].currActNum == 99 or gMarioStates[0].action == ACT_INTRO_CUTSCENE or hud_is_hidden() then + if gNetworkPlayers[0].currActNum == 99 or gMarioStates[0].action == ACT_INTRO_CUTSCENE or hud_is_hidden() or obj_get_first_with_behavior_id(id_bhvActSelector) ~= nil then return end render_hud_mario_lives() render_hud_stars() render_hud_camera_status() + render_hud_health() +end + +local function on_hud_render() + djui_hud_set_resolution(RESOLUTION_N64) + djui_hud_set_font(FONT_HUD) + djui_hud_set_color(255, 255, 255, 255) + + if obj_get_first_with_behavior_id(id_bhvActSelector) ~= nil then + render_act_select_hud() + end end -hook_event(HOOK_ON_HUD_RENDER_BEHIND, on_hud_render_behind) \ No newline at end of file +hook_event(HOOK_ON_HUD_RENDER_BEHIND, on_hud_render_behind) +hook_event(HOOK_ON_HUD_RENDER, on_hud_render) \ No newline at end of file diff --git a/mods/character-select-coop/o-api.lua b/mods/character-select-coop/o-api.lua index fe7eed6c..dfbd499c 100644 --- a/mods/character-select-coop/o-api.lua +++ b/mods/character-select-coop/o-api.lua @@ -1,3 +1,6 @@ + +if incompatibleClient then return 0 end + --- @class CharacterTable --- @field public name string --- @field public saveName string @@ -8,15 +11,20 @@ --- @field public forceChar CharacterType --- @field public lifeIcon TextureInfo --- @field public camScale integer +--- @field public offset integer -- localize functions to improve performance -local smlua_model_util_get_id,table_insert,djui_hud_measure_text,type = smlua_model_util_get_id,table.insert,djui_hud_measure_text,type +local smlua_model_util_get_id,table_insert,type,djui_hud_measure_text,tonumber = smlua_model_util_get_id,table.insert,type,djui_hud_measure_text,tonumber local characterVoices = {} local saveNameTable = {} local E_MODEL_ARMATURE = smlua_model_util_get_id("armature_geo") +--------- +-- API -- +--------- + local function split_text_into_lines(text) local words = {} for word in text:gmatch("%S+") do @@ -39,33 +47,44 @@ local function split_text_into_lines(text) return lines end ---------- --- API -- ---------- +local TYPE_INTEGER = "number" +local TYPE_STRING = "string" +local TYPE_TABLE = "table" +local TYPE_FUNCTION = "function" ---@param name string|nil Underscores turn into Spaces ---@param description table|string|nil {"string"} ---@param credit string|nil ----@param color Color|nil {r, g, b} +---@param color Color|string|nil {r, g, b} ---@param modelInfo ModelExtendedId|integer|nil Use smlua_model_util_get_id() ---@param forceChar CharacterType|nil CT_MARIO, CT_LUIGI, CT_TOAD, CT_WALUIGI, CT_WARIO ---@param lifeIcon TextureInfo|nil Use get_texture_info() ---@param camScale integer|nil Zooms the camera based on a multiplier (Default 1.0) +---@param offset integer|nil Visually offsets the character ---@return integer -local function character_add(name, description, credit, color, modelInfo, forceChar, lifeIcon, camScale) - if type(description) == "string" then +local function character_add(name, description, credit, color, modelInfo, forceChar, lifeIcon, camScale, offset) + if type(description) == TYPE_STRING then description = split_text_into_lines(description) end + if type(color) == TYPE_STRING then + color = {r = tonumber(color:sub(1,2), 16), g = tonumber(color:sub(3,4), 16), b = tonumber(color:sub(5,6), 16) } + end + if type(offset) ~= TYPE_INTEGER then + offset = (forceChar == CT_WALUIGI and 25 or 0) + end table_insert(characterTable, { - name = name and name or "Untitled", - saveName = name and string_space_to_underscore(name) or "Untitled", - description = description and description or {"No description has been provided"}, - credit = credit and credit or "Unknown", - color = color and color or {r = 255, g = 255, b = 255}, - model = modelInfo and modelInfo or E_MODEL_ARMATURE, + name = type(name) == TYPE_STRING and name or "Untitled", + saveName = type(name) == TYPE_STRING and string_space_to_underscore(name) or "Untitled", + description = type(description) == TYPE_TABLE and description or {"No description has been provided"}, + credit = type(credit) == TYPE_STRING and credit or "Unknown", + color = type(color) == TYPE_TABLE and color or {r = 255, g = 255, b = 255}, + model = (modelInfo and modelInfo ~= E_MODEL_ERROR_MODEL) and modelInfo or E_MODEL_ARMATURE, forceChar = forceChar and forceChar or CT_MARIO, - lifeIcon = lifeIcon and lifeIcon or nil, - camScale = camScale and camScale or 1 + offset = offset and offset or 0, + lifeIcon = type(lifeIcon) == TYPE_TABLE and lifeIcon or nil, + starIcon = gTextures.star, + camScale = type(camScale) == TYPE_INTEGER and camScale or 1, + healthTexture = nil, }) saveNameTable[#characterTable] = characterTable[#characterTable].saveName return #characterTable @@ -77,36 +96,56 @@ end ---@param credit string|nil ---@param color Color|nil {r, g, b} ---@param modelInfo ModelExtendedId|integer|nil Use smlua_model_util_get_id() ----@param forceChar CharacterType|nil CT_MARIO, CT_LUIGI, CT_TOAD, CT_WALUIGI, CT_WARIO +---@param forceChar integer|CharacterType|nil CT_MARIO, CT_LUIGI, CT_TOAD, CT_WALUIGI, CT_WARIO ---@param lifeIcon TextureInfo|nil Use get_texture_info() ---@param camScale integer|nil Zooms the camera based on a multiplier (Default 1.0) -local function character_edit(charNum, name, description, credit, color, modelInfo, forceChar, lifeIcon, camScale) - if type(description) == "string" then +---@param offset integer|nil Visually offsets the character +local function character_edit(charNum, name, description, credit, color, modelInfo, forceChar, lifeIcon, camScale, offset) + if tonumber(charNum) == nil or charNum > #characterTable or charNum < 0 then return end + if type(description) == TYPE_STRING then description = split_text_into_lines(description) end + if type(color) == TYPE_STRING then + color = {r = tonumber(color:sub(1,2), 16), g = tonumber(color:sub(3,4), 16), b = tonumber(color:sub(5,6), 16) } + end + if type(offset) ~= TYPE_INTEGER then + offset = (forceChar == CT_WALUIGI and 25 or 0) + end + local tableCache = characterTable[charNum] characterTable[charNum] = characterTable[charNum] and { - name = name and name or characterTable[charNum].name, + name = type(name) == TYPE_STRING and name or tableCache.name, saveName = saveNameTable[charNum], - description = description and description or characterTable[charNum].description, - credit = credit and credit or characterTable[charNum].credit, - color = color and color or characterTable[charNum].color, - model = modelInfo and modelInfo or characterTable[charNum].model, - forceChar = forceChar and forceChar or characterTable[charNum].forceChar, - lifeIcon = lifeIcon and lifeIcon or characterTable[charNum].lifeIcon, - camScale = camScale and camScale or 1 + description = type(description) == TYPE_TABLE and description or tableCache.description, + credit = type(credit) == TYPE_STRING and credit or tableCache.credit, + color = type(color) == TYPE_TABLE and color or tableCache.color, + model = (modelInfo and modelInfo ~= E_MODEL_ERROR_MODEL) and modelInfo or tableCache.model, + forceChar = type(forceChar) == TYPE_INTEGER and forceChar or tableCache.forceChar, + offset = type(offset) == TYPE_INTEGER and offset or tableCache.offset, + lifeIcon = type(lifeIcon) == TYPE_TABLE and lifeIcon or tableCache.lifeIcon, + starIcon = tableCache.starIcon, -- Done to prevent it getting lost in the sauce + camScale = type(camScale) == TYPE_INTEGER and camScale or tableCache.camScale, + healthTexture = tableCache.healthTexture, } or nil end ---@param modelInfo ModelExtendedId|integer ---@param clips table local function character_add_voice(modelInfo, clips) - characterVoices[modelInfo] = clips + characterVoices[modelInfo] = type(clips) == TYPE_TABLE and clips or nil end ---@param modelInfo ModelExtendedId|integer ---@param caps table local function character_add_caps(modelInfo, caps) - characterCaps[modelInfo] = caps + characterCaps[modelInfo] = type(caps) == TYPE_TABLE and caps or nil +end + +---@param charNum integer +---@param healthTexture table|nil +local function character_add_health_meter(charNum, healthTexture) + if type(charNum) ~= TYPE_INTEGER or charNum == nil then return end + characterTable[charNum].healthTexture = type(healthTexture) == TYPE_TABLE and healthTexture or nil + return false end ---@param modelInfo ModelExtendedId|integer @@ -116,24 +155,56 @@ local function character_add_celebration_star(modelInfo, starModel, starIcon) characterCelebrationStar[modelInfo] = starModel for i = 2, #characterTable do if characterTable[i].model == modelInfo then - characterTable[i].starIcon = starIcon and starIcon or gTextures.star + characterTable[i].starIcon = type(starIcon) == TYPE_TABLE and starIcon or gTextures.star return end end return false end +---@param modelInfo ModelExtendedId|integer +---@param paletteTable table +local function character_add_palette_preset(modelInfo, paletteTable) + local paletteTableOut = {} + local defaultColors = characterColorPresets[E_MODEL_MARIO] + for i = 0, 7 do + local color = paletteTable[i] + paletteTableOut[i] = {r = 0, g = 0, b = 0} + if type(color) == TYPE_STRING then + paletteTableOut[i].r = tonumber(color:sub(1,2), 16) and tonumber(color:sub(1,2), 16) or defaultColors[i].r + paletteTableOut[i].g = tonumber(color:sub(3,4), 16) and tonumber(color:sub(3,4), 16) or defaultColors[i].g + paletteTableOut[i].b = tonumber(color:sub(5,6), 16) and tonumber(color:sub(5,6), 16) or defaultColors[i].b + end + if type(color) == TYPE_TABLE then + paletteTableOut[i].r = (type(color) == TYPE_TABLE and color.r ~= nil) and color.r or defaultColors[i].r + paletteTableOut[i].g = (type(color) == TYPE_TABLE and color.g ~= nil) and color.g or defaultColors[i].g + paletteTableOut[i].b = (type(color) == TYPE_TABLE and color.b ~= nil) and color.b or defaultColors[i].b + end + end + characterColorPresets[modelInfo] = paletteTableOut +end + +---@param tablePos integer|nil ---@return CharacterTable -local function character_get_current_table() - return characterTable[currChar] +local function character_get_current_table(tablePos) + tablePos = tablePos and tablePos or currChar + return characterTable[tablePos] end local function character_get_current_number() return currChar end +---@param charNum integer|nil +local function character_set_current_number(charNum) + if type(charNum) ~= TYPE_INTEGER or characterTable[charNum] == nil then return end + currChar = charNum + charBeingSet = true +end + ---@param name string local function character_get_number_from_string(name) + if type(name) ~= TYPE_STRING then return nil end for i = 2, #characterTable do if characterTable[i].name == name or characterTable[i].name == string_space_to_underscore(name) then return i @@ -147,26 +218,54 @@ local function character_get_voice(m) return characterVoices[gPlayerSyncTable[m.playerIndex].modelId] end +---@return string local function version_get() return MOD_VERSION end +---@return boolean local function is_menu_open() return menuAndTransition end +---@param bool boolean|nil Sets if the menu is open +local function set_menu_open(bool) + if bool == nil then bool = true end + menu = bool +end + +---@return table +local function get_menu_color() + return update_menu_color() +end + +---@param func function local function hook_allow_menu_open(func) + if type(func) ~= TYPE_FUNCTION then return end table_insert(allowMenu, func) end -local function hook_render_in_menu(func) - table_insert(renderInMenuTable, func) +---@param func function +local function hook_render_in_menu(func, underText) + if type(func) ~= TYPE_FUNCTION then return end + if underText then + table_insert(renderInMenuTable.back, func) + else + table_insert(renderInMenuTable.front, func) + end end +---@return boolean local function is_options_open() return options end +---@param bool boolean +local function restrict_palettes(bool) + if bool == nil then bool = true end + stopPalettes = bool +end + local controller = { buttonDown = 0, buttonPressed = 0, @@ -179,31 +278,89 @@ local controller = { stickY = 0 } +---@param name string +---@param toggleDefault number|nil Defaults to 0 +---@param toggleMax number|nil Defaults to 1 +---@param toggleNames table|nil Table of Strings {"Off", "On"} +---@param description table|nil Table of Strings {"This toggle allows your", "character to feel everything."} +---@param save boolean|nil Defaults to true +---@return number +local function add_option(name, toggleDefault, toggleMax, toggleNames, description, save) + if save == nil then save = true end + local saveName = string_space_to_underscore(name) + table_insert(optionTable, { + name = type(name) == TYPE_STRING and name or "Unknown Toggle", + toggle = nil, -- Set as nil for Failsafe to Catch + toggleSaveName = save and saveName or nil, + toggleDefault = type(toggleDefault) == TYPE_INTEGER and toggleDefault or 0, + toggleMax = type(toggleMax) == TYPE_INTEGER and toggleMax or 1, + toggleNames = type(toggleNames) == TYPE_TABLE and toggleNames or {"Off", "On"}, + description = type(description) == TYPE_TABLE and description or {""}, + }) + queueStorageFailsafe = true -- Used variable trigger to not save/load in the external mod + return #optionTable +end + ---@param tableNum integer -local function get_status(tableNum) +---@return table|nil +local function get_option(tableNum) + if type(tableNum) ~= TYPE_INTEGER then return nil end + return optionTable[tableNum] +end + +---@param tableNum integer +---@return number|nil +local function get_options_status(tableNum) + if type(tableNum) ~= TYPE_INTEGER then return nil end return optionTable[tableNum].toggle end +---@param tableNum integer +---@param toggle integer +local function set_options_status(tableNum, toggle) + local currOption = optionTable[tableNum] + if currOption == nil or type(toggle) ~= TYPE_INTEGER or toggle > currOption.toggleMax or toggle < 1 then return end + optionTable[tableNum].toggle = toggle + optionTable[tableNum].optionBeingSet = true +end + _G.charSelectExists = true _G.charSelect = { + -- Character Functions -- character_add = character_add, character_edit = character_edit, character_add_voice = character_add_voice, character_add_caps = character_add_caps, character_add_celebration_star = character_add_celebration_star, + character_add_health_meter = character_add_health_meter, + character_add_palette_preset = character_add_palette_preset, character_get_current_table = character_get_current_table, character_get_current_number = character_get_current_number, - character_get_current_model_number --[[Depreiciated Function Name, Not recommended for use]] = character_get_current_number, + character_get_current_model_number = character_get_current_number, -- Outdated function name, Not recommended for use + character_set_current_number = character_set_current_number, character_get_number_from_string = character_get_number_from_string, character_get_voice = character_get_voice, - character_get_life_icon = life_icon_from_local_index, + character_get_life_icon = life_icon_from_local_index, -- Function located in n-hud.lua + character_get_star_icon = star_icon_from_local_index, -- Function located in n-hud.lua + + -- Menu Functions -- header_set_texture = header_set_texture, -- Function located in main.lua version_get = version_get, is_menu_open = is_menu_open, + set_menu_open = set_menu_open, is_options_open = is_options_open, - get_status = get_status, + get_menu_color = get_menu_color, + add_option = add_option, + get_option = get_option, + get_options_status = get_options_status, + set_options_status = set_options_status, + restrict_palettes = restrict_palettes, + + -- Tables -- optionTableRef = optionTableRef, controller = controller, + + -- Custom Hooks -- hook_allow_menu_open = hook_allow_menu_open, hook_render_in_menu = hook_render_in_menu, } \ No newline at end of file diff --git a/mods/character-select-coop/palettes.lua b/mods/character-select-coop/palettes.lua new file mode 100644 index 00000000..3ef73a66 --- /dev/null +++ b/mods/character-select-coop/palettes.lua @@ -0,0 +1,138 @@ + +if incompatibleClient then return 0 end + +characterColorPresets = { + [E_MODEL_MARIO] = { + [PANTS] = {r = 0x00, g = 0x00, b = 0xff}, + [SHIRT] = {r = 0xff, g = 0x00, b = 0x00}, + [GLOVES] = {r = 0xff, g = 0xff, b = 0xff}, + [SHOES] = {r = 0x72, g = 0x1c, b = 0x0e}, + [HAIR] = {r = 0x73, g = 0x06, b = 0x00}, + [SKIN] = {r = 0xfe, g = 0xc1, b = 0x79}, + [CAP] = {r = 0xff, g = 0x00, b = 0x00}, + [EMBLEM] = {r = 0xff, g = 0x00, b = 0x00}, + }, + [E_MODEL_LUIGI] = { + [PANTS] = {r = 0x00, g = 0x00, b = 0xff}, + [SHIRT] = {r = 0x00, g = 0xff, b = 0x00}, + [GLOVES] = {r = 0xff, g = 0xff, b = 0xff}, + [SHOES] = {r = 0x72, g = 0x1c, b = 0x0e}, + [HAIR] = {r = 0x73, g = 0x06, b = 0x00}, + [SKIN] = {r = 0xfe, g = 0xc1, b = 0x79}, + [CAP] = {r = 0x00, g = 0xff, b = 0x00}, + [EMBLEM] = {r = 0x00, g = 0xff, b = 0x00}, + }, + [E_MODEL_TOAD_PLAYER] = { + [PANTS] = {r = 0xff, g = 0xff, b = 0xff}, + [SHIRT] = {r = 0x4c, g = 0x2c, b = 0xd3}, + [GLOVES] = {r = 0xff, g = 0xff, b = 0xff}, + [SHOES] = {r = 0x68, g = 0x40, b = 0x1b}, + [HAIR] = {r = 0x73, g = 0x06, b = 0x00}, + [SKIN] = {r = 0xfe, g = 0xd5, b = 0xa1}, + [CAP] = {r = 0xff, g = 0x00, b = 0x00}, + [EMBLEM] = {r = 0xff, g = 0x00, b = 0x00}, + }, + [E_MODEL_WALUIGI] = { + [PANTS] = {r = 0x16, g = 0x16, b = 0x27}, + [SHIRT] = {r = 0x61, g = 0x26, b = 0xb0}, + [GLOVES] = {r = 0xff, g = 0xff, b = 0xff}, + [SHOES] = {r = 0xfe, g = 0x76, b = 0x00}, + [HAIR] = {r = 0x73, g = 0x53, b = 0x00}, + [SKIN] = {r = 0xfe, g = 0xc1, b = 0x79}, + [CAP] = {r = 0x61, g = 0x26, b = 0xb0}, + [EMBLEM] = {r = 0x4c, g = 0x2c, b = 0xd3}, + }, + [E_MODEL_WARIO] = { + [PANTS] = {r = 0x7f, g = 0x20, b = 0x7a}, + [SHIRT] = {r = 0xe3, g = 0xa9, b = 0x01}, + [GLOVES] = {r = 0xff, g = 0xff, b = 0xff}, + [SHOES] = {r = 0x0e, g = 0x72, b = 0x1c}, + [HAIR] = {r = 0x73, g = 0x53, b = 0x00}, + [SKIN] = {r = 0xfe, g = 0xc1, b = 0x79}, + [CAP] = {r = 0xe3, g = 0xa9, b = 0x01}, + [EMBLEM] = {r = 0x61, g = 0x26, b = 0xb0}, + }, +} + +local defaultModels = { + [CT_MARIO] = E_MODEL_MARIO, + [CT_LUIGI] = E_MODEL_LUIGI, + [CT_TOAD] = E_MODEL_TOAD_PLAYER, + [CT_WALUIGI] = E_MODEL_WALUIGI, + [CT_WARIO] = E_MODEL_WARIO +} + +local paletteLoop = #characterColorPresets[E_MODEL_MARIO] + +local network_player_set_override_palette_color = network_player_set_override_palette_color + +local function network_player_set_full_override_palette(networkPlayer, colorTable) + for i = 0, paletteLoop do + network_player_set_override_palette_color(networkPlayer, i, colorTable[i]) + end +end + +local prevChar = currChar +local stallTimer = 5 + +local prevPresetPalette = {} +local prevModel = {} + +local function mario_update(m) + local np = gNetworkPlayers[m.playerIndex] + local p = gPlayerSyncTable[m.playerIndex] + + if m.playerIndex == 0 and not p.isUpdating then + p.isUpdating = true + for i = 1, MAX_PLAYERS - 1 do + prevPresetPalette[i] = gPlayerSyncTable[i].presetPalette + prevModel[i] = gPlayerSyncTable[i].modelId and gPlayerSyncTable[i].modelId or defaultModels[gMarioStates[i].character.type] + end + end + + if np.connected then + local modelId = p.modelId and p.modelId or defaultModels[m.character.type] + if p.presetPalette == nil or characterColorPresets[modelId] == nil then + if p.presetPalette == nil then + prevPresetPalette[m.playerIndex] = false + end + p.presetPalette = false + end + + if (prevPresetPalette[m.playerIndex] ~= p.presetPalette or prevModel[m.playerIndex] ~= modelId) then + if not p.presetPalette or not characterColorPresets[modelId] then + network_player_reset_override_palette(np) + end + end + + prevPresetPalette[m.playerIndex] = p.presetPalette + prevModel[m.playerIndex] = modelId + + if p.presetPalette and characterColorPresets[modelId] and not stopPalettes then + network_player_set_full_override_palette(np, characterColorPresets[modelId]) + end + else + if p.isUpdating then + p.isUpdating = false + end + end + + if m.playerIndex == 0 then + if (menuAndTransition or prevChar ~= currChar) and stallTimer == 0 then + local modelId = p.modelId and p.modelId or defaultModels[m.character.type] + if optionTable[optionTableRef.autoPalette].toggle > 0 and optionTable[optionTableRef.localModels].toggle > 0 and (currChar ~= 1 and prevChar ~= currChar and not p.presetPalette) and characterColorPresets[modelId] and not stopPalettes then + p.presetPalette = true + end + if optionTable[optionTableRef.localModels].toggle == 0 then + p.presetPalette = false + end + prevChar = currChar + end + + if stallTimer > 0 then + stallTimer = stallTimer - 1 + end + end +end + +hook_event(HOOK_MARIO_UPDATE, mario_update) \ No newline at end of file diff --git a/mods/character-select-coop/z-voice.lua b/mods/character-select-coop/z-voice.lua index 83492d06..b47c233d 100644 --- a/mods/character-select-coop/z-voice.lua +++ b/mods/character-select-coop/z-voice.lua @@ -1,13 +1,11 @@ -for i = 0, MAX_PLAYERS -1, 1 do - gPlayerSyncTable[i].customVoice = 0 -end -local voicecount = 0 +if incompatibleClient then return 0 end + +local voiceTimeout = false local SLEEP_TALK_SNORES = 8 gCustomVoiceSamples = {} -gCustomVoiceSamplesBackup = {} gCustomVoiceStream = nil -- localize functions to improve performance @@ -16,19 +14,8 @@ local audio_sample_stop,audio_sample_destroy,type,math_random,audio_stream_stop, --- @param m MarioState function stop_custom_character_sound(m, sound) local voice_sample = gCustomVoiceSamples[m.playerIndex] - if voice_sample == false and gCustomVoiceSamplesBackup[m.playerIndex].loaded then - audio_sample_stop(gCustomVoiceSamplesBackup[m.playerIndex]) - audio_sample_destroy(gCustomVoiceSamplesBackup[m.playerIndex]) - voicecount = voicecount - 1 - gCustomVoiceSamplesBackup[m.playerIndex] = nil - return - end - if voice_sample == nil or type(voice_sample) == "boolean" then - return - end - if not voice_sample.loaded then - gCustomVoiceSamplesBackup[m.playerIndex] = true - return + if voice_sample == nil or not voice_sample.loaded or voice_sample.isStream then + return nil end audio_sample_stop(voice_sample) @@ -36,14 +23,17 @@ function stop_custom_character_sound(m, sound) return voice_sample end audio_sample_destroy(voice_sample) - voicecount = voicecount - 1 + gCustomVoiceSamples[m.playerIndex] = nil -- prevent this from pointing to another sample or possibly garbage data + + return nil end --- @param m MarioState function play_custom_character_sound(m, voice) + if voiceTimeout then return 0 end local sound if type(voice) == "table" then - sound = voice[math.random(#voice)] + sound = voice[math_random(#voice)] else sound = voice end @@ -66,23 +56,29 @@ function play_custom_character_sound(m, voice) else if voice_sample == nil then voice_sample = audio_sample_load(sound) - while not voice_sample.loaded do end - voicecount = voicecount + 1 + local lagTimer = 0 + repeat + lagTimer = lagTimer + 1 + if lagTimer > 500 then + voiceTimeout = true + if optionTable[optionTableRef.notification].toggle == 1 then + djui_chat_message_create("\\#FFAAAA\\Note: Custom Character Voices are unavalible due to\ninability to load audio, This is most likely because\nyour client does not support custom audio functionality.") + elseif optionTable[optionTableRef.notification].toggle == 2 then + djui_popup_create('Character Select:\nCustom Character Voices\nare unavalible due to\ninability to load audio!', 4) + end + break + end + until voice_sample.loaded end audio_sample_play(voice_sample, m.pos, 1) - - if gCustomVoiceSamplesBackup[m.playerIndex] ~= nil and not(gCustomVoiceSamples[m.playerIndex] == false) then - gCustomVoiceSamplesBackup[m.playerIndex] = voice_sample - else - gCustomVoiceSamples[m.playerIndex] = voice_sample - end + gCustomVoiceSamples[m.playerIndex] = voice_sample end return 0 end --- @param m MarioState local function custom_character_sound(m, characterSound) - if is_game_paused() or optionTable[optionTableRef.localVoices].toggle == 0 then return end + if is_game_paused() or voiceTimeout or optionTable[optionTableRef.localVoices].toggle == 0 then return 0 end if characterSound == CHAR_SOUND_SNORING3 then return 0 end if characterSound == CHAR_SOUND_HAHA and m.hurtCounter > 0 then return 0 end @@ -99,16 +95,10 @@ local SLEEP_TALK_END = SLEEP_TALK_START + SLEEP_TALK_SNORES --- @param m MarioState local function custom_character_snore(m) - if is_game_paused() or optionTable[optionTableRef.localVoices].toggle == 0 then return end - if gCustomVoiceSamplesBackup[m.playerIndex] ~= nil and not (gCustomVoiceSamples[m.playerIndex] == false) then - if gCustomVoiceSamples[m.playerIndex].loaded then - audio_sample_destroy(gCustomVoiceSamples[m.playerIndex]) - voicecount = voicecount - 1 - gCustomVoiceSamples[m.playerIndex] = false - end - end + if is_game_paused() or voiceTimeout or optionTable[optionTableRef.localVoices].toggle == 0 then return end + local SNORE3_TABLE = _G.charSelect.character_get_voice(m)[CHAR_SOUND_SNORING3] - + if m.action ~= ACT_SLEEPING then if m.isSnoring > 0 then stop_custom_character_sound(m) @@ -147,8 +137,24 @@ local function custom_character_snore(m) end end +function stop_all_character_sounds() + if gCustomVoiceStream then + audio_stream_stop(gCustomVoiceStream) + audio_stream_destroy(gCustomVoiceStream) + gCustomVoiceStream = nil + end + for i = 0, MAX_PLAYERS-1 do + stop_custom_character_sound(gMarioStates[i]) + end +end +hook_event(HOOK_ON_WARP, stop_all_character_sounds) +hook_event(HOOK_UPDATE, function () + if is_game_paused() then + stop_all_character_sounds() + end +end) + _G.charSelect.voice = { sound = custom_character_sound, snore = custom_character_snore, } -gPlayerSyncTable[0].customVoice = true \ No newline at end of file