1442 lines
45 KiB
Lua
1442 lines
45 KiB
Lua
-- 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 badgeImages = {
|
|
gfx.CreateSkinImage("badges/played.png", 0),
|
|
gfx.CreateSkinImage("badges/clear.png", 0),
|
|
gfx.CreateSkinImage("badges/hard-clear.png", 0),
|
|
gfx.CreateSkinImage("badges/full-combo.png", 0),
|
|
gfx.CreateSkinImage("badges/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("applause")
|
|
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
|
|
|
|
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)
|
|
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
|
|
|
|
-- Applause SFX
|
|
if result.passed and not played then
|
|
game.PlaySample("applause")
|
|
played = true
|
|
end
|
|
|
|
-- Screenshot notification
|
|
shotTimer = math.max(shotTimer - deltaTime, 0)
|
|
if shotTimer > 1 then
|
|
draw_shotnotif(505,755);
|
|
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
|