Last active
February 13, 2021 22:15
-
-
Save itszn/4529fa62d01251de0106fd31e70697d9 to your computer and use it in GitHub Desktop.
LiqidWave-1.4.1 Challenges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- TODO: move util functions to common.lua | |
local charts = {} | |
local passed = false | |
local desw = 770 | |
local desh = 800 | |
local moveX = 0 | |
local moveY = 0 | |
local currResX = 0 | |
local currResY = 0 | |
local scale = 1 | |
local gradeImg = nil; | |
local badgeImg = nil; | |
local gradeAR = 1 --grade aspect ratio | |
local chartDuration = 0 | |
local chartDurationText = "0m 00s" | |
local badges = { | |
gfx.CreateSkinImage("song_select/medal/played.png", 0), | |
gfx.CreateSkinImage("song_select/medal/clear.png", 0), | |
gfx.CreateSkinImage("song_select/medal/hard-clear.png", 0), | |
gfx.CreateSkinImage("song_select/medal/full-combo.png", 0), | |
gfx.CreateSkinImage("song_select/medal/perfect.png", 0) | |
} | |
local laneNames = {"A", "B", "C", "D", "L", "R"} | |
local diffNames = {"NOV", "ADV", "EXH", "INF"} | |
--local backgroundImage = gfx.CreateSkinImage("bg.png", 1); | |
game.LoadSkinSample("result") | |
local played = false | |
local shotTimer = 0; | |
local shotPath = ""; | |
game.LoadSkinSample("shutter") | |
local hitWindowPerfect = 46 | |
local hitWindowGood = 92 | |
local clearTextBase = "" -- Used to determind the type of clear | |
local clearText = "" | |
local currTime = 0 | |
local critText = "CRIT" | |
local nearText = "NEAR" | |
local hitDeltaScale = game.GetSkinSetting("hit_graph_delta_scale") | |
local showGuide = game.GetSkinSetting("show_result_guide") | |
local showIcons = game.GetSkinSetting("show_result_icons") | |
--local showStatsHit = game.GetSkinSetting("show_detailed_results") | |
local showStatsHit = true | |
local showChartInfo = 0 | |
local scroll = 0.0 | |
local scrolloff = 0.0 | |
local initialRender = true | |
local function startResultSample() | |
game.PlaySample("result", true) | |
game.SetSkinSetting("music_playing", "result") | |
end | |
function waveParam(period, offset) | |
local t = currTime | |
if offset then t = t+offset end | |
t = t / period | |
return 0.5 + 0.5*math.cos(t * math.pi * 2) | |
end | |
function getTextScale(txt, max_width) | |
local x1, y1, x2, y2 = gfx.TextBounds(0, 0, txt) | |
if x2 < max_width then | |
return 1 | |
else | |
return max_width / x2 | |
end | |
end | |
function drawScaledText(txt, x, y, max_width) | |
local text_scale = getTextScale(txt, max_width) | |
if text_scale == 1 then | |
gfx.BeginPath() | |
gfx.Text(txt, x, y) | |
return | |
end | |
gfx.Save() | |
gfx.Translate(x, y) | |
gfx.Scale(text_scale, 1) | |
gfx.BeginPath() | |
gfx.Text(txt, 0, 0) | |
gfx.Restore() | |
end | |
function drawLine(x1,y1,x2,y2,w,r,g,b) | |
gfx.BeginPath() | |
gfx.MoveTo(x1,y1) | |
gfx.LineTo(x2,y2) | |
gfx.StrokeColor(r,g,b) | |
gfx.StrokeWidth(w) | |
gfx.Stroke() | |
end | |
function getScoreBadgeDesc(s) | |
if s.badge == 1 then | |
if s.flags & 1 ~= 0 then return "crash" | |
else return string.format("%.1f%%", s.gauge * 100) | |
end | |
elseif 2 <= s.badge and s.badge <= 4 and s.misses < 10 then | |
return string.format("%d-%d", s.goods, s.misses) | |
end | |
return "" | |
end | |
result_set = function() | |
currentAdded = false | |
passed = result.passed | |
for i,chart in ipairs(result.charts) do | |
chart.index = i | |
chart.chartTitle = string.format("#%u %s", i, chart.title) | |
if chart.duration ~= nil then | |
chart.chartDuration = chart.duration | |
chart.chartDurationText = string.format("%dm %02d.%01ds", chart.chartDuration // 60000, (chart.chartDuration // 1000) % 60, (chart.chartDuration // 100) % 10) | |
hitGraphHoverScale = math.max(chart.chartDuration / 10000, 5) | |
else | |
chartDuration = 0 | |
chartDurationText = "" | |
hitGraphHoverScale = 10 | |
end | |
chart.rawHighScores = chart.highScores | |
chart.highScores = { } | |
chart.highestScore = 0 | |
for i,s in ipairs(chart.rawHighScores) do | |
newScore = { } | |
if currentAdded == false and chart.score > s.score then | |
newScore.score = string.format("%08d", chart.score) | |
newScore.badge = chart.badge | |
newScore.badgeDesc = getScoreBadgeDesc(chart) | |
newScore.color = {255, 127, 0} | |
newScore.subtext = "Now" | |
newScore.xoff = 0 | |
table.insert(chart.highScores, newScore) | |
newScore = { } | |
currentAdded = true | |
end | |
newScore.score = string.format("%08d", s.score) | |
newScore.badge = s.badge | |
newScore.badgeDesc = getScoreBadgeDesc(s) | |
newScore.color = {0, 127, 255} | |
newScore.xoff = 0 | |
if s.timestamp > 0 then | |
newScore.subtext = os.date("%Y-%m-%d %H:%M:%S", s.timestamp) | |
else | |
newScore.subtext = "" | |
end | |
if chart.highestScore < s.score then | |
chart.highestScore = s.score | |
end | |
table.insert(chart.highScores, newScore) | |
end | |
if currentAdded == false then | |
newScore = { } | |
newScore.score = string.format("%08d", chart.score) | |
newScore.badge = chart.badge | |
newScore.badgeDesc = getScoreBadgeDesc(chart) | |
newScore.color = {255, 127, 0} | |
newScore.subtext = "Now" | |
newScore.xoff = 0 | |
table.insert(chart.highScores, newScore) | |
newScore = { } | |
currentAdded = true | |
end | |
chart.scoreString = string.format("%08d", chart.score) | |
chart.badgeDesc = getScoreBadgeDesc(chart) | |
if chart.jacketPath ~= nil and chart.jacketPath ~= "" then | |
chart.jacketImg = gfx.CreateImage(chart.jacketPath, 0) | |
end | |
chart.gradeAR = 1 | |
chart.gradeImg = gfx.CreateSkinImage(string.format("score/%s.png", chart.grade), 0) | |
if chart.gradeImg ~= nil then | |
local gradew, gradeh = gfx.ImageSize(chart.gradeImg) | |
chart.gradeAR = gradew / gradeh | |
end | |
if 1 <= chart.badge and chart.badge <= 5 then | |
chart.badgeImg = badgeImages[chart.badge] | |
else | |
chart.badgeImg = nil | |
end | |
if chart.passed then | |
chart.clearTextBase = "CHALLENGE PASS" | |
else | |
chart.clearTextBase = "CHALLENGE FAIL" | |
end | |
if chart.badge == 2 then chart.clearTextBase = chart.clearTextBase .. " - CLEAR" | |
elseif chart.badge == 3 then chart.clearTextBase = chart.clearTextBase .. " - HARD CLEAR" | |
elseif chart.badge == 4 then chart.clearTextBase = chart.clearTextBase .. " - FULL COMBO" | |
elseif chart.badge == 5 then chart.clearTextBase = chart.clearTextBase .. " - PERFECT" | |
end | |
if chart.playbackSpeed ~= nil and chart.playbackSpeed ~= 1.00 then | |
if chart.clearTextBase == "" then chart.clearText = string.format("x%.2f play", chart.playbackSpeed) | |
else chart.clearText = string.format("%s (x%.2f play)", chart.clearTextBase, chart.playbackSpeed) | |
end | |
else | |
chart.clearText = chart.clearTextBase | |
end | |
if chart.speedModType ~= nil then | |
if chart.speedModType == 0 then | |
chart.speedMod = "XMOD" | |
chart.speedModValue = string.format("%.2f", chart.speedModValue) | |
elseif chart.speedModType == 1 then | |
chart.speedMod = "MMOD" | |
chart.speedModValue = string.format("%.1f", chart.speedModValue) | |
elseif chart.speedModType == 2 then | |
chart.speedMod = "CMOD" | |
chart.speedModValue = string.format("%.1f", chart.speedModValue) | |
else | |
chart.speedMod = "" | |
chart.speedModValue = "" | |
end | |
else | |
chart.speedMod = "" | |
chart.speedModValue = "" | |
end | |
chart.hasHitStat = chart.noteHitStats ~= nil and #(chart.noteHitStats) > 0 | |
chart.hitWindowPerfect = 46 | |
chart.hitWindowGood = 92 | |
if chart.hitWindow ~= nil then | |
chart.hitWindowPerfect = chart.hitWindow.perfect | |
chart.hitWindowGood = chart.hitWindow.good | |
end | |
chart.hitHistogram = {} | |
chart.hitMinDelta = 0 | |
chart.hitMaxDelta = 0 | |
if chart.hasHitStat then | |
for i = 1, #chart.noteHitStats do | |
local hitStat = chart.noteHitStats[i] | |
if hitStat.rating == 1 or hitStat.rating == 2 then | |
if chart.hitHistogram[hitStat.delta] == nil then chart.hitHistogram[hitStat.delta] = 0 end | |
chart.hitHistogram[hitStat.delta] = chart.hitHistogram[hitStat.delta] + 1 | |
if hitStat.delta < chart.hitMinDelta then chart.hitMinDelta = hitStat.delta end | |
if hitStat.delta > chart.hitMaxDelta then chart.hitMaxDelta = hitStat.delta end | |
end | |
end | |
end | |
charts[i] = chart | |
end | |
critText = "CRIT" | |
nearText = "NEAR" | |
end | |
draw_shotnotif = function(x,y) | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.Save() | |
gfx.Translate(x,y) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.BeginPath() | |
gfx.Rect(0,0,200,40) | |
gfx.FillColor(30,30,30) | |
gfx.StrokeColor(255,128,0) | |
gfx.Fill() | |
gfx.Stroke() | |
gfx.FillColor(255,255,255) | |
gfx.FontSize(15) | |
gfx.Text("Screenshot saved to:", 3,5) | |
gfx.Text(shotPath, 3,20) | |
gfx.Restore() | |
end | |
--------------------- | |
-- Subcomponents -- | |
--------------------- | |
draw_stat = function(x,y,w,h, name, value, format,r,g,b) | |
gfx.Save() | |
gfx.Translate(x,y) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.FontSize(h) | |
gfx.Text(name .. ":",0, 0) | |
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT + gfx.TEXT_ALIGN_TOP) | |
gfx.Text(string.format(format, value),w, 0) | |
gfx.BeginPath() | |
gfx.MoveTo(0,h) | |
gfx.LineTo(w,h) | |
if r then gfx.StrokeColor(r,g,b) | |
else gfx.StrokeColor(200,200,200) end | |
gfx.StrokeWidth(1) | |
gfx.Stroke() | |
gfx.Restore() | |
return y + h + 5 | |
end | |
draw_score = function(score, x, y, w, h, pre) | |
local center = x + w * 0.54 | |
local prefix = "" | |
if pre ~= nil then prefix = pre end | |
gfx.LoadSkinFont("NovaMono.ttf") | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT) | |
gfx.FontSize(h) | |
gfx.Text(string.format("%s%04d", prefix, score // 10000), center-h/70, y) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT) | |
gfx.FontSize(h*0.75) | |
gfx.Text(string.format("%04d", score % 10000), center+h/70, y) | |
end | |
draw_highscores = function(chart, full) | |
gfx.FillColor(255,255,255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT) | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.FontSize(30) | |
gfx.Text("High Scores",510,30) | |
gfx.StrokeWidth(1) | |
for i,s in ipairs(chart.highScores) do | |
gfx.Save() | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.BeginPath() | |
local ypos = 45 + (i - 1) * 80 | |
if ypos > desh then | |
break | |
end | |
gfx.Translate(510 + s.xoff, ypos) | |
gfx.RoundedRectVarying(0, 0, 260, 70,0,0,25,0) | |
gfx.FillColor(15,15,15) | |
gfx.StrokeColor(s.color[1], s.color[2], s.color[3]) | |
gfx.Fill() | |
gfx.Stroke() | |
gfx.BeginPath() | |
gfx.FillColor(255,255,255) | |
gfx.FontSize(25) | |
gfx.Text(string.format("#%d",i), 5, 5) | |
if s.badge ~= nil and 1 <= s.badge and s.badge <= 5 then | |
gfx.BeginPath() | |
gfx.ImageRect(37, 7, 36, 36, badgeImages[s.badge], 1, 0) | |
if full then | |
gfx.BeginPath() | |
gfx.FontSize(15) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BOTTOM) | |
gfx.Text(s.badgeDesc, 55, 52) | |
end | |
end | |
draw_score(s.score, 55, 42, 215, 60) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_TOP) | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.FontSize(20) | |
gfx.Text(s.subtext, 135, 45) | |
gfx.Restore() | |
end | |
end | |
draw_gauge_graph = function(chart, x, y, w, h, alpha, xfocus, xscale) | |
if alpha == nil then alpha = 160 end | |
if xfocus == nil then | |
xfocus = 0 | |
xscale = 1 | |
end | |
local leftIndex = math.floor(#(chart.gaugeSamples)/w * (-xfocus / xscale + xfocus)) | |
leftIndex = math.max(1, math.min(#(chart.gaugeSamples), leftIndex)) | |
gfx.BeginPath() | |
gfx.MoveTo(x, y + h - h * chart.gaugeSamples[leftIndex]) | |
for i = leftIndex+1, #(chart.gaugeSamples) do | |
local gaugeX = i * w / #(chart.gaugeSamples) | |
gaugeX = (gaugeX - xfocus) * xscale + xfocus | |
gfx.LineTo(x + gaugeX,y + h - h * chart.gaugeSamples[i]) | |
if gaugeX > w then break end | |
end | |
gfx.StrokeWidth(2.0) | |
if chart.flags & 1 ~= 0 then | |
gfx.StrokeColor(255,80,0,alpha) | |
gfx.Stroke() | |
else | |
gfx.StrokeColor(0,180,255,alpha) | |
gfx.Scissor(x, y + h * 0.3, w, h * 0.7) | |
gfx.Stroke() | |
gfx.ResetScissor() | |
gfx.Scissor(x,y-10,w,10+h*0.3) | |
gfx.StrokeColor(255,0,255,alpha) | |
gfx.Stroke() | |
gfx.ResetScissor() | |
end | |
end | |
draw_hit_graph_lines = function(chart, x, y, w, h) | |
local maxDispDelta = h/2 / hitDeltaScale | |
gfx.StrokeWidth(1) | |
gfx.BeginPath() | |
gfx.StrokeColor(128, 255, 128, 128) | |
gfx.MoveTo(x, y+h/2) | |
gfx.LineTo(x+w, y+h/2) | |
gfx.Stroke() | |
gfx.BeginPath() | |
gfx.StrokeColor(64, 128, 64, 64) | |
for i = -math.floor(maxDispDelta / 10), math.floor(maxDispDelta / 10) do | |
local lineY = y + h/2 + i*10*hitDeltaScale | |
if i ~= 0 then | |
gfx.MoveTo(x, lineY) | |
gfx.LineTo(x+w, lineY) | |
end | |
end | |
gfx.Stroke() | |
end | |
draw_hit_graph = function(chart, x, y, w, h, xfocus, xscale) | |
if not chart.hasHitStat or hitDeltaScale == 0.0 then | |
return | |
end | |
if xfocus == nil then xfocus = 0 end | |
if xscale == nil then xscale = 1 end | |
draw_hit_graph_lines(chart, x, y, w, h) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.FontSize(12) | |
for i = 1, #(chart.noteHitStats) do | |
local hitStat = chart.noteHitStats[i] | |
local hitStatX = (hitStat.timeFrac*w - xfocus)*xscale + xfocus | |
if 0 <= hitStatX then | |
if hitStatX > w then break end | |
local hitStatY = h/2 + hitStat.delta * hitDeltaScale | |
if hitStatY < 0 then hitStatY = 0 | |
elseif hitStatY > h then hitStatY = h | |
end | |
local hitStatSize = 1 | |
if hitStat.rating == 2 then | |
hitStatSize = 1.25 | |
gfx.FillColor(255, 150, 0, 160) | |
elseif hitStat.rating == 1 then | |
hitStatSize = 1.75 | |
gfx.FillColor(255, 0, 200, 128) | |
elseif hitStat.rating == 0 then | |
hitStatSize = 2 | |
gfx.FillColor(255, 0, 0, 128) | |
end | |
gfx.BeginPath() | |
if xscale > 1 then | |
gfx.Text(laneNames[hitStat.lane + 1], x+hitStatX, y+hitStatY) | |
else | |
gfx.Rect(x+hitStatX-hitStatSize/2, y+hitStatY-hitStatSize/2, hitStatSize, hitStatSize) | |
gfx.Fill() | |
end | |
end | |
end | |
end | |
draw_left_graph = function(chart, x, y, w, h) | |
local mx, my = game.GetMousePos() | |
mx = mx / scale - moveX | |
my = my / scale - moveY | |
local mhit = x <= mx and mx <= x+w and y <= my and my <= y+h | |
local hit_xfocus = 0 | |
local hit_xscale = 1 | |
gfx.BeginPath() | |
gfx.Rect(x, y, w, h) | |
gfx.FillColor(255, 255, 255, 32) | |
gfx.Fill() | |
local chartDurationDisp = string.format("Duration: %s", chart.chartDurationText) | |
if mhit then | |
hit_xfocus = mx - x | |
hit_xscale = hitGraphHoverScale | |
local currPos = chart.chartDuration * ((mx - x) / w) | |
chartDurationDisp = string.format("%dm %02d.%01ds / %s" , currPos // 60000, (currPos // 1000) % 60, (currPos // 100) % 10, chart.chartDurationText) | |
drawLine(mx, y, mx, y+h, 1, 64, 96, 64) | |
end | |
gfx.FontSize(17) | |
gfx.FillColor(64, 128, 64, 96) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.BeginPath() | |
gfx.Text(chartDurationDisp, x+5, y) | |
if chart.bpm ~= nil then | |
gfx.BeginPath() | |
gfx.Text(string.format("BPM: %s", chart.bpm), x+5, y+15) | |
end | |
draw_hit_graph(chart, x, y, w, h, hit_xfocus, hit_xscale) | |
if hit_xscale == 1 then | |
draw_gauge_graph(chart, x, y, w, h) | |
else | |
draw_gauge_graph(chart, x, y, w, h, 64, hit_xfocus, hit_xscale) | |
draw_gauge_graph(chart, x, y, w, h) | |
local gaugeInd = math.floor(1 + #(chart.gaugeSamples)/w * ((mx-x - hit_xfocus) / hit_xscale + hit_xfocus)) | |
gaugeInd = math.max(1, math.min(#(chart.gaugeSamples), gaugeInd)) | |
local gaugeY = h - h * chart.gaugeSamples[gaugeInd] | |
gfx.StrokeColor(255, 0, 0, 196) | |
gfx.FillColor(255, 255, 255, 196) | |
gfx.FontSize(16) | |
gfx.BeginPath() | |
gfx.Circle(mx, y + gaugeY, 2) | |
gfx.Stroke() | |
gfx.BeginPath() | |
gfx.Text(string.format("%.1f%%", chart.gaugeSamples[gaugeInd]*100), mx, y + gaugeY - 10) | |
end | |
gfx.FontSize(16) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255, 128) | |
gfx.Text(string.format("Mean: %.1f ms, Median: %d ms", chart.meanHitDelta, chart.medianHitDelta), x+4, y+h) | |
-- End gauge display | |
local endGauge = chart.gauge | |
local endGaugeY = y + h - h * endGauge | |
if endGaugeY > y+h - 10 then endGaugeY = y+h - 10 | |
elseif endGaugeY < y + 10 then endGaugeY = y + 10 | |
end | |
local gaugeText = string.format("%.1f%%", endGauge*100) | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT + gfx.TEXT_ALIGN_MIDDLE) | |
local x1, y1, x2, y2 = gfx.TextBounds(x+w-6, endGaugeY, gaugeText) | |
gfx.BeginPath() | |
gfx.FillColor(80, 80, 80, 128) | |
gfx.RoundedRect(x1-3, y1, x2-x1+6, y2-y1, 4) | |
gfx.Fill() | |
gfx.BeginPath() | |
gfx.LoadSkinFont("NovaMono.ttf") | |
gfx.FillColor(255, 255, 255) | |
gfx.Text(gaugeText, x+w-6, endGaugeY) | |
end | |
draw_hit_histogram = function(chart, x, y, w, h) | |
if not chart.hasHitStat or hitDeltaScale == 0.0 then | |
return | |
end | |
local maxDispDelta = math.floor(h/2 / hitDeltaScale) | |
local mode = 0 | |
local modeCount = 0 | |
for i = -maxDispDelta-1, maxDispDelta+1 do | |
if chart.hitHistogram[i] == nil then chart.hitHistogram[i] = 0 end | |
end | |
for i = -maxDispDelta, maxDispDelta do | |
local count = chart.hitHistogram[i-1] + chart.hitHistogram[i]*2 + chart.hitHistogram[i+1] | |
if count > modeCount then | |
mode = i | |
modeCount = count | |
end | |
end | |
gfx.StrokeWidth(1.5) | |
gfx.BeginPath() | |
gfx.StrokeColor(255, 255, 128, 96) | |
gfx.MoveTo(x, y) | |
for i = -maxDispDelta, maxDispDelta do | |
local count = chart.hitHistogram[i-1] + chart.hitHistogram[i]*2 + chart.hitHistogram[i+1] | |
gfx.LineTo(x + 0.9 * w * count / modeCount, y+h/2 + i*hitDeltaScale) | |
end | |
gfx.LineTo(x, y+h) | |
gfx.Stroke() | |
end | |
draw_right_graph = function(chart, x, y, w, h) | |
if not chart.hasHitStat or hitDeltaScale == 0.0 then | |
return | |
end | |
gfx.BeginPath() | |
gfx.Rect(x, y, w, h) | |
gfx.FillColor(64, 64, 64, 32) | |
gfx.Fill() | |
draw_hit_graph_lines(chart, x, y, w, h) | |
draw_hit_histogram(chart, x, y, w, h) | |
local meanY = h/2 + hitDeltaScale * chart.meanHitDelta | |
local medianY = h/2 + hitDeltaScale * chart.medianHitDelta | |
drawLine(x, y+meanY, x+w, y+meanY, 1.25, 255, 0, 0, 192) | |
drawLine(x, y+medianY, x+w, y+medianY, 1.25, 64, 64, 255, 192) | |
gfx.LoadSkinFont("NovaMono.ttf") | |
gfx.BeginPath() | |
if meanY < medianY then | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
else | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
end | |
gfx.FillColor(255, 128, 128) | |
gfx.FontSize(16) | |
gfx.Text(string.format("Mean: %.1f ms", chart.meanHitDelta), x+2, y+meanY) | |
gfx.BeginPath() | |
if medianY <= meanY then | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
else | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
end | |
gfx.FillColor(196, 196, 255) | |
gfx.FontSize(16) | |
gfx.Text(string.format("Median: %d ms", chart.medianHitDelta), x+2, y+medianY) | |
gfx.FillColor(255, 255, 255) | |
gfx.FontSize(15) | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.Text(string.format("Earliest: %d ms", chart.hitMinDelta), x+5, y) | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
gfx.Text(string.format("Latest: %d ms", chart.hitMaxDelta), x+5, y+h) | |
end | |
draw_laser_icon = function(chart, x, y, s) | |
gfx.Save() | |
gfx.Translate(x, y) | |
local r, g, b = game.GetLaserColor(0) | |
gfx.BeginPath() | |
gfx.FillColor(r, g, b, 96) | |
gfx.MoveTo(s*0.1, s*0.1) | |
gfx.LineTo(s*0.4, s*0.5) | |
gfx.LineTo(s*0.1, s*0.9) | |
gfx.LineTo(s*0.3, s*0.9) | |
gfx.LineTo(s*0.6, s*0.5) | |
gfx.LineTo(s*0.3, s*0.1) | |
gfx.LineTo(s*0.1, s*0.1) | |
gfx.Fill() | |
local r, g, b = game.GetLaserColor(1) | |
gfx.BeginPath() | |
gfx.FillColor(r, g, b, 96) | |
gfx.MoveTo(s*0.7, s*0.1) | |
gfx.LineTo(s*0.4, s*0.5) | |
gfx.LineTo(s*0.7, s*0.9) | |
gfx.LineTo(s*0.9, s*0.9) | |
gfx.LineTo(s*0.6, s*0.5) | |
gfx.LineTo(s*0.9, s*0.1) | |
gfx.LineTo(s*0.7, s*0.1) | |
gfx.Fill() | |
gfx.Restore() | |
return x - s | |
end | |
draw_speed_icon = function(chart, x, y, s) | |
if chart.speedMod == "" then return x end | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.FillColor(255, 255, 255) | |
gfx.BeginPath() | |
gfx.FontSize(15) | |
gfx.Text(chart.speedMod, x + s/2, y + s*0.3) | |
gfx.BeginPath() | |
gfx.FontSize(20) | |
gfx.Text(chart.speedModValue, x + s/2, y + s*0.65) | |
return x - s | |
end | |
draw_hidsud_icon = function(chart, x, y, s) | |
if chart.hidsud == nil then | |
return x | |
end | |
gfx.FontSize(15) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.FillColor(255, 255, 255) | |
gfx.BeginPath() | |
gfx.Text("SUDDEN", x + s/2, y + s*0.13) | |
gfx.Text("HIDDEN", x + s/2, y + s*0.62) | |
gfx.BeginPath() | |
gfx.FontSize(13) | |
gfx.Text(string.format("%.2f fd %.1f", chart.hidsud.suddenCutoff, chart.hidsud.suddenFade), x + s/2, y + s*0.35) | |
gfx.Text(string.format("%.2f fd %.1f", chart.hidsud.hiddenCutoff, chart.hidsud.hiddenFade), x + s/2, y + s*0.84) | |
return x - s | |
end | |
draw_mir_ran_icon = function(chart,x, y, s) | |
if chart.flags & 6 == 0 then return x end | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.FillColor(255, 255, 255) | |
if chart.flags & 2 ~= 0 then | |
gfx.BeginPath() | |
gfx.Text("MIR", x + s/2, y + s*0.3) | |
end | |
if chart.flags & 4 ~= 0 then | |
gfx.BeginPath() | |
gfx.Text("RAN", x + s/2, y + s*0.7) | |
end | |
return x - s | |
end | |
--------------------- | |
-- Main components -- | |
--------------------- | |
draw_challenge_info = function(x, y, w, h) | |
local centerLineY = y+h*0.6 | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(37) | |
drawScaledText(result.title, x+w/2, centerLineY-15, w/2-5) | |
drawLine(x+30, centerLineY, x+w-30,centerLineY, 1, 64, 64, 64) | |
gfx.FontSize(25) | |
if result.passed then | |
gfx.FillColor(150, 255, 150) | |
drawScaledText(string.format("CHALLENGE PASSED: %d%% Percent Complete", result.avgPercentage), x+w/2, centerLineY+25, w/2-5) | |
else | |
gfx.FillColor(255, 20, 20) | |
drawScaledText(string.format("CHALLENGE FAILED: %d%% Percent Complete", result.avgPercentage), x+w/2, centerLineY+25, w/2-5) | |
gfx.FontSize(23) | |
drawScaledText(result.failReason, x+w/2, centerLineY+45, w/2-5) | |
end | |
end | |
draw_title = function(chart,x, y, w, h) | |
local centerLineY = y+h*0.6 | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(48) | |
drawScaledText(chart.chartTitle, x+w/2, centerLineY-18, w/2-5) | |
drawLine(x+30, centerLineY, x+w-30,centerLineY, 1, 64, 64, 64) | |
gfx.FontSize(27) | |
drawScaledText(chart.artist, x+w/2, centerLineY+28, w/2-5) | |
end | |
draw_challenge_title = function(chart, x, y, w, h) | |
local centerLineY = y+h | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(27) | |
drawScaledText(chart.chartTitle, x+w/2, centerLineY-10, w/2-5) | |
drawLine(x+30, centerLineY, x+w-30,centerLineY, 1, 64, 64, 64) | |
end | |
draw_chart_info = function(chart, x, y, w, h, full) | |
local jacket_size = 250 | |
local jacket_y = y+40 | |
if not full then | |
jacket_y = y | |
jacket_size = 300 | |
end | |
local jacket_x = x+(w-jacket_size)/2 | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.BeginPath() | |
if chart.jacketImg ~= nil then | |
gfx.ImageRect(jacket_x, jacket_y, jacket_size, jacket_size, chart.jacketImg, 1, 0) | |
else | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0, 128) | |
gfx.Rect(jacket_x, jacket_y, jacket_size, jacket_size) | |
gfx.Fill() | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255, math.floor(40+80*waveParam(4))) | |
gfx.FontSize(30) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.Text("No Image", x+w/2, jacket_y + jacket_size/2) | |
end | |
if full then | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(30) | |
gfx.Text(string.format("%s %02d", diffNames[chart.difficulty + 1], chart.level), x+w/2, y+30) | |
else | |
do | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
local level_text = string.format("%s %02d", diffNames[chart.difficulty + 1], chart.level) | |
local _a, _b, level_text_width, _c = gfx.TextBounds(0, 0, level_text) | |
local box_width = level_text_width | |
local effector_text = "" | |
if chart.effector ~= nil and chart.effector ~= "" then | |
effector_text = string.format(" by %s", chart.effector) | |
gfx.FontSize(16) | |
local _d, _e, effector_text_width, _f = gfx.TextBounds(0, 0, effector_text) | |
box_width = box_width + effector_text_width | |
end | |
box_width = box_width + 10 | |
if box_width > jacket_size then box_width = jacket_size end | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0, 200) | |
gfx.RoundedRectVarying(jacket_x, jacket_y, box_width, 25, 0, 0, 5, 0) | |
gfx.Fill() | |
gfx.FillColor(255, 255, 255) | |
gfx.BeginPath() | |
gfx.FontSize(20) | |
gfx.Text(level_text, jacket_x+5, jacket_y+22) | |
if effector_text ~= "" then | |
gfx.FontSize(16) | |
drawScaledText(effector_text, jacket_x+level_text_width+5, jacket_y+21, jacket_size-level_text_width-10) | |
end | |
end | |
local graph_height = jacket_size * 0.3 | |
local graph_y = jacket_y+jacket_size - graph_height | |
gfx.BeginPath() | |
gfx.FillColor(0,0,0,200) | |
gfx.Rect(jacket_x, graph_y, jacket_size, graph_height) | |
gfx.Fill() | |
draw_gauge_graph(chart, jacket_x, graph_y, jacket_size, graph_height) | |
if gradeImg ~= nil then | |
gfx.BeginPath() | |
gfx.ImageRect(jacket_x+jacket_size-60*chart.gradeAR, jacket_y+jacket_size-60, 60*chart.gradeAR, 60, chart.gradeImg, 1, 0) | |
end | |
gfx.BeginPath() | |
gfx.FillColor(255,255,255) | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.Text(string.format("%.1f%%", chart.gauge*100), jacket_x+jacket_size+10, jacket_y+jacket_size-graph_height*chart.gauge) | |
return | |
end | |
draw_y = jacket_y + jacket_size + 27 | |
if chart.effector ~= nil and chart.effector ~= "" then | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FillColor(255, 255, 255) | |
gfx.FontSize(16) | |
gfx.Text("Effected by", x+w/2, draw_y) | |
gfx.FontSize(27) | |
drawScaledText(chart.effector, x+w/2, draw_y+24, w/2-5) | |
draw_y = draw_y + 50 | |
end | |
if chart.illustrator ~= nil and chart.illustrator ~= "" then | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(16) | |
gfx.Text("Illustrated by", x+w/2, draw_y) | |
gfx.FontSize(27) | |
drawScaledText(chart.illustrator, x+w/2, draw_y+24, w/2-5) | |
draw_y = draw_y + 50 | |
end | |
end | |
draw_challenge_chart_info = function(chart, x, y, w, h) | |
local jacket_size = 200 | |
local jacket_y = y | |
local jacket_x = x+(w-jacket_size)/2 | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.BeginPath() | |
if chart.jacketImg ~= nil then | |
gfx.ImageRect(jacket_x, jacket_y, jacket_size, jacket_size, chart.jacketImg, 1, 0) | |
else | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0, 128) | |
gfx.Rect(jacket_x, jacket_y, jacket_size, jacket_size) | |
gfx.Fill() | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255, math.floor(40+80*waveParam(4))) | |
gfx.FontSize(30) | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.Text("No Image", x+w/2, jacket_y + jacket_size/2) | |
end | |
do | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
local level_text = string.format("%s %02d", diffNames[chart.difficulty + 1], chart.level) | |
local _a, _b, level_text_width, _c = gfx.TextBounds(0, 0, level_text) | |
local box_width = level_text_width | |
local effector_text = "" | |
if chart.effector ~= nil and chart.effector ~= "" then | |
effector_text = string.format(" by %s", chart.effector) | |
gfx.FontSize(16) | |
local _d, _e, effector_text_width, _f = gfx.TextBounds(0, 0, effector_text) | |
box_width = box_width + effector_text_width | |
end | |
box_width = box_width + 10 | |
if box_width > jacket_size then box_width = jacket_size end | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0, 200) | |
gfx.RoundedRectVarying(jacket_x, jacket_y, box_width, 25, 0, 0, 5, 0) | |
gfx.Fill() | |
gfx.FillColor(255, 255, 255) | |
gfx.BeginPath() | |
gfx.FontSize(20) | |
gfx.Text(level_text, jacket_x+5, jacket_y+22) | |
if effector_text ~= "" then | |
gfx.FontSize(16) | |
drawScaledText(effector_text, jacket_x+level_text_width+5, jacket_y+21, jacket_size-level_text_width-10) | |
end | |
end | |
do | |
gfx.FontSize(17) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
local gauge_text = string.format("%02.1f%% Gauge", chart.gauge*100) | |
local _a, _b, gauge_text_width, _c = gfx.TextBounds(0, 0, gauge_text) | |
local gauge_box_width = gauge_text_width | |
gauge_box_width = gauge_box_width + 10 | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0, 200) | |
gfx.RoundedRectVarying(jacket_x, jacket_y + jacket_size - 25, gauge_box_width, 25, 0, 5, 0, 0) | |
gfx.Fill() | |
gfx.FillColor(255, 255, 255) | |
gfx.BeginPath() | |
gfx.Text(gauge_text, jacket_x+5, jacket_y + jacket_size - 3) | |
end | |
do | |
gfx.BeginPath() | |
if chart.gradeImg ~= nil then | |
gfx.BeginPath() | |
gfx.ImageRect(jacket_x+jacket_size-60*chart.gradeAR, jacket_y+jacket_size-60, 60*chart.gradeAR, 60, chart.gradeImg, 1, 0) | |
end | |
end | |
end | |
draw_basic_hitstat = function(chart, x, y, w, h, full) | |
local grade_width = 70 * chart.gradeAR | |
local stat_y = y | |
local stat_gap = 15 | |
local stat_size = 30 | |
local stat_width = w-8 | |
local showRetryCount = (chart.retryCount ~= nil and chart.retryCount > 0) or (chart.mission ~= nil and chart.mission ~= "") | |
if full then | |
stat_gap = 6 | |
stat_size = 25 | |
stat_width = w-18 | |
if not showRetryCount then | |
stat_gap = 25 | |
stat_y = stat_y + 15 | |
end | |
gfx.BeginPath() | |
gfx.ImageRect(x + (w-grade_width)/2 - 5, stat_y, grade_width, 70, chart.gradeImg, 1, 0) | |
stat_y = stat_y + 85 | |
else | |
stat_y = y + 12 | |
if not showRetryCount then | |
stat_gap = 30 | |
end | |
end | |
if chart.clearTextBase ~= "" then | |
if chart.badge == 5 then gfx.FillColor(255, 255, math.floor(120+125*waveParam(2.0))) | |
elseif chart.badge == 4 then gfx.FillColor(255, 0, 200) | |
elseif chart.badge == 0 then | |
local w = math.floor(128*waveParam(2.0)) | |
gfx.FillColor(255, w, w) | |
else gfx.FillColor(255, 255, 255) | |
end | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(20) | |
gfx.Text(chart.clearText, x+w/2 - 5, stat_y) | |
end | |
stat_y = stat_y + 50 | |
if chart.score == 10000000 then | |
gfx.FillColor(255, 255, math.floor(120+125*waveParam(2.0))) | |
else | |
gfx.FillColor(255, 255, 255) | |
end | |
if full then | |
draw_score(chart.score, x, stat_y, w, 72) | |
stat_y = stat_y + 19 | |
else | |
stat_y = stat_y + 10 | |
stat_gap = stat_gap - 8 | |
draw_score(chart.score, x, stat_y, w, 88) | |
stat_y = stat_y + 19 | |
end | |
if chart.highestScore > 0 then | |
if chart.highestScore > chart.score then | |
gfx.FillColor(255, 32, 32) | |
draw_score(chart.highestScore - chart.score, x+w/2, stat_y, w/2, 25, "-") | |
elseif chart.highestScore == chart.score then | |
gfx.FillColor(128, 128, 128) | |
draw_score(0, x+w/2, stat_y, w/2, 25, utf8.char(0xB1)) | |
else | |
gfx.FillColor(32, 255, 32) | |
draw_score(chart.score - chart.highestScore, x+w/2, stat_y, w/2, 25, "+") | |
end | |
end | |
stat_y = stat_y + stat_gap | |
gfx.FillColor(255, 255, 255) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, critText, chart.perfects, "%d", 255, 150, 0) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, nearText, chart.goods, "%d", 255, 0, 200) | |
local early_late_width = w/2-20 | |
local late_x = x+stat_width-early_late_width | |
draw_stat(late_x-early_late_width-10, stat_y, early_late_width, stat_size-6, "EARLY", chart.earlies, "%d", 255, 0, 255) | |
draw_stat(late_x, stat_y, early_late_width, stat_size-6, "LATE", chart.lates, "%d", 0, 255, 255) | |
stat_y = stat_y + stat_size + 5 | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, "ERROR", chart.misses, "%d", 255, 0, 0) | |
stat_y = draw_stat(x+4, stat_y+15, stat_width, stat_size, "MAX COMBO", chart.maxCombo, "%d", 255, 255, 0) | |
if showRetryCount then | |
local retryCount = 0 | |
if chart.retryCount ~= nil then retryCount = chart.retryCount end | |
stat_y = draw_stat(x+4, stat_y+15, stat_width, stat_size-6, "RETRY", retryCount, "%d") | |
if chart.mission ~= nil and chart.mission ~= "" then | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT) | |
gfx.BeginPath() | |
gfx.FontSize(16) | |
gfx.Text(string.format("Mission: %s", chart.mission), x+4, stat_y) | |
end | |
end | |
end | |
draw_challenge_basic_hitstat = function(chart, x, y, w, h) | |
local stat_y = y + 12 | |
local stat_gap = 25 | |
local stat_size = 25 | |
local stat_width = w-8 | |
if chart.clearTextBase ~= nil and chart.clearTextBase ~= "" then | |
if not chart.passed then gfx.FillColor(math.floor(175+80*waveParam(2.0)), 0, 0) | |
elseif chart.badge == 5 then gfx.FillColor(255, 255, math.floor(120+125*waveParam(2.0))) | |
elseif chart.badge == 4 then gfx.FillColor(255, 0, 200) | |
else gfx.FillColor(150, 255, 150) | |
end | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(19) | |
if chart.passed then | |
gfx.Text(chart.clearTextBase, x+w/2, stat_y+13) | |
else | |
gfx.Text(chart.clearTextBase, x+w/2, stat_y) | |
end | |
end | |
if not chart.passed then | |
stat_y = stat_y + 18 | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER) | |
gfx.FontSize(19) | |
gfx.Text(chart.failReason, x+w/2, stat_y) | |
stat_y = stat_y + 37 | |
else | |
stat_y = stat_y + 55 | |
end | |
if chart.score == 10000000 then | |
gfx.FillColor(255, 255, math.floor(120+125*waveParam(2.0))) | |
else | |
gfx.FillColor(255, 255, 255) | |
end | |
stat_y = stat_y + 10 | |
stat_gap = stat_gap - 8 | |
draw_score(chart.score, x + 5, stat_y, w, 70) | |
stat_y = stat_y | |
gfx.FillColor(170, 210, 255) | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT) | |
gfx.Text(string.format("%00d%% Percent Complete", chart.percent), x+w, stat_y + 15) | |
stat_y = stat_y + stat_gap | |
gfx.FillColor(255, 255, 255) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, critText, chart.perfects, "%d", 255, 150, 0) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, nearText, chart.goods, "%d", 255, 0, 200) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, "ERROR", chart.misses, "%d", 255, 0, 0) | |
stat_y = draw_stat(x+4, stat_y, stat_width, stat_size, "MAX COMBO", chart.maxCombo, "%d", 255, 255, 0) | |
end | |
draw_graphs = function(chart, x, y, w, h) | |
if not chart.hasHitStat or hitDeltaScale == 0.0 then | |
draw_left_graph(chart, x, y, w, h) | |
else | |
draw_left_graph(chart, x, y, w - w//4, h) | |
draw_right_graph(chart, x + (w - w//4), y, w//4, h) | |
end | |
end | |
draw_guide = function(x, y, w, h, full) | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
local fxLText = "FX-L: cycle left" | |
local fxRText = "FX-R: cycle right" | |
local scrollText = "Knobs: scroll results" | |
gfx.FontSize(20) | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM) | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255, 96) | |
gfx.Text(string.format("%s, %s, %s", fxLText, fxRText, scrollText), x+5, y+h) | |
end | |
draw_icons = function(chart, x, y, w, h) | |
gfx.LoadSkinFont("NotoSans-Regular.ttf") | |
local icon_x = x+w-h | |
icon_x = draw_laser_icon(chart, icon_x, y, h) | |
icon_x = draw_speed_icon(chart, icon_x, y, h) | |
--icon_x = draw_hidsud_icon(chart, icon_x, y, h) | |
icon_x = draw_mir_ran_icon(chart, icon_x, y, h) | |
end | |
render = function(deltaTime, newScroll) | |
if initialRender then | |
startResultSample() | |
end | |
scroll = newScroll + scrolloff | |
currTime = currTime + deltaTime | |
-- Note: these keys are also used for viewing other players' scores on multiplayer. | |
local fxLeft = game.GetButton(4) | |
local fxRight = game.GetButton(5) | |
if prevFXLeft ~= fxLeft then | |
prevFXLeft = fxLeft | |
if fxLeft then | |
showChartInfo = (showChartInfo - 1) % (#charts + 1) | |
game.PlaySample("menu_click") | |
end | |
end | |
if prevFXRight ~= fxRight then | |
prevFXRight = fxRight | |
if fxRight then | |
showChartInfo = (showChartInfo + 1) % (#charts + 1) | |
game.PlaySample("menu_click") | |
end | |
end | |
local resx,resy = game.GetResolution() | |
if resx ~= currResX or resy ~= currResY or showHiScore ~= prevShowHiScore then | |
prevShowHiScore = showHiScore | |
if showHiScore then | |
desw = 770 | |
else | |
desw = 500 | |
end | |
local scaleX = resx / desw | |
local scaleY = resy / desh | |
scale = math.min(scaleX, scaleY) | |
if scaleX > scaleY then | |
moveX = resx / (2*scale) - desw / 2 | |
moveY = 0 | |
else | |
moveX = 0 | |
moveY = resy / (2*scale) - desh / 2 | |
end | |
currResX = resX | |
currResY = resY | |
end | |
-- For better screenshot display | |
gfx.BeginPath() | |
gfx.FillColor(0, 0, 0) | |
gfx.Rect(0, 0, resx, resy) | |
gfx.Fill() | |
-- Background image | |
gfx.BeginPath() | |
--gfx.ImageRect(0, 0, resx, resy, backgroundImage, 0.5, 0); | |
gfx.Scale(scale,scale) | |
gfx.Translate(moveX,moveY) | |
gfx.BeginPath() | |
gfx.Rect(0,0,500,800) | |
gfx.FillColor(30,30,30,128) | |
gfx.Fill() | |
if showChartInfo == 0 then | |
draw_challenge_info(0, 0, 500, 100) | |
end | |
local ystart = 75 | |
if passed then | |
ystart = 55 | |
end | |
-- Result | |
if #charts > 0 then | |
if showChartInfo == 0 then | |
ystart = ystart + 50 | |
local boxh = 770 - ystart | |
if scroll < 0 then | |
scrolloff = scrolloff - scroll | |
scroll = 0 | |
end | |
local scrollh = #charts * 250 | |
-- Check if we can scroll further down | |
local overBottom = scrollh - 100*scroll - boxh | |
if overBottom < 0 and scroll > 0 then | |
local scrollend = (scrollh - boxh)/100 | |
if scrollend < 0 then | |
scrollend = 0 | |
end | |
scrolloff = scrolloff - (scroll - scrollend) | |
scroll = scrollend | |
end | |
-- Draw scroll bar | |
if scrollh > boxh then | |
gfx.BeginPath() | |
gfx.Rect(495, ystart, 5, boxh) | |
gfx.FillColor(30,30,30) | |
gfx.Fill() | |
gfx.BeginPath() | |
local barStart = (100*scroll) / scrollh -- Start percent of visible area | |
local barh = (boxh / scrollh) * boxh | |
gfx.Rect(495, ystart + (barStart*boxh)//1, 5, barh//1) | |
gfx.FillColor(80,80,80) | |
gfx.Fill() | |
end | |
gfx.Scissor(0, ystart, 500, boxh) | |
ystart = ystart - 100*scroll | |
for i,chart in ipairs(charts) do | |
local yloc = ystart + (i-1) * 250 - 40 | |
draw_challenge_title(chart, 0, yloc, 500, 70) | |
yloc = yloc + 72 | |
draw_challenge_chart_info(chart, -15, yloc + 5, 300, 310, false) | |
draw_challenge_basic_hitstat(chart, 40 + 220, yloc, 200, 310) | |
end | |
gfx.ResetScissor() | |
else | |
local chart = charts[showChartInfo] | |
draw_title(chart, 0, 0, 500, 110) | |
if showStatsHit then | |
draw_chart_info(chart, 0, 120, 280, 420, true) | |
draw_basic_hitstat(chart, 280, 120, 220, 420, true) | |
draw_graphs(chart, 0, 540, 500, 210) | |
else | |
draw_chart_info(chart, 0, 120, 500, 310, false) | |
draw_basic_hitstat(chart, 50, 430, 400, 400, false) | |
end | |
if showIcons and result.isSelf ~= false then | |
draw_icons(chart, 0, 750, 500, 50) | |
end | |
if showHiScore then | |
draw_highscores(chart, showStatsHit) | |
end | |
end | |
end | |
if showGuide then | |
draw_guide(0, 750, 500, 50, showStatsHit) | |
end | |
--if showIcons and result.isSelf ~= false then | |
-- draw_icons(0, 750, 500, 50) | |
--end | |
--if showHiScore then | |
-- draw_highscores(showStatsHit) | |
--end | |
-- Screenshot notification | |
shotTimer = math.max(shotTimer - deltaTime, 0) | |
if shotTimer > 1 then | |
draw_shotnotif(505,755); | |
end | |
if initialRender then initialRender = false end | |
end | |
get_capture_rect = function() | |
local x = moveX * scale | |
local y = moveY * scale | |
local w = 500 * scale | |
local h = 800 * scale | |
return x,y,w,h | |
end | |
screenshot_captured = function(path) | |
shotTimer = 10; | |
shotPath = path; | |
game.PlaySample("shutter") | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--Horizontal alignment | |
TEXT_ALIGN_LEFT = 1 | |
TEXT_ALIGN_CENTER = 2 | |
TEXT_ALIGN_RIGHT = 4 | |
--Vertical alignment | |
TEXT_ALIGN_TOP = 8 | |
TEXT_ALIGN_MIDDLE = 16 | |
TEXT_ALIGN_BOTTOM = 32 | |
TEXT_ALIGN_BASELINE = 64 | |
local jacket = nil; | |
local selectedIndex = 1 | |
local selectedDiff = 1 | |
local songCache = {} | |
local ioffset = 0 | |
local doffset = 0 | |
local soffset = 0 | |
local diffColors = {{0,0,255}, {0,255,0}, {255,0,0}, {255, 0, 255}} | |
local timer = 0 | |
local scrollmul = 0 | |
local scrollmulOffset = 0 -- bc we have min/max the game doesn't know we have to account for extra | |
local effector = 0 | |
local searchText = gfx.CreateLabel("",5,0) | |
local searchIndex = 1 | |
local jacketFallback = gfx.CreateSkinImage("song_select/jacket_loading.png", 0) | |
--local showGuide = game.GetSkinSetting("show_guide") | |
local showGuide = false | |
local legendTable = { | |
{["labelSingleLine"] = gfx.CreateLabel("SCROLL INFO",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SCROLL\nINFO",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-left.png", 0)}, | |
{["labelSingleLine"] = gfx.CreateLabel("CHALL SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("CHALLENGE\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-right.png", 0)}, | |
{["labelSingleLine"] = gfx.CreateLabel("FILTER CHALLS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("FILTER\nCHALLENGES",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-L.png", 0)}, | |
{["labelSingleLine"] = gfx.CreateLabel("SORT CHALLS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SORT\nCHALLENGES",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-R.png", 0)}, | |
{["labelSingleLine"] = gfx.CreateLabel("GAME SETTINGS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("GAME\nSETTINGS",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-LR.png", 0)}, | |
{["labelSingleLine"] = gfx.CreateLabel("PLAY",16, 0), ["labelMultiLine"] = gfx.CreateLabel("PLAY",16, 0), ["image"] = gfx.CreateSkinImage("legend/start.png", 0)} | |
} | |
local grades = { | |
{["max"] = 6999999, ["image"] = gfx.CreateSkinImage("song_select/grade/D.png", 0)}, | |
{["max"] = 7999999, ["image"] = gfx.CreateSkinImage("song_select/grade/C.png", 0)}, | |
{["max"] = 8699999, ["image"] = gfx.CreateSkinImage("song_select/grade/B.png", 0)}, | |
{["max"] = 8999999, ["image"] = gfx.CreateSkinImage("song_select/grade/A.png", 0)}, | |
{["max"] = 9299999, ["image"] = gfx.CreateSkinImage("song_select/grade/A+.png", 0)}, | |
{["max"] = 9499999, ["image"] = gfx.CreateSkinImage("song_select/grade/AA.png", 0)}, | |
{["max"] = 9699999, ["image"] = gfx.CreateSkinImage("song_select/grade/AA+.png", 0)}, | |
{["max"] = 9799999, ["image"] = gfx.CreateSkinImage("song_select/grade/AAA.png", 0)}, | |
{["max"] = 9899999, ["image"] = gfx.CreateSkinImage("song_select/grade/AAA+.png", 0)}, | |
{["max"] = 99999999, ["image"] = gfx.CreateSkinImage("song_select/grade/S.png", 0)} | |
} | |
local badges = { | |
gfx.CreateSkinImage("song_select/medal/played.png", 0), | |
gfx.CreateSkinImage("song_select/medal/clear.png", 0), | |
gfx.CreateSkinImage("song_select/medal/hard-clear.png", 0), | |
gfx.CreateSkinImage("song_select/medal/full-combo.png", 0), | |
gfx.CreateSkinImage("song_select/medal/perfect.png", 0) | |
} | |
gfx.LoadSkinFont("NotoSans-Regular.ttf"); | |
game.LoadSkinSample("menu_click") | |
game.LoadSkinSample("click-02") | |
game.LoadSkinSample("woosh") | |
local wheelSize = 12 | |
get_page_size = function() | |
return math.floor(wheelSize/2) | |
end | |
-- Responsive UI variables | |
-- Aspect Ratios | |
local aspectFloat = 1.850 | |
local aspectRatio = "widescreen" | |
local landscapeWidescreenRatio = 1.850 | |
local landscapeStandardRatio = 1.500 | |
local portraitWidescreenRatio = 0.5 | |
-- Responsive sizes | |
local fifthX = 0 | |
local fourthX= 0 | |
local thirdX = 0 | |
local halfX = 0 | |
local fullX = 0 | |
local fifthY = 0 | |
local fourthY= 0 | |
local thirdY = 0 | |
local halfY = 0 | |
local fullY = 0 | |
adjustScreen = function(x,y) | |
local a = x/y; | |
if x >= y and a <= landscapeStandardRatio then | |
aspectRatio = "landscapeStandard" | |
aspectFloat = 1.1 | |
elseif x >= y and landscapeStandardRatio <= a and a <= landscapeWidescreenRatio then | |
aspectRatio = "landscapeWidescreen" | |
aspectFloat = 1.2 | |
elseif x <= y and portraitWidescreenRatio <= a and a < landscapeStandardRatio then | |
aspectRatio = "PortraitWidescreen" | |
aspectFloat = 0.5 | |
else | |
aspectRatio = "landscapeWidescreen" | |
aspectFloat = 1.0 | |
end | |
fifthX = x/5 | |
fourthX= x/4 | |
thirdX = x/3 | |
halfX = x/2 | |
fullX = x | |
fifthY = y/5 | |
fourthY= y/4 | |
thirdY = y/3 | |
halfY = y/2 | |
fullY = y | |
end | |
check_or_create_cache = function(song, full) | |
if not songCache[song.id] then songCache[song.id] = {} end | |
if not songCache[song.id]["title"] then | |
songCache[song.id]["title"] = gfx.CreateLabel(song.title, 40, 0) | |
end | |
if not songCache[song.id]["chart_names"] then | |
local names = "Charts:" | |
for _, chart in ipairs(song.charts) do | |
names = names .. " [" .. chart.title .. "]" | |
end | |
if song.missing_chart then | |
names = names .. " *COULD NOT FIND ALL CHARTS!*" | |
end | |
songCache[song.id]["chart_names"] = gfx.CreateLabel(names, 20, 0) | |
end | |
if song.topBadge ~= 0 and ( | |
not songCache[song.id]["percent"] or | |
not songCache[song.id]["percent_value"] or | |
songCache[song.id]["percent_value"] ~= song.bestScore) then | |
songCache[song.id]["percent"] = gfx.CreateLabel(string.format("%u%% Complete", math.max(0,(song.bestScore - 8000000)//10000)), 35, 0) | |
songCache[song.id]["percent_value"] = song.bestScore | |
end | |
if full then | |
if not songCache[song.id]["jackets"] then | |
local jackets = {} | |
for i, chart in ipairs(song.charts) do | |
jackets[i] = gfx.LoadImageJob(chart.jacketPath, jacketFallback, 200,200) | |
end | |
songCache[song.id]["jackets"] = jackets | |
end | |
if not songCache[song.id]["desc"] then | |
local desc = "Charts:\n" | |
for _, chart in ipairs(song.charts) do | |
desc = desc .. " " .. chart.title .. "\n" | |
end | |
if song.missing_chart then | |
desc = desc .. " *COULD NOT FIND ALL CHARTS!*\n" | |
end | |
desc = desc .. "\nGoal:\n" .. song.requirement_text | |
songCache[song.id]["desc"] = gfx.CreateLabel(desc, 20, 0) | |
end | |
end | |
end | |
draw_scores = function(difficulty, x, y, w, h) | |
-- draw the top score for this difficulty | |
local xOffset = 5 | |
local height = h/3 - 10 | |
local ySpacing = h/3 | |
local yOffset = h/3 | |
gfx.FontSize(30); | |
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_CENTER); | |
gfx.FastText("HIGH SCORE", x +(w/2), y+(h/2)) | |
gfx.BeginPath() | |
gfx.Rect(x+xOffset,y+h/2,w-(xOffset*2),h/2) | |
gfx.FillColor(30,30,30,10) | |
gfx.StrokeColor(0,128,255) | |
gfx.StrokeWidth(1) | |
gfx.Fill() | |
gfx.Stroke() | |
if difficulty.scores[1] ~= nil then | |
local highScore = difficulty.scores[1] | |
scoreLabel = gfx.CreateLabel(string.format("%08d",highScore.score), 40, 0) | |
for i,v in ipairs(grades) do | |
if v.max > highScore.score then | |
gfx.BeginPath() | |
iw,ih = gfx.ImageSize(v.image) | |
iar = iw / ih; | |
gfx.ImageRect(x+xOffset,y+h/2 +5, iar * (h/2-10),h/2-10, v.image, 1, 0) | |
break | |
end | |
end | |
if difficulty.topBadge ~= 0 then | |
gfx.BeginPath() | |
gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[difficulty.topBadge], 1, 0) | |
end | |
gfx.FillColor(255,255,255) | |
gfx.FontSize(40); | |
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER); | |
gfx.DrawLabel(scoreLabel, x+(w/2),y+(h/4)*3,w) | |
end | |
end | |
draw_song = function(song, x, y, w, h, selected) | |
check_or_create_cache(song) | |
gfx.BeginPath() | |
gfx.RoundedRectVarying(x,y, w, h,0,0,0,40) | |
gfx.FillColor(30,30,30) | |
gfx.StrokeColor(0,128,255) | |
gfx.StrokeWidth(1) | |
if selected then | |
gfx.StrokeColor(255,128,0) | |
gfx.StrokeWidth(2) | |
end | |
gfx.Fill() | |
gfx.Stroke() | |
gfx.FillColor(255,255,255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT) | |
gfx.DrawLabel(songCache[song.id]["title"], x+10, y + 5, w-10) | |
if (song.missing_chart) then | |
gfx.FillColor(255,20,20) | |
end | |
gfx.DrawLabel(songCache[song.id]["chart_names"], x+20, y + 50, w-10) | |
--gfx.DrawLabel(songCache[song.id]["artist"], x+20, y + 50, w-10) | |
gfx.ForceRender() | |
end | |
draw_diff_icon = function(diff, x, y, w, h, selected) | |
local shrinkX = w/4 | |
local shrinkY = h/4 | |
if selected then | |
gfx.FontSize(h/2) | |
shrinkX = w/6 | |
shrinkY = h/6 | |
else | |
gfx.FontSize(math.floor(h / 3)) | |
end | |
gfx.BeginPath() | |
gfx.RoundedRectVarying(x+shrinkX,y+shrinkY,w-shrinkX*2,h-shrinkY*2,0,0,0,0) | |
gfx.FillColor(15,15,15) | |
gfx.StrokeColor(table.unpack(diffColors[diff.difficulty + 1])) | |
gfx.StrokeWidth(2) | |
gfx.Fill() | |
gfx.Stroke() | |
gfx.FillColor(255,255,255) | |
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER) | |
gfx.FastText(tostring(diff.level), x+(w/2),y+(h/2)) | |
end | |
draw_cursor = function(x,y,rotation,width) | |
gfx.Save() | |
gfx.BeginPath(); | |
gfx.Translate(x,y) | |
gfx.Rotate(rotation) | |
gfx.StrokeColor(255,128,0) | |
gfx.StrokeWidth(4) | |
gfx.Rect(-width/2, -width/2, width, width) | |
gfx.Stroke() | |
gfx.Restore() | |
end | |
draw_diffs = function(diffs, x, y, w, h) | |
local diffWidth = w/2.5 | |
local diffHeight = w/2.5 | |
local diffCount = #diffs | |
gfx.Scissor(x,y,w,h) | |
for i = math.max(selectedDiff - 2, 1), math.max(selectedDiff - 1,1) do | |
local diff = diffs[i] | |
local xpos = x + ((w/2 - diffWidth/2) + (selectedDiff - i + doffset)*(-0.8*diffWidth)) | |
if i ~= selectedDiff then | |
draw_diff_icon(diff, xpos, y, diffWidth, diffHeight, false) | |
end | |
end | |
--after selected | |
for i = math.min(selectedDiff + 2, diffCount), selectedDiff + 1,-1 do | |
local diff = diffs[i] | |
local xpos = x + ((w/2 - diffWidth/2) + (selectedDiff - i + doffset)*(-0.8*diffWidth)) | |
if i ~= selectedDiff then | |
draw_diff_icon(diff, xpos, y, diffWidth, diffHeight, false) | |
end | |
end | |
local diff = diffs[selectedDiff] | |
local xpos = x + ((w/2 - diffWidth/2) + (doffset)*(-0.8*diffWidth)) | |
draw_diff_icon(diff, xpos, y, diffWidth, diffHeight, true) | |
gfx.BeginPath() | |
gfx.FillColor(0,128,255) | |
gfx.Rect(x,y+10,2,diffHeight-h/6) | |
gfx.Fill() | |
gfx.BeginPath() | |
gfx.Rect(x+w-2,y+10,2,diffHeight-h/6) | |
gfx.Fill() | |
gfx.ResetScissor() | |
draw_cursor(x + w/2, y +diffHeight/2, timer * math.pi, diffHeight / 1.5) | |
end | |
draw_selected = function(song, x, y, w, h) | |
check_or_create_cache(song, true) | |
-- set up padding and margins | |
local xPadding = math.floor(w/16) | |
local yPadding = math.floor(h/32) | |
local xMargin = math.floor(w/16) | |
local yMargin = math.floor(h/32) | |
local width = (w-(xMargin*2)) | |
local height = (h-(yMargin*2)) | |
local xpos = x+xMargin | |
local ypos = y+yMargin | |
if aspectRatio == "PortraitWidescreen" then | |
xPadding = math.floor(w/64) | |
yPadding = math.floor(h/32) | |
xMargin = math.floor(w/64) | |
yMargin = math.floor(h/32) | |
width = (w-(xMargin*2)) | |
height = (h-(yMargin*2)) | |
xpos = x+xMargin | |
ypos = y+yMargin | |
end | |
--Border | |
--local diff = song.difficulties[selectedDiff] | |
gfx.BeginPath() | |
gfx.RoundedRectVarying(xpos,ypos,width,height,yPadding,yPadding,yPadding,yPadding) | |
gfx.FillColor(30,30,30) | |
gfx.StrokeColor(0,128,255) | |
gfx.StrokeWidth(1) | |
gfx.Fill() | |
gfx.Stroke() | |
-- jacket should take up 1/3 of height, always be square, and be centered | |
local imageSize = math.floor(height/2) | |
local imageXPos = ((width/2) - (imageSize/2)) + x+xMargin | |
if aspectRatio == "PortraitWidescreen" then | |
--Unless its portrait widesreen.. | |
imageSize = math.floor((height/2)*2)-10 | |
imageXPos = x+xMargin+xPadding | |
end | |
local square_size = math.ceil(math.sqrt(#song.charts)) | |
local origImageSize = imageSize | |
imageSize = math.floor(imageSize / square_size) | |
local bottom_off = math.ceil((square_size*square_size - #song.charts) * imageSize/2) | |
local img_row = 0; | |
local img_col = 0; | |
for i, chart in ipairs(song.charts) do | |
if songCache[song.id]["jackets"][i] == jacketFallback then | |
songCache[song.id]["jackets"][i] = gfx.LoadImageJob(chart.jacketPath, jacketFallback, 200,200) | |
end | |
gfx.BeginPath() | |
local xoff = img_col * imageSize | |
if math.ceil(i / square_size) == square_size then | |
xoff = xoff + bottom_off | |
end | |
gfx.ImageRect(xoff + imageXPos, img_row * imageSize + y+yMargin+yPadding, imageSize, imageSize, songCache[song.id]["jackets"][i], 1, 0) | |
img_col = img_col + 1 | |
if img_col >= square_size then | |
img_row = img_row + 1 | |
img_col = 0 | |
end | |
end | |
local num_img_rows = img_row + 1 | |
if img_col == 0 then | |
num_img_rows = img_row | |
end | |
--if songCache[song.id][selectedDiff] then | |
-- gfx.BeginPath() | |
-- gfx.ImageRect(imageXPos, y+yMargin+yPadding, imageSize, imageSize, songCache[song.id][selectedDiff], 1, 0) | |
--end | |
--gfx.ImageRect(imageXPos, y+yMargin+yPadding, imageSize, imageSize, jacketFallback, 1, 0) | |
-- difficulty should take up 1/6 of height, full width, and be centered | |
--if aspectRatio == "PortraitWidescreen" then | |
--difficulty wheel should be right below the jacketImage, and the same width as | |
--the jacketImage | |
-- draw_diffs(song.difficulties,xpos+xPadding,(ypos+yPadding+imageSize),imageSize,math.floor((height/3)*1)-yPadding) | |
--else | |
-- difficulty should take up 1/6 of height, full width, and be centered | |
-- draw_diffs(song.difficulties,(w/2)-(imageSize/2),(ypos+yPadding+imageSize),imageSize,math.floor(height/6)) | |
--end | |
-- effector / bpm should take up 1/3 of height, full width | |
local gradeImg = nil | |
for i,v in ipairs(grades) do | |
if v.max > song.bestScore then | |
gfx.BeginPath() | |
gradeImg = v.image | |
break | |
end | |
end | |
if scrollmul < 0 then | |
scrollmulOffset = scrollmulOffset - scrollmul | |
scrollmul = 0 | |
end | |
local descw, desch = gfx.LabelSize(songCache[song.id]["desc"]) | |
local boxh, boxw = 0 | |
local starty, startx = 0 | |
if aspectRatio == "PortraitWidescreen" then | |
gfx.FontSize(40) | |
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT) | |
starty = y+yMargin+yPadding | |
startx = xpos+xPadding+origImageSize+10 | |
gfx.DrawLabel(songCache[song.id]["title"], startx, starty, width-origImageSize-30) | |
gfx.FontSize(30) | |
-- Scroll box info | |
starty = starty + 50 | |
boxh = height - starty | |
boxw = width-startx-200 | |
--gfx.DrawLabel(songCache[song.id]["artist"], xpos+xPadding+imageSize+3, y+yMargin+yPadding + 45, width-imageSize-20) | |
--gfx.DrawLabel(songCache[song.id]["artist"], xpos+xPadding+imageSize+3, y+yMargin+yPadding + 45, width-imageSize-20) | |
--gfx.FontSize(20) | |
--gfx.DrawLabel(songCache[song.id]["bpm"], xpos+xPadding+imageSize+3, y+yMargin+yPadding + 85, width-imageSize-20) | |
--gfx.FastText(string.format("Effector: %s", diff.effector), xpos+xPadding+imageSize+3, y+yMargin+yPadding + 115) | |
if song.topBadge ~= 0 then | |
gfx.BeginPath() | |
gfx.ImageRect(width-40, height-50, 50, 50, badges[song.topBadge], 1, 0) | |
local iar = 0 | |
if gradeImg ~= nil then | |
gfx.BeginPath() | |
local iw,ih = gfx.ImageSize(gradeImg) | |
iar = iw/ih | |
gfx.ImageRect(width-40-iar*50, height-50, iar * 50, 50, gradeImg, 1, 0) | |
end | |
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.DrawLabel(songCache[song.id]["percent"], width, height-65, 190) | |
end | |
else | |
starty = (height/10)*6; | |
if num_img_rows < square_size then | |
starty = starty - imageSize*(square_size - num_img_rows) | |
end | |
gfx.FontSize(40) | |
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT) | |
gfx.DrawLabel(songCache[song.id]["title"], xpos+10, starty, width-20) | |
-- Scroll box info | |
boxh = height - starty - 45 - 50 | |
boxw = width-20 | |
startx = xpos + 10 | |
starty = starty + 45 | |
--gfx.DrawLabel(songCache[song.id]["artist"], xpos+10, (height/10)*6 + 45, width-20) | |
--gfx.FillColor(255,255,255) | |
--gfx.FontSize(20) | |
--gfx.DrawLabel(songCache[song.id]["bpm"], xpos+10, (height/10)*6 + 85) | |
if song.topBadge ~= 0 then | |
gfx.BeginPath() | |
gfx.ImageRect(width-25, height-40, 50, 50, badges[song.topBadge], 1, 0) | |
local iar = 0 | |
local iw, ih = 0; | |
if gradeImg ~= nil then | |
gfx.BeginPath() | |
iw,ih = gfx.ImageSize(gradeImg) | |
iar = iw/ih | |
gfx.ImageRect(xpos + 10, height-40, iar * 50, 50, gradeImg, 1, 0) | |
end | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE ) | |
local ledge = iar * 50 + 20 | |
local redge = 50 | |
local maxLen = width - ledge - redge | |
local center = ledge + (maxLen/2) | |
gfx.DrawLabel(songCache[song.id]["percent"], center, height-16, maxLen) | |
end | |
end | |
-- Render desc scrolling box | |
do | |
gfx.BeginPath() | |
gfx.Rect(startx, starty, boxw, boxh) | |
gfx.FillColor(50,50,50) | |
gfx.Fill() | |
local overBottom = desch - 100*scrollmul - boxh | |
if overBottom < 0 and scrollmul > 0 then | |
local scrollend = (desch - boxh)/100 | |
if scrollend < 0 then | |
scrollend = 0 | |
end | |
scrollmulOffset = scrollmulOffset - (scrollmul - scrollend) | |
scrollmul = scrollend | |
end | |
-- Draw scroll bar | |
if desch > boxh then | |
gfx.BeginPath() | |
local barStart = (100*scrollmul) / desch -- Start percent of visible desc | |
local barh = (boxh / desch) * boxh | |
gfx.Rect(startx + boxw - 5, starty + (barStart*boxh)//1, 5, barh//1) | |
gfx.FillColor(20,20,20) | |
gfx.Fill() | |
end | |
gfx.Scissor(startx, starty, boxw, boxh) | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP ) | |
gfx.DrawLabel(songCache[song.id]["desc"], startx+5, starty - (100*scrollmul)//1, boxw) | |
gfx.ResetScissor() | |
end | |
--if aspectRatio == "PortraitWidescreen" then | |
-- draw_scores(diff, xpos+xPadding+imageSize+3, (height/3)*2, width-imageSize-20, (height/3)-yPadding) | |
--else | |
-- draw_scores(diff, xpos, (height/6)*5, width, (height/6)) | |
--end | |
gfx.ForceRender() | |
end | |
draw_songwheel = function(x,y,w,h) | |
local offsetX = fifthX/2 | |
local width = math.floor((w/5)*4) | |
if aspectRatio == "landscapeWidescreen" then | |
wheelSize = 12 | |
offsetX = 80 | |
elseif aspectRatio == "landscapeStandard" then | |
wheelSize = 10 | |
offsetX = 40 | |
elseif aspectRatio == "PortraitWidescreen" then | |
wheelSize = 20 | |
offsetX = 20 | |
width = w | |
end | |
local height = math.floor((h/wheelSize)*1.5) | |
for i = math.max(selectedIndex - wheelSize/2, 1), math.max(selectedIndex - 1,0) do | |
local song = chalwheel.challenges[i] | |
local xpos = x + offsetX + ((selectedIndex - i + ioffset) ^ 2) * 3 | |
local offsetY = (selectedIndex - i + ioffset) * ( height - (wheelSize/2*((selectedIndex-i + ioffset)*aspectFloat))) | |
local ypos = y+((h/2 - height/2) - offsetY) | |
draw_song(song, xpos, ypos, width, height) | |
end | |
--after selected | |
for i = math.min(selectedIndex + wheelSize/2, #chalwheel.challenges), selectedIndex + 1,-1 do | |
local song = chalwheel.challenges[i] | |
local xpos = x + offsetX + ((i - selectedIndex - ioffset) ^ 2) * 2 | |
local offsetY = (selectedIndex - i + ioffset) * ( height - (wheelSize/2*((i-selectedIndex - ioffset)*aspectFloat))) | |
local ypos = y+((h/2 - height/2) - (selectedIndex - i) - offsetY) | |
local alpha = 255 - (selectedIndex - i + ioffset) * 31 | |
draw_song(song, xpos, ypos, width, height) | |
end | |
-- draw selected | |
local xpos = x + offsetX/1.2 + ((-ioffset) ^ 2) * 2 | |
local offsetY = (ioffset) * ( height - (wheelSize/2*((1)*aspectFloat))) | |
local ypos = y+((h/2 - height/2) - (ioffset) - offsetY) | |
draw_song(chalwheel.challenges[selectedIndex], xpos, ypos, width, height, true) | |
return chalwheel.challenges[selectedIndex] | |
end | |
draw_legend_pane = function(x,y,w,h,obj) | |
local xpos = x+5 | |
local ypos = y | |
local imageSize = h | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT) | |
gfx.ImageRect(x, y, imageSize, imageSize, obj.image, 1, 0) | |
xpos = xpos + imageSize + 5 | |
gfx.FontSize(16); | |
if h < (w-(10+imageSize))/2 then | |
gfx.DrawLabel(obj.labelSingleLine, xpos, y+(h/2), w-(10+imageSize)) | |
else | |
gfx.DrawLabel(obj.labelMultiLine, xpos, y+(h/2), w-(10+imageSize)) | |
end | |
gfx.ForceRender() | |
end | |
draw_legend = function(x,y,w,h) | |
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT); | |
gfx.BeginPath() | |
gfx.FillColor(0,0,0,170) | |
gfx.Rect(x,y,w,h) | |
gfx.Fill() | |
local xpos = 10; | |
local legendWidth = math.floor((w-20)/#legendTable) | |
for i,v in ipairs(legendTable) do | |
local xOffset = draw_legend_pane(xpos+(legendWidth*(i-1)), y+5,legendWidth,h-10,legendTable[i]) | |
end | |
end | |
draw_search = function(x,y,w,h) | |
soffset = soffset + (searchIndex) - (chalwheel.searchInputActive and 0 or 1) | |
if searchIndex ~= (chalwheel.searchInputActive and 0 or 1) then | |
game.PlaySample("woosh") | |
end | |
searchIndex = chalwheel.searchInputActive and 0 or 1 | |
gfx.BeginPath() | |
local bgfade = 1 - (searchIndex + soffset) | |
--if not chalwheel.searchInputActive then bgfade = soffset end | |
gfx.FillColor(0,0,0,math.floor(200 * bgfade)) | |
gfx.Rect(0,0,resx,resy) | |
gfx.Fill() | |
gfx.ForceRender() | |
local xpos = x + (searchIndex + soffset)*w | |
gfx.UpdateLabel(searchText ,string.format("Search: %s",chalwheel.searchText), 30, 0) | |
gfx.BeginPath() | |
gfx.RoundedRect(xpos,y,w,h,h/2) | |
gfx.FillColor(30,30,30) | |
gfx.StrokeColor(0,128,255) | |
gfx.StrokeWidth(1) | |
gfx.Fill() | |
gfx.Stroke() | |
gfx.BeginPath(); | |
gfx.LoadSkinFont("NotoSans-Regular.ttf"); | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE); | |
gfx.DrawLabel(searchText, xpos+10,y+(h/2), w-20) | |
end | |
render = function(deltaTime) | |
gfx.ResetTransform() | |
timer = (timer + deltaTime) | |
timer = timer % 2 | |
resx,resy = game.GetResolution(); | |
adjustScreen(resx,resy); | |
gfx.BeginPath(); | |
gfx.LoadSkinFont("NotoSans-Regular.ttf"); | |
gfx.FontSize(40); | |
gfx.FillColor(255,255,255); | |
if chalwheel.challenges == nil then | |
elseif chalwheel.challenges[1] ~= nil then | |
--draw chalwheel and get selected song | |
if aspectRatio == "PortraitWidescreen" then | |
local song = draw_songwheel(0,0,fullX,fullY) | |
--render selected song information | |
draw_selected(song, 0,0,fullX,fifthY) | |
else | |
local song = draw_songwheel(fifthX*2-82,0,fifthX*3+82,fullY) | |
--render selected song information | |
draw_selected(song, 0,0,fifthX*2-82,(fifthY/2)*9) | |
end | |
end | |
--Draw Legend Information | |
if showGuide then | |
if aspectRatio == "PortraitWidescreen" then | |
draw_legend(0,(fifthY/3)*14, fullX, (fifthY/3)*1) | |
else | |
draw_legend(0,(fifthY/2)*9, fullX, (fifthY/2)) | |
end | |
end | |
--draw text search | |
if aspectRatio == "PortraitWidescreen" then | |
draw_search(fifthX*2,5,fifthX*3,fifthY/5) | |
else | |
draw_search(fifthX*2,5,fifthX*3,fifthY/3) | |
end | |
ioffset = ioffset * 0.9 | |
doffset = doffset * 0.9 | |
soffset = soffset * 0.8 | |
if chalwheel.searchStatus then | |
gfx.BeginPath() | |
gfx.FillColor(255,255,255) | |
gfx.FontSize(20); | |
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) | |
gfx.Text(chalwheel.searchStatus, 3, 3) | |
end | |
gfx.LoadSkinFont("NotoSans-Regular.ttf"); | |
gfx.ResetTransform() | |
gfx.ForceRender() | |
end | |
set_index = function(newIndex, scrollamt) | |
if newIndex ~= selectedIndex then | |
game.PlaySample("menu_click") | |
scrollmulOffset = 0 | |
end | |
ioffset = ioffset + selectedIndex - newIndex | |
selectedIndex = newIndex | |
scrollmul = scrollamt + scrollmulOffset | |
end; | |
local badgeRates = { | |
0.5, -- Played | |
1.0, -- Cleared | |
1.02, -- Hard clear | |
1.04, -- UC | |
1.1 -- PUC | |
} | |
local gradeRates = { | |
{["min"] = 9900000, ["rate"] = 1.05}, -- S | |
{["min"] = 9800000, ["rate"] = 1.02}, -- AAA+ | |
{["min"] = 9700000, ["rate"] = 1}, -- AAA | |
{["min"] = 9500000, ["rate"] = 0.97}, -- AA+ | |
{["min"] = 9300000, ["rate"] = 0.94}, -- AA | |
{["min"] = 9000000, ["rate"] = 0.91}, -- A+ | |
{["min"] = 8700000, ["rate"] = 0.88}, -- A | |
{["min"] = 7500000, ["rate"] = 0.85}, -- B | |
{["min"] = 6500000, ["rate"] = 0.82}, -- C | |
{["min"] = 0, ["rate"] = 0.8} -- D | |
} | |
challenges_changed = function(withAll) | |
if not withAll then return end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local DrawCrew = require("shared/draw_crew")local mposx = 0 | |
local mposy = 0 | |
local beginning = false | |
local cursorIndex = 1 | |
local currentTime = 0 | |
local cursorTimer = 0 | |
local mouseHoverActive = false | |
local lastMousePos = {0,0} | |
local buttons = nil | |
local resx, resy = game.GetResolution(); | |
local desw = 720 | |
local desh = 1280 | |
local scale = resy / desh | |
local xshift = (resx - desw * scale) / 2 | |
local yshift = (resy - desh * scale) / 2 | |
local CardSize = 330/1.5 | |
local CardSpacing = 14 | |
local bgVersion = game.GetSkinSetting("menu_bg") | |
local noBgAnim = (bgVersion == "Not animated") | |
local compressAnim = game.GetSkinSetting("compress_animations") | |
local backgroundAnimation | |
if noBgAnim then | |
backgroundAnimation = gfx.CreateSkinImage("song_select/bg_anim_hq"..d.."/testbg2_fhd000.jpg", 0) | |
elseif bgVersion == "High quality" then | |
backgroundAnimation = gfx.LoadSkinAnimation("song_select/bg_anim_hq"..d, 0.033, 0, compressAnim) | |
else | |
backgroundAnimation = gfx.LoadSkinAnimation("song_select/bg_anim"..d, 0.05, 0, compressAnim) | |
end | |
local fillTop = Image.skin("fill_top"..d..".png") | |
local fillBottom = Image.skin("fill_bottom"..d..".png") | |
local fillConsole = Image.skin("fill_console"..d..".png") | |
local fillConsoleTxt = Image.skin("title/fill_console_txt.png") | |
local bgFill = gfx.CreateSkinImage("bg_fill"..d..".png", 0) | |
local topLabel = Image.skin("title/top_label"..d..".png") | |
local backBlack = Image.skin("title/back_black.png") | |
local backYellow = Image.skin("title/back_yellow.png") | |
local lightMask = Image.skin("title/light_mask.png") | |
local rasis = randomNumber() > 0.5 and Image.skin("title/rasis.png") or Image.skin("title/rasis2.png") | |
local sankaku = Image.skin("title/sankaku.png") | |
local playedTheme = false | |
game.LoadSkinSample("title") | |
game.LoadSkinSample("intermission") | |
game.LoadSkinSample("loading") | |
game.LoadSkinSample("tick") | |
local view_update = function() | |
local updateUrl, updateVersion = game.UpdateAvailable() | |
if package.config:sub(1,1) == '\\' then --windows | |
os.execute("start " .. updateUrl) | |
else --unix | |
-- TODO: Mac solution | |
os.execute("xdg-open " .. updateUrl) | |
end | |
end | |
local function normalizeScreenCoords() | |
gfx.ResetTransform() | |
gfx.ResetScissor() | |
gfx.Translate(xshift, yshift) | |
gfx.Scale(scale, scale) | |
gfx.Scissor(0, 0, desw, desh) | |
end | |
local function calculateScreenCoords(x, y) | |
return (x - xshift) / scale, (y - yshift) / scale | |
end | |
local function updateMousePosition() | |
lastMousePos = {mposx, mposy} | |
mposx, mposy = game.GetMousePos() | |
end | |
local function mousePositionChanged() | |
return lastMousePos[1] ~= mposx or lastMousePos[2] ~= mposy | |
end | |
local function mouseClipped(x,y,w,h) | |
local localMposX, localMposY = calculateScreenCoords(mposx, mposy) | |
return localMposX > x and localMposY > y and localMposX < x+w and localMposY < y+h; | |
end; | |
local function setButtons() | |
if buttons == nil then | |
buttons = { | |
{action=Menu.Start, card=Image.skin("title/card_title.png"), txt=Image.skin("title/txt_start.png"), subtxt=Image.skin("title/subtxt_start.png")}, | |
{action=Menu.Settings, card=Image.skin("title/card_green.png"), txt=Image.skin("title/txt_settings.png"), subtxt=Image.skin("title/subtxt_settings.png")}, | |
{action=Menu.Multiplayer, card=Image.skin("title/card_orange.png"), txt=Image.skin("title/txt_friend.png"), subtxt=Image.skin("title/subtxt_friend.png")}, | |
{action=Menu.DLScreen, card=Image.skin("title/card_pink.png"), txt=Image.skin("title/txt_nautica.png"), subtxt=Image.skin("title/subtxt_nautica.png")}, | |
{action=Menu.Exit, card=Image.skin("title/card_purple.png"), txt=Image.skin("title/txt_exit.png"), subtxt=Image.skin("title/subtxt_exit.png")}, | |
{action=Menu.Challenges, card=Image.skin("title/card_green.png"), txt=Image.skin("title/txt_friend.png"), subtxt=Image.skin("title/subtxt_friend.png")}, | |
} | |
end | |
end | |
local function sign(x) | |
return x>0 and 1 or x<0 and -1 or 0 | |
end | |
local function roundToZero(x) | |
if x<0 then return math.ceil(x) | |
elseif x>0 then return math.floor(x) | |
else return 0 end | |
end | |
local function deltaKnob(delta) | |
if math.abs(delta) > 1.5 * math.pi then | |
return delta + 2 * math.pi * sign(delta) * -1 | |
end | |
return delta | |
end | |
local function doAction(action) | |
game.StopSample("title") | |
game.PlaySample("intermission") | |
if action == Menu.Start then | |
game.PlaySample("loading", true) | |
game.SetSkinSetting("music_playing", "loading") | |
end | |
action() | |
end | |
local function setCursorIndex(val) | |
if cursorIndex ~= val then | |
cursorIndex = val | |
game.PlaySample("tick") | |
cursorTimer = 0 | |
end | |
end | |
-- If no textX/textY provided then don't draw any text | |
local function drawButton(action, key, x, y, w, h) | |
local rx = x - (w / 2) | |
local ty = y - (h / 2) | |
if mouseClipped(rx,ty,w,h) then | |
mouseHoverActive = true | |
if key and mousePositionChanged() then setCursorIndex(key) end | |
if cursorIndex == key then | |
return true, true | |
end | |
end | |
if cursorIndex == key then | |
return true | |
end | |
return false | |
end | |
local selectBgMesh = gfx.CreateShadedMesh("cardSelect") | |
selectBgMesh:SetPrimitiveType(selectBgMesh.PRIM_TRIFAN) | |
selectBgMesh:AddSkinTexture("mainTex", "title/card_select_bg.png") | |
local function drawCardButton(buttonKey, x, y, opts) | |
local button = buttons[buttonKey] | |
if not opts then opts = {} end | |
local settings = {x=x, y=y, w=CardSize, h=CardSize} | |
local selected, hovered = drawButton(button.action, buttonKey, | |
settings.x, settings.y, | |
settings.w, settings.h | |
) | |
backYellow:draw(settings) | |
backBlack:draw(settings) | |
gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER) | |
if selected then | |
local ms = CardSize*1.13 | |
local mx = x - ms/2 | |
local my = y - ms/2 | |
selectBgMesh:SetParam("time", currentTime) | |
selectBgMesh:SetData({ | |
{{mx,my},{0,0}}, | |
{{mx+ms,my},{1,0}}, | |
{{mx+ms,my+ms},{1,1}}, | |
{{mx,my+ms},{0,1}}, | |
}) | |
selectBgMesh:Draw() | |
gfx.ForceRender() | |
normalizeScreenCoords() | |
end | |
button.card:draw(settings) | |
if hovered then | |
gfx.GlobalAlpha(0.09); gfx.GlobalCompositeOperation(gfx.BLEND_OP_LIGHTER) | |
lightMask:draw(settings) | |
gfx.GlobalAlpha(1); gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER) | |
end | |
local txtScale = selected and ( math.sin(cursorTimer*10)*0.5+0.5 )*0.1+1 or 1 | |
button.txt:draw({x=settings.x, y=settings.y-30, w=CardSize*txtScale, h=150/1.5*txtScale}) | |
if button.subtxt then | |
button.subtxt:draw({x=settings.x, y=settings.y+50, w=CardSize, h=100/1.5}) | |
end | |
if not selected then | |
gfx.GlobalAlpha(0.12); lightMask:draw(settings); gfx.GlobalAlpha(1) | |
end | |
return selected | |
end | |
local function drawTextButton(button, x, y) | |
local selected = drawButton(button[2], nil, x, y, 300, 30) | |
gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER) | |
gfx.BeginPath() | |
gfx.FontSize(30) | |
gfx.FillColor(0,0,0); | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.LoadSkinFont("airone.ttf") | |
gfx.Text(button[1], x, y) | |
if selected then | |
gfx.FillColor(255,255,255) | |
gfx.Text(button[1], x, y-3) | |
end | |
end | |
local function getCardCoords(i) | |
return | |
125 + (i%3) * (CardSize+CardSpacing), | |
630 + math.floor(i/3) * (CardSize+CardSpacing) | |
end | |
local function drawUIDecoration(deltaTime) | |
local selX, selY | |
for i = 0, #buttons-1 do | |
local x, y = getCardCoords(i) | |
local sel = drawCardButton(i+1, x, y) | |
if sel then | |
selX = x | |
selY = y | |
end | |
end | |
if selX and selY then | |
gfx.Save();gfx.Translate(selX, selY) | |
gfx.Save() | |
for i = 0, 3 do | |
gfx.Rotate((i/4)*math.pi*2) | |
local sankakuOffset = (math.cos(cursorTimer*10)*0.5+0.5) * 6 | |
sankaku:draw({x=100+sankakuOffset, y=-100-sankakuOffset, w=70/1.5, h=70/1.5}) | |
end | |
gfx.Restore() | |
rasis:draw({x=110, y=-100, w=110, h=110}) | |
gfx.Restore() | |
end | |
end | |
local lastKnobs = nil | |
local knobProgress = 0 | |
local function handleController() | |
if lastKnobs == nil then | |
lastKnobs = {game.GetKnob(0), game.GetKnob(1)} | |
else | |
local newKnobs = {game.GetKnob(0), game.GetKnob(1)} | |
knobProgress = knobProgress - deltaKnob(lastKnobs[1] - newKnobs[1]) * 1.2 | |
knobProgress = knobProgress - deltaKnob(lastKnobs[2] - newKnobs[2]) * 1.2 | |
lastKnobs = newKnobs | |
if math.abs(knobProgress) > 0.1 and not beginning then | |
beginning = false | |
keepSelection = true | |
end | |
if math.abs(knobProgress) > 1 then | |
setCursorIndex( (((cursorIndex - 1) + roundToZero(knobProgress)) % #buttons) + 1 ) | |
knobProgress = knobProgress - roundToZero(knobProgress) | |
keepSelection = true | |
end | |
end | |
end | |
local function draw1080pAsset(img, x, y, opts) | |
opts = opts or {} | |
img:draw({x=x, y=y, w=img.w/1.5, h=img.h/1.5, alpha=(opts.alpha or 1), | |
anchor_h=Image.ANCHOR_LEFT, anchor_v=(opts.anchor_v or Image.ANCHOR_TOP)}) | |
end | |
render = function(deltaTime) | |
if not playedTheme then | |
game.PlaySample("title", true) | |
playedTheme = true | |
stopMusic() | |
end | |
DrawCrew.prepareCrew(true, true) | |
currentTime = currentTime + deltaTime | |
cursorTimer = cursorTimer + deltaTime | |
setButtons() | |
updateMousePosition() | |
resx, resy = game.GetResolution() | |
scale = resy / desh | |
xshift = (resx - desw * scale) / 2 | |
yshift = (resy - desh * scale) / 2 | |
if game.GetSkinSetting("landscape_bg_fill") then | |
gfx.BeginPath() | |
gfx.FillColor(255, 255, 255) | |
gfx.ImageRect(0, 0, resx, resy, bgFill, 1, 0) | |
end | |
normalizeScreenCoords() | |
if not noBgAnim then | |
gfx.TickAnimation(backgroundAnimation, deltaTime) | |
end | |
gfx.ImageRect(0, 0, desw, desh, backgroundAnimation, 1, 0) | |
gfx.Save();gfx.Translate(0, -220) | |
DrawCrew.drawCrew(deltaTime, true, -40) | |
gfx.Restore() | |
draw1080pAsset(fillTop, 0, 0) | |
draw1080pAsset(topLabel, 15, 10) | |
draw1080pAsset(fillConsole, 0, desh, {anchor_v=Image.ANCHOR_BOTTOM}) | |
draw1080pAsset(fillConsoleTxt, 0, desh, {anchor_v=Image.ANCHOR_BOTTOM}) | |
draw1080pAsset(fillBottom, 0, desh, {anchor_v=Image.ANCHOR_BOTTOM}) | |
mouseHoverActive = false | |
drawUIDecoration(deltaTime) | |
handleController() | |
local updateUrl, updateVersion = game.UpdateAvailable() | |
if updateUrl then | |
local x, y = getCardCoords(5) | |
if not buttons[6] then buttons[6] = {action=Menu.Update, card=lightMask, txt=Image.skin("title/txt_update.png"), subtxt=nil} end | |
gfx.BeginPath() | |
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) | |
gfx.FontSize(24) | |
gfx.FillColor(0,0,0) | |
gfx.LoadSkinFont("rounded_bold.ttf") | |
gfx.Text("UPGRADE TO:", x, y+35) | |
gfx.Text(string.format("VERSION %s", updateVersion), x, y+55) | |
end | |
Credit:draw({x=desw-90, y=desh-18, w=Credit.w/1.5, h=Credit.h/1.5}) | |
gfx.ForceRender() | |
end | |
mouse_pressed = function(button) | |
if mouseHoverActive then | |
doAction(buttons[cursorIndex].action) | |
end | |
return 0 | |
end | |
button_pressed = function(button) | |
if button == game.BUTTON_STA then | |
doAction(buttons[cursorIndex].action) | |
elseif button == game.BUTTON_BCK then | |
Menu.Exit() | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment