mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-02-20 20:41:07 +00:00
Some checks failed
Build coop / build-linux (push) Has been cancelled
Build coop / build-steamos (push) Has been cancelled
Build coop / build-windows-opengl (push) Has been cancelled
Build coop / build-windows-directx (push) Has been cancelled
Build coop / build-macos-arm (push) Has been cancelled
Build coop / build-macos-intel (push) Has been cancelled
2453 lines
108 KiB
Lua
2453 lines
108 KiB
Lua
-- name: Character Select
|
|
-- description:\\#ffff33\\-- Character Select Coop v1.16 --\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\n\\#AAAAFF\\Updates can be found on\nCharacter Select's Github:\n\\#6666FF\\Squishy6094/character-select-coop
|
|
-- pausable: false
|
|
-- category: cs
|
|
|
|
if incompatibleClient then return 0 end
|
|
|
|
---@param hookEventType LuaHookedEventType
|
|
local function create_hook_wrapper(hookEventType)
|
|
local callbacks = {}
|
|
|
|
hook_event(hookEventType, function(...)
|
|
for _, func in pairs(callbacks) do
|
|
func(...)
|
|
end
|
|
end)
|
|
|
|
return function(func)
|
|
table.insert(callbacks, func)
|
|
end
|
|
end
|
|
|
|
cs_hook_mario_update = create_hook_wrapper(HOOK_MARIO_UPDATE)
|
|
|
|
menu = false
|
|
menuAndTransition = false
|
|
gridMenu = mod_storage_load_bool("PrefGridView")
|
|
options = nil; OPTIONS_MAIN = 0; OPTIONS_CREDITS = 1
|
|
prevOptions = nil; optionsTimer = 0
|
|
bootChar = CT_MARIO
|
|
if gMarioStates[0].character ~= nil then
|
|
bootChar = gMarioStates[0].character.type
|
|
end
|
|
currChar = bootChar
|
|
local prevChar = bootChar
|
|
currCharRender = bootChar
|
|
currCategory = 1
|
|
local currOption = 1
|
|
local currCredits = 1
|
|
local currCreditScroll = 0
|
|
local creditScrollMin = 6
|
|
local totalPlaytime = 0
|
|
|
|
local menuCrossFade = 7
|
|
local menuCrossFadeCap = menuCrossFade
|
|
local menuCrossFadeMath = 255 / menuCrossFade
|
|
|
|
local TYPE_FUNCTION = "function"
|
|
local TYPE_BOOLEAN = "boolean"
|
|
local TYPE_STRING = "string"
|
|
local TYPE_INTEGER = "number"
|
|
local TYPE_TABLE = "table"
|
|
|
|
local MENU_BINDS_DEFAULT = 1
|
|
local MENU_BINDS_GRID = 2
|
|
local MENU_BINDS_OPTIONS = 3
|
|
local MENU_BINDS_CREDITS = 4
|
|
local TEXT_TABLE_MENU_BINDS = {
|
|
[MENU_BINDS_DEFAULT] = {
|
|
{bind = "Up / Down", desc = "Change Character"},
|
|
{bind = "Left / Right", desc = "Change Costume"},
|
|
{bind = "A Button", desc = "Set Preferred Character"},
|
|
{bind = "B Button", desc = "Exit Menu"},
|
|
{bind = "X Button", desc = "Toggle Grid View"},
|
|
{bind = "Y Button", desc = "Toggle Palette"},
|
|
{bind = "L/R Triggers", desc = "Change Categories"},
|
|
{bind = "Start Button", desc = "Options Menu"},
|
|
},
|
|
[MENU_BINDS_GRID] = {
|
|
{bind = "Up / Down / Left / Right", desc = "Change Character"},
|
|
{bind = "A Button", desc = "Set Preferred Character"},
|
|
{bind = "B Button", desc = "Exit Menu"},
|
|
{bind = "X Button", desc = "Toggle List View"},
|
|
{bind = "Y Button", desc = "Toggle Palette"},
|
|
{bind = "L/R Triggers", desc = "Change Categories"},
|
|
{bind = "Start Button", desc = "Options Menu"},
|
|
},
|
|
[MENU_BINDS_OPTIONS] = {
|
|
{bind = "Up / Down", desc = "Scroll Options"},
|
|
{bind = "Left / Right", desc = "Toggle Option"},
|
|
{bind = "B Button", desc = "Exit Options Menu"},
|
|
},
|
|
[MENU_BINDS_CREDITS] = {
|
|
{bind = "Up / Down", desc = "Scroll Credits"},
|
|
{bind = "Left / Right", desc = "Switch Page"},
|
|
{bind = "B Button", desc = "Exit Credits Menu"},
|
|
},
|
|
}
|
|
|
|
local TEX_LOGO = get_texture_info("char_select_logo")
|
|
local TEX_WALL_LEFT = get_texture_info("char_select_wall_left")
|
|
local TEX_WALL_RIGHT = get_texture_info("char_select_wall_right")
|
|
TEX_GRAFFITI_DEFAULT = get_texture_info("char_select_graffiti_default")
|
|
local TEX_NAMEPLATE = get_texture_info("char_select_list_button")
|
|
local TEX_ALBUM_LAYER1 = get_texture_info("char_select_album_back")
|
|
local TEX_ALBUM_LAYER2 = get_texture_info("char_select_album_front")
|
|
local TEX_ALBUM_LAYER3 = get_texture_info("char_select_album_overlay")
|
|
local TEX_CD_LAYER1 = get_texture_info("char_select_cd_layer1")
|
|
local TEX_CD_LAYER2 = get_texture_info("char_select_cd_layer2")
|
|
local TEX_CD_LAYER3 = get_texture_info("char_select_cd_layer3")
|
|
local TEX_CD_LAYER4 = get_texture_info("char_select_cd_layer4")
|
|
local TEX_RECORD = get_texture_info("char_select_record")
|
|
local TEX_PALETTE_BUCKET = get_texture_info("char_select_palette_bucket")
|
|
local TEX_OPTIONS_TV = get_texture_info("char_select_options_tv")
|
|
local TEX_GEAR = get_texture_info("char_select_gear")
|
|
|
|
LOCKED_NEVER = 0
|
|
LOCKED_TRUE = 1
|
|
LOCKED_FALSE = 2
|
|
|
|
local SOUND_CHAR_SELECT_THEME = audio_stream_load("char_select_menu_theme.ogg")
|
|
local SOUND_CHAR_SELECT_DIAL = audio_stream_load("char_select_dial_wind.ogg")
|
|
local menuThemeTargetVolume = 0
|
|
local menuThemeVolume = menuThemeTargetVolume
|
|
audio_stream_set_looping(SOUND_CHAR_SELECT_THEME, true)
|
|
audio_stream_set_loop_points(SOUND_CHAR_SELECT_THEME, 0, 93.659*22050)
|
|
|
|
CS_ANIM_MENU = CHAR_ANIM_MAX + 1
|
|
|
|
local TEXT_PREF_LOAD_NAME = "Default"
|
|
local TEXT_PREF_LOAD_ALT = 1
|
|
|
|
--[[
|
|
Note: Do NOT add characters via the characterTable below,
|
|
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!
|
|
]]
|
|
|
|
characterTable = {
|
|
[CT_MARIO] = {
|
|
saveName = "Mario_CoopDX",
|
|
nickname = "Mario",
|
|
category = "All_CoopDX",
|
|
ogNum = CT_MARIO,
|
|
currAlt = 1,
|
|
hasMoveset = false,
|
|
locked = LOCKED_NEVER,
|
|
playtime = 0,
|
|
autoDialog = true,
|
|
replaceModels = {},
|
|
[1] = {
|
|
name = "Mario",
|
|
description = "The iconic Italian plumber himself! He's quite confident and brave, always prepared to jump into action to save the Mushroom Kingdom!",
|
|
credit = "Nintendo/CoopDX",
|
|
color = { r = 255, g = 50, b = 50 },
|
|
model = E_MODEL_MARIO,
|
|
ogModel = E_MODEL_MARIO,
|
|
baseChar = CT_MARIO,
|
|
lifeIcon = gTextures.mario_head,
|
|
starIcon = gTextures.star,
|
|
camScale = 1.0,
|
|
healthMeter = {
|
|
label = {
|
|
left = get_texture_info("texture_power_meter_left_side"),
|
|
right = get_texture_info("texture_power_meter_right_side"),
|
|
},
|
|
pie = {
|
|
[1] = get_texture_info("texture_power_meter_one_segments"),
|
|
[2] = get_texture_info("texture_power_meter_two_segments"),
|
|
[3] = get_texture_info("texture_power_meter_three_segments"),
|
|
[4] = get_texture_info("texture_power_meter_four_segments"),
|
|
[5] = get_texture_info("texture_power_meter_five_segments"),
|
|
[6] = get_texture_info("texture_power_meter_six_segments"),
|
|
[7] = get_texture_info("texture_power_meter_seven_segments"),
|
|
[8] = get_texture_info("texture_power_meter_full"),
|
|
}
|
|
}
|
|
},
|
|
},
|
|
[CT_LUIGI] = {
|
|
saveName = "Luigi_CoopDX",
|
|
nickname = "Luigi",
|
|
category = "All_CoopDX",
|
|
ogNum = CT_LUIGI,
|
|
currAlt = 1,
|
|
hasMoveset = false,
|
|
locked = LOCKED_NEVER,
|
|
playtime = 0,
|
|
autoDialog = true,
|
|
replaceModels = {},
|
|
[1] = {
|
|
name = "Luigi",
|
|
description = "The other iconic Italian plumber! He's a bit shy and scares easily, but he's willing to follow his brother Mario through any battle that may come their way!",
|
|
credit = "Nintendo/CoopDX",
|
|
color = { r = 50, g = 255, b = 50 },
|
|
model = E_MODEL_LUIGI,
|
|
ogModel = E_MODEL_LUIGI,
|
|
baseChar = CT_LUIGI,
|
|
lifeIcon = gTextures.luigi_head,
|
|
starIcon = gTextures.star,
|
|
camScale = 1.0,
|
|
healthMeter = {
|
|
label = {
|
|
left = get_texture_info("char_select_luigi_meter_left"),
|
|
right = get_texture_info("char_select_luigi_meter_right"),
|
|
},
|
|
pie = defaultMeterInfo.pie
|
|
}
|
|
},
|
|
},
|
|
[CT_TOAD] = {
|
|
saveName = "Toad_CoopDX",
|
|
nickname = "Toad",
|
|
category = "All_CoopDX",
|
|
ogNum = CT_TOAD,
|
|
currAlt = 1,
|
|
hasMoveset = false,
|
|
locked = LOCKED_NEVER,
|
|
playtime = 0,
|
|
autoDialog = true,
|
|
replaceModels = {},
|
|
[1] = {
|
|
name = "Toad",
|
|
description = "Princess Peach's little attendant! He's an energetic little mushroom that's never afraid to follow Mario and Luigi on their adventures!",
|
|
credit = "Nintendo/CoopDX",
|
|
color = { r = 50, g = 50, b = 255 },
|
|
model = E_MODEL_TOAD_PLAYER,
|
|
ogModel = E_MODEL_TOAD_PLAYER,
|
|
baseChar = CT_TOAD,
|
|
lifeIcon = gTextures.toad_head,
|
|
starIcon = gTextures.star,
|
|
camScale = 0.8,
|
|
healthMeter = {
|
|
label = {
|
|
left = get_texture_info("char_select_toad_meter_left"),
|
|
right = get_texture_info("char_select_toad_meter_right"),
|
|
},
|
|
pie = defaultMeterInfo.pie
|
|
}
|
|
},
|
|
},
|
|
[CT_WALUIGI] = {
|
|
saveName = "Waluigi_CoopDX",
|
|
nickname = "Waluigi",
|
|
category = "All_CoopDX",
|
|
ogNum = CT_WALUIGI,
|
|
currAlt = 1,
|
|
hasMoveset = false,
|
|
locked = LOCKED_NEVER,
|
|
playtime = 0,
|
|
autoDialog = true,
|
|
replaceModels = {},
|
|
[1] = {
|
|
name = "Waluigi",
|
|
description = "The mischievous rival of Luigi! He's a narcissistic competitor that takes great taste in others getting pummeled from his success!",
|
|
credit = "Nintendo/CoopDX",
|
|
color = { r = 130, g = 25, b = 130 },
|
|
model = E_MODEL_WALUIGI,
|
|
ogModel = E_MODEL_WALUIGI,
|
|
baseChar = CT_WALUIGI,
|
|
lifeIcon = gTextures.waluigi_head,
|
|
starIcon = gTextures.star,
|
|
camScale = 1.1,
|
|
healthMeter = {
|
|
label = {
|
|
left = get_texture_info("char_select_waluigi_meter_left"),
|
|
right = get_texture_info("char_select_waluigi_meter_right"),
|
|
},
|
|
pie = defaultMeterInfo.pie
|
|
}
|
|
},
|
|
},
|
|
[CT_WARIO] = {
|
|
saveName = "Wario_CoopDX",
|
|
nickname = "Wario",
|
|
category = "All_CoopDX",
|
|
ogNum = CT_WARIO,
|
|
currAlt = 1,
|
|
hasMoveset = false,
|
|
locked = LOCKED_NEVER,
|
|
playtime = 0,
|
|
autoDialog = true,
|
|
replaceModels = {},
|
|
[1] = {
|
|
name = "Wario",
|
|
description = "The mischievous rival of Mario! He's a greed-filled treasure hunter obsessed with money and gold coins. He's always ready for a brawl if his money is on the line!",
|
|
credit = "Nintendo/CoopDX",
|
|
color = { r = 255, g = 255, b = 50 },
|
|
model = E_MODEL_WARIO,
|
|
ogModel = E_MODEL_WARIO,
|
|
baseChar = CT_WARIO,
|
|
lifeIcon = gTextures.wario_head,
|
|
starIcon = gTextures.star,
|
|
camScale = 1.1,
|
|
healthMeter = {
|
|
label = {
|
|
left = get_texture_info("char_select_wario_meter_left"),
|
|
right = get_texture_info("char_select_wario_meter_right"),
|
|
},
|
|
pie = defaultMeterInfo.pie
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
function character_is_vanilla(charNum)
|
|
if charNum == nil then charNum = currChar end
|
|
return charNum < CT_MAX
|
|
end
|
|
|
|
characterCategories = {
|
|
{name = "All", icon1 = CT_MARIO, icon2 = CT_LUIGI},
|
|
{name = "CoopDX", icon1 = CT_WARIO, icon2 = CT_WALUIGI},
|
|
}
|
|
|
|
local characterTableRender = {}
|
|
|
|
characterCaps = {}
|
|
characterColorPresets = {}
|
|
characterpeachletter = {} --the custom texture a character uses for peach's letter in the opening
|
|
characterAnims = {
|
|
[E_MODEL_MARIO] = {
|
|
anims = {[CS_ANIM_MENU] = MARIO_ANIM_CS_MENU},
|
|
eyes = {[CS_ANIM_MENU] = MARIO_EYES_LOOK_RIGHT},
|
|
},
|
|
[E_MODEL_LUIGI] = {
|
|
anims = {[CS_ANIM_MENU] = LUIGI_ANIM_CS_MENU},
|
|
eyes = {[CS_ANIM_MENU] = MARIO_EYES_LOOK_RIGHT},
|
|
hands = {[CS_ANIM_MENU] = MARIO_HAND_OPEN}
|
|
},
|
|
[E_MODEL_TOAD_PLAYER] = {
|
|
anims = {[CS_ANIM_MENU] = TOAD_PLAYER_ANIM_CS_MENU},
|
|
hands = {[CS_ANIM_MENU] = MARIO_HAND_OPEN}
|
|
},
|
|
[E_MODEL_WALUIGI] = {
|
|
anims = {[CS_ANIM_MENU] = WALUIGI_ANIM_CS_MENU},
|
|
eyes = {[CS_ANIM_MENU] = MARIO_EYES_LOOK_RIGHT},
|
|
},
|
|
[E_MODEL_WARIO] = {
|
|
anims = {[CS_ANIM_MENU] = WARIO_ANIM_CS_MENU},
|
|
eyes = {[CS_ANIM_MENU] = MARIO_EYES_LOOK_LEFT},
|
|
},
|
|
}
|
|
characterMovesets = {
|
|
[CT_MARIO] = {},
|
|
[CT_LUIGI] = {},
|
|
[CT_TOAD] = {},
|
|
[CT_WALUIGI] = {},
|
|
[CT_WARIO] = {},
|
|
}
|
|
characterUnlock = {}
|
|
characterInstrumentals = {}
|
|
characterGraffiti = {
|
|
[CT_MARIO] = get_texture_info("char_select_graffiti_mario"),
|
|
[CT_LUIGI] = get_texture_info("char_select_graffiti_luigi"),
|
|
[CT_TOAD] = get_texture_info("char_select_graffiti_toad"),
|
|
[CT_WALUIGI] = get_texture_info("char_select_graffiti_waluigi"),
|
|
[CT_WARIO] = get_texture_info("char_select_graffiti_wario"),
|
|
}
|
|
characterDialog = {}
|
|
|
|
tableRefNum = 0
|
|
local function make_table_ref_num()
|
|
tableRefNum = tableRefNum + 1
|
|
return tableRefNum
|
|
end
|
|
|
|
OPTION_MENU = "Menu"
|
|
OPTION_CHAR = "Character"
|
|
OPTION_MISC = "Misc"
|
|
OPTION_MOD = "Host"
|
|
OPTION_API = "Packs"
|
|
|
|
optionTableRef = {
|
|
-- Menu
|
|
openInputs = make_table_ref_num(),
|
|
notification = make_table_ref_num(),
|
|
menuColor = make_table_ref_num(),
|
|
music = make_table_ref_num(),
|
|
inputLatency = make_table_ref_num(),
|
|
-- Characters
|
|
localMoveset = make_table_ref_num(),
|
|
localVoices = make_table_ref_num(),
|
|
localVisuals = make_table_ref_num(),
|
|
-- CS
|
|
credits = make_table_ref_num(),
|
|
resetSaveData = make_table_ref_num(),
|
|
-- Moderation
|
|
--restrictPalettes = make_table_ref_num(),
|
|
restrictMovesets = make_table_ref_num(),
|
|
}
|
|
|
|
optionTable = {
|
|
[optionTableRef.openInputs] = {
|
|
name = "Menu Bind",
|
|
category = OPTION_MENU,
|
|
toggle = tonumber(mod_storage_load("MenuInput")),
|
|
toggleSaveName = "MenuInput",
|
|
toggleDefault = 1,
|
|
toggleMax = 2,
|
|
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] = {
|
|
name = "Notifications",
|
|
category = OPTION_MENU,
|
|
toggle = tonumber(mod_storage_load("notifs")),
|
|
toggleSaveName = "notifs",
|
|
toggleDefault = 1,
|
|
toggleMax = 2,
|
|
toggleNames = {"Off", "On", "Pop-ups Only"},
|
|
description = {"Toggles whether Pop-ups and", "Chat Messages display."}
|
|
},
|
|
[optionTableRef.menuColor] = {
|
|
name = "Menu Color",
|
|
category = OPTION_MENU,
|
|
toggle = tonumber(mod_storage_load("MenuColor")),
|
|
toggleSaveName = "MenuColor",
|
|
toggleDefault = 0,
|
|
toggleMax = 10,
|
|
toggleNames = {"Auto", "Saved", "Red", "Orange", "Yellow", "Green", "Blue", "Pink", "Purple", "White", "Black"},
|
|
description = {"Toggles the Menu Color"}
|
|
},
|
|
[optionTableRef.music] = {
|
|
name = "Menu Music",
|
|
category = OPTION_MENU,
|
|
toggle = tonumber(mod_storage_load("Music")),
|
|
toggleSaveName = "Music",
|
|
toggleDefault = 1,
|
|
toggleMax = 3,
|
|
toggleNames = {"Off", "On", "Breakroom Only", "Character Only"},
|
|
description = {"Toggles which music plays", "in the menu."}
|
|
},
|
|
[optionTableRef.inputLatency] = {
|
|
name = "Menu Scroll Speed",
|
|
category = OPTION_MENU,
|
|
toggle = tonumber(mod_storage_load("Latency")),
|
|
toggleSaveName = "Latency",
|
|
toggleDefault = 1,
|
|
toggleMax = 2,
|
|
toggleNames = {"Slow", "Normal", "Fast"},
|
|
description = {"Sets how fast you scroll", "throughout the Menu"}
|
|
},
|
|
[optionTableRef.localVoices] = {
|
|
name = "Character Voices",
|
|
category = OPTION_CHAR,
|
|
toggle = tonumber(mod_storage_load("localVoices")),
|
|
toggleSaveName = "localVoices",
|
|
toggleDefault = 1,
|
|
toggleMax = 2,
|
|
toggleNames = {"Off", "On", "Local Only"},
|
|
description = {"Toggle if Custom Voicelines play", "for Characters who support it"}
|
|
},
|
|
[optionTableRef.localVisuals] = {
|
|
name = "Character Visuals",
|
|
category = OPTION_CHAR,
|
|
toggle = tonumber(mod_storage_load("localVisuals")),
|
|
toggleSaveName = "localVisuals",
|
|
toggleDefault = 1,
|
|
toggleMax = 1,
|
|
description = {"Toggle if Characters can", "change how objects/textures appear"}
|
|
},
|
|
[optionTableRef.localMoveset] = {
|
|
name = "Character Moveset",
|
|
category = OPTION_CHAR,
|
|
toggle = tonumber(mod_storage_load("localMoveset")),
|
|
toggleSaveName = "localMoveset",
|
|
toggleDefault = 1,
|
|
toggleMax = 1,
|
|
description = {"Toggles if Custom Movesets", "are active on compatible", "characters"},
|
|
lock = function ()
|
|
if gGlobalSyncTable.charSelectRestrictMovesets ~= 0 then
|
|
return "Forced Off"
|
|
end
|
|
end,
|
|
},
|
|
[optionTableRef.credits] = {
|
|
name = "Credits",
|
|
category = OPTION_MISC,
|
|
toggle = 0,
|
|
toggleDefault = 0,
|
|
toggleMax = 1,
|
|
toggleNames = {"Open Credits", "Open Credits"},
|
|
description = {"Thank you for choosing", "Character Select!"}
|
|
},
|
|
[optionTableRef.resetSaveData] = {
|
|
name = "Reset Save Data",
|
|
category = OPTION_MISC,
|
|
toggle = 0,
|
|
toggleDefault = 0,
|
|
toggleMax = 1,
|
|
toggleNames = {"Reset Save Data", "Reset Save Data"},
|
|
description = {"Resets Character Select's", "Save Data"}
|
|
},
|
|
[optionTableRef.restrictMovesets] = {
|
|
name = "Restrict Movesets",
|
|
category = OPTION_MOD,
|
|
toggle = 0,
|
|
toggleDefault = 1,
|
|
toggleMax = 1,
|
|
description = {"Restricts turning on movesets", "(Host Only)"},
|
|
lock = function ()
|
|
if gGlobalSyncTable.charSelectRestrictMovesets < 2 then
|
|
if not network_is_server() then
|
|
return "Host Only"
|
|
end
|
|
else
|
|
return "API Only"
|
|
end
|
|
end,
|
|
},
|
|
}
|
|
|
|
local gridYOffset = 0
|
|
local function update_character_render_table()
|
|
gridYOffset = -100
|
|
local ogNum = currChar
|
|
local insertNum = 0
|
|
currChar = 0
|
|
currCharRender = 0
|
|
local category = characterCategories[currCategory]
|
|
if category == nil then return false end
|
|
characterTableRender = {}
|
|
for i = 0, #characterTable do
|
|
local charCategories = string_split(characterTable[i].category, "_")
|
|
if characterTable[i].locked ~= LOCKED_TRUE then
|
|
for c = 1, #charCategories do
|
|
if category.name == charCategories[c] then
|
|
characterTableRender[insertNum] = characterTable[i]
|
|
characterTableRender[insertNum].UIOffset = 0
|
|
if ogNum == i then
|
|
currChar = ogNum
|
|
currCharRender = num_wrap(insertNum, 0, #characterTableRender)
|
|
end
|
|
insertNum = insertNum + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #characterTableRender > 0 then
|
|
-- Get icons for category based on name similarity
|
|
if category.icon1 == nil or category.icon2 == nil then
|
|
local sorted = {}
|
|
for i = 0, #characterTableRender do
|
|
local char = characterTableRender[i]
|
|
table.insert(sorted, {ogNum = char.ogNum, sim = string_sim(char.saveName, category.name)})
|
|
end
|
|
table.sort(sorted, function(a, b)
|
|
log_to_console_once(tostring(a.ogNum) .. " - " .. tostring(a.sim), CONSOLE_MESSAGE_INFO)
|
|
log_to_console_once(tostring(b.ogNum) .. " - " .. tostring(b.sim), CONSOLE_MESSAGE_INFO)
|
|
return a.sim < b.sim
|
|
end)
|
|
category.icon1 = category.icon1 or sorted[1].ogNum
|
|
category.icon2 = category.icon2 or sorted[2].ogNum
|
|
end
|
|
-- Set Character if they are in the category
|
|
currChar = (characterTableRender[currCharRender] and characterTableRender[currCharRender].ogNum or characterTableRender[0].ogNum)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function force_set_character(charNum, charAlt)
|
|
if not charNum then charNum = gNetworkPlayers[0].modelIndex end
|
|
if not charAlt then charAlt = 1 end
|
|
currCategory = 1
|
|
currChar = charNum
|
|
characterTable[currChar].currAlt = charAlt
|
|
currCharRender = charNum
|
|
charBeingSet = true
|
|
update_character_render_table()
|
|
end
|
|
|
|
---@description A function that gets an option's status from the Character Select Options Menu
|
|
---@param tableNum integer The table position of the option
|
|
---@return number?
|
|
function get_options_status(tableNum)
|
|
if type(tableNum) ~= TYPE_INTEGER then return nil end
|
|
return optionTable[tableNum].toggle
|
|
end
|
|
|
|
---@class Credits
|
|
---@field packName string
|
|
|
|
---@class Credit
|
|
---@field creditee string
|
|
---@field credit string
|
|
|
|
---@type Credits[]
|
|
creditTable = {
|
|
[1] = {
|
|
packName = "Character Select Coop",
|
|
{ creditee = "Squishy6094", credit = "Creator" },
|
|
{ creditee = "JerThePear", credit = "Menu Assets/Anims" },
|
|
{ creditee = "Trashcam", credit = "Menu Music" },
|
|
{ creditee = "Charity", credit = "Sound Design" },
|
|
{ creditee = "WinbowBreaker", credit = "Menu Asset Renders" },
|
|
{ creditee = "xLuigiGamerx", credit = "HUD Accuracy" },
|
|
{ creditee = "Wibblus", credit = "Menu Anims Code" },
|
|
}
|
|
}
|
|
|
|
if CREDIT_SUPPORTERS ~= nil then
|
|
creditTable[0] = {
|
|
packName = "Character Select Supporters",
|
|
}
|
|
for i = 1, #CREDIT_SUPPORTERS do
|
|
table.insert(creditTable[0], { creditee = CREDIT_SUPPORTERS[i]})
|
|
end
|
|
end
|
|
|
|
local latencyValueTable = {12, 6, 3}
|
|
|
|
local menuColorTable = {
|
|
{ r = 255, g = 50, b = 50 },
|
|
{ r = 255, g = 100, b = 50 },
|
|
{ r = 255, g = 255, b = 50 },
|
|
{ r = 50, g = 255, b = 50 },
|
|
{ r = 50, g = 50, b = 255 },
|
|
{ r = 251, g = 148, b = 220 },
|
|
{ r = 130, g = 25, b = 130 },
|
|
{ r = 255, g = 255, b = 255 },
|
|
{ r = 50, g = 50, b = 50 }
|
|
}
|
|
|
|
---@param m MarioState
|
|
local function nullify_inputs(m)
|
|
local c = m.controller
|
|
_G.charSelect.controller = {
|
|
buttonDown = c.buttonDown,
|
|
buttonPressed = c.buttonPressed & ~_G.charSelect.controller.buttonDown,
|
|
extStickX = c.extStickX,
|
|
extStickY = c.extStickY,
|
|
rawStickX = c.rawStickX,
|
|
rawStickY = c.rawStickY,
|
|
stickMag = c.stickMag,
|
|
stickX = c.stickX,
|
|
stickY = c.stickY
|
|
}
|
|
c.buttonDown = 0
|
|
c.buttonPressed = 0
|
|
c.extStickX = 0
|
|
c.extStickY = 0
|
|
c.rawStickX = 0
|
|
c.rawStickY = 0
|
|
c.stickMag = 0
|
|
c.stickX = 0
|
|
c.stickY = 0
|
|
end
|
|
|
|
local prefCharColor = {r = 255, g = 50, b = 50}
|
|
|
|
local function load_preferred_char()
|
|
local m = gMarioStates[0]
|
|
local savedChar = mod_storage_load("PrefChar")
|
|
local savedNick = mod_storage_load("PrefNick")
|
|
local savedAlt = tonumber(mod_storage_load("PrefAlt"))
|
|
local savedPalette = tonumber(mod_storage_load("PrefPalette"))
|
|
if savedChar == nil or savedChar == "" then
|
|
mod_storage_save("PrefChar", characterTable[bootChar].saveName)
|
|
savedChar = characterTable[bootChar].saveName
|
|
end
|
|
if savedAlt == nil then
|
|
mod_storage_save("PrefAlt", "1")
|
|
savedAlt = 1
|
|
end
|
|
if savedPalette == nil then
|
|
local paletteSave = 0
|
|
mod_storage_save("PrefAlt", tostring(paletteSave))
|
|
savedPalette = paletteSave
|
|
end
|
|
|
|
-- Find Saved Character
|
|
local charFound = false
|
|
for i = 0, #characterTable do
|
|
local char = characterTable[i]
|
|
if char.saveName == savedChar and char.locked ~= LOCKED_TRUE then
|
|
currChar = i
|
|
currCharRender = i
|
|
charFound = true
|
|
if optionTable[optionTableRef.notification].toggle > 0 then
|
|
djui_popup_create('Character Select:\nYour Preferred Character\n"' .. string_underscore_to_space(char[char.currAlt].name) .. '"\nwas applied successfully!', 4)
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Set Alt
|
|
savedAlt = math.clamp(savedAlt, 1, #characterTable[currChar])
|
|
characterTable[currChar].currAlt = savedAlt
|
|
|
|
-- Set Palette
|
|
local model = characterTable[currChar][savedAlt].model
|
|
if characterColorPresets[model] ~= nil then
|
|
gCSPlayers[0].presetPalette = charFound and savedPalette or 0
|
|
characterColorPresets[model].currPalette = gCSPlayers[0].presetPalette
|
|
end
|
|
|
|
local savedCharColors = mod_storage_load("PrefCharColor")
|
|
if savedCharColors ~= nil and savedCharColors ~= "" then
|
|
local savedCharColorsTable = string_split(savedCharColors, "_")
|
|
prefCharColor = {
|
|
r = tonumber(savedCharColorsTable[1]),
|
|
g = tonumber(savedCharColorsTable[2]),
|
|
b = tonumber(savedCharColorsTable[3])
|
|
}
|
|
else
|
|
mod_storage_save("PrefCharColor", "255_50_50")
|
|
end
|
|
|
|
if #characterTable < CT_MAX then
|
|
if optionTable[optionTableRef.notification].toggle > 0 then
|
|
djui_popup_create("Character Select:\nNo Characters were Found", 2)
|
|
end
|
|
else
|
|
if not charFound then
|
|
if savedNick ~= nil then
|
|
djui_popup_create('Character Select:\nYour Preferred Character\n"' .. string_underscore_to_space(savedNick) .. '"\nwas not found.', 4)
|
|
else
|
|
djui_popup_create('Character Select:\nYour Preferred Character\nwas not found.', 3)
|
|
end
|
|
end
|
|
end
|
|
TEXT_PREF_LOAD_NAME = string_space_to_underscore(savedNick or savedChar)
|
|
TEXT_PREF_LOAD_ALT = savedAlt
|
|
update_character_render_table()
|
|
end
|
|
|
|
local function mod_storage_save_pref_char(charTable)
|
|
charTable = charTable or characterTable[bootChar]
|
|
mod_storage_save("PrefChar", charTable.saveName)
|
|
mod_storage_save("PrefNick", string_space_to_underscore(charTable.nickname))
|
|
mod_storage_save("PrefAlt", tostring(charTable.currAlt))
|
|
mod_storage_save("PrefPalette", tostring(gCSPlayers[0].presetPalette))
|
|
mod_storage_save("PrefCharColor", tostring(charTable[charTable.currAlt].color.r) .. "_" .. tostring(charTable[charTable.currAlt].color.g) .. "_" .. tostring(charTable[charTable.currAlt].color.b))
|
|
TEXT_PREF_LOAD_NAME = string_space_to_underscore(charTable.nickname)
|
|
TEXT_PREF_LOAD_ALT = charTable.currAlt
|
|
prefCharColor = charTable[charTable.currAlt].color
|
|
end
|
|
|
|
function failsafe_options()
|
|
for i = 1, #optionTable do
|
|
if optionTable[i].toggle == nil or optionTable[i].toggle == "" then
|
|
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
|
|
end
|
|
if optionTable[i].toggleNames == nil then
|
|
optionTable[i].toggleNames = {"Off", "On"}
|
|
end
|
|
end
|
|
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
|
|
|
|
hookTableOnReset = {}
|
|
local promptedAreYouSure = false
|
|
local function reset_options(wasChatTriggered)
|
|
if not promptedAreYouSure then
|
|
djui_chat_message_create("\\#ffdcdc\\Are you sure you want to reset your Save Data for Character Select, including your Preferred Character\nand Settings?\n" .. (wasChatTriggered and "Type \\#ff3333\\/char-select reset\\#ffdcdc\\ to confirm." or "Press the \\#ff3333\\" .. optionTable[optionTableRef.resetSaveData].name .. "\\#ffdcdc\\ Option again to confirm." ))
|
|
promptedAreYouSure = true
|
|
else
|
|
for i = 1, #optionTable do
|
|
optionTable[i].toggle = optionTable[i].toggleDefault
|
|
if optionTable[i].toggleSaveName ~= nil then
|
|
mod_storage_save(optionTable[i].toggleSaveName, tostring(optionTable[i].toggle))
|
|
end
|
|
if optionTable[i].toggleNames == nil then
|
|
optionTable[i].toggleNames = { "Off", "On" }
|
|
end
|
|
end
|
|
for i = 0, #characterTable do
|
|
characterTable[i].currAlt = 1
|
|
characterTable[i].locked = characterTable[i].locked == LOCKED_NEVER and LOCKED_NEVER or LOCKED_TRUE
|
|
end
|
|
mod_storage_save_pref_char()
|
|
|
|
if #hookTableOnReset > 0 then
|
|
for i = 1, #hookTableOnReset do
|
|
hookTableOnReset[i]()
|
|
end
|
|
end
|
|
|
|
force_set_character()
|
|
|
|
djui_chat_message_create("\\#ff3333\\Character Select Save Data Reset!")
|
|
djui_chat_message_create("Note: If your issue has not been resolved, you may need to manually delete your save data via the directory below:\n\\#dcdcFF\\%appdata%/sm64coopdx/sav/character-select-coop.sav")
|
|
promptedAreYouSure = false
|
|
end
|
|
end
|
|
|
|
local function boot_note()
|
|
local charCount = (#characterTable + 1) - CT_MAX
|
|
if charCount > 0 then
|
|
djui_chat_message_create("Character Select has " .. charCount .. " character" .. (charCount > 1 and "s" or "") .." available!\nYou can use \\#ffff33\\/char-select \\#ffffff\\to open the menu!")
|
|
if charCount > 32 and network_is_server() then
|
|
djui_chat_message_create("\\#FFAAAA\\Warning: Having a lot of characters\nmay be unstable, For a better experience please\ndisable a few packs!")
|
|
end
|
|
else
|
|
djui_chat_message_create("Character Select is active!\nYou can use \\#ffff33\\/char-select \\#ffffff\\to open the menu!")
|
|
end
|
|
end
|
|
|
|
local function menu_is_allowed(m)
|
|
if m == nil then m = gMarioStates[0] end
|
|
-- API Check
|
|
for _, func in pairs(allowMenu) do
|
|
if not func() then
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- C-up Failsafe (Camera Softlocks)
|
|
if m.action == ACT_FIRST_PERSON or (m.prevAction == ACT_FIRST_PERSON and is_game_paused()) then
|
|
return false
|
|
elseif m.prevAction == ACT_FIRST_PERSON and not is_game_paused() then
|
|
m.prevAction = ACT_WALKING
|
|
end
|
|
|
|
-- Cutscene Check
|
|
if m.action & ACT_GROUP_CUTSCENE ~= 0 then return false end
|
|
if gNetworkPlayers[0].currActNum == 99 then return false end
|
|
if m.action == ACT_INTRO_CUTSCENE then return false end
|
|
if obj_get_first_with_behavior_id(id_bhvActSelector) ~= nil then return false end
|
|
|
|
return true
|
|
end
|
|
|
|
hookTableOnCharacterChange = {
|
|
[1] = function (prevChar, currChar)
|
|
-- Check for Non-Vanilla Actions when switching Characters
|
|
local m = gMarioStates[0]
|
|
if is_mario_in_vanilla_action(m) or m.health < 256 then return end
|
|
if m.action & ACT_FLAG_RIDING_SHELL ~= 0 then
|
|
set_mario_action(m, ACT_RIDING_SHELL_FALL, 0)
|
|
elseif m.action & ACT_FLAG_ALLOW_FIRST_PERSON ~= 0 then
|
|
set_mario_action(m, ACT_IDLE, 0)
|
|
elseif m.action & ACT_GROUP_MOVING ~= 0 or m.action & ACT_FLAG_MOVING ~= 0 then
|
|
set_mario_action(m, ACT_WALKING, 0)
|
|
elseif m.action & ACT_GROUP_SUBMERGED ~= 0 or m.action & ACT_FLAG_SWIMMING ~= 0 then
|
|
-- Need to fix upwarping
|
|
set_mario_action(m, ACT_WATER_IDLE, 0)
|
|
else
|
|
set_mario_action(m, ACT_FREEFALL, 0)
|
|
end
|
|
|
|
-- Switch all models to either Vanilla or the Character's
|
|
set_all_models()
|
|
end
|
|
}
|
|
|
|
local function on_character_change(prevChar, currChar)
|
|
for i = 1, #hookTableOnCharacterChange do
|
|
hookTableOnCharacterChange[i](prevChar, currChar)
|
|
end
|
|
end
|
|
|
|
-------------------
|
|
-- Model Handler --
|
|
-------------------
|
|
|
|
-- Port of SM64's idle action without transistion bs
|
|
local ACT_CS_MENU_IDLE = allocate_mario_action(ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT)
|
|
---@param m MarioState
|
|
local function act_cs_menu_idle(m)
|
|
if not m then return 0 end
|
|
local p = gCSPlayers[m.playerIndex]
|
|
if (m.quicksandDepth > 30.0) then
|
|
return set_mario_action(m, ACT_IN_QUICKSAND, 0);
|
|
end
|
|
|
|
if (check_common_idle_cancels(m) ~= 0) then
|
|
return 1;
|
|
end
|
|
|
|
m.actionState = 0
|
|
m.actionTimer = 0
|
|
|
|
local customIdleExists = (characterAnims[p.modelId] and characterAnims[p.modelId].anims and characterAnims[p.modelId].anims[CS_ANIM_MENU])
|
|
set_character_animation(m, customIdleExists and CS_ANIM_MENU or CHAR_ANIM_FIRST_PERSON)
|
|
|
|
stationary_ground_step(m);
|
|
|
|
return 0
|
|
end
|
|
hook_mario_action(ACT_CS_MENU_IDLE, act_cs_menu_idle)
|
|
|
|
CUTSCENE_CS_MENU = 0xFA
|
|
|
|
local prevBaseCharFrame = gNetworkPlayers[0].modelIndex
|
|
local prevBasePalette = {
|
|
[PANTS] = network_player_get_palette_color(gNetworkPlayers[0], PANTS),
|
|
[SHIRT] = network_player_get_palette_color(gNetworkPlayers[0], SHIRT),
|
|
[GLOVES] = network_player_get_palette_color(gNetworkPlayers[0], GLOVES),
|
|
[SHOES] = network_player_get_palette_color(gNetworkPlayers[0], SHOES),
|
|
[HAIR] = network_player_get_palette_color(gNetworkPlayers[0], HAIR),
|
|
[SKIN] = network_player_get_palette_color(gNetworkPlayers[0], SKIN),
|
|
[CAP] = network_player_get_palette_color(gNetworkPlayers[0], CAP),
|
|
[EMBLEM] = network_player_get_palette_color(gNetworkPlayers[0], EMBLEM),
|
|
}
|
|
local worldColor = {
|
|
lighting = {r = 255, g = 255, b = 255},
|
|
skybox = {r = 255, g = 255, b = 255},
|
|
fog = {r = 255, g = 255, b = 255},
|
|
vertex = {r = 255, g = 255, b = 255},
|
|
ambient = {r = 255, g = 255, b = 255}
|
|
}
|
|
local menuOffsetX = 0
|
|
local menuOffsetY = 0
|
|
local camScale = 1
|
|
local prevMusicToggle = 1
|
|
local prevVisualToggle = 1
|
|
---@param m MarioState
|
|
local function mario_update(m)
|
|
if m.playerIndex == 0 and (startup_init_stall(1) or queueStorageFailsafe) then
|
|
failsafe_options()
|
|
if not queueStorageFailsafe then
|
|
load_preferred_char()
|
|
if optionTable[optionTableRef.notification].toggle == 1 then
|
|
boot_note()
|
|
end
|
|
end
|
|
set_all_models()
|
|
queueStorageFailsafe = false
|
|
end
|
|
|
|
local np = gNetworkPlayers[m.playerIndex]
|
|
local p = gCSPlayers[m.playerIndex]
|
|
|
|
if m.playerIndex == 0 then
|
|
-- Used for Viewport stuffs
|
|
m.marioObj.header.gfx.sharedChild.hookProcess = 1
|
|
|
|
-- Check for Locked Chars
|
|
for i = CT_MAX, #characterTable do
|
|
local char = characterTable[i]
|
|
if char.locked ~= LOCKED_NEVER then
|
|
local unlock = characterUnlock[i].check
|
|
local notif = characterUnlock[i].notif
|
|
local prevLockState = char.locked
|
|
if type(unlock) == TYPE_FUNCTION then
|
|
char.locked = (unlock() ~= false) and LOCKED_FALSE or LOCKED_TRUE
|
|
elseif type(unlock) == TYPE_BOOLEAN then
|
|
char.locked = (unlock ~= false) and LOCKED_FALSE or LOCKED_TRUE
|
|
end
|
|
if char.locked ~= prevLockState then
|
|
update_character_render_table()
|
|
if prevLockState == LOCKED_TRUE then -- Character was unlocked
|
|
if startup_init_stall() and notif then
|
|
if optionTable[optionTableRef.notification].toggle > 0 then
|
|
djui_popup_create('Character Select:\nUnlocked '..tostring(char[1].name)..'\nas a Playable Character!', 3)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if djui_hud_is_pause_menu_created() then
|
|
if prevBaseCharFrame ~= np.modelIndex then
|
|
force_set_character(np.modelIndex)
|
|
p.presetPalette = 0
|
|
end
|
|
|
|
if gCSPlayers[0].presetPalette ~= 0 then
|
|
for i = PANTS, EMBLEM do
|
|
local prevColor = prevBasePalette[i]
|
|
local currColor = network_player_get_palette_color(np, i)
|
|
if prevColor.r ~= currColor.r or prevColor.g ~= currColor.g or prevColor.b ~= currColor.b then
|
|
local model = characterTable[currChar][characterTable[currChar].currAlt].model
|
|
gCSPlayers[0].presetPalette = 0
|
|
characterColorPresets[model].currPalette = 0
|
|
prevColor.r = currColor.r
|
|
prevColor.g = currColor.g
|
|
prevColor.b = currColor.b
|
|
end
|
|
end
|
|
end
|
|
end
|
|
prevBaseCharFrame = np.modelIndex
|
|
|
|
local charTable = characterTable[currChar]
|
|
p.saveName = charTable.saveName
|
|
p.currAlt = charTable.currAlt
|
|
|
|
p.modelId = charTable[charTable.currAlt].model
|
|
if charTable[charTable.currAlt].baseChar ~= nil then
|
|
p.baseChar = charTable[charTable.currAlt].baseChar
|
|
end
|
|
p.modelEditOffset = charTable[charTable.currAlt].model - charTable[charTable.currAlt].ogModel
|
|
m.marioObj.hookRender = 1
|
|
|
|
if menu and m.action == ACT_SLEEPING then
|
|
set_mario_action(m, ACT_WAKING_UP, m.actionArg)
|
|
end
|
|
|
|
if menu and options == OPTIONS_MAIN then
|
|
if (network_is_server() or network_is_moderator()) and gGlobalSyncTable.charSelectRestrictMovesets < 2 then
|
|
gGlobalSyncTable.charSelectRestrictMovesets = optionTable[optionTableRef.restrictMovesets].toggle
|
|
end
|
|
else
|
|
optionTable[optionTableRef.restrictMovesets].toggle = gGlobalSyncTable.charSelectRestrictMovesets
|
|
end
|
|
|
|
if prevVisualToggle ~= optionTable[optionTableRef.localVisuals].toggle then
|
|
set_all_models()
|
|
prevVisualToggle = optionTable[optionTableRef.localVisuals].toggle
|
|
end
|
|
|
|
if menuAndTransition then
|
|
local musicToggle = optionTable[optionTableRef.music].toggle
|
|
local charInst = characterInstrumentals[currChar]
|
|
if not p.inMenu or prevMusicToggle ~= musicToggle or prevChar ~= currChar then
|
|
local levelMusic = false
|
|
if musicToggle == 0 then
|
|
levelMusic = true
|
|
end
|
|
audio_stream_play(SOUND_CHAR_SELECT_THEME, false, 0)
|
|
if musicToggle ~= 0 and musicToggle ~= 3 then
|
|
menuThemeTargetVolume = 1
|
|
levelMusic = false
|
|
else
|
|
menuThemeTargetVolume = 0
|
|
end
|
|
|
|
-- Set Target Volumes
|
|
for i = 0, #characterTable do
|
|
local charInst = characterInstrumentals[i]
|
|
if charInst ~= nil then
|
|
audio_stream_play(charInst.audio, false, 1)
|
|
charInst.targetVolume = 0
|
|
end
|
|
end
|
|
if musicToggle ~= 0 and musicToggle ~= 2 then
|
|
if charInst ~= nil then
|
|
charInst.targetVolume = 1
|
|
levelMusic = false
|
|
elseif menuThemeTargetVolume == 0 then
|
|
levelMusic = true
|
|
end
|
|
end
|
|
|
|
if levelMusic then
|
|
stop_secondary_music(50)
|
|
else
|
|
play_secondary_music(0, 0, 0, 50)
|
|
end
|
|
prevMusicToggle = musicToggle
|
|
p.inMenu = true
|
|
end
|
|
|
|
-- Update Volumes
|
|
menuThemeVolume = math.lerp(menuThemeVolume, menuThemeTargetVolume, 0.1)
|
|
audio_stream_set_volume(SOUND_CHAR_SELECT_THEME, menuThemeVolume)
|
|
|
|
for i = 0, #characterTable do
|
|
local charInst = characterInstrumentals[i]
|
|
if charInst ~= nil then
|
|
charInst.volume = math.lerp(charInst.volume, charInst.targetVolume, 0.1)
|
|
audio_stream_set_volume(charInst.audio, charInst.volume)
|
|
end
|
|
end
|
|
|
|
camera_freeze()
|
|
hud_hide()
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
local widthScale = djui_hud_get_screen_width()/320
|
|
if m.area.camera.cutscene == 0 then
|
|
m.area.camera.cutscene = CUTSCENE_CS_MENU
|
|
end
|
|
m.marioBodyState.eyeState = MARIO_EYES_OPEN
|
|
camScale = math.lerp(camScale, charTable[charTable.currAlt].camScale, 0.1)
|
|
local camDist = 400 * camScale
|
|
local camAngle = m.faceAngle.y + 0x800
|
|
local camOffsetX = mirror_mode_number(-menuOffsetX)
|
|
local focusPos = {
|
|
x = m.pos.x + sins(camAngle - 0x4000)*camOffsetX*camScale,
|
|
y = m.pos.y + (100 - menuOffsetY) * camScale ,
|
|
z = m.pos.z + coss(camAngle - 0x4000)*camOffsetX*camScale,
|
|
}
|
|
vec3f_copy(gLakituState.focus, focusPos)
|
|
|
|
local camPos = {
|
|
x = (m.pos.x + sins(camAngle) * camDist + sins(camAngle - 0x4000)*camOffsetX),
|
|
y = m.pos.y - (menuOffsetY*camScale),
|
|
z = (m.pos.z + coss(camAngle) * camDist + sins(camAngle - 0x4000)*camOffsetX),
|
|
}
|
|
camPos.y = collision_find_surface_on_ray(camPos.x, camPos.y + 300, camPos.z, 0, -300, 0).hitPos.y + 20
|
|
|
|
local camHit = collision_find_surface_on_ray(focusPos.x, focusPos.y, focusPos.z, camPos.x - focusPos.x, camPos.y - focusPos.y, camPos.z - focusPos.z).hitPos
|
|
vec3f_copy(gLakituState.pos, camHit)
|
|
set_override_fov(45/widthScale)
|
|
|
|
set_lighting_color(0, (menuColor.r*0.33 + 255*0.66) * worldColor.lighting.r/255)
|
|
set_lighting_color(1, (menuColor.g*0.33 + 255*0.66) * worldColor.lighting.g/255)
|
|
set_lighting_color(2, (menuColor.b*0.33 + 255*0.66) * worldColor.lighting.b/255)
|
|
set_lighting_color_ambient(0, (menuColor.r*0.33 + 255*0.66) * worldColor.ambient.r/127)
|
|
set_lighting_color_ambient(1, (menuColor.g*0.33 + 255*0.66) * worldColor.ambient.g/127)
|
|
set_lighting_color_ambient(2, (menuColor.b*0.33 + 255*0.66) * worldColor.ambient.b/127)
|
|
set_skybox_color(0, menuColor.r * worldColor.lighting.r/255)
|
|
set_skybox_color(1, menuColor.g * worldColor.lighting.g/255)
|
|
set_skybox_color(2, menuColor.b * worldColor.lighting.b/255)
|
|
set_fog_color(0, menuColor.r * worldColor.lighting.r/255)
|
|
set_fog_color(1, menuColor.g * worldColor.lighting.g/255)
|
|
set_fog_color(2, menuColor.b * worldColor.lighting.b/255)
|
|
set_vertex_color(0, menuColor.r * worldColor.lighting.r/255)
|
|
set_vertex_color(1, menuColor.g * worldColor.lighting.g/255)
|
|
set_vertex_color(2, menuColor.b * worldColor.lighting.b/255)
|
|
else
|
|
if p.inMenu then
|
|
audio_stream_stop(SOUND_CHAR_SELECT_THEME)
|
|
for i = 0, #characterTable do
|
|
local charInst = characterInstrumentals[i]
|
|
if charInst ~= nil then
|
|
audio_stream_stop(charInst.audio)
|
|
end
|
|
end
|
|
stop_secondary_music(50)
|
|
m.marioObj.header.gfx.sharedChild.hookProcess = 1
|
|
camera_unfreeze()
|
|
hud_show()
|
|
set_override_fov(0)
|
|
if m.area.camera.cutscene == CUTSCENE_CS_MENU then
|
|
m.area.camera.cutscene = CUTSCENE_STOP
|
|
end
|
|
set_lighting_color(0, worldColor.lighting.r)
|
|
set_lighting_color(1, worldColor.lighting.g)
|
|
set_lighting_color(2, worldColor.lighting.b)
|
|
set_lighting_color_ambient(0, worldColor.ambient.r)
|
|
set_lighting_color_ambient(1, worldColor.ambient.g)
|
|
set_lighting_color_ambient(2, worldColor.ambient.b)
|
|
set_skybox_color(0, worldColor.skybox.r)
|
|
set_skybox_color(1, worldColor.skybox.g)
|
|
set_skybox_color(2, worldColor.skybox.b)
|
|
set_fog_color(0, worldColor.fog.r)
|
|
set_fog_color(1, worldColor.fog.g)
|
|
set_fog_color(2, worldColor.fog.b)
|
|
set_vertex_color(0, worldColor.vertex.r)
|
|
set_vertex_color(1, worldColor.vertex.g)
|
|
set_vertex_color(2, worldColor.vertex.b)
|
|
p.inMenu = false
|
|
end
|
|
|
|
worldColor.lighting.r = get_lighting_color(0)
|
|
worldColor.lighting.g = get_lighting_color(1)
|
|
worldColor.lighting.b = get_lighting_color(2)
|
|
worldColor.ambient.r = get_lighting_color_ambient(0)
|
|
worldColor.ambient.g = get_lighting_color_ambient(1)
|
|
worldColor.ambient.b = get_lighting_color_ambient(2)
|
|
worldColor.skybox.r = get_skybox_color(0)
|
|
worldColor.skybox.g = get_skybox_color(1)
|
|
worldColor.skybox.b = get_skybox_color(2)
|
|
worldColor.fog.r = get_fog_color(0)
|
|
worldColor.fog.g = get_fog_color(1)
|
|
worldColor.fog.b = get_fog_color(2)
|
|
worldColor.vertex.r = get_vertex_color(0)
|
|
worldColor.vertex.g = get_vertex_color(1)
|
|
worldColor.vertex.b = get_vertex_color(2)
|
|
|
|
if startup_init_stall() then
|
|
-- Update playtime
|
|
characterTable[currChar].playtime = characterTable[currChar].playtime + 1
|
|
totalPlaytime = totalPlaytime + 1
|
|
end
|
|
end
|
|
|
|
--Open Credits
|
|
if optionTable[optionTableRef.credits].toggle > 0 then
|
|
options = OPTIONS_CREDITS
|
|
currOption = 1
|
|
optionTable[optionTableRef.credits].toggle = 0
|
|
end
|
|
|
|
--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
|
|
|
|
p.movesetToggle = optionTable[optionTableRef.localMoveset].toggle ~= 0
|
|
if prevChar ~= currChar then
|
|
on_character_change(prevChar, currChar)
|
|
prevChar = currChar
|
|
end
|
|
end
|
|
|
|
if p.inMenu and m.action & ACT_FLAG_ALLOW_FIRST_PERSON ~= 0 then
|
|
set_mario_action(m, ACT_CS_MENU_IDLE, 0)
|
|
m.actionArg = 0
|
|
m.actionState = 0xFFFF
|
|
|
|
-- reset menu anim on character change, starts them at frame 0 and prevents lua anim issues
|
|
if p.prevModelId ~= p.modelId then
|
|
p.prevModelId = p.modelId
|
|
m.marioObj.header.gfx.animInfo.animID = -1
|
|
end
|
|
|
|
m.marioObj.header.gfx.angle.y = m.faceAngle.y
|
|
elseif m.action == ACT_CS_MENU_IDLE then
|
|
set_mario_action(m, ACT_IDLE, 0)
|
|
end
|
|
|
|
np.overrideModelIndex = p.baseChar ~= nil and p.baseChar or CT_MARIO
|
|
|
|
-- Character Animations
|
|
if characterAnims[p.modelId] then
|
|
local animInfo = m.marioObj.header.gfx.animInfo
|
|
local animID = characterAnims[p.modelId].anims and run_func_or_get_var(characterAnims[p.modelId].anims[animInfo.animID], m, animInfo.animFrame)
|
|
if animID then
|
|
smlua_anim_util_set_animation(m.marioObj, animID)
|
|
end
|
|
local eyeState = characterAnims[p.modelId].eyes and run_func_or_get_var(characterAnims[p.modelId].eyes[animInfo.animID], m, animInfo.animFrame)
|
|
if eyeState then
|
|
m.marioBodyState.eyeState = eyeState
|
|
end
|
|
local handState = characterAnims[p.modelId].hands and run_func_or_get_var(characterAnims[p.modelId].hands[animInfo.animID], m, animInfo.animFrame)
|
|
if handState then
|
|
m.marioBodyState.handState = handState
|
|
end
|
|
end
|
|
end
|
|
|
|
function geo_function()
|
|
local viewport = geo_get_current_root()
|
|
if menuAndTransition then
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
viewport.x = 320*0.85
|
|
viewport.y = 205*0.5
|
|
viewport.width = 320*0.15
|
|
viewport.height = 205*0.5
|
|
else
|
|
viewport.x = 320*0.5
|
|
viewport.y = 240*0.5
|
|
viewport.width = 320*0.5
|
|
viewport.height = 240*0.5
|
|
end
|
|
end
|
|
|
|
hook_event(HOOK_ON_GEO_PROCESS, geo_function)
|
|
|
|
local sCapBhvs = {
|
|
[id_bhvNormalCap] = true,
|
|
[id_bhvWingCap] = true,
|
|
[id_bhvVanishCap] = true,
|
|
[id_bhvMetalCap] = true,
|
|
}
|
|
|
|
define_custom_obj_fields({
|
|
oOriginalModel = 'u32',
|
|
oModelHasBeenReplaced = 'u32',
|
|
})
|
|
|
|
---@param o Object
|
|
function set_model(o, model)
|
|
-- Extended Model Incompatible
|
|
if obj_get_model_id_extended(o) == E_MODEL_ERROR_MODEL then return end
|
|
|
|
local visualToggle = optionTable[optionTableRef.localVisuals].toggle == 1
|
|
|
|
-- Player Models
|
|
if obj_has_behavior_id(o, id_bhvMario) ~= 0 then
|
|
local i = network_local_index_from_global(o.globalPlayerIndex)
|
|
local localModelData = nil
|
|
for c = 0, #characterTable do
|
|
if gCSPlayers[i].saveName == characterTable[c].saveName then
|
|
if gCSPlayers[i].currAlt <= #characterTable[c] then
|
|
localModelData = characterTable[c][gCSPlayers[i].currAlt].ogModel + gCSPlayers[i].modelEditOffset
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if localModelData ~= nil then
|
|
if obj_has_model_extended(o, localModelData) == 0 then
|
|
obj_set_model_extended(o, localModelData)
|
|
end
|
|
else
|
|
-- Original/Backup
|
|
if gCSPlayers[i].modelId ~= nil and obj_has_model_extended(o, gCSPlayers[i].modelId) == 0 then
|
|
obj_set_model_extended(o, gCSPlayers[i].modelId)
|
|
end
|
|
end
|
|
return
|
|
elseif sCapBhvs[get_id_from_behavior(o.behavior)] then -- Cap Behaviors
|
|
local playerToObj = nearest_player_to_object(o.parentObj)
|
|
o.globalPlayerIndex = playerToObj and playerToObj.globalPlayerIndex or 0
|
|
|
|
local i = network_local_index_from_global(o.globalPlayerIndex)
|
|
|
|
local c = gMarioStates[i].character
|
|
if model == c.capModelId or
|
|
model == c.capWingModelId or
|
|
model == c.capMetalModelId or
|
|
model == c.capMetalWingModelId then
|
|
local capModels = characterCaps[gCSPlayers[i].modelId]
|
|
if capModels ~= nil then
|
|
local capModel = E_MODEL_NONE
|
|
if model == c.capModelId then
|
|
capModel = capModels.normal
|
|
elseif model == c.capWingModelId then
|
|
capModel = capModels.wing
|
|
elseif model == c.capMetalModelId then
|
|
capModel = capModels.metal
|
|
elseif model == c.capMetalWingModelId then
|
|
capModel = capModels.metalWing
|
|
end
|
|
if capModel ~= E_MODEL_NONE and capModel ~= E_MODEL_ERROR_MODEL and capModel ~= nil then
|
|
if obj_has_model_extended(o, capModel) == 0 then
|
|
obj_set_model_extended(o, capModel)
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
elseif characterTable[currChar].replaceModels ~= nil then -- Other Custom Models
|
|
local currReplace = characterTable[currChar].replaceModels[get_id_from_behavior(o.behavior)]
|
|
if o.oOriginalModel == 0 then
|
|
o.oOriginalModel = obj_get_model_id_extended(o)
|
|
end
|
|
|
|
local model = run_func_or_get_var(currReplace, o, o.oOriginalModel)
|
|
|
|
if model ~= nil and visualToggle then
|
|
o.oModelHasBeenReplaced = 1
|
|
if obj_has_model_extended(o, model) == 0 then
|
|
obj_set_model_extended(o, model)
|
|
end
|
|
elseif o.oModelHasBeenReplaced ~= 0 then
|
|
if obj_has_model_extended(o, o.oOriginalModel) == 0 then
|
|
obj_set_model_extended(o, o.oOriginalModel)
|
|
end
|
|
end
|
|
return
|
|
end
|
|
end
|
|
|
|
function set_all_models()
|
|
for i = 0, NUM_OBJ_LISTS - 1 do
|
|
local o = obj_get_first(i)
|
|
repeat
|
|
if o ~= nil then
|
|
set_model(o, o.oOriginalModel)
|
|
end
|
|
o = obj_get_next(o)
|
|
until o == nil
|
|
end
|
|
end
|
|
|
|
local function koopa_model_update(o)
|
|
if o.oKoopaMovementType == KOOPA_BP_UNSHELLED then
|
|
o.oOriginalModel = E_MODEL_KOOPA_WITHOUT_SHELL
|
|
else
|
|
o.oOriginalModel = E_MODEL_KOOPA_WITH_SHELL
|
|
end
|
|
set_model(o)
|
|
end
|
|
hook_behavior(id_bhvKoopa, OBJ_LIST_PUSHABLE, false, nil, koopa_model_update)
|
|
|
|
cs_hook_mario_update(mario_update)
|
|
hook_event(HOOK_OBJECT_SET_MODEL, set_model)
|
|
|
|
------------------
|
|
-- Menu Handler --
|
|
------------------
|
|
|
|
local function button_to_analog(controller, negInput, posInput)
|
|
local num = 0
|
|
num = num - (controller.buttonDown & negInput ~= 0 and 127 or 0)
|
|
num = num + (controller.buttonDown & posInput ~= 0 and 127 or 0)
|
|
return num
|
|
end
|
|
|
|
local TEX_CAUTION_TAPE = get_texture_info("char_select_caution_tape")
|
|
-- Renders caution tape from xy1 to xy2, tape extends based on dist (0 - 1)
|
|
local function djui_hud_render_caution_tape(x1, y1, x2, y2, dist, scale)
|
|
if not scale then scale = 0.5 end
|
|
local totalDist = math.sqrt((y2 - y1)^2 + (x2 - x1)^2) * dist
|
|
local angle = angle_from_2d_points(x1, y1, x2, y2)
|
|
djui_hud_set_rotation(angle, 0, 0.5)
|
|
local texWidth = TEX_CAUTION_TAPE.width*scale
|
|
local texHeight = TEX_CAUTION_TAPE.height*scale
|
|
local tapeSegments = totalDist/texWidth
|
|
local tapeRemainder = tapeSegments
|
|
while tapeRemainder > 1 do
|
|
tapeRemainder = tapeRemainder - 1
|
|
end
|
|
for i = 0, math.floor(tapeSegments) do
|
|
local remainder = i == math.floor(tapeSegments) and tapeRemainder or 1
|
|
djui_hud_render_texture_tile(TEX_CAUTION_TAPE,
|
|
x1 + texWidth*coss(angle)*i,
|
|
y1 - texWidth*sins(angle)*i,
|
|
TEX_CAUTION_TAPE.height/TEX_CAUTION_TAPE.width*scale, 1*scale, 0, 0, TEX_CAUTION_TAPE.width*remainder, TEX_CAUTION_TAPE.height)
|
|
end
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
end
|
|
|
|
local optionAnimTimer = -200
|
|
local optionAnimTimerCap = optionAnimTimer
|
|
|
|
--Basic Menu Text
|
|
local yearsOfCS = get_date_and_time().year - 123 -- Zero years as of 2023
|
|
local TEXT_VERSION = "Version: " .. MOD_VERSION_STRING .. " | sm64coopdx" .. (seasonalEvent == SEASON_EVENT_BIRTHDAY and (" | " .. tostring(yearsOfCS) .. " year" .. (yearsOfCS > 1 and "s" or "") .. " of Character Select!") or "")
|
|
local TEXT_RATIO_UNSUPPORTED = "Your Current Aspect-Ratio isn't Supported!"
|
|
local TEXT_PAUSE_Z_OPEN = "Z Button - Character Select"
|
|
local TEXT_PAUSE_UNAVAILABLE = "Character Select is Unavailable"
|
|
local TEXT_PAUSE_CURR_CHAR = "Current Character: "
|
|
local TEXT_MOVESET_RESTRICTED = "Movesets are Restricted"
|
|
local TEXT_PALETTE_RESTRICTED = "Palettes are Restricted"
|
|
local TEXT_MOVESET_AND_PALETTE_RESTRICTED = "Moveset and Palettes are Restricted"
|
|
-- Easter Egg if you get lucky loading the mod
|
|
-- Referencing the original sm64ex DynOS options by PeachyPeach >v<
|
|
if math.random(100) == 64 then
|
|
TEXT_PAUSE_Z_OPEN = "Z - DynOS"
|
|
TEXT_PAUSE_CURR_CHAR = "Model: "
|
|
end
|
|
|
|
--Options/Credits Text
|
|
local TEXT_LOCAL_MODEL_ERROR = "Failed to find a Character Model"
|
|
local TEXT_LOCAL_MODEL_ERROR_FIX = "Please Verify the Integrity of the Pack!"
|
|
local TEXT_KOFI_LINK = "ko-fi.com/squishy6094"
|
|
local TEXT_CREDITS_HEADER = "CREDITS"
|
|
|
|
local MATH_DIVIDE_320 = 1/320
|
|
local MATH_DIVIDE_16 = 1/16
|
|
|
|
local targetMenuColor = {r = 0 , g = 0, b = 0}
|
|
menuColor = targetMenuColor
|
|
local menuColorHalf = menuColor
|
|
local menuColorTint = menuColor
|
|
local transSpeed = 0.1
|
|
local playerShirt = network_player_get_override_palette_color(gNetworkPlayers[0], SHIRT)
|
|
local playerPants = network_player_get_override_palette_color(gNetworkPlayers[0], PANTS)
|
|
function update_menu_color()
|
|
if optionTable[optionTableRef.menuColor].toggle == nil then return end
|
|
if optionTable[optionTableRef.menuColor].toggle > 1 then
|
|
targetMenuColor = menuColorTable[optionTable[optionTableRef.menuColor].toggle - 1]
|
|
elseif optionTable[optionTableRef.menuColor].toggle == 1 then
|
|
optionTable[optionTableRef.menuColor].toggleNames[2] = TEXT_PREF_LOAD_NAME .. ((TEXT_PREF_LOAD_ALT ~= 1 and currChar ~= 1) and " ("..TEXT_PREF_LOAD_ALT..")" or "") .. " (Pref)"
|
|
targetMenuColor = prefCharColor
|
|
elseif characterTable[currChar] ~= nil then
|
|
local char = characterTable[currChar]
|
|
targetMenuColor = char[char.currAlt].color
|
|
end
|
|
menuColor.r = math.lerp(menuColor.r, targetMenuColor.r, transSpeed)
|
|
menuColor.g = math.lerp(menuColor.g, targetMenuColor.g, transSpeed)
|
|
menuColor.b = math.lerp(menuColor.b, targetMenuColor.b, transSpeed)
|
|
menuColorHalf = {
|
|
r = menuColor.r * 0.5 + 127,
|
|
g = menuColor.g * 0.5 + 127,
|
|
b = menuColor.b * 0.5 + 127
|
|
}
|
|
menuColorTint = {
|
|
r = 205 + 50*menuColor.r/256,
|
|
g = 205 + 50*menuColor.g/256,
|
|
b = 205 + 50*menuColor.b/256
|
|
}
|
|
|
|
-- Update BG Wall Color
|
|
local shirtColor = network_player_get_override_palette_color(gNetworkPlayers[0], SHIRT)
|
|
local pantsColor = network_player_get_override_palette_color(gNetworkPlayers[0], PANTS)
|
|
playerShirt.r = math.lerp(playerShirt.r, shirtColor.r, transSpeed)
|
|
playerShirt.g = math.lerp(playerShirt.g, shirtColor.g, transSpeed)
|
|
playerShirt.b = math.lerp(playerShirt.b, shirtColor.b, transSpeed)
|
|
playerPants.r = math.lerp(playerPants.r, pantsColor.r, transSpeed)
|
|
playerPants.g = math.lerp(playerPants.g, pantsColor.g, transSpeed)
|
|
playerPants.b = math.lerp(playerPants.b, pantsColor.b, transSpeed)
|
|
return menuColor
|
|
end
|
|
|
|
local function djui_hud_render_life_icon(char, x, y, scale)
|
|
local icon = char and char.lifeIcon or "?"
|
|
local color = char and char.color or {r = 255, g = 255, b = 255}
|
|
local djuiColor = djui_hud_get_color()
|
|
if type(icon) == TYPE_STRING then
|
|
local font = djui_hud_get_font()
|
|
djui_hud_set_font(FONT_RECOLOR_HUD)
|
|
djui_hud_set_color(color.r * djuiColor.r/255, color.g * djuiColor.g/255, color.b * djuiColor.b/255, djuiColor.a)
|
|
djui_hud_print_text(icon, x, y, scale)
|
|
djui_hud_set_font(font)
|
|
else
|
|
djui_hud_set_color(djuiColor.r, djuiColor.g, djuiColor.b, djuiColor.a)
|
|
djui_hud_render_texture(icon, x, y, scale / (icon.width * MATH_DIVIDE_16), scale / (icon.height * MATH_DIVIDE_16))
|
|
end
|
|
djui_hud_set_color(djuiColor.r, djuiColor.g, djuiColor.b, djuiColor.a)
|
|
end
|
|
|
|
local gridButtonsPerRow = 5
|
|
local paletteXOffset = 0
|
|
local gearRotationTarget = 0
|
|
local gearRotation = 0
|
|
local paletteTrans = 0
|
|
local optionsMenuOffset = 0
|
|
local optionsMenuOffsetMax = 210
|
|
local function on_hud_render()
|
|
local FONT_USER = djui_menu_get_font()
|
|
djui_hud_set_font(FONT_ALIASED)
|
|
djui_hud_set_resolution(RESOLUTION_DJUI)
|
|
local djuiWidth = djui_hud_get_screen_width()
|
|
local djuiHeight = djui_hud_get_screen_height()
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
local width = math.max(djuiWidth * (240/djuiHeight), 320) -- Get accurate, unrounded width
|
|
local height = 240
|
|
local widthScale = math.max(width, 320) * MATH_DIVIDE_320
|
|
|
|
if startup_init_stall() then
|
|
update_menu_color()
|
|
if not menu_is_allowed() then
|
|
menu = false
|
|
end
|
|
end
|
|
|
|
if menuAndTransition then
|
|
if characterTable[currChar][characterTable[currChar].currAlt].model == E_MODEL_ERROR_MODEL then
|
|
djui_hud_set_color(0, 0, 0, 200)
|
|
djui_hud_render_rect(0, 0, width, height)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_print_text(TEXT_LOCAL_MODEL_ERROR, width*0.85 - djui_hud_measure_text(TEXT_LOCAL_MODEL_ERROR) * 0.15 * widthScale, height * 0.5, 0.3 * widthScale)
|
|
djui_hud_print_text(TEXT_LOCAL_MODEL_ERROR_FIX, width*0.85 - djui_hud_measure_text(TEXT_LOCAL_MODEL_ERROR_FIX) * 0.1 * widthScale, height * 0.5 + 10 * widthScale, 0.2 * widthScale)
|
|
end
|
|
|
|
optionsMenuOffset = lerp(optionsMenuOffset, options and optionsMenuOffsetMax or 0, 0.1)
|
|
|
|
--Unsupported Res Warning
|
|
if width < 319 or width > 575 then
|
|
djui_hud_print_text(TEXT_RATIO_UNSUPPORTED, 5, 39, 0.5)
|
|
end
|
|
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
djui_hud_render_rect(width * 0.5 - 50 * widthScale, height - 2, 100 * widthScale, 2)
|
|
|
|
-- Render Character Name
|
|
local angle1 = angle_from_2d_points(width*0.7, 8, width*1.1, 30)
|
|
local angle2 = angle_from_2d_points(width*0.7, 40, width*1.1, 35)
|
|
djui_hud_set_color(menuColor.r*0.1, menuColor.g*0.1, menuColor.b*0.1, 200)
|
|
djui_hud_set_rotation(angle1, 0, 1)
|
|
djui_hud_render_rect(width*0.7, -50, width*0.4, 59)
|
|
djui_hud_set_rotation(angle2, 0, 1)
|
|
djui_hud_render_rect(width*0.7, -50, width*0.4, 96)
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
djui_hud_set_rotation(angle1, 0, 1)
|
|
djui_hud_render_caution_tape(width*0.7, 8, width*1.1, 30, 1)
|
|
djui_hud_set_rotation(angle2, 0, 1)
|
|
djui_hud_render_caution_tape(width*0.7, 40, width*1.1, 35, 1)
|
|
|
|
djui_hud_set_font(FONT_CHARACTERISTIC)
|
|
local charName = string.upper(characterTable[currChar][characterTable[currChar].currAlt].name)
|
|
local nameScale = math.min(width*0.23/djui_hud_measure_text(charName), 1)
|
|
local charCreator = string.upper(characterTable[currChar][characterTable[currChar].currAlt].credit)
|
|
local creatorScale = math.min(width*0.23/djui_hud_measure_text(charCreator), 0.4)
|
|
djui_hud_set_color(menuColorHalf.r, menuColorHalf.g, menuColorHalf.b, 255)
|
|
djui_hud_print_text_auto_interpolated("topName", charName, width*0.85 - djui_hud_measure_text(charName)*0.5*nameScale - 2 + menuOffsetX*0.3, 21 - 16*nameScale + menuOffsetY*0.3, nameScale)
|
|
djui_hud_print_text_auto_interpolated("topCreator", charCreator, width*0.85 - djui_hud_measure_text(charCreator)*0.5*creatorScale - 2 + menuOffsetX*0.2, 42 - 16*creatorScale + menuOffsetY*0.2, creatorScale)
|
|
|
|
-- Palette Selection
|
|
local charColor = characterTableRender[currCharRender][characterTableRender[currCharRender].currAlt].color
|
|
local palettes = characterColorPresets[characterTableRender[currCharRender][characterTableRender[currCharRender].currAlt].model]
|
|
if palettes then
|
|
local bucketSpacing = 24
|
|
paletteXOffset = lerp(paletteXOffset, palettes.currPalette*bucketSpacing, 0.1)
|
|
paletteTrans = math.max(paletteTrans - 6, 0)
|
|
local bottomTapeAngle = angle_from_2d_points(-10, height - 50, width + 10, height - 35)
|
|
|
|
for i = 0, #palettes do
|
|
local x = width*0.85 - 16 - paletteXOffset + coss(bottomTapeAngle)*bucketSpacing*i
|
|
local y = height*0.72 - math.abs(math.cos((get_global_timer() - i*10)*0.05))*3 - sins(bottomTapeAngle)*bucketSpacing*(i - paletteXOffset/bucketSpacing)
|
|
local paletteShirt = nil
|
|
local palettePants = nil
|
|
if i == 0 then
|
|
paletteShirt = network_player_get_palette_color(gNetworkPlayers[0], SHIRT)
|
|
palettePants = network_player_get_palette_color(gNetworkPlayers[0], PANTS)
|
|
paletteName = "Custom"
|
|
else
|
|
paletteShirt = palettes[i][SHIRT]
|
|
palettePants = palettes[i][PANTS]
|
|
paletteName = palettes[i].name
|
|
end
|
|
if paletteShirt and palettePants then
|
|
local bucketFrame = (math.floor((get_global_timer() + i*10)*0.2)%10) * 32
|
|
local bucketPaintFrame = (math.floor(get_global_timer()*0.3)%10) * 32
|
|
djui_hud_set_color(charColor.r*0.5 + 127, charColor.g*0.5 + 127, charColor.b*0.5 + 127, math.min(paletteTrans, 255))
|
|
djui_hud_render_texture_tile(TEX_PALETTE_BUCKET, x, y, 1, 1, 0, bucketFrame, 32, 32)
|
|
djui_hud_set_color(paletteShirt.r, paletteShirt.g, paletteShirt.b, math.min(paletteTrans, 255))
|
|
djui_hud_render_texture_tile(TEX_PALETTE_BUCKET, x, y, 1, 1, 32, bucketFrame, 32, 32)
|
|
djui_hud_set_color(palettePants.r, palettePants.g, palettePants.b, math.min(paletteTrans, 255))
|
|
djui_hud_render_texture_tile(TEX_PALETTE_BUCKET, x, y, 1, 1, 64, bucketFrame, 32, 32)
|
|
|
|
if i == palettes.currPalette then
|
|
djui_hud_set_color(paletteShirt.r, paletteShirt.g, paletteShirt.b, math.min(paletteTrans, 255))
|
|
djui_hud_render_texture_tile(TEX_PALETTE_BUCKET, x, y, 1, 1, 96, bucketPaintFrame, 32, 32)
|
|
djui_hud_set_color(palettePants.r, palettePants.g, palettePants.b, math.min(paletteTrans, 255))
|
|
djui_hud_render_texture_tile(TEX_PALETTE_BUCKET, x, y, 1, 1, 128, bucketPaintFrame, 32, 32)
|
|
end
|
|
end
|
|
end
|
|
|
|
local paletteName = (palettes.currPalette == 0) and "Custom" or (palettes[palettes.currPalette].name or ("Palette "..palettes.currPalette))
|
|
djui_hud_set_font(FONT_RECOLOR_HUD)
|
|
local x = width*0.85 - djui_hud_measure_text(paletteName)*0.25
|
|
local y = height*0.68 - math.abs(math.cos((get_global_timer() - palettes.currPalette*10)*0.05))*3
|
|
djui_hud_set_color(charColor.r*0.5 + 127, charColor.g*0.5 + 127, charColor.b*0.5 + 127, math.min(paletteTrans, 255))
|
|
djui_hud_print_text(paletteName, x, y, 0.5)
|
|
end
|
|
|
|
-- Render Background Wall
|
|
local wallWidth = TEX_WALL_LEFT.width
|
|
local wallHeight = TEX_WALL_LEFT.height
|
|
local wallScale = 0.4 * widthScale
|
|
local wallMiddle = width*(0.35 - ((optionsMenuOffset - optionsMenuOffsetMax*0.5)/optionsMenuOffsetMax)*0.3)
|
|
local x = wallMiddle - wallWidth*wallScale*0.5 - menuOffsetX
|
|
local y = height*0.42 - wallHeight*wallScale*0.5 - menuOffsetY
|
|
local scissorWidth = math.max(320/djui_hud_get_screen_width(), 1)*320*0.7 -- Ensure Wall Space doesn't break when under 4:3
|
|
djui_hud_set_scissor(0, 0, scissorWidth, height)
|
|
djui_hud_set_color(playerShirt.r, playerShirt.g, playerShirt.b, 255)
|
|
djui_hud_render_texture_auto_interpolated("wall-l", TEX_WALL_LEFT, x, y, wallScale, wallScale)
|
|
djui_hud_set_color(playerPants.r, playerPants.g, playerPants.b, 255)
|
|
djui_hud_render_texture_auto_interpolated("wall-r", TEX_WALL_RIGHT, x, y, wallScale, wallScale)
|
|
|
|
-- Render Graffiti
|
|
local graffiti = characterGraffiti[currChar] or TEX_GRAFFITI_DEFAULT
|
|
local graffitiWidthScale = 120/graffiti.width
|
|
local graffitiHeightScale = 120/graffiti.width
|
|
djui_hud_set_color(255, 255, 255, 150)
|
|
djui_hud_render_texture_auto_interpolated("graffiti", graffiti, wallMiddle - graffiti.width*0.5*graffitiWidthScale - menuOffsetX, height*0.5 - graffiti.height*0.5*graffitiHeightScale - menuOffsetY, graffitiWidthScale, graffitiHeightScale)
|
|
|
|
-- API Rendering (Below Text)
|
|
if #hookTableRenderInMenu.back > 0 then
|
|
for i = 1, #hookTableRenderInMenu.back do
|
|
hookTableRenderInMenu.back[i]()
|
|
end
|
|
end
|
|
|
|
if prevOptions ~= options then
|
|
optionsTimer = 0
|
|
prevOptions = options
|
|
end
|
|
|
|
local scale = 0.35
|
|
local textScale = scale*1.5
|
|
local buttonSpacing = 32
|
|
|
|
if not gridMenu then
|
|
-- Render Character List
|
|
gridYOffset = lerp(gridYOffset, currCharRender*buttonSpacing, 0.1)
|
|
djui_hud_set_font(FONT_SPECIAL)
|
|
for i = 0, #characterTableRender do
|
|
local currCharScoll = math.floor(gridYOffset/buttonSpacing)
|
|
if i >= currCharScoll - 3 and i <= currCharScoll + 3 then -- Only render if visible
|
|
local charTable = characterTableRender[i]
|
|
local currAlt = characterTableRender[i].currAlt
|
|
local char = characterTableRender[i][currAlt]
|
|
local charName = charTable.nickname
|
|
local charAltName = char.name
|
|
local charNameLength = djui_hud_measure_text(charName)
|
|
local charColor = char.color
|
|
local x = -(math.abs(i - gridYOffset/buttonSpacing)^2)*5 + 32 - menuOffsetX*0.2 - optionsMenuOffset
|
|
local y = height*0.45 - buttonSpacing*0.5 + i*buttonSpacing - gridYOffset - menuOffsetY*0.2
|
|
local segmentsMeasured = (math.ceil(((charNameLength*textScale + 16*scale))/(16*scale)))
|
|
local segments = segmentsMeasured
|
|
local charAltCount = #characterTableRender[i]
|
|
local channel = characterInstrumentals[i] and tostring(math.floor(879 + hash(characterTableRender[i].saveName)%(1029 - 879))*0.1) .. " FM " or "---.- -- "
|
|
channel = channel .. tostring(math.ceil(charTable.playtime / totalPlaytime * 100)) .. "%"
|
|
-- Backlight
|
|
djui_hud_set_color(charColor.r*0.5 + 127, charColor.g*0.5 + 127, charColor.b*0.5 + 127, 255)
|
|
djui_hud_render_rect(x + 96*scale, y + 24*scale, (128*scale + segments*16*scale), 80*scale)
|
|
-- Name Screen
|
|
djui_hud_set_color(charColor.r*0.5, charColor.g*0.5, charColor.b*0.5, 255)
|
|
djui_hud_print_text(charName, x + 112*scale + segments*16*scale*0.5 - charNameLength*textScale*0.5, y + 32*scale, textScale)
|
|
|
|
-- Bottom Info
|
|
djui_hud_render_rect(x + 112*scale, y + 84*scale, segments*16*scale, scale)
|
|
djui_hud_print_text(channel, x + 112*scale, y + 85*scale, 0.3*scale)
|
|
djui_hud_print_text(charAltName, x + segments*16*scale + 112*scale - djui_hud_measure_text(charAltName)*0.3*scale, y + 85*scale, 0.3*scale)
|
|
|
|
-- Icon
|
|
djui_hud_set_color(charColor.r, charColor.g, charColor.b, 150)
|
|
djui_hud_render_life_icon(char, x + 112*scale + segments*16*scale + 45*scale, y + 40*scale, scale*3)
|
|
-- Nameplate Rendering
|
|
djui_hud_set_color(menuColorTint.r, menuColorTint.g, menuColorTint.b, 255)
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, 0, y, (128/8)*x*0.5*scale, scale, 0, 0, 8, 128) -- stretch to left side of screen
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x, y, (128/112)*scale, scale, 0, 0, 112, 128)
|
|
for s = 1, segments do
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x + (112 + (s-1)*16)*scale, y, scale*8, scale, 112, 0, 16, 128)
|
|
end
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x + (112 + segments*16)*scale, y, scale*128/192, scale, 128, 0, 192, 128)
|
|
local angle = -0x10000*((characterTableRender[i].currAlt - 1)/charAltCount)
|
|
if i == currCharRender then
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x + 33*scale, y + 48*scale, scale, scale, 320, 48, 32, 32)
|
|
end
|
|
if charTable.dialAnim == nil then charTable.dialAnim = 0 end
|
|
angleAnim = -0x10000*((1/charAltCount))*charTable.dialAnim/10
|
|
charTable.dialAnim = math.lerp(charTable.dialAnim, 0, 0.2)
|
|
djui_hud_set_rotation(angle + angleAnim, 0.5, 0.5)
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x + (112 + segments*16 + 134)*scale, y + 48*scale, scale, scale, 352, 48, 32, 32)
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
for a = 1, charAltCount do
|
|
local angle = -0x10000*((a - 1)/charAltCount) + 0x8000
|
|
local altColor = characterTableRender[i][a].color
|
|
djui_hud_set_color(altColor.r * 0.5 + 127, altColor.g * 0.5 + 127, altColor.b * 0.5 + 127, 255)
|
|
djui_hud_render_texture_tile(TEX_NAMEPLATE, x + (112 + segments*16 + (134 + 14) + sins(angle)*16)*scale, y + (62 + coss(angle)*16)*scale, scale, scale, 384, 48 + (currAlt ~= a and 16 or 0), 4, 4)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- Render Character Grid
|
|
local currRow = math.floor((currCharRender)/gridButtonsPerRow)
|
|
gridYOffset = lerp(gridYOffset, currRow*buttonSpacing, 0.1)
|
|
for i = 0, #characterTableRender do
|
|
local row = math.floor(i/gridButtonsPerRow)
|
|
local column = i%gridButtonsPerRow
|
|
local charIcon = characterTableRender[i][characterTableRender[i].currAlt].lifeIcon
|
|
local charColor = characterTableRender[i][characterTableRender[i].currAlt].color
|
|
local x = 40 + buttonSpacing*column - math.abs(row - gridYOffset/buttonSpacing)^2*3 + math.sin((get_global_timer() + i*10)*0.1) - menuOffsetX*0.5 - optionsMenuOffset + 4
|
|
local y = height*0.5 - buttonSpacing*0.5 + row*buttonSpacing - gridYOffset + math.cos((get_global_timer() + i*10)*0.1) - characterTableRender[i].UIOffset*0.5 - menuOffsetY*0.5 + 4
|
|
djui_hud_set_color(charColor.r, charColor.g, charColor.b, 255)
|
|
if characterInstrumentals[i] ~= nil then
|
|
djui_hud_render_texture(TEX_ALBUM_LAYER1, x + 3, y, 0.1875, 0.1875)
|
|
local discColors = {charColor, charColor, charColor}
|
|
local palettes = characterColorPresets[characterTableRender[i][characterTableRender[i].currAlt].model]
|
|
if palettes ~= nil then
|
|
local paletteIndex = math.max(palettes.currPalette, 1)
|
|
discColors = {palettes[paletteIndex][PANTS], palettes[paletteIndex][CAP], palettes[paletteIndex][EMBLEM]}
|
|
end
|
|
local discX = x + 6
|
|
local discY = y + 2
|
|
if (i == currCharRender) then
|
|
discX = x + 13
|
|
discY = y + 2
|
|
djui_hud_set_rotation(math.s16(get_global_timer()*-0x1000), 0.5, 0.5)
|
|
x = x - 4
|
|
end
|
|
djui_hud_set_color(discColors[1].r, discColors[1].g, discColors[1].b, 255)
|
|
djui_hud_render_texture(TEX_CD_LAYER1, discX, discY, 0.15625, 0.15625)
|
|
djui_hud_set_color(discColors[2].r, discColors[2].g, discColors[2].b, 255)
|
|
djui_hud_render_texture(TEX_CD_LAYER2, discX, discY, 0.15625, 0.15625)
|
|
djui_hud_set_color(discColors[3].r, discColors[3].g, discColors[3].b, 255)
|
|
djui_hud_render_texture(TEX_CD_LAYER3, discX, discY, 0.15625, 0.15625)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_render_texture(TEX_CD_LAYER4, discX, discY, 0.15625, 0.15625)
|
|
djui_hud_set_rotation(0, 0.5, 0.5)
|
|
elseif i == currCharRender then
|
|
djui_hud_render_texture(TEX_ALBUM_LAYER1, x + 6, y, 0.1875, 0.1875)
|
|
x = x - 4
|
|
else
|
|
djui_hud_render_texture(TEX_ALBUM_LAYER1, x + 3, y, 0.1875, 0.1875)
|
|
end
|
|
djui_hud_set_color(charColor.r, charColor.g, charColor.b, 255)
|
|
djui_hud_render_texture(TEX_ALBUM_LAYER2, x, y, 0.1875, 0.1875)
|
|
x = x + 4
|
|
y = y + 4
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
if type(charIcon) == TYPE_STRING then
|
|
djui_hud_set_font(FONT_RECOLOR_HUD)
|
|
djui_hud_set_color(charColor.r, charColor.g, charColor.b, 255)
|
|
djui_hud_print_text(charIcon, x, y, 1)
|
|
else
|
|
djui_hud_render_texture(charIcon, x, y, 1 / (charIcon.width * MATH_DIVIDE_16), 1 / (charIcon.height * MATH_DIVIDE_16))
|
|
end
|
|
djui_hud_render_texture(TEX_ALBUM_LAYER3, x - 4, y - 4, 0.1875, 0.1875)
|
|
|
|
characterTableRender[i].UIOffset = lerp(characterTableRender[i].UIOffset, currCharRender == i and 15 or 0, 0.1)
|
|
end
|
|
end
|
|
|
|
-- Render OST Record
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_set_rotation(get_global_timer() * 0x10, 0.5, 0.5)
|
|
djui_hud_render_texture(TEX_RECORD, -152 - menuOffsetX*0.1 - optionsMenuOffset, height*0.5 - 96 - menuOffsetY*0.1, 0.75, 0.75)
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
|
|
-- Render Category Gear
|
|
djui_hud_set_color(menuColorTint.r, menuColorTint.g, menuColorTint.b, 255)
|
|
djui_hud_set_rotation(gearRotation, 0.5, 0.5)
|
|
gearRotation = math.lerp(gearRotation, gearRotationTarget, 0.1)
|
|
djui_hud_render_texture(TEX_GEAR, width*0.7 - 15 - TEX_GEAR.width*0.175 - menuOffsetX*0.1, -TEX_GEAR.height*0.175 - menuOffsetY*0.1, 0.35, 0.35)
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
local icon1 = characterCategories[currCategory].icon1
|
|
local icon2 = characterCategories[currCategory].icon2
|
|
local name = characterCategories[currCategory].name
|
|
local char1 = characterTable[icon1] and characterTable[icon1][1]
|
|
local char2 = characterTable[icon2] and characterTable[icon2][1]
|
|
djui_hud_render_life_icon(char1, width*0.7 - 30 - 4 - menuOffsetX*0.1, 10 - 4 - menuOffsetY*0.1, 1)
|
|
djui_hud_render_life_icon(char2, width*0.7 - 30 + 4 - menuOffsetX*0.1, 10 + 4 - menuOffsetY*0.1, 1)
|
|
djui_hud_set_font(FONT_NORMAL)
|
|
djui_hud_print_text(name, width*0.7 - 65 - djui_hud_measure_text(name)*0.4 - menuOffsetX*0.1, 2 - menuOffsetY*0.1, 0.4)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
|
|
-- Render Options Menu
|
|
local tvScale = 0.5
|
|
local tvX = width*0.7 - 170 + (optionsMenuOffsetMax - optionsMenuOffset) + 15 - menuOffsetX*0.2
|
|
local tvY = 70 + (optionsMenuOffsetMax - optionsMenuOffset)*0.2 - menuOffsetY*0.2
|
|
local tvWidth = (320 - 133)*tvScale
|
|
local tvHeight = (323 - 168)*tvScale
|
|
local optionData = optionTable[currOption]
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_render_rect(tvX, tvY, tvWidth, tvHeight)
|
|
if options == OPTIONS_MAIN then
|
|
djui_hud_set_font(FONT_TINY)
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_print_text(optionData.name, tvX + 12 + (tvWidth - 12)*0.5 - djui_hud_measure_text(optionData.name)*0.35, tvY + 20, 0.7)
|
|
local locked = optionTable[currOption].lock ~= nil and optionTable[currOption].lock() or nil
|
|
local toggleString = (locked == nil and "< " .. optionData.toggleNames[optionData.toggle + 1] .. " >" or locked)
|
|
djui_hud_print_text(toggleString, tvX + 12 + (tvWidth - 12)*0.5 - djui_hud_measure_text(toggleString)*0.25, tvY + 30, 0.5)
|
|
|
|
for i = 1, #optionData.description do
|
|
local textMeasure = djui_hud_measure_text(optionData.description[i])
|
|
local x = tvX + 12 + (tvWidth - 12)*0.5 - textMeasure*0.225
|
|
local y = tvY + tvHeight - 7*(#optionData.description + 2) + 7*i
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(x - 2, y, textMeasure*0.45 + 4, 8)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_print_text(optionData.description[i], x, y, 0.45)
|
|
end
|
|
|
|
-- Render Header
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX, tvY, tvWidth, 18)
|
|
djui_hud_set_font(FONT_ALIASED)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_print_text("OPTIONS", tvX + 13, tvY + 2, 0.5)
|
|
djui_hud_set_font(FONT_NORMAL)
|
|
local optionCategory = "... " .. string.upper(optionData.category)
|
|
djui_hud_print_text(optionCategory, tvX + 13 + djui_hud_measure_text("OPTIONS")*0.5, tvY + 8, 0.25)
|
|
|
|
-- Render Sidebar
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX, tvY, 10, tvHeight)
|
|
for i = 1, #optionTable do
|
|
if i == currOption then
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
else
|
|
djui_hud_set_color(100, 100, 100, 255)
|
|
end
|
|
djui_hud_render_rect(tvX + 4, tvY + (tvHeight - 10)*(i/#optionTable), 2, 2)
|
|
end
|
|
elseif options == OPTIONS_CREDITS then
|
|
-- Render Scroll Bar
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX + 3, tvY + 24, 1, 40)
|
|
local creditSize = math.min(1, creditScrollMin/(#creditTable[currCredits] / (currCredits > 0 and 1 or 3)))
|
|
djui_hud_render_rect(tvX + 2, tvY + 24 + (currCreditScroll/(#creditTable[currCredits] - creditScrollMin))*(40*(1 - creditSize)), 3, 40*creditSize)
|
|
-- Render Credits
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_set_font(FONT_TINY)
|
|
if currCredits > 0 then
|
|
for i = 1, #creditTable[currCredits] do
|
|
local creditData = creditTable[currCredits][i]
|
|
djui_hud_print_text(creditData.creditee, tvX + tvWidth*0.5 - 1 - djui_hud_measure_text(creditData.creditee)*0.4, tvY + 19 + creditScrollMin*(i - currCreditScroll), 0.4)
|
|
djui_hud_print_text(creditData.credit, tvX + tvWidth*0.5 + 1, tvY + 19 + creditScrollMin*(i - currCreditScroll), 0.4)
|
|
end
|
|
else
|
|
for i = 1, #creditTable[currCredits] do
|
|
local creditData = creditTable[currCredits][i]
|
|
djui_hud_print_text(creditData.creditee, tvX - tvWidth*0.1 + tvWidth*0.3*(((i - 1)%3) + 1) - djui_hud_measure_text(creditData.creditee)*0.2, tvY + 19 + creditScrollMin*(math.ceil(i/3) - currCreditScroll), 0.4)
|
|
end
|
|
|
|
-- Render Support Link
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX, tvY + tvHeight - 10, tvWidth, 11)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_set_font(FONT_TINY)
|
|
djui_hud_print_text(TEXT_KOFI_LINK, tvX + tvWidth*0.5 - djui_hud_measure_text(TEXT_KOFI_LINK)*0.25, tvY + tvHeight - 10, 0.5)
|
|
end
|
|
-- Render Pack Name
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX, tvY, tvWidth, 21)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_set_font(FONT_ALIASED)
|
|
djui_hud_print_text(TEXT_CREDITS_HEADER, tvX + tvWidth*0.5 - djui_hud_measure_text(TEXT_CREDITS_HEADER)*0.2, tvY + 2, 0.4)
|
|
djui_hud_set_font(FONT_SPECIAL)
|
|
djui_hud_print_text(creditTable[currCredits].packName, tvX + tvWidth*0.5 - djui_hud_measure_text(creditTable[currCredits].packName)*0.1, tvY + 14, 0.2)
|
|
else
|
|
local tvShutOffHeight = tvHeight*0.5*(optionsMenuOffsetMax^3.5 - optionsMenuOffset^3.5)/optionsMenuOffsetMax^3.5
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(tvX, tvY, tvWidth, tvShutOffHeight)
|
|
djui_hud_render_rect(tvX, tvY + tvHeight - tvShutOffHeight, tvWidth, tvShutOffHeight)
|
|
end
|
|
|
|
djui_hud_set_color(menuColorTint.r, menuColorTint.g, menuColorTint.b, 255)
|
|
djui_hud_render_texture_auto_interpolated("tvBoarder", TEX_OPTIONS_TV, tvX - 133*tvScale, tvY - 168*tvScale, tvScale, tvScale)
|
|
|
|
djui_hud_reset_scissor()
|
|
|
|
-- Render Character Description
|
|
djui_hud_set_rotation(angle_from_2d_points(-10, height - 50, width + 10, height - 35), 0, 0)
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(-10, height - 50, width*1.5, 100)
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
|
|
djui_hud_set_font(FONT_TINY)
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
local credit = characterTable[currChar][characterTable[currChar].currAlt].credit
|
|
local desc = characterTable[currChar][characterTable[currChar].currAlt].description
|
|
local descRender = desc .. " - " .. desc
|
|
while djui_hud_measure_text(descRender)*0.8 < width do
|
|
descRender = descRender .. " - " .. desc
|
|
end
|
|
descRender = descRender .. " - " .. desc
|
|
djui_hud_print_text_interpolated(descRender, 5 - (get_global_timer()%djui_hud_measure_text(desc .. " - ") - 1)*0.8 - menuOffsetX*0.1, height - 25 + menuOffsetY*0.15, 0.8, 5 - get_global_timer()%djui_hud_measure_text(desc .. " - ")*0.8 - menuOffsetX*0.1, height - 25 + menuOffsetY*0.15, 0.8)
|
|
|
|
djui_hud_set_rotation(angle_from_2d_points(-10, height - 50, width + 10, height - 35), 0, 0)
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(0, height - 45, width*0.3, 100)
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
djui_hud_print_text(TEXT_VERSION, 2, height - 7, 0.4)
|
|
local currMenu = gridMenu and MENU_BINDS_GRID or MENU_BINDS_DEFAULT
|
|
if options == OPTIONS_MAIN then
|
|
currMenu = MENU_BINDS_OPTIONS
|
|
elseif options == OPTIONS_CREDITS then
|
|
currMenu = MENU_BINDS_GRID
|
|
end
|
|
local bindInfo = TEXT_TABLE_MENU_BINDS[currMenu][math.floor(get_global_timer()/150)%(#TEXT_TABLE_MENU_BINDS[currMenu]) + 1]
|
|
djui_hud_print_text(bindInfo.bind, width*0.15 - djui_hud_measure_text(bindInfo.bind)*0.4, height - 35, 0.8)
|
|
djui_hud_print_text(bindInfo.desc, width*0.15 - djui_hud_measure_text(bindInfo.desc)*0.4, height - 25, 0.8)
|
|
|
|
-- API Rendering (Above Text)
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
if #hookTableRenderInMenu.front > 0 then
|
|
for i = 1, #hookTableRenderInMenu.front do
|
|
hookTableRenderInMenu.front[i]()
|
|
end
|
|
end
|
|
|
|
-- Render Header BG
|
|
djui_hud_set_rotation(angle_from_2d_points(-10, 50, 160, -10), 0.5, 0.5)
|
|
djui_hud_set_color(0, 0, 0, 255)
|
|
djui_hud_render_rect(-150, -50, 300, 100)
|
|
|
|
-- Render Tape
|
|
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
|
djui_hud_render_caution_tape(-10, 50, 160, -10, 1) -- Top Tape
|
|
djui_hud_render_caution_tape(width*0.7 - 5, -10, width*0.7 + 5, height - 35, 1, 0.6) -- Right Tape
|
|
djui_hud_render_caution_tape(width*0.3, height - 45, width*0.3 - 5, height, 1, 0.5) -- Left Bottom Tape
|
|
djui_hud_render_caution_tape(-10, height - 50, width + 10, height - 35, 1) -- Bottom Tape
|
|
|
|
-- Render Header
|
|
djui_hud_set_rotation(0, 0, 0)
|
|
djui_hud_set_color(menuColorHalf.r, menuColorHalf.g, menuColorHalf.b, 255)
|
|
djui_hud_render_texture_auto_interpolated("logo", TEX_LOGO, menuOffsetX*0.1, menuOffsetY*0.1, 0.25, 0.25)
|
|
|
|
-- Anim logic
|
|
if options then
|
|
if optionAnimTimer < -1 then
|
|
optionAnimTimer = optionAnimTimer * 0.9
|
|
end
|
|
else
|
|
if optionAnimTimer > optionAnimTimerCap then
|
|
optionAnimTimer = optionAnimTimer * 1.3
|
|
end
|
|
end
|
|
optionAnimTimer = math.max(optionAnimTimer, -200)
|
|
else
|
|
options = nil
|
|
optionAnimTimer = optionAnimTimerCap
|
|
bindTextTimer = 0
|
|
end
|
|
|
|
-- Fade in/out of menu
|
|
if menu and menuCrossFade > -menuCrossFadeCap then
|
|
menuCrossFade = menuCrossFade - 1
|
|
if menuCrossFade == 0 then menuCrossFade = menuCrossFade - 1 end
|
|
end
|
|
if not menu and menuCrossFade < menuCrossFadeCap then
|
|
menuCrossFade = menuCrossFade + 1
|
|
if menuCrossFade == 0 then menuCrossFade = menuCrossFade + 1 end
|
|
end
|
|
menuAndTransition = menuCrossFade < 0
|
|
|
|
-- Info / Z Open Bind on Pause Menu
|
|
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_USER)
|
|
if optionTable[optionTableRef.openInputs].toggle == 1 then
|
|
currCharY = 27
|
|
local text = menu_is_allowed() and TEXT_PAUSE_Z_OPEN or TEXT_PAUSE_UNAVAILABLE
|
|
width = djui_hud_get_screen_width() - djui_hud_measure_text(text)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
djui_hud_print_text(text, width - 20, 16, 1)
|
|
end
|
|
|
|
local character = characterTable[currChar][characterTable[currChar].currAlt]
|
|
local charName = string_underscore_to_space(character.name)
|
|
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 = character.color
|
|
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)
|
|
|
|
local text = nil
|
|
if gGlobalSyncTable.charSelectRestrictMovesets > 0 and gGlobalSyncTable.charSelectRestrictPalettes > 0 then
|
|
text = TEXT_MOVESET_AND_PALETTE_RESTRICTED
|
|
elseif gGlobalSyncTable.charSelectRestrictMovesets > 0 then
|
|
text = TEXT_MOVESET_RESTRICTED
|
|
elseif gGlobalSyncTable.charSelectRestrictPalettes > 0 then
|
|
text = TEXT_PALETTE_RESTRICTED
|
|
end
|
|
if text ~= nil then
|
|
width = djui_hud_get_screen_width() - djui_hud_measure_text(text)
|
|
djui_hud_set_color(255, 255, 255, 255)
|
|
currCharY = currCharY + 27
|
|
djui_hud_print_text(text, width - 20, 16 + currCharY, 1)
|
|
end
|
|
end
|
|
|
|
-- Cross Fade to Menu
|
|
djui_hud_set_resolution(RESOLUTION_N64)
|
|
djui_hud_set_color(0, 0, 0, (math.abs(menuCrossFade)) * -menuCrossFadeMath)
|
|
djui_hud_render_rect(0, 0, width, height)
|
|
end
|
|
|
|
local FUNC_INDEX_MISC = 0
|
|
local FUNC_INDEX_HORIZONTAL = 1
|
|
local FUNC_INDEX_VERTICAL = 2
|
|
local FUNC_INDEX_CATEGORY = 3
|
|
local FUNC_INDEX_PREFERENCE = 4
|
|
local FUNC_INDEX_PALETTE = 5
|
|
local FUNC_INDEX_ALT = 6
|
|
local FUNC_INDEX_GRID = 7
|
|
|
|
local menuInputCooldowns = {}
|
|
function run_func_with_condition_and_cooldown(funcIndex, condition, func, cooldown)
|
|
if menuInputCooldowns[funcIndex] == nil then
|
|
menuInputCooldowns[funcIndex] = 0
|
|
end
|
|
if not condition then return end
|
|
local cooldown = cooldown and cooldown or latencyValueTable[optionTable[optionTableRef.inputLatency].toggle + 1]
|
|
if menuInputCooldowns[funcIndex] == 0 then
|
|
func()
|
|
menuInputCooldowns[funcIndex] = cooldown
|
|
end
|
|
end
|
|
|
|
local mouseScroll = 0
|
|
---@param m MarioState
|
|
local function before_mario_update(m)
|
|
if m.playerIndex ~= 0 then return end
|
|
if not startup_init_stall() then return end
|
|
local controller = m.controller
|
|
local character = characterTable[currChar]
|
|
for index, num in pairs(menuInputCooldowns) do
|
|
if num and num > 0 then
|
|
menuInputCooldowns[index] = num - 1
|
|
end
|
|
end
|
|
|
|
-- Menu Inputs
|
|
if is_game_paused() and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and (controller.buttonPressed & Z_TRIG) ~= 0 and optionTable[optionTableRef.openInputs].toggle == 1 then
|
|
menu = true
|
|
end
|
|
if not menu and (controller.buttonDown & D_JPAD) ~= 0 and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and optionTable[optionTableRef.openInputs].toggle == 2 then
|
|
if (controller.buttonDown & R_TRIG) ~= 0 or not ommActive then
|
|
menu = true
|
|
end
|
|
end
|
|
|
|
if not menu_is_allowed(m) then
|
|
menu = false
|
|
return
|
|
end
|
|
|
|
mouseScroll = mouseScroll - djui_hud_get_mouse_scroll_y()
|
|
|
|
-- Yo Melee called
|
|
local menuOffsetXRaw = (m.controller.extStickX ~= 0 and m.controller.extStickX or button_to_analog(charSelect.controller, L_CBUTTONS, R_CBUTTONS))*0.1
|
|
local menuOffsetYRaw = (m.controller.extStickY ~= 0 and -m.controller.extStickY or button_to_analog(charSelect.controller, U_CBUTTONS, D_CBUTTONS))*0.1
|
|
menuOffsetX = lerp(menuOffsetX, menuOffsetXRaw, 0.2)
|
|
menuOffsetY = lerp(menuOffsetY, menuOffsetYRaw, 0.2)
|
|
|
|
local cameraToObject = m.marioObj.header.gfx.cameraToObject
|
|
if menuAndTransition and not options then
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_GRID,
|
|
(controller.buttonPressed & X_BUTTON) ~= 0,
|
|
function ()
|
|
gridMenu = not gridMenu
|
|
mod_storage_save_bool("PrefGridView", gridMenu)
|
|
play_sound(SOUND_MENU_CLICK_CHANGE_VIEW, cameraToObject)
|
|
end
|
|
)
|
|
if menu then
|
|
-- Category Switching
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_CATEGORY,
|
|
(controller.buttonPressed & L_TRIG) ~= 0,
|
|
function ()
|
|
repeat
|
|
currCategory = num_wrap(currCategory - 1, 1, #characterCategories)
|
|
until update_character_render_table()
|
|
gearRotationTarget = gearRotationTarget + 0x10000/#characterCategories
|
|
categoryOpenTimer = 150
|
|
|
|
play_sound(SOUND_MENU_CAMERA_TURN, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_CATEGORY,
|
|
(controller.buttonPressed & R_TRIG) ~= 0,
|
|
function ()
|
|
repeat
|
|
currCategory = num_wrap(currCategory + 1, 1, #characterCategories)
|
|
until update_character_render_table()
|
|
gearRotationTarget = gearRotationTarget - 0x10000/#characterCategories
|
|
categoryOpenTimer = 150
|
|
update_character_render_table()
|
|
play_sound(SOUND_MENU_CAMERA_TURN, cameraToObject)
|
|
end
|
|
)
|
|
|
|
if not gridMenu then
|
|
-- List Controls
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & D_JPAD) ~= 0 or controller.stickY < -45 --[[or prevMouseScroll < mouseScroll]],
|
|
function ()
|
|
currCharRender = num_wrap(currCharRender + 1, 0, #characterTableRender)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & U_JPAD) ~= 0 or controller.stickY > 45 --[[or prevMouseScroll > mouseScroll]],
|
|
function ()
|
|
currCharRender = num_wrap(currCharRender - 1, 0, #characterTableRender)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
-- Alt switcher
|
|
if #characterTable[currChar] > 1 then
|
|
if characterTableRender[currCharRender].dialAnim == nil then characterTableRender[currCharRender].dialAnim = 0 end
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & R_JPAD) ~= 0 or controller.stickX > 60,
|
|
function ()
|
|
character.currAlt = num_wrap(character.currAlt + 1, 1, #character)
|
|
characterTableRender[currCharRender].dialAnim = characterTableRender[currCharRender].dialAnim - 10
|
|
audio_stream_set_frequency(SOUND_CHAR_SELECT_DIAL, math.random(20, 80)*0.01 + 0.5)
|
|
audio_stream_play(SOUND_CHAR_SELECT_DIAL, true, 0.75)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & L_JPAD) ~= 0 or controller.stickX < -60,
|
|
function ()
|
|
character.currAlt = num_wrap(character.currAlt - 1, 1, #character)
|
|
characterTableRender[currCharRender].dialAnim = characterTableRender[currCharRender].dialAnim + 10
|
|
audio_stream_set_frequency(SOUND_CHAR_SELECT_DIAL, math.random(20, 80)*0.01 + 0.5)
|
|
audio_stream_play(SOUND_CHAR_SELECT_DIAL, true, 0.75)
|
|
end
|
|
)
|
|
end
|
|
|
|
else
|
|
-- Grid Controls
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & D_JPAD) ~= 0 or controller.stickY < -45 --[[or prevMouseScroll < mouseScroll]],
|
|
function ()
|
|
currCharRender = num_wrap(currCharRender + 5, currCharRender%5, #characterTableRender)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & U_JPAD) ~= 0 or controller.stickY > 45 --[[or prevMouseScroll > mouseScroll]],
|
|
function ()
|
|
local tableCap = #characterTableRender - #characterTableRender%5 + currCharRender%5
|
|
tableCap = tableCap - (tableCap > #characterTable and 5 or 0)
|
|
currCharRender = num_wrap(currCharRender - 5, currCharRender%5, tableCap)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & R_JPAD) ~= 0 or controller.stickX > 60,
|
|
function ()
|
|
currCharRender = num_wrap(currCharRender + 1, 0, #characterTableRender)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & L_JPAD) ~= 0 or controller.stickX < -60,
|
|
function ()
|
|
currCharRender = num_wrap(currCharRender - 1, 0, #characterTableRender)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
-- Alt switcher
|
|
if #characterTable[currChar] > 1 then
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_ALT,
|
|
(controller.buttonPressed & R_CBUTTONS) ~= 0,
|
|
function ()
|
|
character.currAlt = num_wrap(character.currAlt + 1, 1, #character)
|
|
audio_stream_set_frequency(SOUND_CHAR_SELECT_DIAL, math.random(20, 80)*0.01 + 0.5)
|
|
audio_stream_play(SOUND_CHAR_SELECT_DIAL, true, 0.75)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_ALT,
|
|
(controller.buttonPressed & L_CBUTTONS) ~= 0,
|
|
function ()
|
|
character.currAlt = num_wrap(character.currAlt - 1, 1, #character)
|
|
audio_stream_set_frequency(SOUND_CHAR_SELECT_DIAL, math.random(20, 80)*0.01 + 0.5)
|
|
audio_stream_play(SOUND_CHAR_SELECT_DIAL, true, 0.75)
|
|
end
|
|
)
|
|
end
|
|
end
|
|
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_PREFERENCE,
|
|
(controller.buttonPressed & A_BUTTON) ~= 0,
|
|
function ()
|
|
if characterTable[currChar] ~= nil then
|
|
mod_storage_save_pref_char(characterTable[currChar])
|
|
play_sound(SOUND_MENU_CLICK_FILE_SELECT, cameraToObject)
|
|
play_character_sound(m, CHAR_SOUND_LETS_A_GO)
|
|
else
|
|
play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject)
|
|
end
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_PALETTE,
|
|
(controller.buttonPressed & Y_BUTTON) ~= 0,
|
|
function ()
|
|
local currPaletteTable = characterColorPresets[gCSPlayers[0].modelId] and characterColorPresets[gCSPlayers[0].modelId] or {currPalette = 0}
|
|
if #currPaletteTable > 0 and gGlobalSyncTable.charSelectRestrictPalettes == 0 then
|
|
play_sound(SOUND_MENU_CLICK_FILE_SELECT, cameraToObject)
|
|
currPaletteTable.currPalette = currPaletteTable.currPalette + 1
|
|
else
|
|
play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject)
|
|
end
|
|
paletteTrans = 1000
|
|
currPaletteTable.currPalette = num_wrap(currPaletteTable.currPalette, 0, #currPaletteTable)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_MISC,
|
|
(controller.buttonPressed & B_BUTTON) ~= 0,
|
|
function ()
|
|
menu = false
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_MISC,
|
|
(controller.buttonPressed & START_BUTTON) ~= 0,
|
|
function ()
|
|
options = OPTIONS_MAIN
|
|
end
|
|
)
|
|
end
|
|
|
|
-- Handles Camera Posistioning
|
|
camAngle = m.faceAngle.y + 0x800
|
|
eyeState = MARIO_EYES_OPEN
|
|
|
|
nullify_inputs(m)
|
|
if is_game_paused() then
|
|
game_unpause()
|
|
end
|
|
end
|
|
|
|
if options == OPTIONS_MAIN then
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & D_JPAD) ~= 0 or controller.stickY < -60,
|
|
function ()
|
|
currOption = num_wrap(currOption + 1, 1, #optionTable)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & U_JPAD) ~= 0 or controller.stickY > 60,
|
|
function ()
|
|
currOption = num_wrap(currOption - 1, 1, #optionTable)
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & L_JPAD) ~= 0 or controller.stickX < -60,
|
|
function ()
|
|
local locked = optionTable[currOption].lock ~= nil and optionTable[currOption].lock() or nil
|
|
if locked == nil then
|
|
optionTable[currOption].toggle = num_wrap(optionTable[currOption].toggle - 1, 0, optionTable[currOption].toggleMax)
|
|
if optionTable[currOption].toggleSaveName ~= nil then
|
|
mod_storage_save(optionTable[currOption].toggleSaveName, tostring(optionTable[currOption].toggle))
|
|
end
|
|
play_sound(SOUND_MENU_CHANGE_SELECT, cameraToObject)
|
|
else
|
|
play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject)
|
|
end
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & R_JPAD) ~= 0 or controller.stickX > 60,
|
|
function ()
|
|
local locked = optionTable[currOption].lock ~= nil and optionTable[currOption].lock() or nil
|
|
if locked == nil then
|
|
optionTable[currOption].toggle = num_wrap(optionTable[currOption].toggle + 1, 0, optionTable[currOption].toggleMax)
|
|
if optionTable[currOption].toggleSaveName ~= nil then
|
|
mod_storage_save(optionTable[currOption].toggleSaveName, tostring(optionTable[currOption].toggle))
|
|
end
|
|
play_sound(SOUND_MENU_CHANGE_SELECT, cameraToObject)
|
|
else
|
|
play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject)
|
|
end
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_MISC,
|
|
(controller.buttonPressed & B_BUTTON) ~= 0,
|
|
function ()
|
|
options = nil
|
|
end
|
|
)
|
|
|
|
nullify_inputs(m)
|
|
|
|
elseif options == OPTIONS_CREDITS then
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & L_JPAD) ~= 0 or controller.stickX < -60,
|
|
function ()
|
|
currCredits = num_wrap(currCredits - 1, 0, #creditTable)
|
|
currCreditScroll = 0
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_HORIZONTAL,
|
|
(controller.buttonPressed & R_JPAD) ~= 0 or controller.stickX > 60,
|
|
function ()
|
|
currCredits = num_wrap(currCredits + 1, 0, #creditTable)
|
|
currCreditScroll = 0
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
if #creditTable[currCredits] > creditScrollMin then
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & U_JPAD) ~= 0 or controller.stickY > 60,
|
|
function ()
|
|
currCreditScroll = num_wrap(currCreditScroll - 1, 0, math.max(0, math.ceil(#creditTable[currCredits] / (currCredits > 0 and 1 or 3)) - creditScrollMin))
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_VERTICAL,
|
|
(controller.buttonPressed & D_JPAD) ~= 0 or controller.stickY < -60,
|
|
function ()
|
|
currCreditScroll = num_wrap(currCreditScroll + 1, 0, math.max(0, #creditTable[currCredits] / math.ceil(currCredits > 0 and 1 or 3) - creditScrollMin))
|
|
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
|
end
|
|
)
|
|
end
|
|
|
|
run_func_with_condition_and_cooldown(FUNC_INDEX_MISC,
|
|
(controller.buttonPressed & (B_BUTTON | Z_TRIG)) ~= 0,
|
|
function ()
|
|
options = OPTIONS_MAIN
|
|
currOption = optionTableRef.credits
|
|
end
|
|
)
|
|
|
|
nullify_inputs(m)
|
|
end
|
|
|
|
-- Checks
|
|
currChar = characterTableRender[currCharRender].ogNum
|
|
end
|
|
|
|
hook_event(HOOK_BEFORE_MARIO_UPDATE, before_mario_update)
|
|
hook_event(HOOK_ON_HUD_RENDER, on_hud_render)
|
|
|
|
--------------
|
|
-- Commands --
|
|
--------------
|
|
|
|
promptedAreYouSure = false
|
|
|
|
local function chat_command(msg)
|
|
msg = string.lower(msg)
|
|
|
|
-- Open Menu Check
|
|
if (msg == "" or msg == "menu") then
|
|
if menu_is_allowed(gMarioStates[0]) then
|
|
menu = not menu
|
|
return true
|
|
else
|
|
djui_chat_message_create(TEXT_PAUSE_UNAVAILABLE)
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Help Prompt Check
|
|
if msg == "?" or msg == "help" then
|
|
djui_chat_message_create("Character Select's Avalible Commands:" ..
|
|
"\n\\#ffff33\\/char-select help\\#ffffff\\ - Returns Avalible Commands" ..
|
|
"\n\\#ffff33\\/char-select menu\\#ffffff\\ - Opens the Menu" ..
|
|
"\n\\#ffff33\\/char-select [name/num]\\#ffffff\\ - Switches to Character" ..
|
|
"\n\\#ff3333\\/char-select reset\\#ffffff\\ - Resets your Save Data")
|
|
return true
|
|
end
|
|
|
|
-- Reset Save Data Check
|
|
if msg == "reset" or (msg == "confirm" and promptedAreYouSure) then
|
|
reset_options(true)
|
|
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 = 0, #characterTable do
|
|
if characterTable[i].locked ~= LOCKED_TRUE then
|
|
local saveName = string_underscore_to_space(string.lower(characterTable[i].saveName))
|
|
for a = 1, #characterTable[i] do
|
|
if msg == string.lower(characterTable[i][a].name) or msg == saveName then
|
|
force_set_character(i, msg ~= saveName and a or 1)
|
|
djui_chat_message_create('Character set to "' .. characterTable[i][characterTable[i].currAlt].name .. '" Successfully!')
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Number Check
|
|
msgSplit = string_split(msg)
|
|
if tonumber(msgSplit[1]) then
|
|
local charNum = tonumber(msgSplit[1])
|
|
local altNum = tonumber(msgSplit[2])
|
|
altNum = altNum and altNum or 1
|
|
if charNum > 0 and charNum <= #characterTable and characterTable[charNum].locked ~= LOCKED_TRUE then
|
|
force_set_character(charNum, altNum)
|
|
djui_chat_message_create('Character set to "' .. characterTable[charNum][altNum].name .. '" Successfully!')
|
|
return true
|
|
end
|
|
end
|
|
|
|
djui_chat_message_create("Character Not Found")
|
|
return true
|
|
end
|
|
|
|
hook_chat_command("char-select", "- Opens the Character Select Menu", chat_command)
|