------------------------------------------------------ -- Custom HUD Rendering by Agent X and xLuigiGamerx -- ------------------------------------------------------ if incompatibleClient then return 0 end -- localize functions to improve performance - n-hud.lua local og_hud_get_value,og_hud_set_value,djui_hud_print_text,tostring,hud_set_flash,get_global_timer,hud_get_flash,djui_hud_get_screen_width,math_ceil,obj_get_first_with_behavior_id,get_behavior_from_id,count_objects_with_behavior,djui_hud_render_rect,djui_hud_set_resolution,djui_hud_get_screen_height,djui_hud_set_color,djui_hud_set_font,djui_hud_measure_text,djui_chat_message_create,hud_is_hidden,djui_is_playerlist_open = hud_get_value,hud_set_value,djui_hud_print_text,tostring,hud_set_flash,get_global_timer,hud_get_flash,djui_hud_get_screen_width,math.ceil,obj_get_first_with_behavior_id,get_behavior_from_id,count_objects_with_behavior,djui_hud_render_rect,djui_hud_set_resolution,djui_hud_get_screen_height,djui_hud_set_color,djui_hud_set_font,djui_hud_measure_text,djui_chat_message_create,hud_is_hidden,djui_is_playerlist_open --[[ Some functions we need for the hud Color hud code written by EmilyEmmi Djui box code written by xLuigiGamerx (Use outside of character select is forbidden as these functions were made for another mod I'm planning to release) ]] ---@param text string ---@return nil, number, number, number, number local function convert_color(text) if text:sub(2, 2) ~= "#" then return nil end text = text:sub(3, -2) local rstring, gstring, bstring = "", "", "" if text:len() ~= 3 and text:len() ~= 6 then return 255, 255, 255, 255 end if text:len() == 6 then rstring = text:sub(1, 2) or "ff" gstring = text:sub(3, 4) or "ff" bstring = text:sub(5, 6) or "ff" else rstring = text:sub(1, 1) .. text:sub(1, 1) gstring = text:sub(2, 2) .. text:sub(2, 2) bstring = text:sub(3, 3) .. text:sub(3, 3) end local r = tonumber("0x" .. rstring) or 255 local g = tonumber("0x" .. gstring) or 255 local b = tonumber("0x" .. bstring) or 255 return r, g, b, 255 end ---@param text string ---@param get_color boolean|nil ---@return string, string, string, boolean local function remove_color(text, get_color) local start = text:find("\\") local next = 1 while (next ~= nil) and (start ~= nil) do start = text:find("\\") if start ~= nil then next = text:find("\\", start + 1) if next == nil then next = text:len() + 1 end if get_color then local color = text:sub(start, next) local render = text:sub(1, start - 1) text = text:sub(next + 1) return text, color, render else text = text:sub(1, start - 1) .. text:sub(next + 1) end end end return text end ---@param text string ---@return string local function generate_rainbow_text(text) local preResult = {} local postResult = {} for match in text:gmatch(string.format(".", "(.)")) do table.insert(preResult, match) end RED = djui_menu_get_rainbow_string_color(0) GREEN = djui_menu_get_rainbow_string_color(1) BLUE = djui_menu_get_rainbow_string_color(2) YELLOW = djui_menu_get_rainbow_string_color(3) local sRainbowColors = { [0] = YELLOW, [1] = RED, [2] = GREEN, [3] = BLUE, } for i = 1, #preResult do rainbow = sRainbowColors[i % 4] table.insert(postResult, rainbow .. preResult[i]) end local result = table.concat(postResult, "") return result end ---@param x integer X Offset ---@param y integer Y Offset ---@param width integer Width ---@param height integer Height ---@param rectColor DjuiColor Rect Color ---@param borderColor DjuiColor Border Color local function djui_hud_render_djui(x, y, width, height, rectColor, borderColor) djui_hud_set_color(borderColor.r, borderColor.g, borderColor.b, borderColor.a) -- Left djui_hud_render_rect(x, y, 8, height) -- Right djui_hud_render_rect(x + width - 8, y, 8, height) -- Up djui_hud_render_rect(x + 8, y, width - 16, 8) -- Down djui_hud_render_rect(x + 8, y + height - 8, width - 16, 8) -- Inner Rect djui_hud_set_color(rectColor.r, rectColor.g, rectColor.b, rectColor.a) djui_hud_render_rect(x + 8, y + 8, width - 16, height - 16) end ---@param text string Text ---@param x integer X Offset ---@param y integer Y Offset ---@param scale integer Scale ---@param red integer Default text red value ---@param green integer Default text green value ---@param blue integer Default text blue value ---@param alpha integer Default text alpha value local function djui_hud_print_text_with_color(text, x, y, scale, red, green, blue, alpha) djui_hud_set_color(red or 255, green or 255, blue or 255, alpha or 255) local space = 0 local color = "" text, color, render = remove_color(text, true) while render ~= nil do local r, g, b, a = convert_color(color) if alpha then a = alpha end djui_hud_print_text(render, x + space, y, scale) if r then djui_hud_set_color(r, g, b, a) end space = space + djui_hud_measure_text(render) * scale text, color, render = remove_color(text, true) end djui_hud_print_text(text, x + space, y, scale) end ---@param text string Message ---@param headerYOffset integer A y offset for the header ---@param tr integer Text Red ---@param tg integer Text Green ---@param tb integer Text Blue ---@param a integer Text Alpha Value ---@param scale integer Scale ---@param x integer X Offset ---@param y integer Y Offset ---@param width integer Width ---@param height integer Height ---@param rectColor DjuiColor Rect Color ---@param borderColor DjuiColor Border Color local function djui_hud_render_header_box(text, headerYOffset, tr, tg, tb, a, scale, x, y, width, height, rectColor, borderColor) local headerFont = djui_menu_get_theme().panels.hudFontHeader and FONT_HUD or FONT_MENU djui_hud_set_font(headerFont) local hudFont = headerFont == FONT_HUD local scaleMultiplier = hudFont and 4 * 0.7 or 1 local headerFontOffset = hudFont and 31.65 or 14.5 local defaultHeaderOffset = y + headerFontOffset + headerYOffset djui_hud_render_djui(x, y, width, height, rectColor, borderColor) djui_hud_print_text_with_color(text, x + width / 2 - (djui_hud_measure_text(remove_color(text, false)) * scale * scaleMultiplier) / 2, defaultHeaderOffset, scaleMultiplier, tr, tg, tb, a) end --------------------- -- Real HUD Stuffs -- --------------------- -- Updates the Chracter Select hud flags along with the vanilla hud flags local sCharSelectHudDisplayFlags = og_hud_get_value(HUD_DISPLAY_FLAGS) | HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA -- Initializes custom hud flags function flags_update() sCharSelectHudDisplayFlags = sCharSelectHudDisplayFlags & (og_hud_get_value(HUD_DISPLAY_FLAGS) | HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA) -- Updated the custom flags og_hud_set_value(HUD_DISPLAY_FLAGS, og_hud_get_value(HUD_DISPLAY_FLAGS) & ~(HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA)) -- Update the vanilla flags end hook_event(HOOK_ON_HUD_RENDER_BEHIND, flags_update) -- Modified Vanilla Functions -- --[[ These are `_G` on their own to replace vanilla functions ]] ---@param type HudDisplayValue ---@return integer function _G.hud_get_value(type) if type == HUD_DISPLAY_FLAGS then return sCharSelectHudDisplayFlags | og_hud_get_value(HUD_DISPLAY_FLAGS) else return og_hud_get_value(type) end end ---@param type HudDisplayValue ---@param value integer --- Sets a HUD display value function _G.hud_set_value(type, value) if type == HUD_DISPLAY_FLAGS then sCharSelectHudDisplayFlags = value og_hud_set_value(type, value & ~(HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA)) else og_hud_set_value(type, value) end end -- Old CS Hud Functions -- ---Hides the specified custom hud element ---@param hudElement HUDDisplayFlag function hud_hide_element(hudElement) dev_mode_log_to_console("The `charSelect.hud_hide_element()` function is deprecated, please use normal vanilla functions as they have been modified to work with Character Select.", CONSOLE_MESSAGE_WARNING) hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) & ~hudElement) return true end ---Shows the specified custom hud element ---@param hudElement HUDDisplayFlag function hud_show_element(hudElement) dev_mode_log_to_console("The `charSelect.hud_show_element()` function is deprecated, please use normal vanilla functions as they have been modified to work with Character Select.", CONSOLE_MESSAGE_WARNING) hud_set_value(HUD_DISPLAY_FLAGS, hud_get_value(HUD_DISPLAY_FLAGS) | hudElement) return true end ---Gets the specified custom hud element's state ---@param hudElement HUDDisplayFlag ---@return boolean function hud_get_element(hudElement) dev_mode_log_to_console("The `charSelect.hud_get_element()` function is deprecated, please use normal vanilla functions as they have been modified to work with Character Select.", CONSOLE_MESSAGE_WARNING) return (hud_get_value(HUD_DISPLAY_FLAGS) & hudElement) ~= 0 end local MATH_DIVIDE_16 = 1/16 local MATH_DIVIDE_32 = 1/32 local MATH_DIVIDE_64 = 1/64 local FONT_USER = FONT_NORMAL ---@param localIndex integer ---@return string --- This assumes multiple characters will not have the same model, --- Icons can only be seen by users who have the character avalible to them function name_from_local_index(localIndex) if localIndex == nil then localIndex = 0 end local p = gCSPlayers[localIndex] for i = 1, #characterTable do if characterTable[i].saveName == p.saveName then return characterTable[i][(p.currAlt and p.currAlt or 1)].name end end return "???" end ---@param localIndex integer ---@return table --- This assumes multiple characters will not have the same model, --- Icons can only be seen by users who have the character avalible to them function color_from_local_index(localIndex) if localIndex == nil then localIndex = 0 end local p = gCSPlayers[localIndex] for i = 1, #characterTable do if characterTable[i].saveName == p.saveName then return characterTable[i][(p.currAlt and p.currAlt or 1)].color end end return {r = 255, g = 255, b = 255} end ---@param localIndex integer ---@return TextureInfo|string --- This assumes multiple characters will not have the same model, --- Icons can only be seen by users who have the character avalible to them. --- This function can return nil. if this is the case, render `djui_hud_print_text("?", x, y, 1)` function life_icon_from_local_index(localIndex) if localIndex == nil then localIndex = 0 end local p = gCSPlayers[localIndex] for i = 1, #characterTable do local char = characterTable[i] if char.saveName == p.saveName then return char[(p.currAlt and p.currAlt or 1)].lifeIcon end end return "?" end ---@param localIndex integer ---@return TextureInfo --- This assumes multiple characters will not have the same model, --- Icons can only be seen by users who have the character avalible to them function star_icon_from_local_index(localIndex) if localIndex == nil then localIndex = 0 end local p = gCSPlayers[localIndex] for i = 1, #characterTable do local char = characterTable[i] if char.saveName == p.saveName then return char[(p.currAlt and p.currAlt or 1)].starIcon end end return gTextures.star end local TYPE_STRING = "string" ---@param localIndex integer ---@param x integer ---@param y integer ---@param scale integer function render_life_icon_from_local_index(localIndex, x, y, scale) if localIndex == nil then localIndex = 0 end local lifeIcon = life_icon_from_local_index(localIndex) local startFont = djui_hud_get_font() local startColor = djui_hud_get_color() if type(lifeIcon) == TYPE_STRING then local color = color_from_local_index(localIndex) djui_hud_set_font(FONT_RECOLOR_HUD) djui_hud_set_color(color.r/startColor.r*255, color.g/startColor.g*255, color.b/startColor.b*255, startColor.a) djui_hud_print_text(lifeIcon, x, y, scale) -- Reset HUD Modifications djui_hud_set_font(startFont) djui_hud_set_color(startColor.r, startColor.g, startColor.b, startColor.a) else djui_hud_render_texture(lifeIcon, x, y, scale / (lifeIcon.width * MATH_DIVIDE_16), scale / (lifeIcon.height * MATH_DIVIDE_16)) end end ---@param localIndex integer ---@param prevX integer ---@param prevY integer ---@param prevScale integer ---@param x integer ---@param y integer ---@param scale integer function render_life_icon_from_local_index_interpolated(localIndex, prevX, prevY, prevScale, x, y, scale) if localIndex == nil then localIndex = 0 end local lifeIcon = life_icon_from_local_index(localIndex) local startFont = djui_hud_get_font() local startColor = djui_hud_get_color() if type(lifeIcon) == TYPE_STRING then local color = color_from_local_index(localIndex) djui_hud_set_font(FONT_RECOLOR_HUD) djui_hud_set_color(color.r/startColor.r*255, color.g/startColor.g*255, color.b/startColor.b*255, startColor.a) djui_hud_print_text_interpolated(lifeIcon, prevX - prevScale/4, prevY - 10*prevScale - prevScale/4, prevScale, x - scale/4, y - 10*scale - scale/4, scale) -- Reset HUD Modifications djui_hud_set_font(startFont) djui_hud_set_color(startColor.r, startColor.g, startColor.b, startColor.a) else djui_hud_render_texture_interpolated(lifeIcon, prevX, prevY, prevScale / (lifeIcon.width * MATH_DIVIDE_16), prevScale / (lifeIcon.height * MATH_DIVIDE_16), x, y, scale / (lifeIcon.width * MATH_DIVIDE_16), scale / (lifeIcon.height * MATH_DIVIDE_16)) end end ---@param localIndex integer ---@param x integer ---@param y integer ---@param scale integer function render_star_icon_from_local_index(localIndex, x, y, scale) if localIndex == nil then localIndex = 0 end local starIcon = star_icon_from_local_index(localIndex) djui_hud_render_texture(starIcon, x, y, scale / (starIcon.width * MATH_DIVIDE_16), scale / (starIcon.height * MATH_DIVIDE_16)) end ---@param localIndex integer ---@param x integer ---@param y integer ---@param scale integer function render_star_icon_from_local_index_interpolated(localIndex, prevX, prevY, prevScale, x, y, scale) if localIndex == nil then localIndex = 0 end local starIcon = star_icon_from_local_index(localIndex) djui_hud_render_texture_interpolated(starIcon, prevX, prevY, prevScale / (starIcon.width * MATH_DIVIDE_16), prevScale / (starIcon.height * MATH_DIVIDE_16), x, y, scale / (starIcon.width * MATH_DIVIDE_16), scale / (starIcon.height * MATH_DIVIDE_16)) end -- Health Meter -- local TEXT_DEFAULT_METER_PREFIX = "char-select-custom-meter-" local TEX_DEFAULT_METER_LEFT = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."left") local TEX_DEFAULT_METER_RIGHT = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."right") local TEX_DEFAULT_METER_PIE1 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie1") local TEX_DEFAULT_METER_PIE2 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie2") local TEX_DEFAULT_METER_PIE3 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie3") local TEX_DEFAULT_METER_PIE4 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie4") local TEX_DEFAULT_METER_PIE5 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie5") local TEX_DEFAULT_METER_PIE6 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie6") local TEX_DEFAULT_METER_PIE7 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie7") local TEX_DEFAULT_METER_PIE8 = get_texture_info(TEXT_DEFAULT_METER_PREFIX.."pie8") local defaultMeterInfo = { label = { left = TEX_DEFAULT_METER_LEFT, right = TEX_DEFAULT_METER_RIGHT, }, pie = { TEX_DEFAULT_METER_PIE1, TEX_DEFAULT_METER_PIE2, TEX_DEFAULT_METER_PIE3, TEX_DEFAULT_METER_PIE4, TEX_DEFAULT_METER_PIE5, TEX_DEFAULT_METER_PIE6, TEX_DEFAULT_METER_PIE7, TEX_DEFAULT_METER_PIE8, } } local TEXT_DEFAULT_COURSE_PREFIX = "char-select-custom-course-" local TEX_DEFAULT_COURSE_TOP = get_texture_info(TEXT_DEFAULT_COURSE_PREFIX.."top") local TEX_DEFAULT_COURSE_BOTTOM = get_texture_info(TEXT_DEFAULT_COURSE_PREFIX.."bottom") local defaultCourseInfo = { top = TEX_DEFAULT_COURSE_TOP, bottom = TEX_DEFAULT_COURSE_BOTTOM, } ---@param localIndex integer ---@return table --- This assumes multiple characters will not have the same model, --- Icons can only be seen by users who have the character avalible to them function health_meter_from_local_index(localIndex) localIndex = localIndex or 0 local p = gCSPlayers[localIndex] for i = 1, #characterTable do local char = characterTable[i] if char.saveName == p.saveName and char[(p.currAlt and p.currAlt or 1)].healthTexture ~= nil then if not char[(p.currAlt and p.currAlt or 1)].healthTexture.label then char[(p.currAlt and p.currAlt or 1)].healthTexture.label = defaultMeterInfo.label elseif not char[(p.currAlt and p.currAlt or 1)].healthTexture.pie then char[(p.currAlt and p.currAlt or 1)].healthTexture.pie = defaultMeterInfo.pie end return char[(p.currAlt and p.currAlt or 1)].healthTexture end end return defaultMeterInfo end ---@param localIndex integer ---@param health integer ---@param x integer ---@param y integer ---@param scaleX integer ---@param scaleY integer function render_health_meter_from_local_index(localIndex, health, x, y, scaleX, scaleY) localIndex = localIndex or 0 health = health >> 8 local textureTable = health_meter_from_local_index(localIndex) local tex = textureTable.label.left djui_hud_render_texture(tex, x, y, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64) tex = textureTable.label.right djui_hud_render_texture(tex, x + 31*scaleX*MATH_DIVIDE_64, y, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64) if health > 0 then tex = textureTable.pie[health] djui_hud_render_texture(tex, x + 15*scaleX*MATH_DIVIDE_64, y + 16*scaleY*MATH_DIVIDE_64, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_32) * MATH_DIVIDE_64) end end ---@param localIndex integer ---@param health integer ---@param x integer ---@param y integer ---@param scaleX integer ---@param scaleY integer ---@param prevX integer ---@param prevY integer ---@param prevScaleX integer ---@param prevScaleY integer function render_health_meter_from_local_index_interpolated(localIndex, health, prevX, prevY, prevScaleX, prevScaleY, x, y, scaleX, scaleY) localIndex = localIndex or 0 health = health >> 8 local textureTable = health_meter_from_local_index(localIndex) local tex = textureTable.label.left djui_hud_render_texture_interpolated(tex, prevX, prevY, prevScaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, prevScaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64, x, y, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64) tex = textureTable.label.right djui_hud_render_texture_interpolated(tex, prevX + 31*prevScaleX*MATH_DIVIDE_64, prevY, prevScaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, prevScaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64, x + 31*scaleX*MATH_DIVIDE_64, y, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_64) * MATH_DIVIDE_64) if health > 0 then tex = textureTable.pie[health] djui_hud_render_texture_interpolated(tex, prevX + 15*prevScaleX*MATH_DIVIDE_64, prevY + 16*scaleY*MATH_DIVIDE_64, prevScaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, prevScaleY / (tex.height * MATH_DIVIDE_32) * MATH_DIVIDE_64, x + 15*scaleX*MATH_DIVIDE_64, y + 16*scaleY*MATH_DIVIDE_64, scaleX / (tex.width * MATH_DIVIDE_32) * MATH_DIVIDE_64, scaleY / (tex.height * MATH_DIVIDE_32) * MATH_DIVIDE_64) end end local pieTextureNames = { "one_segments", "two_segments", "three_segments", "four_segments", "five_segments", "six_segments", "seven_segments", "full", } local function render_hud_health() if currChar == 1 and characterTable[1].currAlt == 1 then texture_override_reset("texture_power_meter_left_side") texture_override_reset("texture_power_meter_right_side") for i = 1, 8 do texture_override_reset("texture_power_meter_" .. pieTextureNames[i]) end return end local textureTable = characterTable[currChar][characterTable[currChar].currAlt].healthTexture if textureTable then -- sets health HUD to custom textures if textureTable.label.left and textureTable.label.right then -- if left and right label textures exist. BOTH have to exist to be set! texture_override_set("texture_power_meter_left_side", textureTable.label.left) texture_override_set("texture_power_meter_right_side", textureTable.label.right) end for i = 1, 8 do texture_override_set("texture_power_meter_" .. pieTextureNames[i], (textureTable.pie and textureTable.pie[i]) and textureTable.pie[i] or defaultMeterInfo.pie[i]) end else -- resets the health HUD texture_override_set("texture_power_meter_left_side", defaultMeterInfo.label.left) texture_override_set("texture_power_meter_right_side", defaultMeterInfo.label.right) for i = 1, 8 do texture_override_set("texture_power_meter_" .. pieTextureNames[i], defaultMeterInfo.pie[i]) end end end local function render_hud_act_select_course() if currChar == 1 then texture_override_reset("texture_menu_course_upper") texture_override_reset("texture_menu_course_lower") return end local textureTable = characterTable[currChar][characterTable[currChar].currAlt].courseTexture if textureTable then -- sets health HUD to custom textures if textureTable.top and textureTable.bottom then -- if left and right label textures exist. BOTH have to exist to be set! texture_override_set("texture_menu_course_upper", textureTable.top) texture_override_set("texture_menu_course_lower", textureTable.bottom) end else -- resets the course HUD texture_override_set("texture_menu_course_upper", defaultCourseInfo.top) texture_override_set("texture_menu_course_lower", defaultCourseInfo.bottom) end end local function render_hud_mario_lives() if (hud_get_value(HUD_DISPLAY_FLAGS) & HUD_DISPLAY_FLAG_LIVES) == 0 then return end local x = 22 local y = 15 -- SCREEN_HEIGHT - 209 - 16 render_life_icon_from_local_index(0, x, y, 1) djui_hud_print_text("@", x + 16, y, 1) djui_hud_print_text(tostring(hud_get_value(HUD_DISPLAY_LIVES)):gsub("-", "M"), x + 32, y, 1) end local function render_hud_stars() if (hud_get_value(HUD_DISPLAY_FLAGS) & HUD_DISPLAY_FLAG_STAR_COUNT) == 0 then return end if hud_get_flash ~= nil then -- prevent star count from flashing outside of castle if gNetworkPlayers[0].currCourseNum ~= COURSE_NONE then hud_set_flash(0) end if hud_get_flash() == 1 and (get_global_timer() & 0x08) == 0 then return end end local x = math_ceil(djui_hud_get_screen_width() - 76) if x % 2 ~= 0 then x = x - 1 end local y = math_ceil(240 - 209 - 16) local showX = 0 local hudDisplayStars = hud_get_value(HUD_DISPLAY_STARS) if hudDisplayStars < 100 then showX = 1 end render_star_icon_from_local_index(0, x, y, 1) if showX == 1 then djui_hud_print_text("@", x + 16, y, 1) end djui_hud_print_text(tostring(hudDisplayStars):gsub("-", "M"), (showX * 14) + x + 16, y, 1) end local function render_hud_camera_status() if (hud_get_value(HUD_DISPLAY_FLAGS) & HUD_DISPLAY_FLAG_CAMERA) == 0 or (hud_get_value(HUD_DISPLAY_FLAGS) & HUD_DISPLAY_FLAG_CAMERA_AND_POWER) == 0 then return end local x = djui_hud_get_screen_width() - 54 local y = 205 local cameraHudStatus = hud_get_value(HUD_DISPLAY_CAMERA_STATUS) -- doesn't show the status of freecam because it's currently bugged when we hide the hud camera -- TODO: keep nagging the coopers to "fix `hud_get_value(HUD_DISPLAY_CAMERA_STATUS)` when using freecam and hiding the hud camera" :trollface: if cameraHudStatus == CAM_STATUS_NONE then return end djui_hud_render_texture(gTextures.camera, x, y, 1, 1) switch(cameraHudStatus & CAM_STATUS_MODE_GROUP, { [CAM_STATUS_MARIO] = function() render_life_icon_from_local_index(0, x + 16, y, 1) end, [CAM_STATUS_LAKITU] = function() djui_hud_render_texture(gTextures.lakitu, x + 16, y, 1, 1) end, [CAM_STATUS_FIXED] = function() djui_hud_render_texture(gTextures.no_camera, x + 16, y, 1, 1) end }) switch(cameraHudStatus & CAM_STATUS_C_MODE_GROUP, { [CAM_STATUS_C_DOWN] = function() djui_hud_render_texture(gTextures.arrow_down, x + 4, y + 16, 1, 1) end, [CAM_STATUS_C_UP] = function() djui_hud_render_texture(gTextures.arrow_up, x + 4, y - 8, 1, 1) end }) end -- Act Select Hud -- local STAR_SELECTOR_NOT_SELECTED = 0 local STAR_SELECTOR_SELECTED = 1 local STAR_SELECTOR_100_COINS = 2 local sVisibleStars = 0 local function render_act_select_hud() local course = gNetworkPlayers[0].currCourseNum if course == 0 or not obj_get_first_with_behavior_id(id_bhvActSelector) then return end if sVisibleStars == 0 then local starObj = obj_get_first_with_behavior_id(id_bhvActSelectorStarType) while starObj do if starObj.oStarSelectorType ~= STAR_SELECTOR_100_COINS then sVisibleStars = sVisibleStars + 1 end starObj = obj_get_next_with_same_behavior_id(starObj) end end for a = 1, sVisibleStars do local x = (139 - sVisibleStars * 17 + a * 34) + (djui_hud_get_screen_width() / 2) - 160 + 0.5 for j = 1, MAX_PLAYERS - 1 do -- 0 is not needed, you're never supposed to see yourself in act select local np = gNetworkPlayers[j] if np and np.connected and np.currCourseNum == course and np.currActNum == a then render_life_icon_from_local_index(j, x - 4, 17, 1) break end end end local selectedStar = obj_get_first_with_behavior_id(id_bhvActSelectorStarType) local starsList = {} while selectedStar do table.insert(starsList, selectedStar) selectedStar = obj_get_next_with_same_behavior_id(selectedStar) end if (sVisibleStars > 0) then local playersInAct = 0 local sSelectedActIndex = 0 for i = 1, #starsList do local curStar = starsList[i] if curStar.oStarSelectorType == STAR_SELECTOR_SELECTED then sSelectedActIndex = i - 1 end end local gCurrCourseNum = gNetworkPlayers[0].currCourseNum for j = 1, MAX_PLAYERS - 1 do local np = gNetworkPlayers[j] if not (np or np.connected) then goto continue end if (np.currCourseNum ~= gCurrCourseNum) then goto continue end if (np.currActNum ~= sSelectedActIndex + 1) then goto continue end playersInAct = playersInAct + 1 ::continue:: end if (playersInAct > 0) then local message = "" if (playersInAct == 1) then message = message .. " Join " else message = message .. string.format("%d Players", playersInAct) end djui_hud_set_font(FONT_NORMAL) djui_hud_set_color(100, 100, 100, 255) local textScale = .5 local textWidth = djui_hud_measure_text(message) * textScale local xPos = ((sSelectedActIndex + 1) * 34 - sVisibleStars * 17 + 139 - (textWidth / 2) + 4) + (djui_hud_get_screen_width() / 2) - 160 + 2 local yPos = -1 if message:find("Players") then message = string.format("%d Player", playersInAct) end djui_hud_print_text(message, xPos, yPos, textScale) -- Not fully accurate because the font in act select is stretched in a way unachievable with normal fonts, will revisit in the future end end end ---@param table table ---@return table function zero_index_to_one_index(table) local tableOne = {} for i = 0, #table do tableOne[i+1] = table[i] end return tableOne end local activeNonCSMods = {} local nonCSModPosition = 0 local CSPacks = 0 for i = 0, #gActiveMods do if gActiveMods[i].name == "Character Select" then table.insert(activeNonCSMods, tostring(gActiveMods[i].name)) nonCSModPosition = #activeNonCSMods elseif (remove_color(gActiveMods[i].name):sub(1, 4) ~= "[CS]" and gActiveMods[i].category ~= "cs") then table.insert(activeNonCSMods, tostring(gActiveMods[i].name)) else CSPacks = CSPacks + 1 end end activeNonCSMods[nonCSModPosition] = "Character Select (+"..CSPacks..")" function render_playerlist_and_modlist() -- DjuiTheme Data local sDjuiTheme = djui_menu_get_theme() local hudFont = sDjuiTheme.panels.hudFontHeader local rectColor = sDjuiTheme.threePanels.rectColor local borderColor = sDjuiTheme.threePanels.borderColor -- PlayerList playerListWidth = 710 playerListHeight = (16 * 32) + (16 - 1) * 4 + (32 + 16) + 32 + 32 local x = djui_hud_get_screen_width()/2 - playerListWidth/2 local y = djui_hud_get_screen_height()/2 - playerListHeight/2 listMargins = 16 local playerList = {} gNetworkPlayersOne = zero_index_to_one_index(gNetworkPlayers) for index, player in pairs(gNetworkPlayersOne) do if player.connected then playerList[#playerList + 1] = player end end local playersString = hudFont and djui_language_get("PLAYER_LIST", "PLAYERS") or generate_rainbow_text(djui_language_get("PLAYER_LIST", "PLAYERS")) djui_hud_render_header_box(playersString, 0, 0xff, 0xff, 0xff, 0xff, 1, x, y, playerListWidth, playerListHeight, rectColor, borderColor) djui_hud_set_font(FONT_USER) for i = 1, #playerList do np = playerList[i] local v = (i % 2) == 0 and 16 or 32 djui_hud_set_color(v, v, v, 128) local entryWidth = playerListWidth - ((8 + listMargins) * 2) local entryHeight = 32 local entryX = x + 8 + listMargins local entryY = y + 88 + ((entryHeight + 4) * (i-1)) djui_hud_render_rect(entryX, entryY, entryWidth, entryHeight) playerNameColor = { r = 127 + network_player_get_override_palette_color_channel(np, CAP, 0) / 2, g = 127 + network_player_get_override_palette_color_channel(np, CAP, 1) / 2, b = 127 + network_player_get_override_palette_color_channel(np, CAP, 2) / 2 } djui_hud_set_color(255, 255, 255, 255) render_life_icon_from_local_index(np.localIndex, entryX, entryY, 2) djui_hud_print_text_with_color(np.name, entryX + 40, entryY, 1, playerNameColor.r, playerNameColor.g, playerNameColor.b, 255) local levelName = np.overrideLocation ~= "" and np.overrideLocation or get_level_name(np.currCourseNum, np.currLevelNum, np.currAreaIndex) if levelName then djui_hud_print_text_with_color(levelName, ((entryX + entryWidth) - djui_hud_measure_text((string.gsub(levelName, "\\(.-)\\", "")))) - 126, entryY, 1, 0xdc, 0xdc, 0xdc, 255) end if np.currActNum then currActNum = np.currActNum == 99 and "Done" or np.currActNum ~= 0 and "# "..tostring(np.currActNum) or "" printedcurrActNum = currActNum djui_hud_print_text_with_color(printedcurrActNum, entryX + entryWidth - djui_hud_measure_text(printedcurrActNum) - 18, entryY, 1, 0xdc, 0xdc, 0xdc, 255) end if np.description then djui_hud_print_text_with_color(np.description, (entryX + 278) - (djui_hud_measure_text((string.gsub(np.description, "\\(.-)\\", "")))/2), entryY, 1, np.descriptionR, np.descriptionG, np.descriptionB, np.descriptionA) end end -- ModList local modListWidth = 280 local modListHeight = (#activeNonCSMods * 32) + (#activeNonCSMods - 1) * 4 + (32 + 16) + 32 + 32 local mX = djui_hud_get_screen_width()/2 + 363 local mY = djui_hud_get_screen_height()/2 - modListHeight/2 local modsString = hudFont and djui_language_get("MODLIST", "MODS") or generate_rainbow_text(djui_language_get("MODLIST", "MODS")) djui_hud_render_header_box(modsString, 0, 0xff, 0xff, 0xff, 0xff, 1, mX, mY, modListWidth, modListHeight, rectColor, borderColor) djui_hud_set_font(FONT_USER) for i = 0, #activeNonCSMods - 1 do --local i = i - packFilter v = (i % 2) ~= 0 and 16 or 32 djui_hud_set_color(v, v, v, 128) local entryWidth = modListWidth - ((8 + listMargins) * 2) local entryHeight = 32 local entryX = mX + 8 + listMargins local entryY = mY + 124 + 0 + ((entryHeight + 4) * (i - 1)) djui_hud_render_rect(entryX, entryY, entryWidth, entryHeight) local modName = activeNonCSMods[i + 1] local stringSubCount = 23 local inColor = false for i = 1, #modName do if modName:sub(i, i) == "\\" then inColor = not inColor end if inColor then stringSubCount = stringSubCount + 1 end end djui_hud_print_text_with_color(modName:sub(1, stringSubCount), entryX, entryY, 1, 0xdc, 0xdc, 0xdc, 255) end end -- Yes the ending stuffs is hardcoded, no there's not much of a better way to do it local DIALOG_ENDING_REPLACE_1 = "$CHARNAME!" local DIALOG_ENDING_REPLACE_2 = "Thank you $CHARNAME!" local DIALOG_ENDING_REPLACE_3 = "...for $CHARNAME..." local END_PEACH_CUTSCENE_DIALOG_1 = 6 local END_PEACH_CUTSCENE_DIALOG_2 = 7 local END_PEACH_CUTSCENE_DIALOG_3 = 10 local END_PEACH_CUTSCENE_RUN_TO_CASTLE = 11 local fadeLength = 5 local function render_hud_ending_dialog() djui_hud_set_font(FONT_TINY) local m = gMarioStates[0] if m.action ~= ACT_END_PEACH_CUTSCENE then return end local width = djui_hud_get_screen_width() local charName = characterTable[currChar][characterTable[currChar].currAlt].name local string = "" local startTime = 0 local endTime = 0 if m.actionArg == END_PEACH_CUTSCENE_DIALOG_1 and m.actionTimer >= 230 and m.actionTimer <= 275 then string = DIALOG_ENDING_REPLACE_1 startTime = 230 endTime = 275 elseif m.actionArg == END_PEACH_CUTSCENE_DIALOG_2 and m.actionTimer >= 75 and m.actionTimer <= 130 then string = DIALOG_ENDING_REPLACE_2 startTime = 75 endTime = 130 elseif m.actionArg == END_PEACH_CUTSCENE_DIALOG_3 and m.actionTimer >= 130 and m.actionTimer <= 195 then string = DIALOG_ENDING_REPLACE_3 startTime = 130 endTime = 195 elseif m.actionArg == END_PEACH_CUTSCENE_RUN_TO_CASTLE and m.actionTimer >= 95 and m.actionTimer <= 150 then string = DIALOG_ENDING_REPLACE_1 startTime = 95 endTime = 150 end if string ~= "" then djui_hud_set_color(0, 0, 0, 255) djui_hud_render_rect(0, 210, width, 30) string = string:gsub("$CHARNAME", charName) local opacity = 255 local startToTimer = m.actionTimer - startTime local endToTimer = endTime - m.actionTimer if startToTimer >= 0 then opacity = math.min(startToTimer, fadeLength)/fadeLength * 255 end if endToTimer >= 0 and startToTimer >= fadeLength then opacity = math.min(endToTimer, fadeLength)/fadeLength * 255 end djui_hud_set_color(255, 255, 255, opacity) local x = width*0.5 - djui_hud_measure_text(string)*0.5 djui_hud_print_text(string, x, 210, 1) end end -- Nametags Powermeter Hud -- local nametagsActionBlacklist = { [ACT_START_CROUCHING] = true, [ACT_CROUCHING] = true, [ACT_STOP_CROUCHING] = true, [ACT_START_CRAWLING] = true, [ACT_CRAWLING] = true, [ACT_STOP_CRAWLING] = true, [ACT_IN_CANNON] = true, [ACT_DISAPPEARED] = true, } local FADE_SCALE = 4.0 --- @class StateExtras --- @field public prevPos Vec3f --- @field public prevScale number --- @field public inited boolean --- @type StateExtras[] local sStateExtras = {} for i = 0, MAX_PLAYERS - 1 do sStateExtras[i] = {} local _ENV = setmetatable(sStateExtras[i], { __index = _G }) prevPos = gVec3fZero() prevScale = 0.0 inited = false end local function render_nametag_powermeter() local sGlobalTimer = get_global_timer() for i = 1, MAX_PLAYERS - 1 do local m = gMarioStates[i] if is_player_active(m) == 0 then goto continue end local np = gNetworkPlayers[i] if not np.currAreaSyncValid then goto continue end if nametagsActionBlacklist[m.action] then goto continue end if (m.marioBodyState.mirrorMario or m.marioBodyState.updateHeadPosTime ~= sGlobalTimer) then goto continue end local pos = gVec3fZero() local out = gVec3fZero() vec3f_copy(pos, m.marioBodyState.headPos) pos.y = pos.y + 100 if not djui_hud_world_pos_to_screen_pos(pos, out) then goto continue end local scale = -300 / out.z * djui_hud_get_fov_coeff() out.y = out.y - 16 * scale local alpha = (math.min(np.fadeOpacity << 3, 255)) * math.clamp(FADE_SCALE - scale, 0.0, 1.0) local e = sStateExtras[i] if not e.inited then vec3f_copy(e.prevPos, out) e.prevScale = scale e.inited = true end if gNametagsSettings.showHealth then djui_hud_set_color(255, 255, 255, alpha) local healthScale = 90 * scale local prevHealthScale = 90 * e.prevScale render_health_meter_from_local_index_interpolated(i, m.health, e.prevPos.x - (prevHealthScale * 0.5), e.prevPos.y - 72 * scale, prevHealthScale, prevHealthScale, out.x - ( healthScale * 0.5), out.y - 72 * scale, healthScale, healthScale ) end vec3f_copy(e.prevPos, out) e.prevScale = scale ::continue:: end end local function nametags_reset() for i = 0, MAX_PLAYERS - 1 do sStateExtras[i].inited = false end end local function on_level_init() nametags_reset() end hook_event(HOOK_ON_LEVEL_INIT, on_level_init) local sServerSettings = gServerSettings local sNametagsSettings = gNametagsSettings _G.gServerSettings = { enablePlayerList = sServerSettings.enablePlayerList, enablePlayersInLevelDisplay = sServerSettings.enablePlayersInLevelDisplay, } _G.gNametagsSettings = { showHealth = sNametagsSettings.showHealth, } local _ServerSettingsMetaTable = { __index = function (t, k) return rawget(t, k) or sServerSettings[k] end, __newindex = function (_, k, v) sServerSettings[k] = v end, } local _NametagsSettingsMetaTable = { __index = function (t, k) return rawget(t, k) or sNametagsSettings[k] end, __newindex = function (_, k, v) sNametagsSettings[k] = v end, } setmetatable(gServerSettings, _ServerSettingsMetaTable) setmetatable(gNametagsSettings, _NametagsSettingsMetaTable) function nametags_settings() if sNametagsSettings.showHealth then gNametagsSettings.showHealth = not gNametagsSettings.showHealth end sNametagsSettings.showHealth = false end hook_event(HOOK_ON_NAMETAGS_RENDER, nametags_settings) local function on_hud_render_behind() FONT_USER = djui_menu_get_font() djui_hud_set_resolution(RESOLUTION_N64) djui_hud_set_font(FONT_HUD) render_nametag_powermeter() -- Render before setting the color, it sets its own djui_hud_set_color(255, 255, 255, 255) if gNetworkPlayers[0].currActNum == 99 or gMarioStates[0].action == ACT_INTRO_CUTSCENE or hud_is_hidden() then return end sServerSettings.enablePlayersInLevelDisplay = false -- Disables the original playersInLevel Display local enablePlayersInLevelDisplay = gServerSettings.enablePlayersInLevelDisplay if not obj_get_first_with_behavior_id(id_bhvActSelector) then render_hud_mario_lives() render_hud_stars() render_hud_camera_status() render_hud_health() sVisibleStars = 0 else if enablePlayersInLevelDisplay then render_act_select_hud() end render_hud_act_select_course() end end local function on_hud_render() djui_hud_set_resolution(RESOLUTION_N64) djui_hud_set_font(FONT_HUD) djui_hud_set_color(255, 255, 255, 255) if gNetworkPlayers[0].currActNum == 99 then render_hud_ending_dialog() end sServerSettings.enablePlayerList = false -- Disables the original playerlist and modlist local enablePlayerList = gServerSettings.enablePlayerList djui_hud_set_resolution(RESOLUTION_DJUI) if djui_attempting_to_open_playerlist() and enablePlayerList then render_playerlist_and_modlist() end end hook_event(HOOK_ON_HUD_RENDER_BEHIND, on_hud_render_behind) hook_event(HOOK_ON_HUD_RENDER, on_hud_render)