ExperimentalGear/scripts/songselect/songwheel.lua

509 lines
17 KiB
Lua
Raw Normal View History

2021-07-25 20:10:06 +02:00
2021-07-30 21:34:36 +02:00
local Easing = require('common.easings');
local backgroundImage = gfx.CreateSkinImage("song_select/bg.png", 1)
local dataPanelImage = gfx.CreateSkinImage("song_select/data_bg_overlay.png", 1)
local dataGlowOverlayImage = gfx.CreateSkinImage("song_select/data_panel/data_glow_overlay.png", 1)
local gradeBgImage = gfx.CreateSkinImage("song_select/data_panel/grade_bg.png", 1)
local badgeBgImage = gfx.CreateSkinImage("song_select/data_panel/clear_badge_bg.png", 1)
local effectedBgImage = gfx.CreateSkinImage("song_select/data_panel/effected_bg.png", 1)
local illustratedBgImage = gfx.CreateSkinImage("song_select/data_panel/illust_bg.png", 1)
local songPlateBg = gfx.CreateSkinImage("song_select/plate/bg.png", 1)
local songPlateBottomBarOverlayImage = gfx.CreateSkinImage("song_select/plate/bottom_bar_overlay.png", 1)
2021-07-30 18:27:43 +02:00
local cursorImage = gfx.CreateSkinImage("song_select/cursor.png", 1)
local diffCursorImage = gfx.CreateSkinImage("song_select/level_cursor.png", 1)
2021-07-30 18:27:43 +02:00
local defaultJacketImage = gfx.CreateSkinImage("song_select/loading.png", 0)
local difficultyLabelImages = {
gfx.CreateSkinImage("song_select/plate/difficulty_labels/novice.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/advanced.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/exhaust.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/maximum.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/infinite.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/gravity.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/heavenly.png", 1),
gfx.CreateSkinImage("song_select/plate/difficulty_labels/vivid.png", 1),
2021-07-25 20:10:06 +02:00
}
2021-07-30 18:27:43 +02:00
local badgeImages = {
gfx.CreateSkinImage("song_select/medal/nomedal.png", 1),
gfx.CreateSkinImage("song_select/medal/played.png", 1),
gfx.CreateSkinImage("song_select/medal/clear.png", 1),
2021-07-30 18:27:43 +02:00
gfx.CreateSkinImage("song_select/medal/hard.png", 1),
gfx.CreateSkinImage("song_select/medal/uc.png", 1),
gfx.CreateSkinImage("song_select/medal/puc.png", 1),
}
2021-07-31 18:43:17 +02:00
local gradeCutoffs = {
D = 0000000,
C = 7000000,
B = 8000000,
A = 8700000,
A_P = 9000000,
AA = 9300000,
AA_P = 9500000,
AAA = 9700000,
AAA_P = 9800000,
S = 9900000,
}
local gradeImages = {
S = gfx.CreateSkinImage("common/grades/S.png", 0),
AAA_P = gfx.CreateSkinImage("common/grades/AAA+.png", 0),
AAA = gfx.CreateSkinImage("common/grades/AAA.png", 0),
AA_P = gfx.CreateSkinImage("common/grades/AA+.png", 0),
AA = gfx.CreateSkinImage("common/grades/AA.png", 0),
A_P = gfx.CreateSkinImage("common/grades/A+.png", 0),
A = gfx.CreateSkinImage("common/grades/A.png", 0),
B = gfx.CreateSkinImage("common/grades/B.png", 0),
C = gfx.CreateSkinImage("common/grades/C.png", 0),
D = gfx.CreateSkinImage("common/grades/D.png", 0),
none = gfx.CreateSkinImage("common/grades/none.png", 0),
}
local difficultyLabelUnderImages = {
gfx.CreateSkinImage("songtransition/difficulty_labels/nov.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/adv.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/exh.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/mxm.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/inf.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/grv.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/hvn.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/vvd.png", 0),
}
2021-07-30 22:06:08 +02:00
game.LoadSkinSample('song_wheel/cursor_change.wav');
local difficultyNumbers;
2021-07-31 20:36:05 +02:00
local scoreNumbers;
2021-07-25 20:10:06 +02:00
local resx, resy = game.GetResolution()
local desw, desh;
local scale;
2021-07-25 20:10:06 +02:00
2021-07-30 21:34:36 +02:00
local songPlateHeight = 172;
local selectedIndex = 1;
local selectedDifficulty = 1;
2021-07-25 20:10:06 +02:00
local jacketCache = {}
2021-07-25 20:10:06 +02:00
2021-07-30 21:34:36 +02:00
local transitionScrollScale = 0;
local transitionScrollOffsetY = 0;
local scrollingUp = false;
local transitionAfterscrollScale = 0;
local transitionAfterscrollDataOverlayAlpha = 0;
local transitionAfterscrollGradeAlpha = 0;
local transitionAfterscrollBadgeAlpha = 0;
local transitionAfterscrollTextSongTitle = 0;
local transitionAfterscrollTextSongArtist = 0;
local transitionAfterscrollDifficultiesAlpha = 0;
local transitionAfterscrollJacketBgAlpha = 0;
function resetLayoutInformation()
resx, resy = game.GetResolution()
desw = 1080
desh = 1920
scale = resx / desw
2021-07-25 20:10:06 +02:00
end
function load_number_image(path)
local images = {}
for i = 0, 9 do
images[i + 1] = gfx.CreateSkinImage(string.format("%s/%d.png", path, i), 0)
2021-07-25 20:10:06 +02:00
end
return images
2021-07-25 20:10:06 +02:00
end
function draw_number(x, y, alpha, num, digits, images, is_dim, scale, kern)
scale = scale or 1;
kern = kern or 1;
local tw, th = gfx.ImageSize(images[1])
tw = tw * scale;
th = th * scale;
x = x + (tw * (digits - 1)) / 2
y = y - th / 2
for i = 1, digits do
local mul = 10 ^ (i - 1)
local digit = math.floor(num / mul) % 10
local a = alpha
if is_dim and num < mul then
a = 0.4
2021-07-25 20:10:06 +02:00
end
gfx.BeginPath()
gfx.ImageRect(x, y, tw, th, images[digit + 1], a, 0)
x = x - (tw * kern)
2021-07-25 20:10:06 +02:00
end
end
2021-07-30 20:18:03 +02:00
function getCorrectedIndex(from, offset)
total = #songwheel.songs;
index = from + offset;
if index < 1 then
index = total + (from+offset) -- this only happens if the offset is negative
end;
if index > total then
indexesUntilEnd = total - from;
index = offset - indexesUntilEnd -- this only happens if the offset is positive
end;
return index;
end;
function getJacketImage(song)
if not jacketCache[song.id] or jacketCache[song.id]==defaultJacketImage then
jacketCache[song.id] = gfx.LoadImageJob(song.difficulties[1].jacketPath, defaultJacketImage, 0, 0);
2021-07-25 20:10:06 +02:00
end
return jacketCache[song.id];
2021-07-25 20:10:06 +02:00
end
2021-07-31 18:43:17 +02:00
function getGradeImageForScore(score)
local gradeImage = gradeImages.none;
local bestGradeCutoff = 0;
for gradeName, scoreCutoff in pairs(gradeCutoffs) do
if scoreCutoff <= score then
if scoreCutoff > bestGradeCutoff then
gradeImage = gradeImages[gradeName];
bestGradeCutoff = scoreCutoff;
end
end
end
return gradeImage;
end
function drawBackground()
gfx.BeginPath()
gfx.ImageRect(0, 0, desw, desh, backgroundImage, 1, 0)
2021-07-31 20:36:05 +02:00
-- If the score for song exists
local song = songwheel.songs[selectedIndex];
local diff = song.difficulties[selectedDifficulty];
local bestScore = diff.scores[1];
if song and diff then
local jacketImage = getJacketImage(song);
if jacketImage ~= defaultJacketImage then
gfx.BeginPath()
gfx.ImageRect(0+transitionAfterscrollScale*-300, 0, 900, 900, jacketImage or defaultJacketImage, transitionAfterscrollJacketBgAlpha, 0)
gfx.BeginPath();
gfx.FillColor(0,0,0,math.floor(transitionAfterscrollJacketBgAlpha*64));
gfx.Rect(0,0,900,900);
gfx.Fill();
gfx.ClosePath();
end
2021-07-31 20:36:05 +02:00
gfx.BeginPath()
end
gfx.ImageRect(0, 0, desw, desh, dataPanelImage, 1, 0)
if song and diff then
gfx.BeginPath()
gfx.ImageRect(0, 0, desw, desh, dataGlowOverlayImage, transitionAfterscrollDataOverlayAlpha, 0)
gfx.BeginPath()
gfx.ImageRect(341, 754, 85, 85, gradeBgImage, transitionAfterscrollDataOverlayAlpha, 0)
gfx.BeginPath()
gfx.ImageRect(391, 687, 180*0.85, 226*0.85, badgeBgImage, transitionAfterscrollDataOverlayAlpha, 0)
gfx.BeginPath()
gfx.ImageRect(95, 1165, 433, 30, effectedBgImage, transitionAfterscrollDataOverlayAlpha, 0)
gfx.BeginPath()
gfx.ImageRect(95, 1195, 433, 30, illustratedBgImage, transitionAfterscrollDataOverlayAlpha, 0)
2021-07-31 20:36:05 +02:00
end
2021-07-25 20:10:06 +02:00
end
function drawSong(song, y)
2021-07-31 20:36:05 +02:00
if (not song) then return end;
local songX = desw/2+28
2021-07-30 18:27:43 +02:00
local selectedSongDifficulty = song.difficulties[selectedDifficulty]
2021-07-31 18:43:17 +02:00
if not selectedSongDifficulty then
return;
end
local bestScore;
if selectedSongDifficulty.scores then
bestScore = selectedSongDifficulty.scores[1];
end
2021-07-25 20:10:06 +02:00
-- Draw the bg for the song plate
gfx.BeginPath()
2021-07-30 16:42:01 +02:00
gfx.ImageRect(songX, y, 515, 172, songPlateBg, 1, 0)
-- Draw jacket
local jacketImage = getJacketImage(song);
gfx.BeginPath()
gfx.ImageRect(songX+4, y+4, 163, 163, jacketImage or defaultJacketImage, 1, 0)
2021-07-25 20:10:06 +02:00
-- Draw the overlay for the song plate (that bottom black bar)
gfx.BeginPath()
2021-07-30 16:42:01 +02:00
gfx.ImageRect(songX, y, 515, 172, songPlateBottomBarOverlayImage, 1, 0)
-- Draw the difficulty notch background
2021-07-25 20:10:06 +02:00
gfx.BeginPath()
2021-07-30 18:27:43 +02:00
gfx.ImageRect(songX, y+95, 83, 74, difficultyLabelImages[selectedSongDifficulty.difficulty+1], 1, 0)
2021-07-25 20:10:06 +02:00
-- Draw the difficulty level number
gfx.BeginPath()
2021-07-30 18:27:43 +02:00
draw_number(songX+30, y+125, 1.0, selectedSongDifficulty.level, 2, difficultyNumbers, false, 0.65, 1)
2021-07-30 22:10:04 +02:00
-- Draw song title
gfx.FontSize(24)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
gfx.Text(song.title, songX+90, y+155);
2021-07-30 18:27:43 +02:00
-- Draw score badge
local badgeImage = badgeImages[1];
if bestScore then
2021-07-30 18:27:43 +02:00
badgeImage = badgeImages[bestScore.badge+1];
end
gfx.BeginPath()
gfx.ImageRect(songX+282, y+44, 79, 69, badgeImage, 1, 0)
2021-07-31 18:43:17 +02:00
-- Draw grade
local gradeImage = gradeImages.none;
if bestScore then
gradeImage = getGradeImageForScore(bestScore.score)
end
gfx.BeginPath();
gfx.ImageRect(songX+390, y+47, 60, 60, gradeImage, 1, 0);
2021-07-25 20:10:06 +02:00
end
function drawSongList()
2021-07-30 21:34:36 +02:00
local numOfSongsAround = 7; -- How many songs should be up and how many should be down of the selected one
local i=1;
while (i <= numOfSongsAround) do
2021-07-30 20:18:03 +02:00
local songIndex = getCorrectedIndex(selectedIndex, -i)
2021-07-30 21:34:36 +02:00
drawSong(songwheel.songs[songIndex], desh/2-songPlateHeight/2-songPlateHeight*i + transitionScrollOffsetY)
i=i+1;
end;
-- Draw the selected song
2021-07-30 21:34:36 +02:00
drawSong(songwheel.songs[selectedIndex], desh/2-songPlateHeight/2 + transitionScrollOffsetY)
i=1;
while (i <= numOfSongsAround) do
2021-07-30 20:18:03 +02:00
local songIndex = getCorrectedIndex(selectedIndex, i)
2021-07-30 21:34:36 +02:00
drawSong(songwheel.songs[songIndex], desh/2-songPlateHeight/2+songPlateHeight*i + transitionScrollOffsetY)
i=i+1;
end;
2021-07-25 20:10:06 +02:00
end
2021-07-30 18:27:43 +02:00
function drawCursor()
gfx.BeginPath()
gfx.ImageRect(desw/2-14, desh/2-213/2, 555, 213, cursorImage, 1, 0)
end
2021-07-31 20:36:05 +02:00
local scoreNumbers = load_number_image("score_num");
function drawData() -- Draws the song data on the left panel
local song = songwheel.songs[selectedIndex];
local diff = song.difficulties[selectedDifficulty];
local bestScore = diff.scores[1];
local jacketImage = getJacketImage(song);
gfx.BeginPath()
gfx.ImageRect(96, 324, 348, 348, jacketImage or defaultJacketImage, 1, 0)
gfx.Save()
2021-07-31 20:36:05 +02:00
if bestScore then
-- Draw best score
gfx.BeginPath()
draw_number(100, 793, 1.0, math.floor(bestScore.score / 10000), 4, scoreNumbers, true, 0.3, 1.12)
draw_number(245, 797, 1.0, bestScore.score, 4, scoreNumbers, true, 0.22, 1.12)
-- Draw grade
local gradeImage = gradeImages.none;
if bestScore then
gradeImage = getGradeImageForScore(bestScore.score)
end
gfx.BeginPath();
gfx.ImageRect(360, 773, 45, 45, gradeImage, transitionAfterscrollGradeAlpha, 0);
2021-07-31 20:36:05 +02:00
-- Draw badge
badgeImage = badgeImages[bestScore.badge+1];
gfx.BeginPath()
gfx.ImageRect(425, 724, 93/1.1, 81/1.1, badgeImage, transitionAfterscrollBadgeAlpha, 0)
2021-07-31 20:36:05 +02:00
end
gfx.Restore()
2021-07-31 20:36:05 +02:00
-- Draw BPM
gfx.BeginPath();
2021-07-31 20:36:05 +02:00
gfx.FontSize(24)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
gfx.Save()
gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
gfx.GlobalAlpha(transitionAfterscrollDataOverlayAlpha) -- TODO: split this out
2021-07-31 20:36:05 +02:00
gfx.Text(song.bpm, 85, 920);
gfx.Restore()
-- Draw song title
gfx.FontSize(28)
gfx.GlobalAlpha(transitionAfterscrollTextSongTitle);
gfx.Text(song.title, 30+(1-transitionAfterscrollTextSongTitle)*20, 955);
2021-07-31 20:36:05 +02:00
-- Draw artist
gfx.GlobalAlpha(transitionAfterscrollTextSongArtist);
gfx.Text(song.artist, 30+(1-transitionAfterscrollTextSongArtist)*20, 997);
gfx.GlobalAlpha(1);
-- Draw difficulties
local DIFF_X_START = 98.5
local DIFF_GAP = 114.8;
gfx.GlobalAlpha(transitionAfterscrollDifficultiesAlpha);
for index, diff in ipairs(song.difficulties) do
gfx.BeginPath()
if index == selectedDifficulty then
gfx.ImageRect(DIFF_X_START+(index-1)*DIFF_GAP-(163*0.8)/2, 1028, 163*0.8, 163*0.8, diffCursorImage, 1, 0)
end
draw_number(85+(index-1)*DIFF_GAP, 1085, 1.0, diff.level, 2, difficultyNumbers, false, 0.8, 1)
local diffLabelImage = difficultyLabelUnderImages[diff.difficulty+1];
local tw, th = gfx.ImageSize(diffLabelImage)
tw=tw*0.9
th=th*0.9
gfx.BeginPath()
gfx.ImageRect(DIFF_X_START+(index-1)*DIFF_GAP-tw/2, 1050, tw, th, diffLabelImage, 1, 0)
end
gfx.GlobalAlpha(1);
2021-07-31 20:36:05 +02:00
end
2021-07-30 21:34:36 +02:00
function tickTransitions(deltaTime)
if transitionScrollScale < 1 then
transitionScrollScale = transitionScrollScale + deltaTime / 0.1 -- transition should last for that time in seconds
else
transitionScrollScale = 1
end
if transitionAfterscrollScale < 1 then
if transitionScrollScale == 1 then
-- Only start the after scroll transition when the scroll transition is finished
transitionAfterscrollScale = transitionAfterscrollScale + deltaTime / 15
end
else
transitionAfterscrollScale = 1;
end
2021-07-30 21:34:36 +02:00
if scrollingUp then
transitionScrollOffsetY = Easing.inQuad(1-transitionScrollScale) * songPlateHeight;
else
transitionScrollOffsetY = Easing.inQuad(1-transitionScrollScale) * -songPlateHeight;
end
if transitionAfterscrollScale < 0.02 then
transitionAfterscrollDataOverlayAlpha = math.min(1, transitionAfterscrollScale / 0.02)
else
transitionAfterscrollDataOverlayAlpha = 1;
end
-- Grade alpha
if transitionAfterscrollScale >= 0.03 and transitionAfterscrollScale < 0.033 then
transitionAfterscrollGradeAlpha = 0.5;
elseif transitionAfterscrollScale >= 0.04 then
transitionAfterscrollGradeAlpha = 1;
else
transitionAfterscrollGradeAlpha = 0;
end
-- Badge alpha
if transitionAfterscrollScale >= 0.032 and transitionAfterscrollScale < 0.035 then
transitionAfterscrollBadgeAlpha = 0.5;
elseif transitionAfterscrollScale >= 0.042 then
transitionAfterscrollBadgeAlpha = 1;
else
transitionAfterscrollBadgeAlpha = 0;
end
-- Song title alpha and pos
if transitionAfterscrollScale < 0.02 then
transitionAfterscrollTextSongTitle = Easing.outQuad(math.min(1, (transitionAfterscrollScale) / 0.02));
else
transitionAfterscrollTextSongTitle = 1
end
-- Song artist alpha and pos
if transitionAfterscrollScale < 0.03 then
transitionAfterscrollTextSongArtist = Easing.outQuad(math.min(1, (transitionAfterscrollScale) / 0.03));
else
transitionAfterscrollTextSongArtist = 1
end
-- Difficulties alpha
if transitionAfterscrollScale < 0.025 then
transitionAfterscrollDifficultiesAlpha = math.min(1, transitionAfterscrollScale / 0.025)
else
transitionAfterscrollDifficultiesAlpha = 1;
end
-- Jacket background alpha
if transitionAfterscrollScale < 0.05 or transitionAfterscrollScale >= 1 then
transitionAfterscrollJacketBgAlpha = 0;
elseif transitionAfterscrollScale >= 0.05 and transitionAfterscrollScale < 0.1 then
transitionAfterscrollJacketBgAlpha = math.min(1, (transitionAfterscrollScale-0.05) / 0.05);
elseif transitionAfterscrollScale >= 0.95 and transitionAfterscrollScale < 1 then
transitionAfterscrollJacketBgAlpha = math.min(1, 1-((transitionAfterscrollScale-0.95) / 0.05));
else
transitionAfterscrollJacketBgAlpha = 1;
end
2021-07-30 21:34:36 +02:00
end
2021-07-31 20:36:05 +02:00
render = function (deltaTime)
resetLayoutInformation();
2021-07-30 21:34:36 +02:00
tickTransitions(deltaTime);
gfx.Scale(scale, scale);
2021-07-25 20:10:06 +02:00
if not difficultyNumbers then
difficultyNumbers = load_number_image('diff_num')
end
2021-07-25 20:10:06 +02:00
drawBackground();
2021-07-25 20:10:06 +02:00
drawSongList()
2021-07-30 18:27:43 +02:00
drawCursor()
2021-07-25 20:10:06 +02:00
2021-07-31 20:36:05 +02:00
drawData()
2021-07-30 21:34:36 +02:00
2021-07-25 20:10:06 +02:00
gfx.BeginPath();
gfx.FontSize(18)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
2021-07-30 21:34:36 +02:00
local debugScrollingUp= "FALSE"
if scrollingUp then debugScrollingUp = "TRUE" end;
gfx.Text('DELTA: ' .. deltaTime .. ' // SELECTED_INDEX: ' .. selectedIndex .. ' // SELECTED_DIFF: ' .. selectedDifficulty .. ' // SCROLLING_UP: ' .. debugScrollingUp .. ' // AC_TS: ' .. transitionAfterscrollScale, 8, 8);
2021-07-25 20:10:06 +02:00
end
set_index = function(newIndex)
2021-07-30 21:34:36 +02:00
transitionScrollScale = 0;
transitionAfterscrollScale = 0;
2021-07-30 21:34:36 +02:00
scrollingUp = false;
if ((newIndex > selectedIndex and not (newIndex == #songwheel.songs and selectedIndex == 1)) or (newIndex == 1 and selectedIndex == #songwheel.songs)) then
scrollingUp = true;
end;
2021-07-30 22:06:08 +02:00
game.PlaySample('song_wheel/cursor_change.wav');
2021-07-30 21:34:36 +02:00
selectedIndex = newIndex;
2021-07-25 20:10:06 +02:00
end;
set_diff = function(newDiff)
selectedDifficulty = newDiff;
end;