mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-28 23:13:02 +00:00
Add Character Select to built in mods
This commit is contained in:
parent
949a05fd14
commit
c3ea12ff8c
9 changed files with 1176 additions and 0 deletions
90
mods/character-select-coop/a-utils.lua
Normal file
90
mods/character-select-coop/a-utils.lua
Normal file
|
@ -0,0 +1,90 @@
|
|||
----------------------
|
||||
-- Global Functions --
|
||||
----------------------
|
||||
|
||||
local saveableCharacters = {
|
||||
["1"] = true,
|
||||
["2"] = true,
|
||||
["3"] = true,
|
||||
["4"] = true,
|
||||
["5"] = true,
|
||||
["6"] = true,
|
||||
["7"] = true,
|
||||
["8"] = true,
|
||||
["9"] = true,
|
||||
["0"] = true,
|
||||
["a"] = true,
|
||||
["b"] = true,
|
||||
["c"] = true,
|
||||
["d"] = true,
|
||||
["e"] = true,
|
||||
["f"] = true,
|
||||
["g"] = true,
|
||||
["h"] = true,
|
||||
["i"] = true,
|
||||
["j"] = true,
|
||||
["k"] = true,
|
||||
["l"] = true,
|
||||
["m"] = true,
|
||||
["n"] = true,
|
||||
["o"] = true,
|
||||
["p"] = true,
|
||||
["q"] = true,
|
||||
["r"] = true,
|
||||
["s"] = true,
|
||||
["t"] = true,
|
||||
["u"] = true,
|
||||
["v"] = true,
|
||||
["w"] = true,
|
||||
["x"] = true,
|
||||
["y"] = true,
|
||||
["z"] = true,
|
||||
["_"] = true,
|
||||
["-"] = true,
|
||||
}
|
||||
|
||||
function string_underscore_to_space(string)
|
||||
local s = ''
|
||||
for i = 1, #string do
|
||||
local c = string:sub(i,i)
|
||||
if c ~= '_' then
|
||||
s = s .. c
|
||||
else
|
||||
s = s .. " "
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
function string_space_to_underscore(string)
|
||||
local s = ''
|
||||
for i = 1, #string do
|
||||
local c = string:sub(i,i)
|
||||
if saveableCharacters[string.lower(c)] then
|
||||
s = s .. c
|
||||
else
|
||||
s = s .. "_"
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
----------------------
|
||||
-- Global Variables --
|
||||
----------------------
|
||||
|
||||
modVersion = "1.5"
|
||||
|
||||
allowMenu = {}
|
||||
|
||||
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 = 100, g = 100, 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 },
|
||||
}
|
BIN
mods/character-select-coop/actors/armature_geo.bin
Normal file
BIN
mods/character-select-coop/actors/armature_geo.bin
Normal file
Binary file not shown.
788
mods/character-select-coop/main.lua
Normal file
788
mods/character-select-coop/main.lua
Normal file
|
@ -0,0 +1,788 @@
|
|||
-- name: Character Select
|
||||
-- description: A Library / API made to make adding and\nusing Custom Characters as simple as possible!\n\nCreated by:\\#008800\\ Squishy6094\n\\#dcdcdc\\Concepts by:\\#4496f5\\ AngelicMiracles\\#AAAAFF\\\n\nGithub:\nSquishy6094/character-select-coop
|
||||
|
||||
|
||||
menu = false
|
||||
options = false
|
||||
|
||||
currChar = 1
|
||||
local currOption = 1
|
||||
|
||||
|
||||
local TEX_HEADER = get_texture_info("char-select-text")
|
||||
|
||||
local TEXT_PREF_LOAD = "Default"
|
||||
|
||||
local ommActive = _G.OmmApi ~= nil
|
||||
|
||||
--[[
|
||||
Note: Do NOT add characters via the characterTable below,
|
||||
We highly reccomend 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 = {
|
||||
[1] = {
|
||||
name = "Default",
|
||||
description = {"The vanilla cast for sm64ex-coop!", "", "These Characters are swappable", "via the default Options Menu"},
|
||||
credit = "Nintendo / Coop Team",
|
||||
color = {r = 255, g = 50, b = 50},
|
||||
model = nil,
|
||||
forceChar = nil,
|
||||
lifeIcon = gTextures.mario_head,
|
||||
},
|
||||
}
|
||||
|
||||
local optionTableRef = {
|
||||
openInputs = 1,
|
||||
menuColor = 2,
|
||||
anims = 3,
|
||||
inputLatency = 4,
|
||||
localModels = 5,
|
||||
prefToDefault = 6,
|
||||
}
|
||||
|
||||
optionTable = {
|
||||
[optionTableRef.openInputs] = {
|
||||
name = "Open Binds",
|
||||
toggle = tonumber(mod_storage_load("MenuInput")),
|
||||
toggleSaveName = "MenuInput",
|
||||
toggleDefault = 0,
|
||||
toggleMax = 2,
|
||||
toggleNames = {"None", ommActive and "D-pad Down + R" or "D-pad Down", "Z (Pause Menu)"},
|
||||
},
|
||||
[optionTableRef.menuColor] = {
|
||||
name = "Menu Color",
|
||||
toggle = tonumber(mod_storage_load("MenuColor")),
|
||||
toggleSaveName = "MenuColor",
|
||||
toggleDefault = 0,
|
||||
toggleMax = 9,
|
||||
toggleNames = {"Auto", "Red", "Orange", "Yellow", "Green", "Blue", "Pink", "Purple", "White", "Black"},
|
||||
},
|
||||
[optionTableRef.anims] = {
|
||||
name = "Animations",
|
||||
toggle = tonumber(mod_storage_load("Anims")),
|
||||
toggleSaveName = "Anims",
|
||||
toggleDefault = 1,
|
||||
toggleMax = 1,
|
||||
},
|
||||
[optionTableRef.inputLatency] = {
|
||||
name = "Scroll Speed",
|
||||
toggle = tonumber(mod_storage_load("Latency")),
|
||||
toggleSaveName = "Latency",
|
||||
toggleDefault = 1,
|
||||
toggleMax = 2,
|
||||
toggleNames = {"Slow", "Normal", "Fast"},
|
||||
},
|
||||
[optionTableRef.localModels] = {
|
||||
name = "Locally Display Models",
|
||||
toggle = tonumber(mod_storage_load("localModels")),
|
||||
toggleSaveName = "localModels",
|
||||
toggleDefault = 1,
|
||||
toggleMax = 1,
|
||||
},
|
||||
[optionTableRef.prefToDefault] = {
|
||||
name = "Set Preference to Default",
|
||||
toggle = 0,
|
||||
toggleDefault = 0,
|
||||
toggleMax = 1,
|
||||
toggleNames = {"", ""},
|
||||
},
|
||||
}
|
||||
|
||||
local defaultPlayerColors = {
|
||||
[CT_MARIO] = {r = 255, g = 50, b = 50},
|
||||
[CT_LUIGI] = {r = 50, g = 255, b = 50},
|
||||
[CT_TOAD] = {r = 100, g = 100, b = 255},
|
||||
[CT_WALUIGI] = {r = 130, g = 25, b = 130},
|
||||
[CT_WARIO] = {r = 255, g = 255, b = 50},
|
||||
}
|
||||
|
||||
local defaultIcons = {
|
||||
[CT_MARIO] = gTextures.mario_head,
|
||||
[CT_LUIGI] = gTextures.luigi_head,
|
||||
[CT_TOAD] = gTextures.toad_head,
|
||||
[CT_WALUIGI] = gTextures.waluigi_head,
|
||||
[CT_WARIO] = gTextures.wario_head,
|
||||
}
|
||||
|
||||
local latencyValueTable = {15, 10, 5}
|
||||
|
||||
local camera_freeze = camera_freeze
|
||||
local camera_unfreeze = camera_unfreeze
|
||||
local network_local_index_from_global = network_local_index_from_global
|
||||
local obj_set_model_extended = obj_set_model_extended
|
||||
local hud_hide = hud_hide
|
||||
local hud_show = hud_show
|
||||
local djui_chat_message_create = djui_chat_message_create
|
||||
local djui_hud_set_resolution = djui_hud_set_resolution
|
||||
local djui_hud_set_font = djui_hud_set_font
|
||||
local djui_hud_set_color = djui_hud_set_color
|
||||
local djui_hud_get_screen_width = djui_hud_get_screen_width
|
||||
local djui_hud_render_rect = djui_hud_render_rect
|
||||
local djui_hud_print_text = djui_hud_print_text
|
||||
local djui_hud_render_texture = djui_hud_render_texture
|
||||
local math_max = math.max
|
||||
local math_min = math.min
|
||||
local math_sin = math.sin
|
||||
local math_random = math.random
|
||||
local play_sound = play_sound
|
||||
local mod_storage_save = mod_storage_save
|
||||
local mod_storage_load = mod_storage_load
|
||||
local string_lower = string.lower
|
||||
|
||||
---@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 function load_preferred_char()
|
||||
if mod_storage_load("PrefChar") ~= nil and mod_storage_load("PrefChar") ~= "Default" then
|
||||
for i = 2, #characterTable do
|
||||
if characterTable[i].name == mod_storage_load("PrefChar") then
|
||||
currChar = i
|
||||
djui_popup_create('Character Select:\nYour Preferred Character\n"'..string_underscore_to_space(characterTable[i].name)..'"\nwas applied successfully!', 4)
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif mod_storage_load("PrefChar") == nil then
|
||||
mod_storage_save("PrefChar", "Default")
|
||||
end
|
||||
if #characterTable == 1 then
|
||||
djui_popup_create("Character Select:\nNo Characters were Found", 2)
|
||||
end
|
||||
TEXT_PREF_LOAD = mod_storage_load("PrefChar")
|
||||
end
|
||||
|
||||
local function failsafe_options()
|
||||
for i = 1, #optionTable do
|
||||
if optionTable[i].toggle == nil then
|
||||
optionTable[i].toggle = optionTable[i].toggleDefault
|
||||
if optionTable[i].toggleSaveName ~= nil then
|
||||
mod_storage_save(optionTable[i].toggleSaveName, tostring(optionTable[i].toggle))
|
||||
end
|
||||
end
|
||||
if optionTable[i].toggleNames == nil then
|
||||
optionTable[i].toggleNames = {"Off", "On"}
|
||||
end
|
||||
end
|
||||
if optionTable[optionTableRef.openInputs].toggle == 1 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
|
||||
|
||||
local hasOpenedMenu = false
|
||||
|
||||
local function idiot_proof_note()
|
||||
if mod_storage_load("openedmenu") == nil then
|
||||
if #characterTable > 1 then
|
||||
djui_chat_message_create("Character Select is active and has "..(#characterTable - 1).." characters available!\nYou can use \\#ffff00\\/char-select \\#ffffff\\to open the menu!")
|
||||
else
|
||||
djui_chat_message_create("Character Select is active!\nYou can use \\#ffff00\\/char-select \\#ffffff\\to open the menu!")
|
||||
end
|
||||
else
|
||||
hasOpenedMenu = true
|
||||
end
|
||||
end
|
||||
|
||||
-------------------
|
||||
-- Model Handler --
|
||||
-------------------
|
||||
|
||||
local stallFrame = 0
|
||||
local noLoop = false
|
||||
|
||||
-- Respecfully, GO FUCK YOURSELVES. I hate EVERY SINGLE ONE OF YOU. Your lives are NOTHING. You serve ZERO PURPOSE. You should kill yourselves, NOW!
|
||||
local ignored_surfaces = {
|
||||
SURFACE_BURNING, SURFACE_QUICKSAND, SURFACE_INSTANT_QUICKSAND, SURFACE_INSTANT_MOVING_QUICKSAND, SURFACE_DEEP_MOVING_QUICKSAND, SURFACE_INSTANT_QUICKSAND, SURFACE_DEEP_QUICKSAND, SURFACE_SHALLOW_MOVING_QUICKSAND,
|
||||
SURFACE_SHALLOW_QUICKSAND, SURFACE_WARP, SURFACE_LOOK_UP_WARP, SURFACE_WOBBLING_WARP, SURFACE_INSTANT_WARP_1B, SURFACE_INSTANT_WARP_1C, SURFACE_INSTANT_WARP_1D, SURFACE_INSTANT_WARP_1E
|
||||
}
|
||||
-- Yes, floral gave me permission to use this table full of USELESS PIECES OF SHITS
|
||||
|
||||
--- @param m MarioState
|
||||
local function mario_update(m)
|
||||
if stallFrame == 1 then
|
||||
load_preferred_char()
|
||||
failsafe_options()
|
||||
idiot_proof_note()
|
||||
end
|
||||
|
||||
if stallFrame < 2 then
|
||||
stallFrame = stallFrame + 1
|
||||
end
|
||||
|
||||
if m.playerIndex == 0 and stallFrame > 1 then
|
||||
characterTable[1].forceChar = gNetworkPlayers[m.playerIndex].modelIndex
|
||||
if currChar == 1 then
|
||||
characterTable[1].color = defaultPlayerColors[gNetworkPlayers[m.playerIndex].modelIndex]
|
||||
characterTable[1].lifeIcon = defaultIcons[gNetworkPlayers[m.playerIndex].modelIndex]
|
||||
end
|
||||
if optionTable[optionTableRef.localModels].toggle > 0 then
|
||||
gPlayerSyncTable[0].modelId = characterTable[currChar].model
|
||||
gPlayerSyncTable[0].capModels = characterTable[currChar].capModels
|
||||
if characterTable[currChar].forceChar ~= nil then
|
||||
gNetworkPlayers[m.playerIndex].overrideModelIndex = characterTable[currChar].forceChar
|
||||
end
|
||||
else
|
||||
gPlayerSyncTable[0].modelId = nil
|
||||
gPlayerSyncTable[0].capModels = nil
|
||||
gNetworkPlayers[m.playerIndex].overrideModelIndex = characterTable[1].forceChar
|
||||
end
|
||||
|
||||
if menu then
|
||||
for _, func in pairs(allowMenu) do
|
||||
if not func() then
|
||||
menu = false
|
||||
return
|
||||
end
|
||||
end
|
||||
camera_freeze()
|
||||
hud_hide()
|
||||
if _G.PersonalStarCounter then
|
||||
_G.PersonalStarCounter.hide_star_counters(true)
|
||||
end
|
||||
local focusPos = {
|
||||
x = m.pos.x,
|
||||
y = m.pos.y + 120,
|
||||
z = m.pos.z,
|
||||
}
|
||||
vec3f_copy(gLakituState.focus, focusPos)
|
||||
gLakituState.pos.x = m.pos.x + sins(m.faceAngle.y) * 500
|
||||
gLakituState.pos.y = m.pos.y + 100
|
||||
gLakituState.pos.z = m.pos.z + coss(m.faceAngle.y) * 500
|
||||
|
||||
if m.forwardVel == 0 and m.pos.y == m.floorHeight and not ignored_surfaces[m.floor.type] and m.health > 255 then
|
||||
set_mario_action(m, ACT_IDLE, 0)
|
||||
set_mario_animation(m, MARIO_ANIM_STAR_DANCE)
|
||||
end
|
||||
noLoop = false
|
||||
elseif not noLoop then
|
||||
camera_unfreeze()
|
||||
hud_show()
|
||||
if _G.PersonalStarCounter then
|
||||
_G.PersonalStarCounter.hide_star_counters(false)
|
||||
end
|
||||
noLoop = true
|
||||
end
|
||||
end
|
||||
|
||||
--Set Pref to Default Check
|
||||
if optionTable[optionTableRef.prefToDefault].toggle > 0 then
|
||||
mod_storage_save("PrefChar", "Default")
|
||||
TEXT_PREF_LOAD = "Default"
|
||||
optionTable[optionTableRef.prefToDefault].toggle = 0
|
||||
end
|
||||
end
|
||||
|
||||
function set_model(o, model)
|
||||
if obj_has_behavior_id(o, id_bhvMario) ~= 0 then
|
||||
local i = network_local_index_from_global(o.globalPlayerIndex)
|
||||
if gPlayerSyncTable[i].modelId ~= nil and obj_has_model_extended(o, gPlayerSyncTable[i].modelId) == 0 then
|
||||
obj_set_model_extended(o, gPlayerSyncTable[i].modelId)
|
||||
return
|
||||
end
|
||||
end
|
||||
if get_object_list_from_behavior(o.behavior) == OBJ_LIST_LEVEL then
|
||||
local i = network_local_index_from_global(o.globalPlayerIndex)
|
||||
local capModels = gPlayerSyncTable[i].capModels
|
||||
local capModel = nil
|
||||
if capModels ~= nil then
|
||||
if model == gMarioStates[i].character.capModelId then
|
||||
capModel = capModels.normal
|
||||
elseif model == gMarioStates[i].character.capWingModelId then
|
||||
capModel = capModels.wing
|
||||
elseif model == gMarioStates[i].character.capMetalModelId then
|
||||
capModel = capModels.metal
|
||||
elseif model == gMarioStates[i].character.capMetalWingModelId then
|
||||
capModel = capModels.metalWing
|
||||
end
|
||||
if capModel ~= nil and obj_has_model_extended(o, capModel) == 0 then
|
||||
obj_set_model_extended(o, capModel)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook_event(HOOK_MARIO_UPDATE, mario_update)
|
||||
hook_event(HOOK_OBJECT_SET_MODEL, set_model)
|
||||
|
||||
------------------
|
||||
-- Menu Handler --
|
||||
------------------
|
||||
|
||||
local buttonAnimTimer = 0
|
||||
local buttonScroll = 0
|
||||
local buttonScrollCap = 30
|
||||
|
||||
local optionAnimTimer = -200
|
||||
local optionAnimTimerCap = optionAnimTimer
|
||||
|
||||
local inputStallTimer = 0
|
||||
local inputStallTo = 15
|
||||
|
||||
local TEXT_OPTIONS_HEADER = "Menu Options"
|
||||
local TEXT_RATIO_UNSUPPORTED = "Your Current Aspect-Ratio isn't Supported!"
|
||||
local TEXT_DESCRIPTION = "Character Description:"
|
||||
local TEXT_PREF_SAVE = "Press A to Set as Preferred Character"
|
||||
local TEXT_PAUSE_Z_OPEN = "Z Button - Character Select"
|
||||
local TEXT_PAUSE_CURR_CHAR = "Current Character: "
|
||||
if math_random(100) == 64 then -- Easter Egg if you get lucky loading the mod >v<
|
||||
TEXT_PAUSE_Z_OPEN = "Z - DynOS" -- Referencing the original sm64ex DynOS options
|
||||
TEXT_PAUSE_CURR_CHAR = "Model: "
|
||||
end
|
||||
local TEXT_OPTIONS_OPEN = "Press START to open Options"
|
||||
local TEXT_MENU_CLOSE = "Press B to Exit Menu"
|
||||
local TEXT_OPTIONS_SELECT = "A - Select | B - Exit "
|
||||
local TEXT_LOCAL_MODEL_OFF = "Locally Display Models is Off"
|
||||
local TEXT_LOCAL_MODEL_OFF_OPTIONS = "You can turn it back on in the Options Menu"
|
||||
|
||||
local menuColor = characterTable[currChar].color
|
||||
|
||||
local function on_hud_render()
|
||||
djui_hud_set_resolution(RESOLUTION_N64)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
|
||||
local width = djui_hud_get_screen_width() + 1.4
|
||||
local height = 240
|
||||
local widthHalf = width*0.5
|
||||
local heightHalf = height*0.5
|
||||
local widthScale = math_max(width, 321.4)*0.00311332503
|
||||
|
||||
if menu then
|
||||
if optionTable[optionTableRef.menuColor].toggle ~= 0 then
|
||||
menuColor = menuColorTable[optionTable[optionTableRef.menuColor].toggle]
|
||||
else
|
||||
menuColor = characterTable[currChar].color
|
||||
end
|
||||
|
||||
if optionTable[optionTableRef.localModels].toggle == 0 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_OFF, widthHalf - djui_hud_measure_text(TEXT_LOCAL_MODEL_OFF)*0.15*widthScale, heightHalf, 0.3 * widthScale)
|
||||
djui_hud_print_text(TEXT_LOCAL_MODEL_OFF_OPTIONS, widthHalf - djui_hud_measure_text(TEXT_LOCAL_MODEL_OFF_OPTIONS)*0.1*widthScale, heightHalf + 10*widthScale, 0.2*widthScale)
|
||||
end
|
||||
|
||||
--Character Buttons
|
||||
local x = 135 * widthScale * 0.8
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(0, 0, x, height)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(2, 2, x - 4, height - 4)
|
||||
|
||||
if optionTable[optionTableRef.anims].toggle > 0 then
|
||||
buttonAnimTimer = buttonAnimTimer + 1
|
||||
end
|
||||
if optionTable[optionTableRef.anims].toggle == 0 then
|
||||
buttonScroll = 0
|
||||
elseif buttonScroll > 0.1 or buttonScroll < -0.1 then
|
||||
buttonScroll = buttonScroll*0.03*inputStallTo
|
||||
end
|
||||
|
||||
local buttonColor = {}
|
||||
for i = -1, 4 do
|
||||
if characterTable[i + currChar] ~= nil then
|
||||
buttonColor = characterTable[i + currChar].color
|
||||
djui_hud_set_color(buttonColor.r, buttonColor.g, buttonColor.b, 255)
|
||||
local buttonX = 20 * widthScale
|
||||
if optionTable[optionTableRef.anims].toggle > 0 then
|
||||
if i == 0 then buttonX = buttonX + math_sin(buttonAnimTimer*0.05)*2.5 + 5 end
|
||||
else
|
||||
if i == 0 then buttonX = buttonX + 10 end
|
||||
end
|
||||
local y = (i + 3) * 30 + buttonScroll
|
||||
djui_hud_render_rect(buttonX, y, 70, 20)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(buttonX + 1, y + 1, 68, 18)
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
djui_hud_set_color(buttonColor.r, buttonColor.g, buttonColor.b, 255)
|
||||
djui_hud_print_text(string_underscore_to_space(characterTable[currChar + i].name), buttonX + 5, y + 5, 0.6)
|
||||
end
|
||||
end
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(0, height-2, x, 2)
|
||||
|
||||
-- Scroll Bar
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(7 * widthScale, 55, 7, 180)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(7 * widthScale + 1, 56, 5, 178)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(7 * widthScale + 2, 57 + 176 * ((currChar - 1) / #characterTable) - (buttonScroll*0.03333333333)*(176/#characterTable), 3, 176/#characterTable)
|
||||
|
||||
|
||||
--Character Description
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(width - x, 0, x, height)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(width - x + 2, 2, x - 4, height - 4)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
|
||||
local TEXT_NAME = string_underscore_to_space(characterTable[currChar].name)
|
||||
local TEXT_CREDIT = "By: "..characterTable[currChar].credit
|
||||
local TEXT_DESCRIPTION_TABLE = characterTable[currChar].description
|
||||
local TEXT_PREF = 'Preferred Character: "'..string_underscore_to_space(TEXT_PREF_LOAD)..'"'
|
||||
|
||||
local textX = x * 0.5
|
||||
djui_hud_print_text(TEXT_NAME, width - textX - djui_hud_measure_text(TEXT_NAME)*0.3, 55, 0.6)
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
djui_hud_print_text(TEXT_CREDIT, width - textX - djui_hud_measure_text(TEXT_CREDIT)*0.3, 72, 0.6)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_print_text(TEXT_DESCRIPTION, width - textX - djui_hud_measure_text(TEXT_DESCRIPTION)*0.2, 85, 0.4)
|
||||
for i = 1, #TEXT_DESCRIPTION_TABLE do
|
||||
djui_hud_print_text(TEXT_DESCRIPTION_TABLE[i], width - textX - djui_hud_measure_text(TEXT_DESCRIPTION_TABLE[i])*0.15, 90 + i*9, 0.3)
|
||||
end
|
||||
djui_hud_print_text(TEXT_PREF, width - textX - djui_hud_measure_text(TEXT_PREF)*0.15, height - 20, 0.3)
|
||||
djui_hud_print_text(TEXT_PREF_SAVE, width - textX - djui_hud_measure_text(TEXT_PREF_SAVE)*0.15, height - 30, 0.3)
|
||||
|
||||
--Character Select Header
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(0, 0, width, 50)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(2, 2, width - 4, 46)
|
||||
djui_hud_set_color(menuColor.r * 0.5 + 127, menuColor.g * 0.5 + 127, menuColor.b * 0.5 + 127, 255)
|
||||
djui_hud_render_texture(TEX_HEADER, widthHalf - 128, 10, 1, 1)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
djui_hud_print_text("Version: "..modVersion, 5, 3, 0.5)
|
||||
--Unsupported Res Warning
|
||||
if width < 321.2 or width > 575 then
|
||||
djui_hud_print_text(TEXT_RATIO_UNSUPPORTED, 5, 39, 0.5)
|
||||
end
|
||||
|
||||
--Options display
|
||||
if options or optionAnimTimer > optionAnimTimerCap then
|
||||
djui_hud_set_color(menuColor.r * 0.25, menuColor.g * 0.25, menuColor.b * 0.25, 205 + math_max(-200, optionAnimTimer))
|
||||
djui_hud_render_rect(0, 0, width, height)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(width*0.5 - 50 * widthScale, math.min(55 - optionAnimTimer, height - 25 * widthScale), 100 * widthScale, 200)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(width*0.5 - 50 * widthScale + 2, math.min(55 - optionAnimTimer + 2, height - 25 * widthScale + 2), 100 * widthScale - 4, 196)
|
||||
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)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_set_color(menuColor.r * 0.5 + 127, menuColor.g * 0.5 + 127, menuColor.b * 0.5 + 127, 255)
|
||||
djui_hud_print_text(TEXT_OPTIONS_HEADER, widthHalf - djui_hud_measure_text(TEXT_OPTIONS_HEADER)*0.3*widthScale, 65 + optionAnimTimer * -1, 0.6*widthScale)
|
||||
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
for i = 1, #optionTable do
|
||||
local toggleName = optionTable[i].name
|
||||
local scale = 0.5
|
||||
local yOffset = 70 + 10 * math_min(widthScale, 1.8) + i * 9 * math_min(widthScale, 1.8) - optionAnimTimer
|
||||
if i == currOption then
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
scale = 0.3
|
||||
yOffset = yOffset - 1
|
||||
if optionTable[i].toggleNames[optionTable[i].toggle + 1] ~= "" then
|
||||
toggleName = "> " .. toggleName .. " - " .. optionTable[i].toggleNames[optionTable[i].toggle + 1]
|
||||
else
|
||||
toggleName = "> " .. toggleName
|
||||
end
|
||||
else
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
end
|
||||
scale = scale * math_min(widthScale, 1.8)
|
||||
djui_hud_print_text(toggleName, widthHalf - djui_hud_measure_text(toggleName)*scale*0.5, yOffset, scale)
|
||||
end
|
||||
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
djui_hud_print_text(TEXT_OPTIONS_SELECT, widthHalf - djui_hud_measure_text(TEXT_OPTIONS_SELECT)*0.3, height - 20 - optionAnimTimer, 0.6)
|
||||
else
|
||||
-- How to open options display
|
||||
local widthScaleUnlimited = widthScale
|
||||
local widthScale = math_min(widthScale, 1.42)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(widthHalf - 50 * widthScaleUnlimited, height - 25 * widthScale, 100 * widthScaleUnlimited, 26 * widthScale)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_render_rect(widthHalf - 50 * widthScaleUnlimited + 2, height - 25 * widthScale + 2, 100 * widthScaleUnlimited - 4, 22 * widthScale)
|
||||
djui_hud_set_color(menuColor.r, menuColor.g, menuColor.b, 255)
|
||||
djui_hud_render_rect(widthHalf - 50 * widthScaleUnlimited, height - 2, 100 * widthScaleUnlimited, 2)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_print_text(TEXT_OPTIONS_OPEN, widthHalf - djui_hud_measure_text(TEXT_OPTIONS_OPEN)*0.175 * widthScale, height - 23 * widthScale + optionAnimTimer + 202, 0.35 * widthScale)
|
||||
djui_hud_set_font(FONT_TINY)
|
||||
djui_hud_print_text(TEXT_MENU_CLOSE, widthHalf - djui_hud_measure_text(TEXT_MENU_CLOSE)*0.25 * widthScale, height - 13 * widthScale + optionAnimTimer + 202, 0.5 * widthScale)
|
||||
end
|
||||
|
||||
-- Anim logic
|
||||
if options then
|
||||
if optionTable[optionTableRef.anims].toggle > 0 then
|
||||
if optionAnimTimer < -1 then
|
||||
optionAnimTimer = optionAnimTimer/1.1
|
||||
end
|
||||
else
|
||||
optionAnimTimer = -1
|
||||
end
|
||||
else
|
||||
if optionTable[optionTableRef.anims].toggle > 0 then
|
||||
if optionAnimTimer > optionAnimTimerCap then
|
||||
optionAnimTimer = optionAnimTimer*1.2
|
||||
end
|
||||
else
|
||||
optionAnimTimer = optionAnimTimerCap
|
||||
end
|
||||
end
|
||||
optionAnimTimer = math_max(optionAnimTimer, -200)
|
||||
else
|
||||
options = false
|
||||
optionAnimTimer = optionAnimTimerCap
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if optionTable[optionTableRef.openInputs].toggle == 2 then
|
||||
currCharY = 27
|
||||
local width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_PAUSE_Z_OPEN)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_print_text(TEXT_PAUSE_Z_OPEN, width - 19, 17, 1)
|
||||
djui_hud_set_color(255, 255, 255, 255)
|
||||
djui_hud_print_text(TEXT_PAUSE_Z_OPEN, width - 20, 16, 1)
|
||||
end
|
||||
|
||||
if optionTable[optionTableRef.localModels].toggle == 1 then
|
||||
local charName = string_underscore_to_space(characterTable[currChar].name)
|
||||
local TEXT_PAUSE_CURR_CHAR_WITH_NAME = TEXT_PAUSE_CURR_CHAR..charName
|
||||
local width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_PAUSE_CURR_CHAR_WITH_NAME)
|
||||
local charColor = characterTable[currChar].color
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_print_text(TEXT_PAUSE_CURR_CHAR_WITH_NAME, width - 19, 17 + currCharY, 1)
|
||||
djui_hud_set_color(255, 255, 255, 255)
|
||||
djui_hud_print_text(TEXT_PAUSE_CURR_CHAR, width - 20, 16 + currCharY, 1)
|
||||
djui_hud_set_color(charColor.r, charColor.g, charColor.b, 255)
|
||||
djui_hud_print_text(charName, djui_hud_get_screen_width() - djui_hud_measure_text(charName) - 20, 16 + currCharY, 1)
|
||||
else
|
||||
local width = djui_hud_get_screen_width() - djui_hud_measure_text(TEXT_LOCAL_MODEL_OFF)
|
||||
djui_hud_set_font(FONT_NORMAL)
|
||||
djui_hud_set_color(0, 0, 0, 255)
|
||||
djui_hud_print_text(TEXT_LOCAL_MODEL_OFF, width - 19, 17 + currCharY, 1)
|
||||
djui_hud_set_color(255, 255, 255, 255)
|
||||
djui_hud_print_text(TEXT_LOCAL_MODEL_OFF, width - 20, 16 + currCharY, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Custom life icon rendering (Thanks LuigiGamer)
|
||||
function on_life_counter_render()
|
||||
if obj_get_first_with_behavior_id(id_bhvActSelector) ~= nil then return end
|
||||
-- Rendering settings --
|
||||
djui_hud_set_font(FONT_HUD)
|
||||
djui_hud_set_resolution(RESOLUTION_N64)
|
||||
djui_hud_set_color(255, 255, 255, 255);
|
||||
|
||||
-- Texture scale --
|
||||
|
||||
-- Texture position --
|
||||
local x = 22
|
||||
local y = 15
|
||||
|
||||
-- Texture Rendering --
|
||||
if gNetworkPlayers[0].currActNum == 99 then return end
|
||||
if not hud_is_hidden() then
|
||||
local icon = characterTable[currChar].lifeIcon
|
||||
djui_hud_render_texture(icon, x, y, 1/(icon.width/16), 1/(icon.height/16))
|
||||
djui_hud_print_text("@", x + 16, y, 1)
|
||||
djui_hud_print_text(tostring(gMarioStates[0].numLives), x + 32, y, 1)
|
||||
hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) &~ HUD_DISPLAY_FLAG_LIVES) -- Hides the lives counter
|
||||
else
|
||||
hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) | HUD_DISPLAY_FLAG_LIVES) -- Shows the lives counter, use this when you're no longer using a custom character
|
||||
end
|
||||
end
|
||||
|
||||
local function before_mario_update(m)
|
||||
if m.playerIndex ~= 0 then return end
|
||||
if inputStallTimer > 0 then inputStallTimer = inputStallTimer - 1 end
|
||||
|
||||
if menu and inputStallTo ~= latencyValueTable[optionTable[optionTableRef.inputLatency].toggle + 1] then
|
||||
inputStallTo = latencyValueTable[optionTable[optionTableRef.inputLatency].toggle + 1]
|
||||
end
|
||||
|
||||
-- Menu Inputs
|
||||
if not menu and (m.controller.buttonDown & D_JPAD) ~= 0 and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and optionTable[optionTableRef.openInputs].toggle == 1 then
|
||||
if ommActive then
|
||||
if (m.controller.buttonDown & R_TRIG) ~= 0 then
|
||||
menu = true
|
||||
end
|
||||
else
|
||||
menu = true
|
||||
end
|
||||
inputStallTimer = inputStallTo
|
||||
end
|
||||
if is_game_paused() and m.action ~= ACT_EXIT_LAND_SAVE_DIALOG and (m.controller.buttonPressed & Z_TRIG) ~= 0 and optionTable[optionTableRef.openInputs].toggle == 2 then
|
||||
menu = true
|
||||
end
|
||||
|
||||
local cameraToObject = gMarioStates[0].marioObj.header.gfx.cameraToObject
|
||||
|
||||
-- C-up Failsafe (Camera Softlocks)
|
||||
if m.action == ACT_FIRST_PERSON or (m.prevAction == ACT_FIRST_PERSON and is_game_paused()) then
|
||||
menu = false
|
||||
elseif m.prevAction == ACT_FIRST_PERSON and not is_game_paused() then
|
||||
m.prevAction = ACT_WALKING
|
||||
end
|
||||
|
||||
if gNetworkPlayers[0].currActNum == 99 then
|
||||
menu = false
|
||||
end
|
||||
|
||||
if menu and not options then
|
||||
if inputStallTimer == 0 then
|
||||
if (m.controller.buttonPressed & D_JPAD) ~= 0 then
|
||||
currChar = currChar + 1
|
||||
inputStallTimer = inputStallTo
|
||||
buttonScroll = buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & U_JPAD) ~= 0 then
|
||||
currChar = currChar - 1
|
||||
inputStallTimer = inputStallTo
|
||||
buttonScroll = -buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & D_CBUTTONS) ~= 0 then
|
||||
currChar = currChar + 1
|
||||
inputStallTimer = inputStallTo*0.6
|
||||
buttonScroll = buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & U_CBUTTONS) ~= 0 then
|
||||
currChar = currChar - 1
|
||||
inputStallTimer = inputStallTo*0.6
|
||||
buttonScroll = -buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if m.controller.stickY < -60 then
|
||||
currChar = currChar + 1
|
||||
inputStallTimer = inputStallTo
|
||||
buttonScroll = buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if m.controller.stickY > 60 then
|
||||
currChar = currChar - 1
|
||||
inputStallTimer = inputStallTo
|
||||
buttonScroll = -buttonScrollCap
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & A_BUTTON) ~= 0 then
|
||||
if characterTable[currChar] ~= nil then
|
||||
TEXT_PREF_LOAD = characterTable[currChar].name
|
||||
mod_storage_save("PrefChar", TEXT_PREF_LOAD)
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_CLICK_FILE_SELECT, cameraToObject)
|
||||
else
|
||||
play_sound(SOUND_MENU_CAMERA_BUZZ, cameraToObject)
|
||||
end
|
||||
end
|
||||
if (m.controller.buttonPressed & B_BUTTON) ~= 0 then
|
||||
menu = false
|
||||
end
|
||||
if (m.controller.buttonPressed & START_BUTTON) ~= 0 then
|
||||
options = true
|
||||
end
|
||||
end
|
||||
if currChar > #characterTable then currChar = 1 end
|
||||
if currChar < 1 then currChar = #characterTable end
|
||||
nullify_inputs(m)
|
||||
if is_game_paused() then
|
||||
m.controller.buttonPressed = START_BUTTON
|
||||
end
|
||||
|
||||
-- Idiot Proof Check
|
||||
if not hasOpenedMenu then
|
||||
mod_storage_save("openedmenu", "youdidit")
|
||||
hasOpenedMenu = true
|
||||
end
|
||||
end
|
||||
|
||||
if options then
|
||||
if inputStallTimer == 0 then
|
||||
if (m.controller.buttonPressed & D_JPAD) ~= 0 then
|
||||
currOption = currOption + 1
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & U_JPAD) ~= 0 then
|
||||
currOption = currOption - 1
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if m.controller.stickY < -60 then
|
||||
currOption = currOption + 1
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if m.controller.stickY > 60 then
|
||||
currOption = currOption - 1
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & A_BUTTON) ~= 0 then
|
||||
optionTable[currOption].toggle = optionTable[currOption].toggle + 1
|
||||
if optionTable[currOption].toggle > optionTable[currOption].toggleMax then optionTable[currOption].toggle = 0 end
|
||||
if optionTable[currOption].toggleSaveName ~= nil then
|
||||
mod_storage_save(optionTable[currOption].toggleSaveName, tostring(optionTable[currOption].toggle))
|
||||
end
|
||||
inputStallTimer = inputStallTo
|
||||
play_sound(SOUND_MENU_CHANGE_SELECT, cameraToObject)
|
||||
end
|
||||
if (m.controller.buttonPressed & B_BUTTON) ~= 0 then
|
||||
options = false
|
||||
inputStallTimer = inputStallTo
|
||||
end
|
||||
end
|
||||
if currOption > #optionTable then currOption = 1 end
|
||||
if currOption < 1 then currOption = #optionTable end
|
||||
nullify_inputs(m)
|
||||
end
|
||||
end
|
||||
|
||||
hook_event(HOOK_BEFORE_MARIO_UPDATE, before_mario_update)
|
||||
hook_event(HOOK_ON_HUD_RENDER, on_hud_render)
|
||||
hook_event(HOOK_ON_HUD_RENDER_BEHIND, on_life_counter_render)
|
||||
|
||||
--------------
|
||||
-- Commands --
|
||||
--------------
|
||||
|
||||
local function chat_command(msg)
|
||||
if msg ~= "" then
|
||||
msg = string_lower(msg)
|
||||
for i = 1, #characterTable do
|
||||
if msg == string_underscore_to_space(string_lower(characterTable[i].name)) then
|
||||
currChar = i
|
||||
return true
|
||||
end
|
||||
end
|
||||
djui_chat_message_create("Character Not Found")
|
||||
return true
|
||||
else
|
||||
menu = not menu
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
hook_chat_command("char-select", "- Opens the Character Select Menu", chat_command)
|
147
mods/character-select-coop/o-api.lua
Normal file
147
mods/character-select-coop/o-api.lua
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
|
||||
local characterVoices = {}
|
||||
|
||||
local TEX_UNKNOWN_CHAR = get_texture_info("unknown-icon")
|
||||
|
||||
local E_MODEL_ARMATURE = smlua_model_util_get_id("armature_geo")
|
||||
|
||||
local table_insert = table.insert
|
||||
local type = type
|
||||
|
||||
---------
|
||||
-- API --
|
||||
---------
|
||||
|
||||
---@param name string|nil Underscores turn into Spaces
|
||||
---@param description table|nil {"string"}
|
||||
---@param credit string|nil
|
||||
---@param color Color|nil {r, g, b}
|
||||
---@param modelInfo ModelExtendedId|table|nil Use smlua_model_util_get_id()
|
||||
---@param forceChar CharacterType|nil CT_MARIO, CT_LUIGI, CT_TOAD, CT_WALUIGI, CT_WARIO
|
||||
---@param lifeIcon TextureInfo|nil Use get_texture_info()
|
||||
---@return integer
|
||||
local character_add = function(name, description, credit, color, modelInfo, forceChar, lifeIcon)
|
||||
table_insert(characterTable, {
|
||||
name = name and string_space_to_underscore(name) or "Untitled",
|
||||
description = description and description or {"No description has been provided"},
|
||||
credit = credit and credit or "Unknown",
|
||||
color = color and color or menuColorTable[8],
|
||||
model = modelInfo and (type(modelInfo) == "table" and modelInfo[1] or modelInfo) or E_MODEL_ARMATURE,
|
||||
capModels = type(modelInfo) == "table" and modelInfo[2] or nil,
|
||||
forceChar = forceChar and forceChar or CT_MARIO,
|
||||
lifeIcon = lifeIcon and lifeIcon or TEX_UNKNOWN_CHAR,
|
||||
})
|
||||
return #characterTable
|
||||
end
|
||||
|
||||
---@param charNum integer Use _G.charSelect.character_get_number_from_string() or _G.charSelect.character_add()'s return value
|
||||
---@param name string|nil Underscores turn into Spaces
|
||||
---@param description table|nil {"string"}
|
||||
---@param credit string|nil
|
||||
---@param color Color|nil {r, g, b}
|
||||
---@param modelInfo ModelExtendedId|integer|table|nil Use smlua_model_util_get_id()
|
||||
---@param forceChar CharacterType|nil CT_MARIO, CT_LUIGI, CT_TOAD, CT_WALUIGI, CT_WARIO
|
||||
---@param lifeIcon TextureInfo|nil Use get_texture_info()
|
||||
local character_edit = function(charNum, name, description, credit, color, modelInfo, forceChar, lifeIcon)
|
||||
characterTable[charNum] = characterTable[charNum] and {
|
||||
name = name and string_space_to_underscore(name) or characterTable[charNum].name,
|
||||
description = description and description or characterTable[charNum].description,
|
||||
credit = credit and credit or characterTable[charNum].credit,
|
||||
color = color and color or characterTable[charNum].color,
|
||||
model = modelInfo and (type(modelInfo) == "table" and modelInfo[1] or modelInfo) or characterTable[charNum].model,
|
||||
capModels = type(modelInfo) == "table" and modelInfo[2] or characterTable[charNum].capModels,
|
||||
forceChar = forceChar and forceChar or characterTable[charNum].forceChar,
|
||||
lifeIcon = lifeIcon and lifeIcon or characterTable[charNum].lifeIcon,
|
||||
} or nil
|
||||
end
|
||||
|
||||
---@param modelInfo ModelExtendedId|integer
|
||||
---@param clips table
|
||||
local character_add_voice = function(modelInfo, clips)
|
||||
characterVoices[modelInfo] = clips
|
||||
end
|
||||
|
||||
---@return table
|
||||
local character_get_current_table = function ()
|
||||
return characterTable[currChar]
|
||||
end
|
||||
|
||||
local character_get_current_model_number = function ()
|
||||
return currChar
|
||||
end
|
||||
|
||||
---@param name string
|
||||
local character_get_number_from_string = function (name)
|
||||
for i = 2, #characterTable do
|
||||
if characterTable[i].name == name or characterTable[i].name == string_space_to_underscore(name) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param m MarioState
|
||||
local character_get_voice = function (m)
|
||||
return characterVoices[gPlayerSyncTable[m.playerIndex].modelId]
|
||||
end
|
||||
|
||||
local version_get = function ()
|
||||
return modVersion
|
||||
end
|
||||
|
||||
local is_menu_open = function ()
|
||||
return menu
|
||||
end
|
||||
|
||||
local hook_allow_menu_open = function (func)
|
||||
table.insert(allowMenu, func)
|
||||
end
|
||||
|
||||
local is_options_open = function ()
|
||||
return options
|
||||
end
|
||||
|
||||
local optionTableRef = {
|
||||
openInputs = 1,
|
||||
menuColor = 2,
|
||||
anims = 3,
|
||||
inputLatency = 4,
|
||||
localModels = 5,
|
||||
prefToDefault = 6,
|
||||
}
|
||||
|
||||
local controller = {
|
||||
buttonDown = 0,
|
||||
buttonPressed = 0,
|
||||
extStickX = 0,
|
||||
extStickY = 0,
|
||||
rawStickX = 0,
|
||||
rawStickY = 0,
|
||||
stickMag = 0,
|
||||
stickX = 0,
|
||||
stickY = 0
|
||||
}
|
||||
|
||||
---@param tableNum integer
|
||||
local get_status = function (tableNum)
|
||||
return optionTable[tableNum].toggle
|
||||
end
|
||||
|
||||
_G.charSelectExists = true -- Ace
|
||||
_G.charSelect = {
|
||||
character_add = character_add,
|
||||
character_edit = character_edit,
|
||||
character_add_voice = character_add_voice,
|
||||
character_get_current_table = character_get_current_table,
|
||||
character_get_current_model_number = character_get_current_model_number,
|
||||
character_get_number_from_string = character_get_number_from_string,
|
||||
character_get_voice = character_get_voice,
|
||||
version_get = version_get,
|
||||
is_menu_open = is_menu_open,
|
||||
hook_allow_menu_open = hook_allow_menu_open,
|
||||
is_options_open = is_options_open,
|
||||
get_status = get_status,
|
||||
optionTableRef = optionTableRef,
|
||||
controller = controller,
|
||||
}
|
BIN
mods/character-select-coop/textures/char-select-text.png
Normal file
BIN
mods/character-select-coop/textures/char-select-text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 458 B |
BIN
mods/character-select-coop/textures/char-select-text.tex
Normal file
BIN
mods/character-select-coop/textures/char-select-text.tex
Normal file
Binary file not shown.
BIN
mods/character-select-coop/textures/unknown-icon.png
Normal file
BIN
mods/character-select-coop/textures/unknown-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 384 B |
BIN
mods/character-select-coop/textures/unknown-icon.tex
Normal file
BIN
mods/character-select-coop/textures/unknown-icon.tex
Normal file
Binary file not shown.
151
mods/character-select-coop/voice.lua
Normal file
151
mods/character-select-coop/voice.lua
Normal file
|
@ -0,0 +1,151 @@
|
|||
for i = 0, MAX_PLAYERS -1, 1 do
|
||||
gPlayerSyncTable[i].customVoice = 0
|
||||
end
|
||||
|
||||
local voicecount = 0
|
||||
|
||||
local SLEEP_TALK_SNORES = 8
|
||||
|
||||
gCustomVoiceSamples = {}
|
||||
gCustomVoiceSamplesBackup = {}
|
||||
gCustomVoiceStream = nil
|
||||
|
||||
--- @param m MarioState
|
||||
function stop_custom_character_sound(m, sound)
|
||||
local voice_sample = gCustomVoiceSamples[m.playerIndex]
|
||||
if voice_sample == false and gCustomVoiceSamplesBackup[m.playerIndex].loaded then
|
||||
audio_sample_stop(gCustomVoiceSamplesBackup[m.playerIndex])
|
||||
audio_sample_destroy(gCustomVoiceSamplesBackup[m.playerIndex])
|
||||
voicecount = voicecount - 1
|
||||
gCustomVoiceSamplesBackup[m.playerIndex] = nil
|
||||
return
|
||||
end
|
||||
if voice_sample == nil or type(voice_sample) == "boolean" then
|
||||
return
|
||||
end
|
||||
if not voice_sample.loaded then
|
||||
gCustomVoiceSamplesBackup[m.playerIndex] = true
|
||||
return
|
||||
end
|
||||
|
||||
audio_sample_stop(voice_sample)
|
||||
if voice_sample.file.relativePath:match('^.+/(.+)$') == sound then
|
||||
return voice_sample
|
||||
end
|
||||
audio_sample_destroy(voice_sample)
|
||||
voicecount = voicecount - 1
|
||||
end
|
||||
|
||||
--- @param m MarioState
|
||||
function play_custom_character_sound(m, voice)
|
||||
local sound
|
||||
if type(voice) == "table" then
|
||||
sound = voice[math.random(#voice)]
|
||||
else
|
||||
sound = voice
|
||||
end
|
||||
if sound == nil then return 0 end
|
||||
|
||||
--Get current sample and stop it
|
||||
local voice_sample = stop_custom_character_sound(m, sound)
|
||||
|
||||
if type(sound) ~= "string" then
|
||||
return sound
|
||||
end
|
||||
|
||||
if (m.area == nil or m.area.camera == nil) and m.playerIndex == 0 then
|
||||
if gCustomVoiceStream ~= nil then
|
||||
audio_stream_stop(gCustomVoiceStream)
|
||||
audio_stream_destroy(gCustomVoiceStream)
|
||||
end
|
||||
gCustomVoiceStream = audio_stream_load(sound)
|
||||
audio_stream_play(gCustomVoiceStream, true, 1)
|
||||
else
|
||||
if voice_sample == nil then
|
||||
voice_sample = audio_sample_load(sound)
|
||||
while not voice_sample.loaded do end
|
||||
voicecount = voicecount + 1
|
||||
end
|
||||
audio_sample_play(voice_sample, m.pos, 1)
|
||||
|
||||
if gCustomVoiceSamplesBackup[m.playerIndex] ~= nil and not(gCustomVoiceSamples[m.playerIndex] == false) then
|
||||
gCustomVoiceSamplesBackup[m.playerIndex] = voice_sample
|
||||
else
|
||||
gCustomVoiceSamples[m.playerIndex] = voice_sample
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
--- @param m MarioState
|
||||
local function custom_character_sound(m, characterSound)
|
||||
if is_game_paused() then return end
|
||||
if characterSound == CHAR_SOUND_SNORING3 then return 0 end
|
||||
if characterSound == CHAR_SOUND_HAHA and m.hurtCounter > 0 then return 0 end
|
||||
|
||||
local voice = _G.charSelect.character_get_voice(m)[characterSound]
|
||||
if voice ~= nil then
|
||||
return play_custom_character_sound(m, voice)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local STARTING_SNORE = 46
|
||||
local SLEEP_TALK_START = STARTING_SNORE + 49
|
||||
local SLEEP_TALK_END = SLEEP_TALK_START + SLEEP_TALK_SNORES
|
||||
|
||||
--- @param m MarioState
|
||||
local function custom_character_snore(m)
|
||||
if is_game_paused() then return end
|
||||
if gCustomVoiceSamplesBackup[m.playerIndex] ~= nil and not (gCustomVoiceSamples[m.playerIndex] == false) then
|
||||
if gCustomVoiceSamples[m.playerIndex].loaded then
|
||||
audio_sample_destroy(gCustomVoiceSamples[m.playerIndex])
|
||||
voicecount = voicecount - 1
|
||||
gCustomVoiceSamples[m.playerIndex] = false
|
||||
end
|
||||
end
|
||||
local SNORE3_TABLE = _G.charSelect.character_get_voice(m)[CHAR_SOUND_SNORING3]
|
||||
|
||||
if m.action ~= ACT_SLEEPING then
|
||||
if m.isSnoring > 0 then
|
||||
stop_custom_character_sound(m)
|
||||
end
|
||||
return
|
||||
|
||||
elseif not (m.actionState == 2 and (m.flags & MARIO_MARIO_SOUND_PLAYED) ~= 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local animFrame = m.marioObj.header.gfx.animInfo.animFrame
|
||||
|
||||
if SNORE3_TABLE ~= nil and #SNORE3_TABLE >= 2 then
|
||||
if animFrame == 2 and m.actionTimer < SLEEP_TALK_START then
|
||||
play_custom_character_sound(m, SNORE3_TABLE[2])
|
||||
elseif animFrame == 25 then
|
||||
if #SNORE3_TABLE >= 3 then
|
||||
m.actionTimer = m.actionTimer + 1
|
||||
if m.actionTimer >= SLEEP_TALK_END then
|
||||
m.actionTimer = STARTING_SNORE
|
||||
end
|
||||
if m.actionTimer == SLEEP_TALK_START then
|
||||
play_custom_character_sound(m, SNORE3_TABLE[3])
|
||||
elseif m.actionTimer < SLEEP_TALK_START then
|
||||
play_custom_character_sound(m, SNORE3_TABLE[1])
|
||||
end
|
||||
else
|
||||
play_custom_character_sound(m, SNORE3_TABLE[1])
|
||||
end
|
||||
end
|
||||
elseif animFrame == 2 then
|
||||
play_character_sound(m, CHAR_SOUND_SNORING2)
|
||||
|
||||
elseif animFrame == 25 then
|
||||
play_character_sound(m, CHAR_SOUND_SNORING1)
|
||||
end
|
||||
end
|
||||
|
||||
_G.charSelect.voice = {
|
||||
sound = custom_character_sound,
|
||||
snore = custom_character_snore,
|
||||
}
|
||||
gPlayerSyncTable[0].customVoice = true
|
Loading…
Reference in a new issue