local Easing = require('common.easings'); local resx, resy = game.GetResolution() local desw = 1080 local desh = 1920 local bgSfxPlayed = false; local backgroundImage = gfx.CreateSkinImage("bg.png", 0); local topBarImage = gfx.CreateSkinImage("result/top_bar.png", 0); local jacketPanelImage = gfx.CreateSkinImage("result/panels/jacket.png", 0); local rightPanelImage = gfx.CreateSkinImage("result/panels/right.png", 0); local bottomPanelImage = gfx.CreateSkinImage("result/panels/bottom.png", 0); local jacketImage = gfx.CreateSkinImage("result/default_jacket.png", 0); -- ANIMS local idolAnimation = gfx.LoadSkinAnimation('idol', 1/30, 0, false); local transitionEnterScale = 0; local idolAnimTransitionScale = 0; local rightPanelX = 0; local rightPanelY = 850; local jacketPanelX = 0; local jacketPanelY = 820; local JACKET_PANEL_TRANSTION_ENTER_OFFSET = -128; local RIGHT_PANEL_TRANSTION_ENTER_OFFSET = 128; local BOTTOM_PANEL_TRANSTION_ENTER_OFFSET = 128; local earlyLateBarsStats = { earlyErrors = 0, earlyNears = 0, criticals = 0, lateNears = 0, lateErrors = 0 }; local objectTypeTimingStats = { chip = {criticals = 0, nears = 0, errors = 0}, long = {criticals = 0, errors = 0}, vol = {criticals = 0, errors = 0} } game.LoadSkinSample("result") function resetLayoutInformation() resx, resy = game.GetResolution() desw = 1080 desh = 1920 scale = resx / desw end local handleSfx = function() if not bgSfxPlayed then game.PlaySample("result", true) bgSfxPlayed = true end if game.GetButton(game.BUTTON_STA) then game.StopSample("result") end if game.GetButton(game.BUTTON_BCK) then game.StopSample("result") end end function drawTimingBar(y, value, max, type) gfx.BeginPath(); local barAlpha = math.floor(Easing.outQuad(transitionEnterScale)*255) if type == 'crit' then gfx.FillColor(253,243,24,barAlpha); elseif type == 'early' then gfx.FillColor(215,48,182,barAlpha); elseif type == 'late' then gfx.FillColor(46,211,241,barAlpha); end gfx.Rect(rightPanelX+696,rightPanelY+y,293*(value/max), 8); gfx.Fill(); gfx.ClosePath(); end local drawIdol = function (deltaTime) local idolAnimTickRes = gfx.TickAnimation(idolAnimation, deltaTime); if idolAnimTickRes == 1 then gfx.GlobalAlpha(idolAnimTransitionScale); idolAnimTransitionScale = idolAnimTransitionScale + 1/60; if (idolAnimTransitionScale > 1) then idolAnimTransitionScale = 1; end gfx.ImageRect(0, 0, resx, resy, idolAnimation, 1, 0); gfx.GlobalAlpha(1); end end local drawTopBar = function() gfx.BeginPath(); local tw, th = gfx.ImageSize(topBarImage); gfx.ImageRect(0, -th * (1 - Easing.outQuad(transitionEnterScale)), desw, th, topBarImage, 1, 0); end local drawRightPanel = function() gfx.BeginPath(); local tw, th = gfx.ImageSize(rightPanelImage); gfx.ImageRect(rightPanelX, rightPanelY, tw, th, rightPanelImage, Easing.outQuad(transitionEnterScale), 0); end local drawRightPanelContent = function() -- Draw song name and artist gfx.FontSize(28) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE) gfx.Text(result.realTitle, rightPanelX + 435, rightPanelY + 108); gfx.Text(result.artist, rightPanelX + 435, rightPanelY + 143); -- Draw err/early/critical/late/err texts gfx.FontSize(24) gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT + gfx.TEXT_ALIGN_MIDDLE) gfx.LoadSkinFont('Digital-Serial-Bold.ttf') gfx.Text(earlyLateBarsStats.earlyErrors, rightPanelX + 683, rightPanelY + 370); gfx.Text(earlyLateBarsStats.earlyNears, rightPanelX + 683, rightPanelY + 401); gfx.Text(earlyLateBarsStats.criticals, rightPanelX + 683, rightPanelY + 432); gfx.Text(earlyLateBarsStats.lateNears, rightPanelX + 683, rightPanelY + 463); gfx.Text(earlyLateBarsStats.lateErrors, rightPanelX + 683, rightPanelY + 494); -- Draw hit timing bars local totalHits = earlyLateBarsStats.earlyErrors + earlyLateBarsStats.earlyNears + earlyLateBarsStats.criticals + earlyLateBarsStats.lateNears + earlyLateBarsStats.lateErrors gfx.Save() drawTimingBar(365, earlyLateBarsStats.earlyErrors, totalHits, 'early') drawTimingBar(396, earlyLateBarsStats.earlyNears, totalHits, 'early') drawTimingBar(427, earlyLateBarsStats.criticals, totalHits, 'crit') drawTimingBar(458, earlyLateBarsStats.lateNears, totalHits, 'late') drawTimingBar(489, earlyLateBarsStats.lateErrors, totalHits, 'late') gfx.Restore() -- Draw hit stats based on objects -- CHIP gfx.Text(objectTypeTimingStats.chip.criticals, rightPanelX + 255, rightPanelY + 365); gfx.Text(objectTypeTimingStats.chip.nears, rightPanelX + 255, rightPanelY + 395); gfx.Text(objectTypeTimingStats.chip.errors, rightPanelX + 255, rightPanelY + 425); -- LONG gfx.Text('???', rightPanelX + 333, rightPanelY + 365); gfx.Text('-', rightPanelX + 333, rightPanelY + 395); gfx.Text('???', rightPanelX + 333, rightPanelY + 425); -- VOL gfx.Text('???', rightPanelX + 411, rightPanelY + 365); gfx.Text('-', rightPanelX + 411, rightPanelY + 395); gfx.Text('???', rightPanelX + 411, rightPanelY + 425); -- Draw max combo gfx.Text(result.maxCombo, rightPanelX + 371, rightPanelY + 466); end local drawBottomPanel = function() gfx.BeginPath(); local tw, th = gfx.ImageSize(bottomPanelImage); gfx.ImageRect(0, 1110 + (BOTTOM_PANEL_TRANSTION_ENTER_OFFSET * (1 - Easing.outQuad(transitionEnterScale))), tw, th, bottomPanelImage, Easing.outQuad(transitionEnterScale), 0); end local drawJacketPanel = function() gfx.BeginPath(); local tw, th = gfx.ImageSize(jacketPanelImage); gfx.ImageRect(jacketPanelX, jacketPanelY, tw, th, jacketPanelImage, Easing.outQuad(transitionEnterScale), 0); end local drawJacketPanelContent = function() gfx.BeginPath(); gfx.ImageRect(jacketPanelX + 12, jacketPanelY + 26, 273, 273, jacketImage, Easing.outQuad(transitionEnterScale), 0); end local tickTransitions = function(deltaTime) if transitionEnterScale < 1 then transitionEnterScale = transitionEnterScale + deltaTime / 1 -- transition should last for 1s else transitionEnterScale = 1 end rightPanelX = 0 + (RIGHT_PANEL_TRANSTION_ENTER_OFFSET * (1 - Easing.outQuad(transitionEnterScale))) jacketPanelX = 40 + (JACKET_PANEL_TRANSTION_ENTER_OFFSET * (1 - Easing.outQuad(transitionEnterScale))) end result_set = function() if result.jacketPath ~= nil and result.jacketPath ~= "" then jacketImage = gfx.CreateImage(result.jacketPath, 0) end -- This only counts the "CHIP" objects, not LONGs or LAZERs for hitStatIndex = 1, #result.noteHitStats do local hitStat = result.noteHitStats[hitStatIndex]; if (hitStat.rating == 0) then -- Miss objectTypeTimingStats.chip.errors = objectTypeTimingStats.chip.errors + 1; if hitStat.delta < 0 then earlyLateBarsStats.earlyErrors = earlyLateBarsStats.earlyErrors + 1; else earlyLateBarsStats.lateErrors = earlyLateBarsStats.lateErrors + 1; end elseif hitStat.rating == 1 then objectTypeTimingStats.chip.nears = objectTypeTimingStats.chip.nears + 1; if hitStat.delta < 0 then earlyLateBarsStats.earlyNears = earlyLateBarsStats.earlyNears + 1; else earlyLateBarsStats.lateNears = earlyLateBarsStats.lateNears + 1; end else objectTypeTimingStats.chip.criticals = objectTypeTimingStats.chip.criticals + 1; end end -- misses on LONGs or LAZERs are automatically late errors -- so we add errors that are not ealy or late to late errors earlyLateBarsStats.lateErrors = earlyLateBarsStats.lateErrors + (result.misses - earlyLateBarsStats.lateErrors - earlyLateBarsStats.earlyErrors) -- criticals are same for all objects so we just copy them from results earlyLateBarsStats.criticals = result.perfects end render = function(deltaTime, showStats) gfx.Save(); resetLayoutInformation() gfx.ResetTransform(); gfx.ResetScissor(); gfx.BeginPath() gfx.Scale(scale, scale) gfx.ImageRect(0, 0, desw, desh, backgroundImage, 1, 0); drawIdol(deltaTime) drawTopBar() drawBottomPanel() drawRightPanel() drawRightPanelContent() drawJacketPanel() drawJacketPanelContent() handleSfx() -- debug gfx.FontSize(18) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.Text('DELTA: ' .. deltaTime .. ' // TRANSITION_ENTER_SCALE: ' .. transitionEnterScale .. ' // EASING_OUT_QUAD: ' .. Easing.outQuad(transitionEnterScale), 8, 8); tickTransitions(deltaTime) gfx.Restore(); end