444 lines
13 KiB
Lua
444 lines
13 KiB
Lua
local Easing = require('common.easing')
|
|
local Dim = require("common.dimensions")
|
|
local SongSelectHeader = require('components.headers.songSelectHeader')
|
|
local Footer = require('components.footer')
|
|
|
|
local defaultFolderBgImage = gfx.CreateSkinImage('song_select/filter_wheel/bg.png', 0)
|
|
local collectionFolderBgImage = gfx.CreateSkinImage('song_select/filter_wheel/col_bg.png', 0)
|
|
local subFolderBgImage = gfx.CreateSkinImage('song_select/filter_wheel/sub_bg.png', 0)
|
|
|
|
local scrollBarBackgroundImage = gfx.CreateSkinImage("song_select/scrollbar/bg.png", 1)
|
|
local scrollBarFillImage = gfx.CreateSkinImage("song_select/scrollbar/fill.png", 1)
|
|
|
|
local cursorImages = {
|
|
gfx.CreateSkinImage("song_select/cursor.png", 1), -- Effective rate or fallback
|
|
gfx.CreateSkinImage("song_select/cursor_exc.png", 1), -- Excessive rate
|
|
gfx.CreateSkinImage("song_select/cursor_perm.png", 1), -- Premissive rate
|
|
gfx.CreateSkinImage("song_select/cursor_blast.png", 1), -- Blastive rate
|
|
}
|
|
|
|
local ITEM_HEIGHT = 172
|
|
|
|
local specialFolders = {
|
|
{
|
|
keys = {
|
|
'SOUND VOLTEX BOOTH', 'SOUND VOLTEX I BOOTH', 'SDVX BOOTH',
|
|
'SOUND VOLTEX I', 'SDVX I',
|
|
'SOUND VOLTEX 1', 'SDVX 1', 'SDVX I BOOTH'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Booth.png', 0)
|
|
}, {
|
|
keys = {
|
|
'SOUND VOLTEX INFINITE INFECTION', 'SDVX INFINITE INFECTION',
|
|
'SOUND VOLTEX II INFINITE INFECTION',
|
|
'SOUND VOLTEX II', 'SDVX II', 'SOUND VOLTEX 2', 'SDVX 2',
|
|
'SDVX II INFINITE INFECTION'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Infinite Infection.png',
|
|
0)
|
|
},
|
|
{
|
|
keys = {
|
|
'SOUND VOLTEX GRAVITY WARS',
|
|
'SDVX GRAVITY WARS',
|
|
'SOUND VOLTEX III',
|
|
'SOUND VOLTEX III GRAVITY WARS',
|
|
'SDVX III',
|
|
'SOUND VOLTEX 3',
|
|
'SDVX 3',
|
|
'SDVX III GRAVITY WARS'
|
|
},
|
|
folderBg = gfx.CreateSkinImage('song_select/filter_wheel/special_folder_bgs/Gravity Wars.png', 0)
|
|
},
|
|
{
|
|
keys = {
|
|
'SOUND VOLTEX HEAVENLY HAVEN', 'SDVX HEAVENLY HAVEN', 'SOUND VOLTEX IV HEAVENLY HAVEN',
|
|
'SOUND VOLTEX IV', 'SDVX IV', 'SOUND VOLTEX 4', 'SDVX 4', 'SDVX IV HEAVENLY HAVEN'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Heavenly Haven.png', 0)
|
|
}, {
|
|
keys = {
|
|
'SOUND VOLTEX VIVID WAVE', 'SDVX VIVID WAVE', 'SOUND VOLTEX V',
|
|
'SDVX V', 'SOUND VOLTEX 5', 'SDVX 5', 'SDVX V VIVID WAVE', 'SOUND VOLTEX V VIVID WAVE'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Vivid Wave.png', 0)
|
|
}, {
|
|
keys = {
|
|
'SOUND VOLTEX EXCEED GEAR', 'SDVX EXCEED GEAR', 'SOUND VOLTEX VI',
|
|
'SDVX VI', 'SOUND VOLTEX 6', 'SDVX 6', 'SDVX VI EXCEED GEAR',
|
|
'SOUND VOLTEX VI EXCEED GEAR'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Exceed Gear.png', 0)
|
|
}, {
|
|
keys = {
|
|
'NAUTICA', 'nautica'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/Nautica.png', 0)
|
|
}, {
|
|
keys = {
|
|
'KSHOOTMANIA', 'K-SHOOT MANIA', 'K SHOOT MANIA'
|
|
},
|
|
folderBg = gfx.CreateSkinImage(
|
|
'song_select/filter_wheel/special_folder_bgs/KShootMania.png', 0)
|
|
},
|
|
}
|
|
|
|
-- AUDIO
|
|
game.LoadSkinSample('song_wheel/cursor_change.wav')
|
|
game.LoadSkinSample('filter_wheel/open_close.wav')
|
|
|
|
local resx, resy = game.GetResolution()
|
|
local desw, desh = 1080, 1920
|
|
local scale = 1
|
|
|
|
local isFilterWheelActive = false
|
|
local previousActiveState = false -- for open/close sounds
|
|
|
|
local selectionMode = 'folders'
|
|
local selectedFolder = 1
|
|
local selectedLevel = 1
|
|
|
|
local transitionScrollScale = 0
|
|
local transitionScrollOffsetY = 0
|
|
local scrollingUp = false
|
|
|
|
local transitionLeaveScale = 1
|
|
local transitionLeaveReappearTimer = 0
|
|
local TRANSITION_LEAVE_DURATION = 0.1
|
|
|
|
-- Window variables
|
|
local resX, resY
|
|
|
|
-- Aspect Ratios
|
|
local landscapeWidescreenRatio = 16 / 9
|
|
local landscapeStandardRatio = 4 / 3
|
|
local portraitWidescreenRatio = 9 / 16
|
|
|
|
-- Portrait sizes
|
|
local fullX, fullY
|
|
|
|
local resolutionChange = function(x, y)
|
|
resX = x
|
|
resY = y
|
|
fullX = portraitWidescreenRatio * y
|
|
fullY = y
|
|
end
|
|
|
|
function resetLayoutInformation()
|
|
resx, resy = game.GetResolution()
|
|
scale = resx / desw
|
|
end
|
|
|
|
function getCorrectedIndex(from, offset)
|
|
local total = 1
|
|
if selectionMode == 'folders' then
|
|
total = #filters.folder
|
|
else
|
|
total = #filters.level
|
|
end
|
|
|
|
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 getFolderData(folderLabel)
|
|
local folderType = 'unknown'
|
|
local isSpecial = false
|
|
local folderBgImage = defaultFolderBgImage
|
|
|
|
if not folderLabel then
|
|
return {
|
|
type = folderType,
|
|
label = 'UNKNOWN',
|
|
bgImage = folderBgImage,
|
|
isSpecial = isSpecial
|
|
}
|
|
end
|
|
|
|
|
|
if selectionMode == 'levels' then
|
|
folderBgImage = subFolderBgImage
|
|
end
|
|
|
|
|
|
if (string.find(folderLabel, 'Folder: ')) then
|
|
folderType = 'folder'
|
|
folderLabel = folderLabel:gsub('Folder: ', '') -- Delete default prefix
|
|
elseif (string.find(folderLabel, 'Collection: ')) then
|
|
folderType = 'collection'
|
|
folderLabel = folderLabel:gsub('Collection: ', '') -- Delete default prefix
|
|
folderBgImage = collectionFolderBgImage
|
|
elseif (string.find(folderLabel, 'Level: ')) then
|
|
folderType = 'level'
|
|
folderLabel = folderLabel:gsub('Level: ', '') -- Delete default prefix
|
|
folderLabel = 'LEVEL ' .. folderLabel
|
|
end
|
|
|
|
local labelMatcherString = string.upper(folderLabel)
|
|
|
|
for i, specialFolder in ipairs(specialFolders) do
|
|
for i, specialFolderKey in ipairs(specialFolder.keys) do
|
|
if (specialFolderKey == labelMatcherString) then
|
|
folderBgImage = specialFolder.folderBg
|
|
isSpecial = true
|
|
end
|
|
end
|
|
end
|
|
|
|
return {
|
|
type = folderType,
|
|
label = folderLabel,
|
|
bgImage = folderBgImage,
|
|
isSpecial = isSpecial
|
|
}
|
|
end
|
|
|
|
function drawFolder(label, y)
|
|
if (not label) then return end
|
|
|
|
gfx.LoadSkinFont('NotoSans-Regular.ttf')
|
|
|
|
local x = desw / 2 + 0
|
|
|
|
local folderData = getFolderData(label)
|
|
|
|
-- Draw the bg
|
|
gfx.BeginPath()
|
|
gfx.ImageRect(x, y, 630 * 0.86, 200 * 0.86, folderData.bgImage, 1, 0)
|
|
|
|
-- Draw the folder label, but only if the folder is not special
|
|
if (not folderData.isSpecial) then
|
|
gfx.BeginPath()
|
|
gfx.FontSize(38)
|
|
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
|
gfx.FillColor(255, 255, 255, 255)
|
|
gfx.Text(folderData.label, x + 18, y + 72)
|
|
end
|
|
end
|
|
|
|
function drawFolderList()
|
|
gfx.GlobalAlpha(1-transitionLeaveScale)
|
|
|
|
local numOfItemsAround = 7
|
|
local selectedIndex = 1
|
|
local folderList = filters.folder
|
|
|
|
if selectionMode == 'folders' then
|
|
selectedIndex = selectedFolder
|
|
folderList = filters.folder
|
|
else
|
|
selectedIndex = selectedLevel
|
|
folderList = filters.level
|
|
end
|
|
|
|
local yOffset = transitionScrollOffsetY
|
|
|
|
local i = 1
|
|
while (i <= numOfItemsAround) do
|
|
local index = getCorrectedIndex(selectedIndex, -i)
|
|
drawFolder(folderList[index],
|
|
desh / 2 - ITEM_HEIGHT / 2 - ITEM_HEIGHT * i + yOffset)
|
|
i = i + 1
|
|
end
|
|
|
|
-- Draw the selected song
|
|
drawFolder(folderList[selectedIndex], desh / 2 - ITEM_HEIGHT / 2 + yOffset)
|
|
|
|
i = 1
|
|
while (i <= numOfItemsAround) do
|
|
local index = getCorrectedIndex(selectedIndex, i)
|
|
drawFolder(folderList[index],
|
|
desh / 2 - ITEM_HEIGHT / 2 + ITEM_HEIGHT * i + yOffset)
|
|
i = i + 1
|
|
end
|
|
|
|
gfx.GlobalAlpha(1)
|
|
end
|
|
|
|
function drawCursor()
|
|
if not isFilterWheelActive or transitionLeaveScale ~= 0 then return end
|
|
|
|
gfx.BeginPath()
|
|
|
|
local cursorImageIndex = game.GetSkinSetting('_gaugeType')
|
|
local cursorImage = cursorImages[cursorImageIndex or 1]
|
|
|
|
gfx.ImageRect(desw / 2 - 14, desh / 2 - 213 / 2, 555, 213, cursorImage, 1, 0)
|
|
end
|
|
|
|
function drawScrollbar()
|
|
if not isFilterWheelActive or transitionLeaveScale ~= 0 then return end
|
|
|
|
gfx.BeginPath()
|
|
local resize = 0.85
|
|
local lw, lh = gfx.ImageSize(scrollBarBackgroundImage)
|
|
local lw = lw * resize
|
|
local lh = lh * resize
|
|
local xPos = desw-20
|
|
local backgroundYPos = desh/2 - lh/2
|
|
gfx.ImageRect(xPos, backgroundYPos, lw, lh, scrollBarBackgroundImage, 1, 0)
|
|
|
|
gfx.BeginPath()
|
|
local sw, sh = gfx.ImageSize(scrollBarFillImage)
|
|
local sw = sw * resize
|
|
local sh = sh * resize
|
|
local fillXPos = xPos - 6
|
|
|
|
-- figure out index and total
|
|
local index = 1
|
|
local total = 1
|
|
if selectionMode == 'folders' then
|
|
index = selectedFolder
|
|
total = #filters.folder
|
|
else
|
|
index = selectedLevel
|
|
total = #filters.level
|
|
end
|
|
|
|
|
|
local minScrollYPos = backgroundYPos
|
|
local maxScrollYPos = backgroundYPos + lh - sh
|
|
local scrollStep = (maxScrollYPos - minScrollYPos) / (total - 1)
|
|
local scrollbarYOffset = (index - 1) * scrollStep
|
|
local scrollbarYPos = minScrollYPos + scrollbarYOffset
|
|
|
|
gfx.ImageRect(fillXPos, scrollbarYPos, sw, sh, scrollBarFillImage, 1, 0)
|
|
end
|
|
|
|
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 scrollingUp then
|
|
transitionScrollOffsetY = Easing.inQuad(1 - transitionScrollScale) *
|
|
ITEM_HEIGHT
|
|
else
|
|
transitionScrollOffsetY = Easing.inQuad(1 - transitionScrollScale) *
|
|
-ITEM_HEIGHT
|
|
end
|
|
|
|
-- LEAVE TRANSITION
|
|
if (not isFilterWheelActive) then
|
|
if transitionLeaveScale < 1 then
|
|
transitionLeaveScale = transitionLeaveScale + deltaTime / TRANSITION_LEAVE_DURATION -- transition should last for that time in seconds
|
|
else
|
|
transitionLeaveScale = 1
|
|
end
|
|
transitionLeaveReappearTimer = 1
|
|
else
|
|
if (transitionLeaveReappearTimer == 1) then
|
|
-- This stuff happens right after filterwheel is deactivated
|
|
end
|
|
|
|
transitionLeaveReappearTimer = transitionLeaveReappearTimer - deltaTime / (TRANSITION_LEAVE_DURATION + 0.05) -- same reasoning as in the songwheel
|
|
|
|
if (transitionLeaveReappearTimer <= 0) then
|
|
transitionLeaveScale = 0
|
|
transitionLeaveReappearTimer = 0
|
|
end
|
|
end
|
|
end
|
|
|
|
function drawFilterWheelContent(deltatime)
|
|
tickTransitions(deltatime)
|
|
drawFolderList()
|
|
end
|
|
|
|
local drawFilterWheel = function (deltaTime)
|
|
|
|
drawFilterWheelContent(deltaTime)
|
|
drawCursor()
|
|
drawScrollbar()
|
|
|
|
if (game.GetSkinSetting('_currentScreen') == 'songwheel') then
|
|
SongSelectHeader.draw(deltaTime)
|
|
Footer.draw(deltaTime)
|
|
end
|
|
|
|
if (isFilterWheelActive ~= previousActiveState) then
|
|
game.PlaySample('filter_wheel/open_close.wav')
|
|
previousActiveState = isFilterWheelActive
|
|
end
|
|
|
|
-- Debug text
|
|
gfx.BeginPath()
|
|
gfx.FontSize(18)
|
|
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
|
gfx.FillColor(255, 255, 255, 255)
|
|
if game.GetSkinSetting('debug_showInformation') then
|
|
gfx.Text('S_M: ' .. selectionMode .. ' // S_F: ' .. selectedFolder ..
|
|
' // S_L: ' .. selectedLevel .. ' // L_TS: ' ..
|
|
transitionLeaveScale .. ' // L_TRT: ' .. transitionLeaveReappearTimer, 8, 1870)
|
|
end
|
|
end
|
|
|
|
render = function(deltaTime, shown)
|
|
isFilterWheelActive = shown
|
|
|
|
if not shown then
|
|
game.SetSkinSetting('_songWheelOverlayActive', 0)
|
|
else
|
|
game.SetSkinSetting('_songWheelOverlayActive', 1)
|
|
end
|
|
|
|
game.SetSkinSetting('_songWheelActiveFolderLabel', getFolderData(filters.folder[selectedFolder]).label)
|
|
game.SetSkinSetting('_songWheelActiveSubFolderLabel', getFolderData(filters.level[selectedLevel]).label)
|
|
|
|
Dim.updateResolution()
|
|
|
|
Dim.transformToScreenSpace()
|
|
|
|
gfx.GlobalAlpha(1)
|
|
|
|
drawFilterWheel(deltaTime)
|
|
end
|
|
|
|
set_selection = function(newIndex, isFolder)
|
|
local oldIndex = 1
|
|
local total = 1
|
|
if isFolder then
|
|
oldIndex = selectedFolder
|
|
selectedFolder = newIndex
|
|
total = #filters.folder
|
|
else
|
|
oldIndex = selectedLevel
|
|
selectedLevel = newIndex
|
|
total = #filters.level
|
|
end
|
|
|
|
transitionScrollScale = 0
|
|
|
|
scrollingUp = false
|
|
if ((newIndex > oldIndex and not (newIndex == total and oldIndex == 1)) or
|
|
(newIndex == 1 and oldIndex == total)) then scrollingUp = true end
|
|
|
|
game.PlaySample('song_wheel/cursor_change.wav')
|
|
end
|
|
|
|
set_mode = function(isFolder)
|
|
if isFolder then
|
|
selectionMode = 'folders'
|
|
else
|
|
selectionMode = 'levels'
|
|
end
|
|
end
|