-- 320x200 -- Don't mind my variable names local coolCounter = 0 local goRed = false local speedometerEnabled = true -- Width and Height local width = 130; local height = 10; -- X and Y offsets local ypos = 1 local xpos = -width - 48 local targetxpos = 0 -- Text X and Y offsets local txoffset = 2 local tyoffset = 0 local rtxoffset = 0 local rtyoffset = 0 local hudEnabled = true local useOpposite = false local curplayer = nil local customIntermission = false local a = 0x00090000 -- Water/Space timer X offsets local timerxoffset = 1; local animspeed = 10 local started = false local curspeed = animspeed local dmgshake = 0 local bossBarOfs = -25; local boss = nil local bossAnimW = 0 addHook("BossThinker", function (mo) boss = mo end) addHook("MobjDamage", function(mo) if curplayer ~= mo.player then return end dmgshake = TICRATE/3 end, MT_PLAYER) addHook("MapLoad", function () curspeed = 0 bossBarOfs = -25; started = false bossAnimW = 0 xpos = -width - 48; a = 0x00090000 end) local function disableHudElements() if hud.enabled("intermissiontally") and customIntermission then hud.disable("intermissiontally") end if not hud.enabled("intermissiontally") and not customIntermission then hud.enable("intermissiontally") end if hudEnabled then if hud.enabled("score") then hud.disable("score") end if hud.enabled("time") then hud.disable("time") end if hud.enabled("rings") then hud.disable("rings") end if hud.enabled("lives") then hud.disable("lives") end else if not hud.enabled("score") then hud.enable("score") end if not hud.enabled("time") then hud.enable("time") end if not hud.enabled("rings") then hud.enable("rings") end if not hud.enabled("lives") then hud.enable("lives") end end end addHook("PreThinkFrame", disableHudElements) CV_RegisterVar({name="hud_width", defaultValue="" + width, flags=CV_CALL, pv={MIN=100,MAX=320}, func=function (var) width = var.value; end}) CV_RegisterVar({name="hud_xpos", defaultValue="" + targetxpos, flags=CV_CALL, pv={MIN=100,MAX=320}, func=function (var) targetxpos = var.value end}) CV_RegisterVar({name="hud_ypos", defaultValue="" + ypos, flags=CV_CALL, pv={MIN=100,MAX=320}, func=function (var) ypos = var.value end}) CV_RegisterVar({name="hud_speedometer", defaultValue=1, flags=CV_CALL, pv=CV_OnOff, func=function (var) local v = string.lower(var.string) if v == "on" or v == "1" or v == "Yes" then speedometerEnabled = true else speedometerEnabled = false end end}) local livesMode = 1 CV_RegisterVar({name="hud_lives", defaultValue=livesMode, flags=CV_CALL, pv={}, func=function(var) if var.value < 0 or var.value > 3 then print("Invalid mode: " + var.value + ", must be in the range 0 - 3") end livesMode = var.value if livesMode == 0 then print("Lives mode set to Icon only") elseif livesMode == 1 then print("Lives mode set to Icon and character name") elseif livesMode == 2 then print("Lives mode set to Icon and full character name") elseif livesMode == 3 then print("Lives mode set to Icon and player name") end end}) CV_RegisterVar({name="hud_enabled", defaultValue=1, flags=CV_CALL, pv=CV_OnOff, func=function (var) local v = string.lower(var.string) if v == "on" or v == "1" or v == "Yes" then hudEnabled = true else hudEnabled = false end print(var.value) end}) CV_RegisterVar({name="hud_opposite", defaultValue=0, flags=CV_CALL, pv=CV_OnOff, func=function (var) local v = string.lower(var.string) if v == "on" or v == "1" or v == "Yes" then useOpposite = true else useOpposite = false end print(var.value) end}) CV_RegisterVar({name="hud_intermission", defaultValue=0, flags=CV_CALL, pv=CV_OnOff, func=function (var) local v = string.lower(var.string) if v == "on" or v == "1" or v == "Yes" then customIntermission = true else customIntermission = false end print(var.value) end}) local deccel = FRACUNIT local accelAdd = -1 local minSpeed = 1 local color local h = 1 local redstart = 38 local rcolor = redstart local redend = 47 local prevBossHp = 0 local shake = 0 local play = nil local function hudHandler(v, p, cam) curplayer = p if p.customHudThing then p.customHudThing.hudEnabled = hudEnabled end if not hudEnabled then return end if not (p.mo or p.mo.valid) then return end if p.mo.skin == "modernsonic" then return end if v.renderer() == "none" then return end if p.mo == nil or not p.valid then return end if p.powers[pw_carry] == CR_NIGHTSMODE then return end if G_IsSpecialStage() then return end if p.mo.color == nil then return end local ramp = nil if p.mo.color then if useOpposite then ramp = skincolors[ColorOpposite(p.mo.color)].ramp else ramp = skincolors[p.mo.color].ramp end end local timer = 0 local timerMax = 0 if p.powers[pw_underwater] then timerMax = 1050 timer = min(p.powers[pw_underwater], timerMax) elseif p.powers[pw_spacetime] then timerMax = 405 timer = min(p.powers[pw_spacetime], timerMax) end --Underwater/Space timer if timerMax > 0 then local ramp = skincolors[SKINCOLOR_SAPPHIRE].ramp if timer < TICRATE*12 then if timer % 4 == 0 then ramp = skincolors[SKINCOLOR_RED].ramp end end v.drawFill(xpos + timerxoffset, p.customHudThing.leftypos, width - timerxoffset - 1, 2, ramp[15]|a|V_SNAPTOTOP|a|V_SNAPTOLEFT) v.drawFill(xpos + timerxoffset, p.customHudThing.leftypos, FixedInt(FixedMul(FixedDiv(timer*FRACUNIT, timerMax*FRACUNIT), width*FRACUNIT)) - timerxoffset - 1, 2, ramp[4]|a|V_SNAPTOTOP|a|V_SNAPTOLEFT) end if not ramp then return end play = p color = p.mo.color xpos = xpos + (max(min(targetxpos - xpos, curspeed), -curspeed)) if a > 0 and xpos == targetxpos then a = a - 0x00010000 end coolCounter = coolCounter + 1 if not started and p.speed > FRACUNIT/10 then started = true curspeed = animspeed end local col = 29 if v.RandomChance(deccel) then if curspeed > minSpeed then curspeed = curspeed - accelAdd end end if coolCounter > 7 then coolCounter = 0 if not goRed then goRed = true else goRed = false end end if p.rings > 0 then h = 1 end rcolor = rcolor + h if (rcolor > redend) then rcolor = redend h = -1 end if (rcolor < redstart) then rcolor = redstart h = 1 end if (rcolor ~= redend) then col = rcolor end local txpos = 5; local rpos = -6; if dmgshake > 0 then dmgshake = dmgshake - 1 end local shakeval = sin(FixedAngle((FixedDiv(leveltime*FRACUNIT, TICRATE*FRACUNIT)/25)*FRACUNIT)) local ofs = 0 if (dmgshake > 0) then ofs = (shakeval*3)/FRACUNIT end --Rings v.drawFill(xpos, ypos + ofs, width, height, col|a|V_SNAPTOTOP|a|V_SNAPTOLEFT) local i = 0 local step = width / 16 if not ramp then return end v.drawFill(xpos, ypos + height + ofs, width, 1, ramp[15]|a|V_SNAPTOTOP|a|V_SNAPTOLEFT) while i < 16 do local c = ramp[i] local x = 0 while x < step do v.drawFill(xpos + (i * step) + x, ypos + height + ofs, 1, 1, c|a|V_SNAPTOTOP|a|V_SNAPTOLEFT) x = x + 1 end i = i + 1 end v.drawString(xpos + txpos + rtxoffset, 1 + ypos + ofs, "Rings", V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") local twidth = v.stringWidth("" + p.rings, V_SNAPTOTOP|a|V_SNAPTOLEFT) v.drawString(xpos + width + txpos - twidth + rpos, 1 + ypos + ofs, "" + p.rings, V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") --Time v.drawString(xpos + txpos + txoffset, 4 + ypos + height, "Time", V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") local f = "%02d" local str = string.format(f, G_TicsToMinutes(p.realtime, true)) + ":" + string.format(f, G_TicsToSeconds(p.realtime)) + "." + string.format(f, G_TicsToCentiseconds(p.realtime)) local twidth = v.stringWidth(str, V_SNAPTOTOP|a|V_SNAPTOLEFT) v.drawString(xpos + width + txpos - twidth + rpos, 4 + ypos + height, str, V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") --Score v.drawString(xpos + txpos + txoffset, 4 + ypos + height + 10, "Score", V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") local str = "" + p.score local twidth = v.stringWidth(str, V_SNAPTOTOP|a|V_SNAPTOLEFT) v.drawString(xpos + width + txpos - twidth + rpos, 4 + ypos + height + 10, str, V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") if p.customHudThing then p.customHudThing.ypos = ypos p.customHudThing.xpos = xpos p.customHudThing.leftypos = 4 + ypos + height + 20 p.customHudThing.leftxpos = xpos + txpos p.customHudThing.txoffset = txoffset p.customHudThing.a = a p.customHudThing.width = width p.customHudThing.height = height p.customHudThing.drawLabeledThing = function (label, value, widthMul, labelVF, valueVF) if widthMul == nil then widthMul = FRACUNIT end local c = p.customHudThing local width = FixedInt(FixedMul(c.width*FRACUNIT, widthMul)) v.drawString(c.leftxpos + c.txoffset, c.leftypos, label, labelVF|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") local str = "" + value local twidth = v.stringWidth(str, V_SNAPTOTOP|a|V_SNAPTOLEFT) if not labelVF then labelVF = 0 end if not valueVF then valueVF = V_YELLOWMAP end v.drawString((c.leftxpos + c.txoffset + width + rpos) - twidth - 1, c.leftypos, str, valueVF|a|V_SNAPTOTOP|a|V_SNAPTOLEFT, "left") c.leftypos = c.leftypos + 10 end end --Life counter local xwidth = v.stringWidth("x", V_SNAPTOBOTTOM|a|V_SNAPTOLEFT, "small") local s = v.getSprite2Patch(p.realmo.skin, SPR2_LIFE, false, 0) local scaleMul = FRACUNIT; local colormap = v.getColormap(p.mo.skin, p.mo.color); if not s then s = v.getSpritePatch("TV1U", A) scaleMul = FRACUNIT/3 end local off = 10; local nyoff = -2; local lyoff = -2; local iconSize = max(s.width, s.height); local x = 320 - xpos; v.drawScaled(((320 - xpos) - (iconSize))*FRACUNIT + (s.leftoffset * (FRACUNIT/2)), (ypos+iconSize+1)*FRACUNIT - (s.topoffset * (FRACUNIT/2)), FixedMul(FRACUNIT + (2/FRACUNIT), scaleMul), s, V_SNAPTOTOP|a|V_SNAPTORIGHT, colormap) x = x - (iconSize + 8) if livesMode == 1 or livesMode == 2 or livesMode == 3 then local t = skins[p.skin].hudname or skins[p.skin].realname local f = 0 if livesMode == 2 then t = skins[p.skin].realname f = V_ALLOWLOWERCASE elseif livesMode == 3 then t = p.name f = V_ALLOWLOWERCASE end local tw = v.stringWidth(t, a|f) x = x - (tw + 4) v.drawString(x, ypos + 3, t, a|V_SNAPTOTOP|V_SNAPTORIGHT|skincolors[p.mo.color].chatcolor|f) x = x - 6 end if G_GametypeUsesLives() and p.lives < 127 then v.drawString(x - (iconSize + 16), ypos + 3, string.format("%02d", p.lives), V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTORIGHT) v.drawString((x - (iconSize)), ypos + 3, "x", V_YELLOWMAP|a|V_SNAPTOTOP|a|V_SNAPTORIGHT|V_ALLOWLOWERCASE) end local h = 3; if boss and boss.valid and boss.health > 0 and P_CheckSight(p.mo, boss) then bossBarOfs = bossBarOfs + max(min(0 - bossBarOfs, 2), -2) else bossBarOfs = bossBarOfs + max(min(-25 - bossBarOfs, 2), -2) bossAnimW = 0 end local bossHealth = 0 local bossMaxHealth = 1 if boss and boss.valid then bossHealth = boss.health bossMaxHealth = boss.info.spawnhealth end if prevBossHp > bossHealth then shake = TICRATE/3 end prevBossHp = bossHealth if shake > 0 then shake = shake - 1 end if bossBarOfs < 20 then local w = 150; local center = (320 / 2) local x = center - (w / 2) local ramp = skincolors[SKINCOLOR_RED].ramp local ogypos = ypos; local ypos = ogypos + bossBarOfs; local s = TC_DEFAULT if shake > 0 then ypos = ypos - ((shakeval*3)/FRACUNIT) end if shake > 0 then if shake % 3 == 0 then ramp = skincolors[SKINCOLOR_WHITE].ramp s = TC_BOSS end end v.drawString(center, ypos, "BOSS", V_REDMAP|a|V_SNAPTOTOP, "center") ypos = ypos + 10 local targetW = FixedMul(FixedDiv(bossHealth*FRACUNIT, bossMaxHealth*FRACUNIT), (w - 4)*FRACUNIT)/FRACUNIT; if bossBarOfs < 5 then bossAnimW = bossAnimW + max(min(targetW - bossAnimW, 3), -3) end local v1 = targetW; local v2 = bossAnimW if (v1 > v2) then v1 = bossAnimW v2 = targetW end v.drawFill(x, ypos, w, height, 29|V_SNAPTOTOP) v.drawFill(x + 2, ypos + 2, w - 4, height - 4, ramp[15]|V_SNAPTOTOP) v.drawFill(x + 2, ypos + 2, v2, height - 4, ramp[8]|V_SNAPTOTOP) v.drawFill(x + 2, ypos + 2, v1, height - 4, ramp[4]|V_SNAPTOTOP) if (boss and boss.valid) then local patch = v.getSpritePatch(boss.sprite, boss.frame) if patch then local scale = FixedDiv(16*FRACUNIT, (max(patch.height, patch.width)*FRACUNIT)) v.drawScaled((x*FRACUNIT) + FixedMul(patch.leftoffset * FRACUNIT, scale), ((ypos)*FRACUNIT), scale, patch, a|V_SNAPTOTOP, v.getColormap(s, boss.color)) end end end --Speedometer local yofs = 0 if speedometerEnabled then local off = (8 * 5) + 4; local px = 0; v.drawString(px + xpos, 200 - height - 9, "SPEED", skincolors[p.mo.color].chatcolor|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local spd = p.speed local int = spd / FRACUNIT local dec = FixedMul(spd % FRACUNIT, 10*FRACUNIT)/FRACUNIT local t = int + "." + dec + " FU/t" local tw = v.stringWidth(t, 0) v.drawString(px + xpos + (width - tw), 200 - height - 9, t, skincolors[p.mo.color].chatcolor|a|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE) v.drawFill(px + xpos, 200 - height, width, height, 29|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local w = width - (h * 2) v.drawFill(px + xpos + h, 200 - height + h, w, height - (h * 2), ramp[15]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) v.drawFill(px + xpos + h, 200 - height + h, min(FixedInt(FixedMul(FixedDiv((p.speed),(skins[0].normalspeed*2 + (skins[0].normalspeed/2))), w*FRACUNIT)), w), height - (h * 2), ramp[4]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local i = 0; local sstep = width / 16; v.drawFill(px + xpos, (200 - 1), width, 1, ramp[15]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) while i < 16 do local c = ramp[i] local x = 0 while x < sstep do v.drawFill(px + xpos + (i * sstep) + x, (200 - 1), 1, 1, c|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) x = x + 1 end i = i + 1 end yofs = -20 end local ogWidth = width if p.customHudThing then p.customHudThing.yofs = yofs; if not p.customHudThing.drawBar then p.customHudThing.drawBar = function(bar) if p.mo == nil or not p.valid then return end if p.powers[pw_carry] == CR_NIGHTSMODE then return end if G_IsSpecialStage() then return end if not hudEnabled then return end local width = FixedInt(FixedMul(ogWidth*FRACUNIT, bar.widthMul)) local color = skincolors[p.mo.color] if bar.color ~= -1 then color = skincolors[bar.color] end local px = 0; local ramp = color.ramp; local val = bar.value; local max = bar.max; local yofs = p.customHudThing.yofs; local t = bar.valueString if t then local tw = v.stringWidth(t, 0) v.drawString(px + xpos + (width - tw), 200 - height - 9 + yofs, t, color.chatcolor|a|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE) end v.drawString(px + xpos, 200 - height - 9 + yofs, bar.label, color.chatcolor|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) v.drawFill(px + xpos, 200 - height + yofs, width, height, 29|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local w = width - (h * 2) v.drawFill(px + xpos + h, 200 - height + h + yofs, w, height - (h * 2), ramp[15]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) v.drawFill(px + xpos + h, 200 - height + h + yofs, min(FixedInt(FixedMul(FixedDiv((val*FRACUNIT),(max*FRACUNIT)), w*FRACUNIT)), w), height - (h * 2), ramp[5]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local sstep = width / 16; v.drawFill(px + xpos, (200 - 1) + yofs, width, 1, ramp[15]|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) local i = 0 while i < 16 do local c = ramp[i] local x = 0 while x < sstep do v.drawFill(px + xpos + (i * sstep) + x, (200 - 1) + yofs, 1, 1, c|a|V_SNAPTOBOTTOM|a|V_SNAPTOLEFT) x = x + 1 end i = i + 1 end p.customHudThing.yofs = yofs - 20 end end if p.charability == CA_FLY and p.powers[pw_tailsfly] > 0 then local int = p.powers[pw_tailsfly] / TICRATE local dec = (FixedDiv((p.powers[pw_tailsfly] % TICRATE)*FRACUNIT, FRACUNIT*35) * 10) / FRACUNIT local vstr = int + "." + dec + "s" p.customHudThing.drawBar({ label = "FLY", color = -1, valueString = vstr, widthMul = FRACUNIT - (FRACUNIT/8), value = p.powers[pw_tailsfly], max = 280 }) end end end local function intermissionScreen(v) if v.renderer() == "none" then return end if play == nil or not play.valid then return end if not customIntermission then return end local str = "test" local tw = v.stringWidth(str) local testScore = play.score local testTime = play.realtime local p = play --Time v.drawString(targetxpos + txoffset, 4 + ypos, "Time", V_SNAPTOTOP|V_SNAPTOLEFT, "left") local f = "%02d" local time = testTime local str = string.format(f, G_TicsToMinutes(time, true)) + ":" + string.format(f, G_TicsToSeconds(time)) + "." + string.format(f, G_TicsToCentiseconds(time)) local twidth = v.stringWidth(str, V_SNAPTOTOP|V_SNAPTOLEFT) v.drawString(targetxpos + width - twidth, 4 + ypos, str, V_YELLOWMAP|V_SNAPTOTOP|V_SNAPTOLEFT, "left") --Score v.drawString(targetxpos + txoffset, 4 + ypos + 10, "Score", V_SNAPTOTOP|V_SNAPTOLEFT, "left") local str = "" + testScore local twidth = v.stringWidth(str, V_SNAPTOTOP|V_SNAPTOLEFT) v.drawString(targetxpos + width - twidth, 4 + ypos + 10, str, V_YELLOWMAP|V_SNAPTOTOP|V_SNAPTOLEFT, "left") --Now i have to calculate the bonuses, pain local ringBonus = p.rings * 100 local seconds = G_TicsToSeconds(play.realtime, true) local timeBonus = 0 if seconds <= 600 then timeBonus = 0 end if seconds <= 599 then timeBonus = 100 end if seconds <= 539 then timeBonus = 200 end if seconds <= 479 then timeBonus = 300 end if seconds <= 419 then timeBonus = 400 end if seconds <= 359 then timeBonus = 500 end if seconds <= 299 then timeBonus = 1000 end if seconds <= 239 then timeBonus = 2000 end if seconds <= 179 then timeBonus = 3000 end if seconds <= 119 then timeBonus = 4000 end if seconds <= 89 then timeBonus = 5000 end if seconds <= 59 then timeBonus = 10000 end if seconds <= 29 then timeBonus = 50000 end local y = 100 - 32 local h = 175 local w = 200 local name = skins[play.skin].realname local col = skincolors[color] local c = col.chatcolor local ramp = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} if color then if useOpposite then ramp = skincolors[ColorOpposite(color)].ramp else ramp = skincolors[color].ramp end end local tw = v.stringWidth(name + " ") + v.stringWidth("Got Through") local pos = 160 + (tw / 2) local ww = w + 8; local step = w / 16; v.drawString(pos - tw, y, name, c) v.drawString(pos - (tw - v.stringWidth(name + " ")), y, "Got Through", V_YELLOWMAP) y = y + 20 pos = 160 + (w / 2) v.drawFill(pos - w, y + 8, w, 1, ramp[15]) local i = 0 while i < 16 do local c = ramp[i] local x = 0 while x < step do v.drawFill((pos - w) + (i * step) + x, y + 8, 1, 1, c) x = x + 1 end i = i + 1 end v.drawString(pos - w, y, "RING BONUS") local s = ringBonus + "" local wi = v.stringWidth(s) v.drawString(pos - wi, y, s, V_YELLOWMAP) y = y + 12 v.drawFill(pos - w, y + 8, w, 1, ramp[15]) local i = 0 while i < 16 do local c = ramp[i] local x = 0 while x < step do v.drawFill((pos - w) + (i * step) + x, y + 8, 1, 1, c) x = x + 1 end i = i + 1 end v.drawString(pos - w, y, "TIME BONUS") local s = timeBonus + "" local wi = v.stringWidth(s) v.drawString(pos - wi, y, s, V_YELLOWMAP) end addHook("PlayerThink", function (player) if player.mo == nil or not player.mo.valid then return end local p = player if not player.customHudThing then p.customHudThing = { customBars = {} } end p.iscustomhud = hudEnabled end) hud.add(hudHandler, "game") hud.add(intermissionScreen, "intermission")