Merge pull request 'cleanup' (#2) from cleanup into master
Reviewed-on: #2
|
@ -0,0 +1 @@
|
|||
* text=auto
|
|
@ -1,7 +1,15 @@
|
|||
# IDE files
|
||||
.vscode
|
||||
|
||||
# secret(?) assets
|
||||
_asset
|
||||
song-assets
|
||||
skin-assets
|
||||
|
||||
# generated skin files
|
||||
nautica.json
|
||||
skin.cfg
|
||||
.vscode
|
||||
/textures/crew/
|
||||
|
||||
# any crew that's not the default one, we do not package crew
|
||||
textures/crew/anim/*
|
||||
!textures/crew/anim/nothing
|
74
README.md
|
@ -1,37 +1,37 @@
|
|||
# ExperimentalGear skin for USC
|
||||
|
||||
Project Starter: GSK Bladez
|
||||
|
||||
## Coding
|
||||
- [REDACTED]
|
||||
- Hersi
|
||||
- RealFD
|
||||
- fdigl
|
||||
- Hoshikara
|
||||
- GSK Bladez
|
||||
- Local
|
||||
|
||||
## Graphics
|
||||
- GSK Bladez
|
||||
- Neardayo
|
||||
- YellowBird
|
||||
- Dengekiko
|
||||
|
||||
## Translation
|
||||
- GSK Bladez
|
||||
- Neardayo
|
||||
- RealFD
|
||||
|
||||
## Misc. Help
|
||||
- Neardayo
|
||||
- DDX
|
||||
- GM*DEO
|
||||
|
||||
## Beta Testing
|
||||
- Gam
|
||||
- TealStar
|
||||
- Dengikiko
|
||||
- Adamyes
|
||||
- Gio
|
||||
- Mattadome
|
||||
|
||||
# ExperimentalGear skin for USC
|
||||
|
||||
Project Starter: GSK Bladez
|
||||
|
||||
## Coding
|
||||
- [REDACTED]
|
||||
- Hersi
|
||||
- RealFD
|
||||
- fdigl
|
||||
- Hoshikara
|
||||
- GSK Bladez
|
||||
- Local
|
||||
|
||||
## Graphics
|
||||
- GSK Bladez
|
||||
- Neardayo
|
||||
- YellowBird
|
||||
- Dengekiko
|
||||
|
||||
## Translation
|
||||
- GSK Bladez
|
||||
- Neardayo
|
||||
- RealFD
|
||||
|
||||
## Misc. Help
|
||||
- Neardayo
|
||||
- DDX
|
||||
- GM*DEO
|
||||
|
||||
## Beta Testing
|
||||
- Gam
|
||||
- TealStar
|
||||
- Dengikiko
|
||||
- Adamyes
|
||||
- Gio
|
||||
- Mattadome
|
||||
|
||||
|
|
|
@ -1,86 +1,86 @@
|
|||
local newFormatTemplate = {
|
||||
{
|
||||
Bg={
|
||||
Base={
|
||||
Tex={{"",""}},
|
||||
Tilt=false,
|
||||
OffsetY=0.17,
|
||||
Anim=true,
|
||||
ScaleSoft=false,
|
||||
ClampTiling=false,
|
||||
},
|
||||
Overlay={ -- is meant to be displayed overtop Base
|
||||
Tex="",
|
||||
Float=true,
|
||||
FloatFactor=2.0,
|
||||
OffsetY=0.12,
|
||||
FlashEffect=true,
|
||||
Tilt=true,
|
||||
},
|
||||
Layer={
|
||||
Tex={{"",""}},
|
||||
ScaleHard=false,
|
||||
Brighten=0.6,
|
||||
},
|
||||
u={
|
||||
Pivot=0.36
|
||||
},
|
||||
},
|
||||
Center={
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Scale=2.8,
|
||||
Pulse=true,
|
||||
Float=true,
|
||||
FloatFactor=0.5,
|
||||
FloatXFactor=0,
|
||||
FloatRotationFactor=0,
|
||||
FadeEffect=true,
|
||||
Tilt=false,
|
||||
Anim=true,
|
||||
OffsetY=-0.1,
|
||||
Glow=false,
|
||||
Rotate=false,
|
||||
},
|
||||
LayerEffect={
|
||||
Tex="",
|
||||
Fade=true,
|
||||
Rotate=true,
|
||||
RotateSpeed=-2.0,
|
||||
DodgeBlend=true,
|
||||
Glow=true,
|
||||
Alpha=0.7,
|
||||
Scale=0.5,
|
||||
},
|
||||
},
|
||||
Tunnel={ -- todo: give option to do sth with blend mode
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Stretch=0.3,
|
||||
ScaleX=0.8,
|
||||
ScaleY=0.9,
|
||||
Fog=10.0,
|
||||
FlashEffect=true,
|
||||
VortexEffect=false,
|
||||
VortexFactor=1.0,
|
||||
DodgeBlend=false
|
||||
}
|
||||
},
|
||||
Particle={
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Speed=0.3,
|
||||
OffsetY=-0.02,
|
||||
Amount=3,
|
||||
Scale=1.0,
|
||||
ExtraRotation=0.125
|
||||
},
|
||||
},
|
||||
luaParticleEffect={
|
||||
particles={
|
||||
{"star-particle.png", 32}
|
||||
}
|
||||
},
|
||||
speed=0.6,
|
||||
}
|
||||
local newFormatTemplate = {
|
||||
{
|
||||
Bg={
|
||||
Base={
|
||||
Tex={{"",""}},
|
||||
Tilt=false,
|
||||
OffsetY=0.17,
|
||||
Anim=true,
|
||||
ScaleSoft=false,
|
||||
ClampTiling=false,
|
||||
},
|
||||
Overlay={ -- is meant to be displayed overtop Base
|
||||
Tex="",
|
||||
Float=true,
|
||||
FloatFactor=2.0,
|
||||
OffsetY=0.12,
|
||||
FlashEffect=true,
|
||||
Tilt=true,
|
||||
},
|
||||
Layer={
|
||||
Tex={{"",""}},
|
||||
ScaleHard=false,
|
||||
Brighten=0.6,
|
||||
},
|
||||
u={
|
||||
Pivot=0.36
|
||||
},
|
||||
},
|
||||
Center={
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Scale=2.8,
|
||||
Pulse=true,
|
||||
Float=true,
|
||||
FloatFactor=0.5,
|
||||
FloatXFactor=0,
|
||||
FloatRotationFactor=0,
|
||||
FadeEffect=true,
|
||||
Tilt=false,
|
||||
Anim=true,
|
||||
OffsetY=-0.1,
|
||||
Glow=false,
|
||||
Rotate=false,
|
||||
},
|
||||
LayerEffect={
|
||||
Tex="",
|
||||
Fade=true,
|
||||
Rotate=true,
|
||||
RotateSpeed=-2.0,
|
||||
DodgeBlend=true,
|
||||
Glow=true,
|
||||
Alpha=0.7,
|
||||
Scale=0.5,
|
||||
},
|
||||
},
|
||||
Tunnel={ -- todo: give option to do sth with blend mode
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Stretch=0.3,
|
||||
ScaleX=0.8,
|
||||
ScaleY=0.9,
|
||||
Fog=10.0,
|
||||
FlashEffect=true,
|
||||
VortexEffect=false,
|
||||
VortexFactor=1.0,
|
||||
DodgeBlend=false
|
||||
}
|
||||
},
|
||||
Particle={
|
||||
Tex={{"",""}},
|
||||
u={
|
||||
Speed=0.3,
|
||||
OffsetY=-0.02,
|
||||
Amount=3,
|
||||
Scale=1.0,
|
||||
ExtraRotation=0.125
|
||||
},
|
||||
},
|
||||
luaParticleEffect={
|
||||
particles={
|
||||
{"star-particle.png", 32}
|
||||
}
|
||||
},
|
||||
speed=0.6,
|
||||
}
|
||||
}
|
|
@ -1,68 +1,68 @@
|
|||
{
|
||||
"User information": {"type": "label"},
|
||||
"username": {
|
||||
"type": "text",
|
||||
"label": "Username (max 8 characters)",
|
||||
"default": "GUEST"
|
||||
},
|
||||
|
||||
"separator_a": {},
|
||||
|
||||
"MSG": {
|
||||
"type": "text",
|
||||
"label": "Message (max 8 characters)",
|
||||
"default": "Hellooooooo"
|
||||
},
|
||||
|
||||
"separator_b": {},
|
||||
"Animations": {"type": "label"},
|
||||
|
||||
"animations_affectWithBPM": {
|
||||
"type": "bool",
|
||||
"label": "Affect speed of some animations with the current song's BPM",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_c": {},
|
||||
|
||||
"Crew": { "type": "label" },
|
||||
"single_idol": {
|
||||
"label": "!!!ALWAYS MATCH THE NAME!!!",
|
||||
"type": "text",
|
||||
"default": "nothing"
|
||||
},
|
||||
|
||||
"words": {
|
||||
"type": "selection",
|
||||
"label": "Language",
|
||||
"default": "EN",
|
||||
"values": ["EN", "DE", "SK", "HU", "test2"]
|
||||
},
|
||||
|
||||
"separator_d": {},
|
||||
"Audio": { "type": "label" },
|
||||
|
||||
"audio_systemVoice": {
|
||||
"label": "Turn on Rasis",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_e": {},
|
||||
"Gameplay": { "type": "label" },
|
||||
|
||||
"gameplay_ucDifferentColor": {
|
||||
"label": "Use different colors for UC and PUC chain numbers",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_f": {},
|
||||
"Debug": { "type": "label" },
|
||||
|
||||
"debug_showInformation": {
|
||||
"label": "Show debug information (sometimes in the middle of the screen when you're playing)",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
{
|
||||
"User information": {"type": "label"},
|
||||
"username": {
|
||||
"type": "text",
|
||||
"label": "Username (max 8 characters)",
|
||||
"default": "GUEST"
|
||||
},
|
||||
|
||||
"separator_a": {},
|
||||
|
||||
"MSG": {
|
||||
"type": "text",
|
||||
"label": "Message (max 8 characters)",
|
||||
"default": "Hellooooooo"
|
||||
},
|
||||
|
||||
"separator_b": {},
|
||||
"Animations": {"type": "label"},
|
||||
|
||||
"animations_affectWithBPM": {
|
||||
"type": "bool",
|
||||
"label": "Affect speed of some animations with the current song's BPM",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_c": {},
|
||||
|
||||
"Crew": { "type": "label" },
|
||||
"single_idol": {
|
||||
"label": "!!!ALWAYS MATCH THE NAME!!!",
|
||||
"type": "text",
|
||||
"default": "nothing"
|
||||
},
|
||||
|
||||
"words": {
|
||||
"type": "selection",
|
||||
"label": "Language",
|
||||
"default": "EN",
|
||||
"values": ["EN", "DE", "SK", "test2"]
|
||||
},
|
||||
|
||||
"separator_d": {},
|
||||
"Audio": { "type": "label" },
|
||||
|
||||
"audio_systemVoice": {
|
||||
"label": "Turn on Rasis",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_e": {},
|
||||
"Gameplay": { "type": "label" },
|
||||
|
||||
"gameplay_ucDifferentColor": {
|
||||
"label": "Use different colors for UC and PUC chain numbers",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"separator_f": {},
|
||||
"Debug": { "type": "label" },
|
||||
|
||||
"debug_showInformation": {
|
||||
"label": "Show debug information (sometimes in the middle of the screen when you're playing)",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,302 +1,302 @@
|
|||
local Easing = require("common.easing");
|
||||
local Footer = require("components.footer");
|
||||
local DiffRectangle = require('components.diff_rectangle');
|
||||
local common = require('common.common');
|
||||
local Numbers = require('common.numbers')
|
||||
|
||||
local VolforceWindow = require("components.volforceWindow")
|
||||
|
||||
-- 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 desw = 1080
|
||||
local desh = 1920
|
||||
|
||||
local function resolutionChange(x, y)
|
||||
resX = x
|
||||
resY = y
|
||||
fullX = portraitWidescreenRatio * y
|
||||
fullY = y
|
||||
end
|
||||
|
||||
local bgSfxPlayed = false;
|
||||
|
||||
local BAR_ALPHA = 191;
|
||||
local HEADER_HEIGHT = 100
|
||||
|
||||
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
|
||||
local resultBgImage = gfx.CreateSkinImage("challenge_result/bg.png", 0);
|
||||
local playerInfoOverlayBgImage = gfx.CreateSkinImage("challenge_result/player_info_overlay_bg.png", 0);
|
||||
|
||||
local headerTitleImage = gfx.CreateSkinImage("challenge_result/header/title.png", 0);
|
||||
|
||||
local username = game.GetSkinSetting("username");
|
||||
local appealCardImage = gfx.CreateSkinImage("crew/appeal_card.png", 0);
|
||||
local danBadgeImage = gfx.CreateSkinImage("dan/inf.png", 0);
|
||||
local crewImage = gfx.CreateSkinImage("crew/portrait.png", 0);
|
||||
|
||||
local notchesImage = gfx.CreateSkinImage("challenge_result/notches.png", 0);
|
||||
local trackBarsImage = gfx.CreateSkinImage("challenge_result/track_bars.png", 0);
|
||||
|
||||
local completionFailImage = gfx.CreateSkinImage("challenge_result/pass_states/fail.png", 0);
|
||||
local completionPassImage = gfx.CreateSkinImage("challenge_result/pass_states/pass.png", 0);
|
||||
|
||||
local irHeartbeatRequested = false;
|
||||
local IRserverName = "";
|
||||
|
||||
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),
|
||||
gfx.CreateSkinImage("song_select/medal/hard.png", 1),
|
||||
gfx.CreateSkinImage("song_select/medal/uc.png", 1),
|
||||
gfx.CreateSkinImage("song_select/medal/puc.png", 1),
|
||||
}
|
||||
|
||||
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 percRequired = 1;
|
||||
local percGet = 0;
|
||||
|
||||
-- AUDIO
|
||||
game.LoadSkinSample("challenge_result.wav")
|
||||
|
||||
function resetLayoutInformation()
|
||||
resx, resy = game.GetResolution()
|
||||
desw = 1080
|
||||
desh = 1920
|
||||
scale = resx / desw
|
||||
end
|
||||
|
||||
local function handleSfx()
|
||||
if not bgSfxPlayed then
|
||||
common.stopMusic();
|
||||
game.PlaySample("challenge_result.wav", true)
|
||||
bgSfxPlayed = true
|
||||
end
|
||||
if game.GetButton(game.BUTTON_STA) then
|
||||
game.StopSample("challenge_result.wav")
|
||||
end
|
||||
if game.GetButton(game.BUTTON_BCK) then
|
||||
game.StopSample("challenge_result.wav")
|
||||
end
|
||||
end
|
||||
|
||||
function drawBackground()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, desw, desh, resultBgImage, 1, 0);
|
||||
end
|
||||
|
||||
function drawHeader()
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0, 0, 0, BAR_ALPHA);
|
||||
gfx.Rect(0, 0, desw, HEADER_HEIGHT);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath()
|
||||
|
||||
gfx.ImageRect(desw / 2 - 209, HEADER_HEIGHT / 2 - 52, 419, 105, headerTitleImage, 1, 0)
|
||||
end
|
||||
|
||||
function drawPlayerInfo()
|
||||
-- Draw crew
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(460, 215, 522, 362, crewImage, 1, 0);
|
||||
|
||||
-- Draw the info bg
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(300, 352, 374 * 0.85, 222 * 0.85, playerInfoOverlayBgImage, 1, 0);
|
||||
|
||||
-- Draw appeal card
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(145, 364, 103 * 1.25, 132 * 1.25, appealCardImage, 1, 0);
|
||||
|
||||
-- Draw description
|
||||
gfx.FontSize(28)
|
||||
gfx.LoadSkinFont("Digital-Serial-Bold.ttf")
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.Text("Hellooooooo", 310, 370);
|
||||
|
||||
-- Draw username
|
||||
gfx.FontSize(40)
|
||||
gfx.Text(username, 310, 413);
|
||||
|
||||
-- Draw IR server name
|
||||
gfx.FontSize(28)
|
||||
gfx.Text(IRserverName, 310, 453);
|
||||
|
||||
-- Draw dan badge
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(311, 490, 107 * 1.25, 29 * 1.25, danBadgeImage, 1, 0);
|
||||
end
|
||||
|
||||
local scoreNumber = Numbers.load_number_image("score_num");
|
||||
|
||||
function drawChartResult(deltaTime, x, y, chartResult)
|
||||
gfx.Save()
|
||||
gfx.LoadSkinFont('NotoSans-Regular.ttf')
|
||||
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.BeginPath()
|
||||
gfx.GlobalAlpha(1);
|
||||
gfx.Text(chartResult.title, x+160,y+32);
|
||||
|
||||
DiffRectangle.render(deltaTime, x+287.5, y+67, 0.85, chartResult.difficulty, chartResult.level)
|
||||
|
||||
local score = chartResult.score or 0;
|
||||
|
||||
Numbers.draw_number(x + 500, y+80, 1.0, math.floor(score / 10000), 4, scoreNumber, true, 0.30, 1.12)
|
||||
Numbers.draw_number(x + 655, y+85, 1.0, score, 4, scoreNumber, true, 0.22, 1.12)
|
||||
|
||||
|
||||
local gradeImageKey = string.gsub(chartResult.grade, '+', '_P');
|
||||
local gradeImage = gradeImages[gradeImageKey] or gradeImages.D
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+800, y+12, 79, 79, gradeImage, 1, 0)
|
||||
|
||||
|
||||
if chartResult.badge then
|
||||
local badgeImage = badgeImages[chartResult.badge+1];
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+900, y+16, 79*1.05, 69*1.05, badgeImage, 1, 0)
|
||||
end
|
||||
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function drawScorePanelContent(deltaTime)
|
||||
-- game.Log("Drawing scores...", game.LOGGER_INFO) -- debug
|
||||
for i, chart in ipairs(result.charts) do
|
||||
-- if chart.score == nil then
|
||||
-- game.Log("Score does not exist? Quitting loop...", game.LOGGER_WARNING)
|
||||
-- break
|
||||
-- end
|
||||
|
||||
drawChartResult(deltaTime, 0, 836+(165*(i-1)), chart);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function drawDecorations()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(118, 846.5, 43*0.855, 429*0.855, notchesImage, 1, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(400, 807, 367*0.857, 429*0.857, trackBarsImage, 1, 0)
|
||||
end
|
||||
|
||||
function drawCompletion()
|
||||
local completitionImage = completionFailImage;
|
||||
if result.passed then
|
||||
completitionImage = completionPassImage;
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(63, 1331, 766*0.85, 130*0.85, completitionImage, 1, 0)
|
||||
|
||||
Numbers.draw_number(925, 1370, 1.0, percGet, 3, scoreNumber, true, 0.3, 1.12)
|
||||
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.Rect(741, 1402, 278*math.min(1, percGet / percRequired), 6);
|
||||
gfx.FillColor(255, 128, 0, 255);
|
||||
gfx.Fill()
|
||||
end
|
||||
|
||||
|
||||
function result_set()
|
||||
local reqTextWords = common.splitString(result.requirement_text, ' ');
|
||||
for index, word in ipairs(reqTextWords) do
|
||||
if string.find(word, '%%') ~= nil then -- %% = %, because % is an escape char
|
||||
local percNumber = tonumber(string.gsub(word, '%%', ''), 10)
|
||||
percRequired = percNumber;
|
||||
end
|
||||
end
|
||||
|
||||
game.Log(percRequired, game.LOGGER_ERROR);
|
||||
|
||||
local a = 0;
|
||||
for i, chart in ipairs(result.charts) do
|
||||
a = a + chart.percent;
|
||||
game.Log('#' .. i .. ' got ' .. chart.percent .. '% // ACC at ' .. a, game.LOGGER_ERROR);
|
||||
end
|
||||
percGet = a / #result.charts;
|
||||
end
|
||||
|
||||
|
||||
local IR_HeartbeatResponse = function(res)
|
||||
if res.statusCode == IRData.States.Success then
|
||||
IRserverName = res.body.serverName .. " " .. res.body.irVersion;
|
||||
else
|
||||
game.Log("Can't connect to IR!", game.LOGGER_WARNING)
|
||||
end
|
||||
end
|
||||
|
||||
local IR_Handle = function()
|
||||
if not irHeartbeatRequested then
|
||||
IR.Heartbeat(IR_HeartbeatResponse)
|
||||
irHeartbeatRequested = true;
|
||||
end
|
||||
end
|
||||
|
||||
local function drawResultScreen(x, y, w, h, deltaTime)
|
||||
gfx.BeginPath()
|
||||
|
||||
gfx.Translate(x, y);
|
||||
gfx.Scale(w / 1080, h / 1920);
|
||||
gfx.Scissor(0, 0, 1080, 1920);
|
||||
|
||||
handleSfx()
|
||||
IR_Handle()
|
||||
|
||||
drawBackground()
|
||||
|
||||
drawDecorations()
|
||||
|
||||
drawPlayerInfo()
|
||||
|
||||
drawScorePanelContent(deltaTime)
|
||||
|
||||
drawCompletion()
|
||||
|
||||
drawHeader()
|
||||
Footer.draw(deltaTime);
|
||||
|
||||
gfx.ResetTransform()
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
-- detect resolution change
|
||||
local resx, resy = game.GetResolution()
|
||||
if resx ~= resX or resy ~= resY then
|
||||
resolutionChange(resx, resy)
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
|
||||
gfx.Rect(0, 0, resX, resY)
|
||||
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
|
||||
gfx.Fill()
|
||||
|
||||
drawResultScreen((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
|
||||
end
|
||||
local Easing = require("common.easing");
|
||||
local Footer = require("components.footer");
|
||||
local DiffRectangle = require('components.diff_rectangle');
|
||||
local common = require('common.common');
|
||||
local Numbers = require('common.numbers')
|
||||
|
||||
local VolforceWindow = require("components.volforceWindow")
|
||||
|
||||
-- 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 desw = 1080
|
||||
local desh = 1920
|
||||
|
||||
local function resolutionChange(x, y)
|
||||
resX = x
|
||||
resY = y
|
||||
fullX = portraitWidescreenRatio * y
|
||||
fullY = y
|
||||
end
|
||||
|
||||
local bgSfxPlayed = false;
|
||||
|
||||
local BAR_ALPHA = 191;
|
||||
local HEADER_HEIGHT = 100
|
||||
|
||||
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
|
||||
local resultBgImage = gfx.CreateSkinImage("challenge_result/bg.png", 0);
|
||||
local playerInfoOverlayBgImage = gfx.CreateSkinImage("challenge_result/player_info_overlay_bg.png", 0);
|
||||
|
||||
local headerTitleImage = gfx.CreateSkinImage("challenge_result/header/title.png", 0);
|
||||
|
||||
local username = game.GetSkinSetting("username");
|
||||
local appealCardImage = gfx.CreateSkinImage("crew/appeal_card.png", 0);
|
||||
local danBadgeImage = gfx.CreateSkinImage("dan/inf.png", 0);
|
||||
local crewImage = gfx.CreateSkinImage("crew/portrait.png", 0);
|
||||
|
||||
local notchesImage = gfx.CreateSkinImage("challenge_result/notches.png", 0);
|
||||
local trackBarsImage = gfx.CreateSkinImage("challenge_result/track_bars.png", 0);
|
||||
|
||||
local completionFailImage = gfx.CreateSkinImage("challenge_result/pass_states/fail.png", 0);
|
||||
local completionPassImage = gfx.CreateSkinImage("challenge_result/pass_states/pass.png", 0);
|
||||
|
||||
local irHeartbeatRequested = false;
|
||||
local IRserverName = "";
|
||||
|
||||
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),
|
||||
gfx.CreateSkinImage("song_select/medal/hard.png", 1),
|
||||
gfx.CreateSkinImage("song_select/medal/uc.png", 1),
|
||||
gfx.CreateSkinImage("song_select/medal/puc.png", 1),
|
||||
}
|
||||
|
||||
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 percRequired = 1;
|
||||
local percGet = 0;
|
||||
|
||||
-- AUDIO
|
||||
game.LoadSkinSample("challenge_result.wav")
|
||||
|
||||
function resetLayoutInformation()
|
||||
resx, resy = game.GetResolution()
|
||||
desw = 1080
|
||||
desh = 1920
|
||||
scale = resx / desw
|
||||
end
|
||||
|
||||
local function handleSfx()
|
||||
if not bgSfxPlayed then
|
||||
common.stopMusic();
|
||||
game.PlaySample("challenge_result.wav", true)
|
||||
bgSfxPlayed = true
|
||||
end
|
||||
if game.GetButton(game.BUTTON_STA) then
|
||||
game.StopSample("challenge_result.wav")
|
||||
end
|
||||
if game.GetButton(game.BUTTON_BCK) then
|
||||
game.StopSample("challenge_result.wav")
|
||||
end
|
||||
end
|
||||
|
||||
function drawBackground()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, desw, desh, resultBgImage, 1, 0);
|
||||
end
|
||||
|
||||
function drawHeader()
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0, 0, 0, BAR_ALPHA);
|
||||
gfx.Rect(0, 0, desw, HEADER_HEIGHT);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath()
|
||||
|
||||
gfx.ImageRect(desw / 2 - 209, HEADER_HEIGHT / 2 - 52, 419, 105, headerTitleImage, 1, 0)
|
||||
end
|
||||
|
||||
function drawPlayerInfo()
|
||||
-- Draw crew
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(460, 215, 522, 362, crewImage, 1, 0);
|
||||
|
||||
-- Draw the info bg
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(300, 352, 374 * 0.85, 222 * 0.85, playerInfoOverlayBgImage, 1, 0);
|
||||
|
||||
-- Draw appeal card
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(145, 364, 103 * 1.25, 132 * 1.25, appealCardImage, 1, 0);
|
||||
|
||||
-- Draw description
|
||||
gfx.FontSize(28)
|
||||
gfx.LoadSkinFont("Digital-Serial-Bold.ttf")
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.Text("Hellooooooo", 310, 370);
|
||||
|
||||
-- Draw username
|
||||
gfx.FontSize(40)
|
||||
gfx.Text(username, 310, 413);
|
||||
|
||||
-- Draw IR server name
|
||||
gfx.FontSize(28)
|
||||
gfx.Text(IRserverName, 310, 453);
|
||||
|
||||
-- Draw dan badge
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(311, 490, 107 * 1.25, 29 * 1.25, danBadgeImage, 1, 0);
|
||||
end
|
||||
|
||||
local scoreNumber = Numbers.load_number_image("score_num");
|
||||
|
||||
function drawChartResult(deltaTime, x, y, chartResult)
|
||||
gfx.Save()
|
||||
gfx.LoadSkinFont('NotoSans-Regular.ttf')
|
||||
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.BeginPath()
|
||||
gfx.GlobalAlpha(1);
|
||||
gfx.Text(chartResult.title, x+160,y+32);
|
||||
|
||||
DiffRectangle.render(deltaTime, x+287.5, y+67, 0.85, chartResult.difficulty, chartResult.level)
|
||||
|
||||
local score = chartResult.score or 0;
|
||||
|
||||
Numbers.draw_number(x + 500, y+80, 1.0, math.floor(score / 10000), 4, scoreNumber, true, 0.30, 1.12)
|
||||
Numbers.draw_number(x + 655, y+85, 1.0, score, 4, scoreNumber, true, 0.22, 1.12)
|
||||
|
||||
|
||||
local gradeImageKey = string.gsub(chartResult.grade, '+', '_P');
|
||||
local gradeImage = gradeImages[gradeImageKey] or gradeImages.D
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+800, y+12, 79, 79, gradeImage, 1, 0)
|
||||
|
||||
|
||||
if chartResult.badge then
|
||||
local badgeImage = badgeImages[chartResult.badge+1];
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+900, y+16, 79*1.05, 69*1.05, badgeImage, 1, 0)
|
||||
end
|
||||
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function drawScorePanelContent(deltaTime)
|
||||
-- game.Log("Drawing scores...", game.LOGGER_INFO) -- debug
|
||||
for i, chart in ipairs(result.charts) do
|
||||
-- if chart.score == nil then
|
||||
-- game.Log("Score does not exist? Quitting loop...", game.LOGGER_WARNING)
|
||||
-- break
|
||||
-- end
|
||||
|
||||
drawChartResult(deltaTime, 0, 836+(165*(i-1)), chart);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function drawDecorations()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(118, 846.5, 43*0.855, 429*0.855, notchesImage, 1, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(400, 807, 367*0.857, 429*0.857, trackBarsImage, 1, 0)
|
||||
end
|
||||
|
||||
function drawCompletion()
|
||||
local completitionImage = completionFailImage;
|
||||
if result.passed then
|
||||
completitionImage = completionPassImage;
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(63, 1331, 766*0.85, 130*0.85, completitionImage, 1, 0)
|
||||
|
||||
Numbers.draw_number(925, 1370, 1.0, percGet, 3, scoreNumber, true, 0.3, 1.12)
|
||||
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.Rect(741, 1402, 278*math.min(1, percGet / percRequired), 6);
|
||||
gfx.FillColor(255, 128, 0, 255);
|
||||
gfx.Fill()
|
||||
end
|
||||
|
||||
|
||||
function result_set()
|
||||
local reqTextWords = common.splitString(result.requirement_text, ' ');
|
||||
for index, word in ipairs(reqTextWords) do
|
||||
if string.find(word, '%%') ~= nil then -- %% = %, because % is an escape char
|
||||
local percNumber = tonumber(string.gsub(word, '%%', ''), 10)
|
||||
percRequired = percNumber;
|
||||
end
|
||||
end
|
||||
|
||||
game.Log(percRequired, game.LOGGER_ERROR);
|
||||
|
||||
local a = 0;
|
||||
for i, chart in ipairs(result.charts) do
|
||||
a = a + chart.percent;
|
||||
game.Log('#' .. i .. ' got ' .. chart.percent .. '% // ACC at ' .. a, game.LOGGER_ERROR);
|
||||
end
|
||||
percGet = a / #result.charts;
|
||||
end
|
||||
|
||||
|
||||
local IR_HeartbeatResponse = function(res)
|
||||
if res.statusCode == IRData.States.Success then
|
||||
IRserverName = res.body.serverName .. " " .. res.body.irVersion;
|
||||
else
|
||||
game.Log("Can't connect to IR!", game.LOGGER_WARNING)
|
||||
end
|
||||
end
|
||||
|
||||
local IR_Handle = function()
|
||||
if not irHeartbeatRequested then
|
||||
IR.Heartbeat(IR_HeartbeatResponse)
|
||||
irHeartbeatRequested = true;
|
||||
end
|
||||
end
|
||||
|
||||
local function drawResultScreen(x, y, w, h, deltaTime)
|
||||
gfx.BeginPath()
|
||||
|
||||
gfx.Translate(x, y);
|
||||
gfx.Scale(w / 1080, h / 1920);
|
||||
gfx.Scissor(0, 0, 1080, 1920);
|
||||
|
||||
handleSfx()
|
||||
IR_Handle()
|
||||
|
||||
drawBackground()
|
||||
|
||||
drawDecorations()
|
||||
|
||||
drawPlayerInfo()
|
||||
|
||||
drawScorePanelContent(deltaTime)
|
||||
|
||||
drawCompletion()
|
||||
|
||||
drawHeader()
|
||||
Footer.draw(deltaTime);
|
||||
|
||||
gfx.ResetTransform()
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
-- detect resolution change
|
||||
local resx, resy = game.GetResolution()
|
||||
if resx ~= resX or resy ~= resY then
|
||||
resolutionChange(resx, resy)
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
|
||||
gfx.Rect(0, 0, resX, resY)
|
||||
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
|
||||
gfx.Fill()
|
||||
|
||||
drawResultScreen((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
|
||||
end
|
||||
|
|
|
@ -1,107 +1,107 @@
|
|||
options = {}
|
||||
yscale = 0.0
|
||||
selectedIndex = 0
|
||||
resx, resy = game.GetResolution()
|
||||
width = 500
|
||||
titleText = ""
|
||||
scale = 1.0
|
||||
|
||||
function make_option(name)
|
||||
return function()
|
||||
menu.Confirm(name)
|
||||
end
|
||||
end
|
||||
|
||||
function open()
|
||||
yscale = 0.0
|
||||
options = {}
|
||||
selectedIndex = 0
|
||||
resx, resy = game.GetResolution()
|
||||
|
||||
index = 1
|
||||
if #dialog.collections == 0 then
|
||||
options[index] = {"Favourites", make_option("Favourites"), {255,255,255}}
|
||||
end
|
||||
|
||||
for i,value in ipairs(dialog.collections) do
|
||||
options[i] = {value.name, make_option(value.name), {255,255,255}}
|
||||
if value.exists then options[i][3] = {255,0,0} end
|
||||
|
||||
end
|
||||
table.insert(options, {"New Collection", menu.ChangeState, {0, 255, 128}})
|
||||
table.insert(options, {"Cancel", menu.Cancel, {200,200,200}})
|
||||
|
||||
gfx.FontFace("fallback")
|
||||
gfx.FontSize(50)
|
||||
titleText = string.format("Add %s to collection:", dialog.title)
|
||||
xmi,ymi,xma,yma = gfx.TextBounds(0,0, titleText)
|
||||
width = xma - xmi + 50
|
||||
width = math.max(500, width)
|
||||
scale = math.min(resy / 1280, 1.0)
|
||||
scale = math.min(scale, resx / width)
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
if dialog.closing then
|
||||
yscale = math.min(yscale - deltaTime * 4, 1.0)
|
||||
else
|
||||
yscale = math.min(yscale + deltaTime * 4, 1.0)
|
||||
end
|
||||
gfx.Translate(resx / 2, resy / 2)
|
||||
gfx.Scale(scale, yscale * scale)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-width/2, -250, width, 500)
|
||||
gfx.FillColor(50,50,50)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontFace("fallback")
|
||||
gfx.FontSize(50)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_CENTER)
|
||||
gfx.Text(titleText, 0, -240)
|
||||
|
||||
if dialog.isTextEntry then
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-width/2 + 20, -30, width - 40, 60)
|
||||
gfx.FillColor(25,25,25)
|
||||
gfx.Fill()
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(dialog.newName, 0, 0)
|
||||
else
|
||||
local yshift = 20
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT)
|
||||
for i, option in ipairs(options) do
|
||||
local y = yshift + 60 * ((i-1) - selectedIndex)
|
||||
if y > -190 and y < 220 then
|
||||
gfx.FillColor(option[3][1], option[3][2], option[3][3])
|
||||
gfx.Text(option[1], 40 - width/2, y)
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(20 - width/2, -10 + yshift)
|
||||
gfx.LineTo(30 - width/2, 0 + yshift)
|
||||
gfx.LineTo(20 - width/2, 10 + yshift)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Fill()
|
||||
end
|
||||
if dialog.closing == true and yscale <= 0.0 then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function button_pressed(button)
|
||||
if button == game.BUTTON_BCK then
|
||||
menu.Cancel()
|
||||
elseif button == game.BUTTON_STA then
|
||||
options[selectedIndex+1][2]()
|
||||
end
|
||||
end
|
||||
|
||||
function advance_selection(value)
|
||||
selectedIndex = (selectedIndex + value) % #options
|
||||
options = {}
|
||||
yscale = 0.0
|
||||
selectedIndex = 0
|
||||
resx, resy = game.GetResolution()
|
||||
width = 500
|
||||
titleText = ""
|
||||
scale = 1.0
|
||||
|
||||
function make_option(name)
|
||||
return function()
|
||||
menu.Confirm(name)
|
||||
end
|
||||
end
|
||||
|
||||
function open()
|
||||
yscale = 0.0
|
||||
options = {}
|
||||
selectedIndex = 0
|
||||
resx, resy = game.GetResolution()
|
||||
|
||||
index = 1
|
||||
if #dialog.collections == 0 then
|
||||
options[index] = {"Favourites", make_option("Favourites"), {255,255,255}}
|
||||
end
|
||||
|
||||
for i,value in ipairs(dialog.collections) do
|
||||
options[i] = {value.name, make_option(value.name), {255,255,255}}
|
||||
if value.exists then options[i][3] = {255,0,0} end
|
||||
|
||||
end
|
||||
table.insert(options, {"New Collection", menu.ChangeState, {0, 255, 128}})
|
||||
table.insert(options, {"Cancel", menu.Cancel, {200,200,200}})
|
||||
|
||||
gfx.FontFace("fallback")
|
||||
gfx.FontSize(50)
|
||||
titleText = string.format("Add %s to collection:", dialog.title)
|
||||
xmi,ymi,xma,yma = gfx.TextBounds(0,0, titleText)
|
||||
width = xma - xmi + 50
|
||||
width = math.max(500, width)
|
||||
scale = math.min(resy / 1280, 1.0)
|
||||
scale = math.min(scale, resx / width)
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
if dialog.closing then
|
||||
yscale = math.min(yscale - deltaTime * 4, 1.0)
|
||||
else
|
||||
yscale = math.min(yscale + deltaTime * 4, 1.0)
|
||||
end
|
||||
gfx.Translate(resx / 2, resy / 2)
|
||||
gfx.Scale(scale, yscale * scale)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-width/2, -250, width, 500)
|
||||
gfx.FillColor(50,50,50)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontFace("fallback")
|
||||
gfx.FontSize(50)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_CENTER)
|
||||
gfx.Text(titleText, 0, -240)
|
||||
|
||||
if dialog.isTextEntry then
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-width/2 + 20, -30, width - 40, 60)
|
||||
gfx.FillColor(25,25,25)
|
||||
gfx.Fill()
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(dialog.newName, 0, 0)
|
||||
else
|
||||
local yshift = 20
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT)
|
||||
for i, option in ipairs(options) do
|
||||
local y = yshift + 60 * ((i-1) - selectedIndex)
|
||||
if y > -190 and y < 220 then
|
||||
gfx.FillColor(option[3][1], option[3][2], option[3][3])
|
||||
gfx.Text(option[1], 40 - width/2, y)
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(20 - width/2, -10 + yshift)
|
||||
gfx.LineTo(30 - width/2, 0 + yshift)
|
||||
gfx.LineTo(20 - width/2, 10 + yshift)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Fill()
|
||||
end
|
||||
if dialog.closing == true and yscale <= 0.0 then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function button_pressed(button)
|
||||
if button == game.BUTTON_BCK then
|
||||
menu.Cancel()
|
||||
elseif button == game.BUTTON_STA then
|
||||
options[selectedIndex+1][2]()
|
||||
end
|
||||
end
|
||||
|
||||
function advance_selection(value)
|
||||
selectedIndex = (selectedIndex + value) % #options
|
||||
end
|
|
@ -1,429 +1,429 @@
|
|||
json = require "json"
|
||||
local header = {}
|
||||
header["user-agent"] = "unnamed_sdvx_clone"
|
||||
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local diffColors = {{50,50,127}, {50,127,50}, {127,50,50}, {127, 50, 127}}
|
||||
local entryW = 770
|
||||
local entryH = 320
|
||||
local resX,resY = game.GetResolution()
|
||||
local xCount = math.max(1, math.floor(resX / entryW))
|
||||
local yCount = math.max(1, math.floor(resY / entryH))
|
||||
local xOffset = (resX - xCount * entryW) / 2
|
||||
local cursorPos = 0
|
||||
local cursorPosX = 0
|
||||
local cursorPosY = 0
|
||||
local displayCursorPosX = 0
|
||||
local displayCursorPosY = 0
|
||||
local nextUrl = "https://ksm.dev/app/songs"
|
||||
local screenState = 0 --0 = normal, 1 = level, 2 = sorting
|
||||
local loading = true
|
||||
local downloaded = {}
|
||||
local songs = {}
|
||||
local selectedLevels = {}
|
||||
local selectedSorting = "Uploaded"
|
||||
local lastPlaying = nil
|
||||
for i = 1, 20 do
|
||||
selectedLevels[i] = false
|
||||
end
|
||||
|
||||
local cachepath = path.Absolute("skins/" .. game.GetSkin() .. "/nautica.json")
|
||||
local levelcursor = 0
|
||||
local sortingcursor = 0
|
||||
local sortingOptions = {"Uploaded", "Oldest"}
|
||||
local needsReload = false
|
||||
|
||||
function addsong(song)
|
||||
if song.jacket_url ~= nil then
|
||||
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
|
||||
else
|
||||
song.jacket = jacketFallback
|
||||
end
|
||||
if downloaded[song.id] then
|
||||
song.status = "Downloaded"
|
||||
end
|
||||
table.insert(songs, song)
|
||||
end
|
||||
local yOffset = 0
|
||||
local backgroundImage = gfx.CreateSkinImage("bg.png", 1);
|
||||
|
||||
dlcache = io.open(cachepath, "r")
|
||||
if dlcache then
|
||||
downloaded = json.decode(dlcache:read("*all"))
|
||||
dlcache:close()
|
||||
end
|
||||
function encodeURI(str)
|
||||
if (str) then
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^%w ])",
|
||||
function (c)
|
||||
local dontChange = "-/_:."
|
||||
for i = 1, #dontChange do
|
||||
if c == dontChange:sub(i,i) then return c end
|
||||
end
|
||||
return string.format ("%%%02X", string.byte(c))
|
||||
end)
|
||||
str = string.gsub(str, " ", "%%20")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
function gotSongsCallback(response)
|
||||
if response.status ~= 200 then
|
||||
error()
|
||||
return
|
||||
end
|
||||
local jsondata = json.decode(response.text)
|
||||
for i,song in ipairs(jsondata.data) do
|
||||
addsong(song)
|
||||
end
|
||||
nextUrl = jsondata.links.next
|
||||
loading = false
|
||||
end
|
||||
|
||||
Http.GetAsync(nextUrl, header, gotSongsCallback)
|
||||
|
||||
|
||||
function render_song(song, x,y)
|
||||
gfx.Save()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Translate(x,y)
|
||||
gfx.Scissor(0,0,750,300)
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,0,0,140)
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(30)
|
||||
gfx.Text(song.title, 2,2)
|
||||
gfx.FontSize(24)
|
||||
gfx.Text(song.artist, 2,26)
|
||||
if song.jacket_url ~= nil and song.jacket == jacketFallback then
|
||||
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 50, 250, 250, song.jacket, 1, 0)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(250,50,500,250)
|
||||
gfx.FillColor(55,55,55,128)
|
||||
gfx.Fill()
|
||||
for i, diff in ipairs(song.charts) do
|
||||
local col = diffColors[diff.difficulty]
|
||||
local diffY = 50 + 250/4 * (diff.difficulty - 1)
|
||||
gfx.BeginPath()
|
||||
|
||||
gfx.Rect(250,diffY, 500, 250 / 4)
|
||||
gfx.FillColor(col[1], col[2], col[3])
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.Text(string.format("%d Effected by %s", diff.level, diff.effector), 255, diffY + 250 / 8)
|
||||
end
|
||||
if downloaded[song.id] then
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.FillColor(0,0,0,127)
|
||||
gfx.Fill()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.FontSize(60)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(downloaded[song.id], 375, 150)
|
||||
elseif song.status then
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.FillColor(0,0,0,127)
|
||||
gfx.Fill()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.FontSize(60)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(song.status, 375, 150)
|
||||
end
|
||||
gfx.ResetScissor()
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function load_more()
|
||||
if nextUrl ~= nil and not loading then
|
||||
Http.GetAsync(nextUrl, header, gotSongsCallback)
|
||||
loading = true
|
||||
end
|
||||
end
|
||||
|
||||
function render_cursor()
|
||||
local x = displayCursorPosX * entryW
|
||||
local y = displayCursorPosY * entryH
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x,y,750,300)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(5)
|
||||
gfx.Stroke()
|
||||
end
|
||||
|
||||
function render_loading()
|
||||
if not loading then return end
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(resX, resY)
|
||||
gfx.LineTo(resX - 350, resY)
|
||||
gfx.LineTo(resX - 300, resY - 50)
|
||||
gfx.LineTo(resX, resY - 50)
|
||||
gfx.ClosePath()
|
||||
gfx.FillColor(33,33,33)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.FontSize(70)
|
||||
gfx.Text("LOADING...", resX - 20, resY - 3)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_hotkeys()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,0,0,240)
|
||||
gfx.Rect(0,resY - 50, resX, 50)
|
||||
gfx.Fill()
|
||||
gfx.FontSize(30)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.Text("FXR: Sorting", resX/2 + 20, resY - 10)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.Text("FXL: Levels", resX/2 - 20, resY - 10)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_info()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, resY)
|
||||
gfx.LineTo(350, resY)
|
||||
gfx.LineTo(300, resY - 50)
|
||||
gfx.LineTo(0, resY - 50)
|
||||
gfx.ClosePath()
|
||||
gfx.FillColor(33,33,33)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.FontSize(70)
|
||||
gfx.Text("Nautica", 3, resY - 3)
|
||||
local xmin,ymin,xmax,ymax = gfx.TextBounds(3, resY - 3, "Nautica")
|
||||
gfx.FontSize(20)
|
||||
gfx.Text("https://ksm.dev/", xmax + 13, resY - 3)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, resX, resY, backgroundImage, 1, 0);
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
displayCursorPosX = displayCursorPosX - (displayCursorPosX - cursorPosX) * deltaTime * 10
|
||||
displayCursorPosY = displayCursorPosY - (displayCursorPosY - cursorPosY) * deltaTime * 10
|
||||
if displayCursorPosY - yOffset > yCount - 1 then --scrolling down
|
||||
yOffset = yOffset - (yOffset - displayCursorPosY) - yCount + 1
|
||||
elseif displayCursorPosY - yOffset < 0 then
|
||||
yOffset = yOffset - (yOffset - displayCursorPosY)
|
||||
end
|
||||
gfx.Translate(xOffset, 50 - yOffset * entryH)
|
||||
for i, song in ipairs(songs) do
|
||||
if math.abs(cursorPos - i) <= xCount * yCount + xCount then
|
||||
i = i - 1
|
||||
local x = entryW * (i % xCount)
|
||||
local y = math.floor(i / xCount) * entryH
|
||||
render_song(song, x, y)
|
||||
if math.abs(#songs - i) < 4 then load_more() end
|
||||
end
|
||||
end
|
||||
render_cursor()
|
||||
if needsReload then reload_songs() end
|
||||
if screenState == 1 then render_level_filters()
|
||||
elseif screenState == 2 then render_sorting_selection()
|
||||
end
|
||||
render_hotkeys()
|
||||
render_loading()
|
||||
render_info()
|
||||
end
|
||||
|
||||
function archive_callback(entries, id)
|
||||
game.Log("Listing entries for " .. id, 0)
|
||||
local songsfolder = dlScreen.GetSongsPath()
|
||||
res = {}
|
||||
folders = { songsfolder .. "/nautica/" }
|
||||
local hasFolder = false
|
||||
for i, entry in ipairs(entries) do
|
||||
for j = 1, #entry do
|
||||
if entry:sub(j,j) == '/' then
|
||||
hasFolder = true
|
||||
table.insert(folders, songsfolder .. "/nautica/" .. entry:sub(1,j))
|
||||
end
|
||||
end
|
||||
game.Log(entry, 0)
|
||||
res[entry] = songsfolder .. "/nautica/" .. entry
|
||||
end
|
||||
|
||||
if not hasFolder then
|
||||
for i, entry in ipairs(entries) do
|
||||
res[entry] = songsfolder .. "/nautica/" .. id .. "/" .. entry
|
||||
end
|
||||
table.insert(folders, songsfolder .. "/nautica/" .. id .. "/")
|
||||
end
|
||||
downloaded[id] = "Downloaded"
|
||||
res[".folders"] = table.concat(folders, "|")
|
||||
return res
|
||||
end
|
||||
|
||||
function reload_songs()
|
||||
needsReload = true
|
||||
if loading then return end
|
||||
local useLevels = false
|
||||
local levelarr = {}
|
||||
|
||||
for i,value in ipairs(selectedLevels) do
|
||||
if value then
|
||||
useLevels = true
|
||||
table.insert(levelarr, i)
|
||||
end
|
||||
end
|
||||
nextUrl = string.format("https://ksm.dev/app/songs?sort=%s", selectedSorting:lower())
|
||||
if useLevels then
|
||||
nextUrl = nextUrl .. "&levels=" .. table.concat(levelarr, ",")
|
||||
end
|
||||
songs = {}
|
||||
cursorPos = 0
|
||||
cursorPosX = 0
|
||||
cursorPosY = 0
|
||||
displayCursorPosX = 0
|
||||
displayCursorPosY = 0
|
||||
load_more()
|
||||
game.Log(nextUrl, 0)
|
||||
needsReload = false
|
||||
|
||||
end
|
||||
|
||||
function button_pressed(button)
|
||||
if button == game.BUTTON_STA then
|
||||
if screenState == 0 then
|
||||
local song = songs[cursorPos + 1]
|
||||
if song == nil then return end
|
||||
dlScreen.DownloadArchive(encodeURI(song.cdn_download_url), header, song.id, archive_callback)
|
||||
downloaded[song.id] = "Downloading..."
|
||||
elseif screenState == 1 then
|
||||
if selectedLevels[levelcursor + 1] then
|
||||
selectedLevels[levelcursor + 1] = false
|
||||
else
|
||||
selectedLevels[levelcursor + 1] = true
|
||||
end
|
||||
reload_songs()
|
||||
elseif screenState == 2 then
|
||||
selectedSorting = sortingOptions[sortingcursor + 1]
|
||||
reload_songs()
|
||||
end
|
||||
|
||||
elseif button == game.BUTTON_BTA then
|
||||
if screenState == 0 then
|
||||
local song = songs[cursorPos + 1]
|
||||
if song == nil then return end
|
||||
dlScreen.PlayPreview(encodeURI(song.preview_url), header, song.id)
|
||||
song.status = "Playing"
|
||||
if lastPlaying ~=nil then
|
||||
lastPlaying.status = nil
|
||||
end
|
||||
lastPlaying = song
|
||||
end
|
||||
|
||||
elseif button == game.BUTTON_FXL then
|
||||
if screenState ~= 1 then
|
||||
screenState = 1
|
||||
else
|
||||
screenState = 0
|
||||
end
|
||||
elseif button == game.BUTTON_FXR then
|
||||
if screenState ~= 2 then
|
||||
screenState = 2
|
||||
else
|
||||
screenState = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function key_pressed(key)
|
||||
if key == 27 then --escape pressed
|
||||
dlcache = io.open(cachepath, "w")
|
||||
dlcache:write(json.encode(downloaded))
|
||||
dlcache:close()
|
||||
dlScreen.Exit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function advance_selection(steps)
|
||||
if screenState == 0 and #songs > 0 then
|
||||
cursorPos = (cursorPos + steps) % #songs
|
||||
cursorPosX = cursorPos % xCount
|
||||
cursorPosY = math.floor(cursorPos / xCount)
|
||||
if cursorPos > #songs - 6 then
|
||||
load_more()
|
||||
end
|
||||
elseif screenState == 1 then
|
||||
levelcursor = (levelcursor + steps) % 20
|
||||
elseif screenState == 2 then
|
||||
sortingcursor = (sortingcursor + steps) % #sortingOptions
|
||||
end
|
||||
end
|
||||
|
||||
function render_level_filters()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0, resX, resY)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.FontSize(60)
|
||||
gfx.Text("Level filters:", 10, 10)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(resX/2 - 30, resY/2 - 22, 60, 44)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
for i = 1, 20 do
|
||||
y = (resY/2) + (i - (levelcursor + 1)) * 40
|
||||
if selectedLevels[i] then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
|
||||
gfx.Text(tostring(i), resX/2, y)
|
||||
end
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_sorting_selection()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0, resX, resY)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.FontSize(60)
|
||||
gfx.Text("Sorting method:", 10, 10)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(resX/2 - 75, resY/2 - 22, 150, 44)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
for i, opt in ipairs(sortingOptions) do
|
||||
y = (resY/2) + (i - (sortingcursor + 1)) * 40
|
||||
if selectedSorting == opt then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
|
||||
gfx.Text(opt, resX/2, y)
|
||||
end
|
||||
gfx.Restore()
|
||||
json = require "json"
|
||||
local header = {}
|
||||
header["user-agent"] = "unnamed_sdvx_clone"
|
||||
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local diffColors = {{50,50,127}, {50,127,50}, {127,50,50}, {127, 50, 127}}
|
||||
local entryW = 770
|
||||
local entryH = 320
|
||||
local resX,resY = game.GetResolution()
|
||||
local xCount = math.max(1, math.floor(resX / entryW))
|
||||
local yCount = math.max(1, math.floor(resY / entryH))
|
||||
local xOffset = (resX - xCount * entryW) / 2
|
||||
local cursorPos = 0
|
||||
local cursorPosX = 0
|
||||
local cursorPosY = 0
|
||||
local displayCursorPosX = 0
|
||||
local displayCursorPosY = 0
|
||||
local nextUrl = "https://ksm.dev/app/songs"
|
||||
local screenState = 0 --0 = normal, 1 = level, 2 = sorting
|
||||
local loading = true
|
||||
local downloaded = {}
|
||||
local songs = {}
|
||||
local selectedLevels = {}
|
||||
local selectedSorting = "Uploaded"
|
||||
local lastPlaying = nil
|
||||
for i = 1, 20 do
|
||||
selectedLevels[i] = false
|
||||
end
|
||||
|
||||
local cachepath = path.Absolute("skins/" .. game.GetSkin() .. "/nautica.json")
|
||||
local levelcursor = 0
|
||||
local sortingcursor = 0
|
||||
local sortingOptions = {"Uploaded", "Oldest"}
|
||||
local needsReload = false
|
||||
|
||||
function addsong(song)
|
||||
if song.jacket_url ~= nil then
|
||||
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
|
||||
else
|
||||
song.jacket = jacketFallback
|
||||
end
|
||||
if downloaded[song.id] then
|
||||
song.status = "Downloaded"
|
||||
end
|
||||
table.insert(songs, song)
|
||||
end
|
||||
local yOffset = 0
|
||||
local backgroundImage = gfx.CreateSkinImage("bg.png", 1);
|
||||
|
||||
dlcache = io.open(cachepath, "r")
|
||||
if dlcache then
|
||||
downloaded = json.decode(dlcache:read("*all"))
|
||||
dlcache:close()
|
||||
end
|
||||
function encodeURI(str)
|
||||
if (str) then
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^%w ])",
|
||||
function (c)
|
||||
local dontChange = "-/_:."
|
||||
for i = 1, #dontChange do
|
||||
if c == dontChange:sub(i,i) then return c end
|
||||
end
|
||||
return string.format ("%%%02X", string.byte(c))
|
||||
end)
|
||||
str = string.gsub(str, " ", "%%20")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
function gotSongsCallback(response)
|
||||
if response.status ~= 200 then
|
||||
error()
|
||||
return
|
||||
end
|
||||
local jsondata = json.decode(response.text)
|
||||
for i,song in ipairs(jsondata.data) do
|
||||
addsong(song)
|
||||
end
|
||||
nextUrl = jsondata.links.next
|
||||
loading = false
|
||||
end
|
||||
|
||||
Http.GetAsync(nextUrl, header, gotSongsCallback)
|
||||
|
||||
|
||||
function render_song(song, x,y)
|
||||
gfx.Save()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Translate(x,y)
|
||||
gfx.Scissor(0,0,750,300)
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,0,0,140)
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(30)
|
||||
gfx.Text(song.title, 2,2)
|
||||
gfx.FontSize(24)
|
||||
gfx.Text(song.artist, 2,26)
|
||||
if song.jacket_url ~= nil and song.jacket == jacketFallback then
|
||||
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 50, 250, 250, song.jacket, 1, 0)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(250,50,500,250)
|
||||
gfx.FillColor(55,55,55,128)
|
||||
gfx.Fill()
|
||||
for i, diff in ipairs(song.charts) do
|
||||
local col = diffColors[diff.difficulty]
|
||||
local diffY = 50 + 250/4 * (diff.difficulty - 1)
|
||||
gfx.BeginPath()
|
||||
|
||||
gfx.Rect(250,diffY, 500, 250 / 4)
|
||||
gfx.FillColor(col[1], col[2], col[3])
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.Text(string.format("%d Effected by %s", diff.level, diff.effector), 255, diffY + 250 / 8)
|
||||
end
|
||||
if downloaded[song.id] then
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.FillColor(0,0,0,127)
|
||||
gfx.Fill()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.FontSize(60)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(downloaded[song.id], 375, 150)
|
||||
elseif song.status then
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0,750,300)
|
||||
gfx.FillColor(0,0,0,127)
|
||||
gfx.Fill()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.FontSize(60)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.Text(song.status, 375, 150)
|
||||
end
|
||||
gfx.ResetScissor()
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function load_more()
|
||||
if nextUrl ~= nil and not loading then
|
||||
Http.GetAsync(nextUrl, header, gotSongsCallback)
|
||||
loading = true
|
||||
end
|
||||
end
|
||||
|
||||
function render_cursor()
|
||||
local x = displayCursorPosX * entryW
|
||||
local y = displayCursorPosY * entryH
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x,y,750,300)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(5)
|
||||
gfx.Stroke()
|
||||
end
|
||||
|
||||
function render_loading()
|
||||
if not loading then return end
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(resX, resY)
|
||||
gfx.LineTo(resX - 350, resY)
|
||||
gfx.LineTo(resX - 300, resY - 50)
|
||||
gfx.LineTo(resX, resY - 50)
|
||||
gfx.ClosePath()
|
||||
gfx.FillColor(33,33,33)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.FontSize(70)
|
||||
gfx.Text("LOADING...", resX - 20, resY - 3)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_hotkeys()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,0,0,240)
|
||||
gfx.Rect(0,resY - 50, resX, 50)
|
||||
gfx.Fill()
|
||||
gfx.FontSize(30)
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.Text("FXR: Sorting", resX/2 + 20, resY - 10)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.Text("FXL: Levels", resX/2 - 20, resY - 10)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_info()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, resY)
|
||||
gfx.LineTo(350, resY)
|
||||
gfx.LineTo(300, resY - 50)
|
||||
gfx.LineTo(0, resY - 50)
|
||||
gfx.ClosePath()
|
||||
gfx.FillColor(33,33,33)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.FontSize(70)
|
||||
gfx.Text("Nautica", 3, resY - 3)
|
||||
local xmin,ymin,xmax,ymax = gfx.TextBounds(3, resY - 3, "Nautica")
|
||||
gfx.FontSize(20)
|
||||
gfx.Text("https://ksm.dev/", xmax + 13, resY - 3)
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, resX, resY, backgroundImage, 1, 0);
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
displayCursorPosX = displayCursorPosX - (displayCursorPosX - cursorPosX) * deltaTime * 10
|
||||
displayCursorPosY = displayCursorPosY - (displayCursorPosY - cursorPosY) * deltaTime * 10
|
||||
if displayCursorPosY - yOffset > yCount - 1 then --scrolling down
|
||||
yOffset = yOffset - (yOffset - displayCursorPosY) - yCount + 1
|
||||
elseif displayCursorPosY - yOffset < 0 then
|
||||
yOffset = yOffset - (yOffset - displayCursorPosY)
|
||||
end
|
||||
gfx.Translate(xOffset, 50 - yOffset * entryH)
|
||||
for i, song in ipairs(songs) do
|
||||
if math.abs(cursorPos - i) <= xCount * yCount + xCount then
|
||||
i = i - 1
|
||||
local x = entryW * (i % xCount)
|
||||
local y = math.floor(i / xCount) * entryH
|
||||
render_song(song, x, y)
|
||||
if math.abs(#songs - i) < 4 then load_more() end
|
||||
end
|
||||
end
|
||||
render_cursor()
|
||||
if needsReload then reload_songs() end
|
||||
if screenState == 1 then render_level_filters()
|
||||
elseif screenState == 2 then render_sorting_selection()
|
||||
end
|
||||
render_hotkeys()
|
||||
render_loading()
|
||||
render_info()
|
||||
end
|
||||
|
||||
function archive_callback(entries, id)
|
||||
game.Log("Listing entries for " .. id, 0)
|
||||
local songsfolder = dlScreen.GetSongsPath()
|
||||
res = {}
|
||||
folders = { songsfolder .. "/nautica/" }
|
||||
local hasFolder = false
|
||||
for i, entry in ipairs(entries) do
|
||||
for j = 1, #entry do
|
||||
if entry:sub(j,j) == '/' then
|
||||
hasFolder = true
|
||||
table.insert(folders, songsfolder .. "/nautica/" .. entry:sub(1,j))
|
||||
end
|
||||
end
|
||||
game.Log(entry, 0)
|
||||
res[entry] = songsfolder .. "/nautica/" .. entry
|
||||
end
|
||||
|
||||
if not hasFolder then
|
||||
for i, entry in ipairs(entries) do
|
||||
res[entry] = songsfolder .. "/nautica/" .. id .. "/" .. entry
|
||||
end
|
||||
table.insert(folders, songsfolder .. "/nautica/" .. id .. "/")
|
||||
end
|
||||
downloaded[id] = "Downloaded"
|
||||
res[".folders"] = table.concat(folders, "|")
|
||||
return res
|
||||
end
|
||||
|
||||
function reload_songs()
|
||||
needsReload = true
|
||||
if loading then return end
|
||||
local useLevels = false
|
||||
local levelarr = {}
|
||||
|
||||
for i,value in ipairs(selectedLevels) do
|
||||
if value then
|
||||
useLevels = true
|
||||
table.insert(levelarr, i)
|
||||
end
|
||||
end
|
||||
nextUrl = string.format("https://ksm.dev/app/songs?sort=%s", selectedSorting:lower())
|
||||
if useLevels then
|
||||
nextUrl = nextUrl .. "&levels=" .. table.concat(levelarr, ",")
|
||||
end
|
||||
songs = {}
|
||||
cursorPos = 0
|
||||
cursorPosX = 0
|
||||
cursorPosY = 0
|
||||
displayCursorPosX = 0
|
||||
displayCursorPosY = 0
|
||||
load_more()
|
||||
game.Log(nextUrl, 0)
|
||||
needsReload = false
|
||||
|
||||
end
|
||||
|
||||
function button_pressed(button)
|
||||
if button == game.BUTTON_STA then
|
||||
if screenState == 0 then
|
||||
local song = songs[cursorPos + 1]
|
||||
if song == nil then return end
|
||||
dlScreen.DownloadArchive(encodeURI(song.cdn_download_url), header, song.id, archive_callback)
|
||||
downloaded[song.id] = "Downloading..."
|
||||
elseif screenState == 1 then
|
||||
if selectedLevels[levelcursor + 1] then
|
||||
selectedLevels[levelcursor + 1] = false
|
||||
else
|
||||
selectedLevels[levelcursor + 1] = true
|
||||
end
|
||||
reload_songs()
|
||||
elseif screenState == 2 then
|
||||
selectedSorting = sortingOptions[sortingcursor + 1]
|
||||
reload_songs()
|
||||
end
|
||||
|
||||
elseif button == game.BUTTON_BTA then
|
||||
if screenState == 0 then
|
||||
local song = songs[cursorPos + 1]
|
||||
if song == nil then return end
|
||||
dlScreen.PlayPreview(encodeURI(song.preview_url), header, song.id)
|
||||
song.status = "Playing"
|
||||
if lastPlaying ~=nil then
|
||||
lastPlaying.status = nil
|
||||
end
|
||||
lastPlaying = song
|
||||
end
|
||||
|
||||
elseif button == game.BUTTON_FXL then
|
||||
if screenState ~= 1 then
|
||||
screenState = 1
|
||||
else
|
||||
screenState = 0
|
||||
end
|
||||
elseif button == game.BUTTON_FXR then
|
||||
if screenState ~= 2 then
|
||||
screenState = 2
|
||||
else
|
||||
screenState = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function key_pressed(key)
|
||||
if key == 27 then --escape pressed
|
||||
dlcache = io.open(cachepath, "w")
|
||||
dlcache:write(json.encode(downloaded))
|
||||
dlcache:close()
|
||||
dlScreen.Exit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function advance_selection(steps)
|
||||
if screenState == 0 and #songs > 0 then
|
||||
cursorPos = (cursorPos + steps) % #songs
|
||||
cursorPosX = cursorPos % xCount
|
||||
cursorPosY = math.floor(cursorPos / xCount)
|
||||
if cursorPos > #songs - 6 then
|
||||
load_more()
|
||||
end
|
||||
elseif screenState == 1 then
|
||||
levelcursor = (levelcursor + steps) % 20
|
||||
elseif screenState == 2 then
|
||||
sortingcursor = (sortingcursor + steps) % #sortingOptions
|
||||
end
|
||||
end
|
||||
|
||||
function render_level_filters()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0, resX, resY)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.FontSize(60)
|
||||
gfx.Text("Level filters:", 10, 10)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(resX/2 - 30, resY/2 - 22, 60, 44)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
for i = 1, 20 do
|
||||
y = (resY/2) + (i - (levelcursor + 1)) * 40
|
||||
if selectedLevels[i] then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
|
||||
gfx.Text(tostring(i), resX/2, y)
|
||||
end
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function render_sorting_selection()
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(0,0, resX, resY)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.FontSize(60)
|
||||
gfx.Text("Sorting method:", 10, 10)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(resX/2 - 75, resY/2 - 22, 150, 44)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
for i, opt in ipairs(sortingOptions) do
|
||||
y = (resY/2) + (i - (sortingcursor + 1)) * 40
|
||||
if selectedSorting == opt then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
|
||||
gfx.Text(opt, resX/2, y)
|
||||
end
|
||||
gfx.Restore()
|
||||
end
|
|
@ -1,247 +1,247 @@
|
|||
function clamp(x, min, max)
|
||||
if x < min then
|
||||
x = min
|
||||
end
|
||||
if x > max then
|
||||
x = max
|
||||
end
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
function smootherstep(edge0, edge1, x)
|
||||
-- Scale, and clamp x to 0..1 range
|
||||
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
|
||||
-- Evaluate polynomial
|
||||
return x * x * x * (x * (x * 6 - 15) + 10)
|
||||
end
|
||||
|
||||
function to_range(val, start, stop)
|
||||
return start + (stop - start) * val
|
||||
end
|
||||
|
||||
Animation = {
|
||||
start = 0,
|
||||
stop = 0,
|
||||
progress = 0,
|
||||
duration = 1,
|
||||
smoothStart = false
|
||||
}
|
||||
|
||||
function Animation:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function Animation:restart(start, stop, duration)
|
||||
self.progress = 0
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
self.duration = duration
|
||||
end
|
||||
|
||||
function Animation:tick(deltaTime)
|
||||
self.progress = math.min(1, self.progress + deltaTime / self.duration)
|
||||
if self.progress == 1 then return self.stop end
|
||||
if self.smoothStart then
|
||||
return to_range(smootherstep(0, 1, self.progress), self.start, self.stop)
|
||||
else
|
||||
return to_range(smootherstep(-1, 1, self.progress) * 2 - 1, self.start, self.stop)
|
||||
end
|
||||
end
|
||||
|
||||
local yScale = Animation:new()
|
||||
local diagWidth = 600
|
||||
local diagHeight = 400
|
||||
local tabStroke = {start=0, stop=1}
|
||||
local tabStrokeAnimation = {start=Animation:new(), stop=Animation:new()}
|
||||
local settingsStrokeAnimation = {x=Animation:new(), y=Animation:new()}
|
||||
local prevTab = -1
|
||||
local prevSettingStroke = {x=0, y=0}
|
||||
local settingStroke = {x=0, y=0}
|
||||
local prevVis = false
|
||||
|
||||
function processSkinSettings()
|
||||
for ti, tab in ipairs(SettingsDiag.tabs) do
|
||||
for si, setting in ipairs(tab.settings) do
|
||||
|
||||
if (tab.name == 'Game') then
|
||||
if (setting.name == 'Gauge') then
|
||||
game.SetSkinSetting('_gaugeType', setting.value);
|
||||
end
|
||||
if (setting.name == 'Backup Gauge') then
|
||||
game.SetSkinSetting('_gaugeARS', setting.value and 1 or 0);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function render(deltaTime, visible)
|
||||
if visible and not prevVis then
|
||||
yScale:restart(0, 1, 0.25)
|
||||
elseif not visible and prevVis then
|
||||
yScale:restart(1, 0, 0.25)
|
||||
end
|
||||
processSkinSettings()
|
||||
|
||||
if not visible and yScale:tick(0) < 0.05 then return end
|
||||
|
||||
local posX = SettingsDiag.posX or 0.5
|
||||
local posY = SettingsDiag.posY or 0.5
|
||||
local message_1 = "Press both FXs to open/close. Use the Start button to press buttons."
|
||||
local message_2 = "Use FX keys to navigate tabs. Use arrow keys to navigate and modify settings."
|
||||
|
||||
resX, resY = game.GetResolution()
|
||||
local scale = resY / 1080
|
||||
gfx.ResetTransform()
|
||||
gfx.Translate(math.floor(diagWidth/2 + posX*(resX-diagWidth)), math.floor(diagHeight/2 + posY*(resY-diagHeight)))
|
||||
gfx.Scale(scale, scale)
|
||||
gfx.Scale(1.0, smootherstep(0, 1, yScale:tick(deltaTime)))
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-diagWidth/2, -diagHeight/2, diagWidth, diagHeight)
|
||||
gfx.FillColor(50,50,50)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
|
||||
gfx.FontSize(20)
|
||||
|
||||
local m_xmin, m_ymin, m_xmax, m_ymax = gfx.TextBounds(0, 0, message_1)
|
||||
gfx.Text(message_1, diagWidth/2 - m_xmax, diagHeight/2 - m_ymax - 20)
|
||||
|
||||
m_xmin, m_ymin, m_xmax, m_ymax = gfx.TextBounds(0, 0, message_2)
|
||||
gfx.Text(message_2, diagWidth/2 - m_xmax, diagHeight/2 - m_ymax)
|
||||
|
||||
tabStroke.start = tabStrokeAnimation.start:tick(deltaTime)
|
||||
tabStroke.stop = tabStrokeAnimation.stop:tick(deltaTime)
|
||||
|
||||
settingStroke.x = settingsStrokeAnimation.x:tick(deltaTime)
|
||||
settingStroke.y = settingsStrokeAnimation.y:tick(deltaTime)
|
||||
|
||||
local tabBarHeight = 0
|
||||
local nextTabX = 5
|
||||
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.FontSize(35)
|
||||
gfx.Save() --draw tab bar
|
||||
gfx.Translate(-diagWidth / 2, -diagHeight / 2)
|
||||
for ti, tab in ipairs(SettingsDiag.tabs) do
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(nextTabX, 5, tab.name)
|
||||
|
||||
if ti == SettingsDiag.currentTab and SettingsDiag.currentTab ~= prevTab then
|
||||
tabStrokeAnimation.start:restart(tabStroke.start, nextTabX, 0.1)
|
||||
tabStrokeAnimation.stop:restart(tabStroke.stop, xmax, 0.1)
|
||||
end
|
||||
tabBarHeight = math.max(tabBarHeight, ymax + 5)
|
||||
gfx.Text(tab.name, nextTabX, 5)
|
||||
nextTabX = xmax + 10
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, tabBarHeight)
|
||||
gfx.LineTo(diagWidth, tabBarHeight)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.Stroke()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(tabStroke.start, tabBarHeight)
|
||||
gfx.LineTo(tabStroke.stop, tabBarHeight)
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
gfx.Stroke()
|
||||
gfx.Restore() --draw tab bar end
|
||||
|
||||
gfx.FontSize(30)
|
||||
gfx.Save() --draw current tab
|
||||
gfx.Translate(-diagWidth / 2, -diagHeight / 2)
|
||||
gfx.Translate(5, tabBarHeight + 5)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, settingStroke.y)
|
||||
gfx.LineTo(settingStroke.x, settingStroke.y)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
gfx.Stroke()
|
||||
|
||||
local settingHeight = 30
|
||||
local tab = SettingsDiag.tabs[SettingsDiag.currentTab]
|
||||
for si, setting in ipairs(tab.settings) do
|
||||
processSkinSettings(setting.name, setting.value)
|
||||
|
||||
local disp = ""
|
||||
if setting.type == "enum" then
|
||||
disp = string.format("%s: %s", setting.name, setting.options[setting.value])
|
||||
elseif setting.type == "int" then
|
||||
disp = string.format("%s: %d", setting.name, setting.value)
|
||||
elseif setting.type == "float" then
|
||||
disp = string.format("%s: %.2f", setting.name, setting.value)
|
||||
if setting.max == 1 and setting.min == 0 then --draw slider
|
||||
disp = setting.name .. ": "
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
local width = diagWidth - 20 - xmax
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(xmax + 5, 20)
|
||||
gfx.LineTo(xmax + 5 + width, 20)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(xmax + 5, 20)
|
||||
gfx.LineTo(xmax + 5 + width * setting.value, 20)
|
||||
gfx.StrokeColor(255,127,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
end
|
||||
elseif setting.type == "button" then
|
||||
disp = string.format("%s", setting.name)
|
||||
local xmin, ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-2, 3, 4+xmax-xmin, 28)
|
||||
gfx.FillColor(0, 64, 128)
|
||||
if si == SettingsDiag.currentSetting then
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
else
|
||||
gfx.StrokeColor(0,127,255)
|
||||
end
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
else
|
||||
disp = string.format("%s:", setting.name)
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(xmax + 5, 5, 20,20)
|
||||
gfx.FillColor(255, 127, 0, setting.value and 255 or 0)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
end
|
||||
gfx.Text(disp, 0 ,0)
|
||||
if si == SettingsDiag.currentSetting then
|
||||
local setting_name = setting.name .. ":"
|
||||
if setting.type == "button" then
|
||||
setting_name = setting.name
|
||||
end
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, setting_name)
|
||||
ymax = ymax + settingHeight * (si - 1)
|
||||
if xmax ~= prevSettingStroke.x or ymax ~= prevSettingStroke.y then
|
||||
settingsStrokeAnimation.x:restart(settingStroke.x, xmax, 0.1)
|
||||
settingsStrokeAnimation.y:restart(settingStroke.y, ymax, 0.1)
|
||||
end
|
||||
|
||||
prevSettingStroke.x = xmax
|
||||
prevSettingStroke.y = ymax
|
||||
end
|
||||
gfx.Translate(0, settingHeight)
|
||||
end
|
||||
|
||||
|
||||
gfx.Restore() --draw current tab end
|
||||
prevTab = SettingsDiag.currentTab
|
||||
prevVis = visible
|
||||
|
||||
function clamp(x, min, max)
|
||||
if x < min then
|
||||
x = min
|
||||
end
|
||||
if x > max then
|
||||
x = max
|
||||
end
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
function smootherstep(edge0, edge1, x)
|
||||
-- Scale, and clamp x to 0..1 range
|
||||
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
|
||||
-- Evaluate polynomial
|
||||
return x * x * x * (x * (x * 6 - 15) + 10)
|
||||
end
|
||||
|
||||
function to_range(val, start, stop)
|
||||
return start + (stop - start) * val
|
||||
end
|
||||
|
||||
Animation = {
|
||||
start = 0,
|
||||
stop = 0,
|
||||
progress = 0,
|
||||
duration = 1,
|
||||
smoothStart = false
|
||||
}
|
||||
|
||||
function Animation:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function Animation:restart(start, stop, duration)
|
||||
self.progress = 0
|
||||
self.start = start
|
||||
self.stop = stop
|
||||
self.duration = duration
|
||||
end
|
||||
|
||||
function Animation:tick(deltaTime)
|
||||
self.progress = math.min(1, self.progress + deltaTime / self.duration)
|
||||
if self.progress == 1 then return self.stop end
|
||||
if self.smoothStart then
|
||||
return to_range(smootherstep(0, 1, self.progress), self.start, self.stop)
|
||||
else
|
||||
return to_range(smootherstep(-1, 1, self.progress) * 2 - 1, self.start, self.stop)
|
||||
end
|
||||
end
|
||||
|
||||
local yScale = Animation:new()
|
||||
local diagWidth = 600
|
||||
local diagHeight = 400
|
||||
local tabStroke = {start=0, stop=1}
|
||||
local tabStrokeAnimation = {start=Animation:new(), stop=Animation:new()}
|
||||
local settingsStrokeAnimation = {x=Animation:new(), y=Animation:new()}
|
||||
local prevTab = -1
|
||||
local prevSettingStroke = {x=0, y=0}
|
||||
local settingStroke = {x=0, y=0}
|
||||
local prevVis = false
|
||||
|
||||
function processSkinSettings()
|
||||
for ti, tab in ipairs(SettingsDiag.tabs) do
|
||||
for si, setting in ipairs(tab.settings) do
|
||||
|
||||
if (tab.name == 'Game') then
|
||||
if (setting.name == 'Gauge') then
|
||||
game.SetSkinSetting('_gaugeType', setting.value);
|
||||
end
|
||||
if (setting.name == 'Backup Gauge') then
|
||||
game.SetSkinSetting('_gaugeARS', setting.value and 1 or 0);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function render(deltaTime, visible)
|
||||
if visible and not prevVis then
|
||||
yScale:restart(0, 1, 0.25)
|
||||
elseif not visible and prevVis then
|
||||
yScale:restart(1, 0, 0.25)
|
||||
end
|
||||
processSkinSettings()
|
||||
|
||||
if not visible and yScale:tick(0) < 0.05 then return end
|
||||
|
||||
local posX = SettingsDiag.posX or 0.5
|
||||
local posY = SettingsDiag.posY or 0.5
|
||||
local message_1 = "Press both FXs to open/close. Use the Start button to press buttons."
|
||||
local message_2 = "Use FX keys to navigate tabs. Use arrow keys to navigate and modify settings."
|
||||
|
||||
resX, resY = game.GetResolution()
|
||||
local scale = resY / 1080
|
||||
gfx.ResetTransform()
|
||||
gfx.Translate(math.floor(diagWidth/2 + posX*(resX-diagWidth)), math.floor(diagHeight/2 + posY*(resY-diagHeight)))
|
||||
gfx.Scale(scale, scale)
|
||||
gfx.Scale(1.0, smootherstep(0, 1, yScale:tick(deltaTime)))
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-diagWidth/2, -diagHeight/2, diagWidth, diagHeight)
|
||||
gfx.FillColor(50,50,50)
|
||||
gfx.Fill()
|
||||
gfx.FillColor(255,255,255)
|
||||
|
||||
gfx.FontSize(20)
|
||||
|
||||
local m_xmin, m_ymin, m_xmax, m_ymax = gfx.TextBounds(0, 0, message_1)
|
||||
gfx.Text(message_1, diagWidth/2 - m_xmax, diagHeight/2 - m_ymax - 20)
|
||||
|
||||
m_xmin, m_ymin, m_xmax, m_ymax = gfx.TextBounds(0, 0, message_2)
|
||||
gfx.Text(message_2, diagWidth/2 - m_xmax, diagHeight/2 - m_ymax)
|
||||
|
||||
tabStroke.start = tabStrokeAnimation.start:tick(deltaTime)
|
||||
tabStroke.stop = tabStrokeAnimation.stop:tick(deltaTime)
|
||||
|
||||
settingStroke.x = settingsStrokeAnimation.x:tick(deltaTime)
|
||||
settingStroke.y = settingsStrokeAnimation.y:tick(deltaTime)
|
||||
|
||||
local tabBarHeight = 0
|
||||
local nextTabX = 5
|
||||
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.FontSize(35)
|
||||
gfx.Save() --draw tab bar
|
||||
gfx.Translate(-diagWidth / 2, -diagHeight / 2)
|
||||
for ti, tab in ipairs(SettingsDiag.tabs) do
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(nextTabX, 5, tab.name)
|
||||
|
||||
if ti == SettingsDiag.currentTab and SettingsDiag.currentTab ~= prevTab then
|
||||
tabStrokeAnimation.start:restart(tabStroke.start, nextTabX, 0.1)
|
||||
tabStrokeAnimation.stop:restart(tabStroke.stop, xmax, 0.1)
|
||||
end
|
||||
tabBarHeight = math.max(tabBarHeight, ymax + 5)
|
||||
gfx.Text(tab.name, nextTabX, 5)
|
||||
nextTabX = xmax + 10
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, tabBarHeight)
|
||||
gfx.LineTo(diagWidth, tabBarHeight)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.Stroke()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(tabStroke.start, tabBarHeight)
|
||||
gfx.LineTo(tabStroke.stop, tabBarHeight)
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
gfx.Stroke()
|
||||
gfx.Restore() --draw tab bar end
|
||||
|
||||
gfx.FontSize(30)
|
||||
gfx.Save() --draw current tab
|
||||
gfx.Translate(-diagWidth / 2, -diagHeight / 2)
|
||||
gfx.Translate(5, tabBarHeight + 5)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(0, settingStroke.y)
|
||||
gfx.LineTo(settingStroke.x, settingStroke.y)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
gfx.Stroke()
|
||||
|
||||
local settingHeight = 30
|
||||
local tab = SettingsDiag.tabs[SettingsDiag.currentTab]
|
||||
for si, setting in ipairs(tab.settings) do
|
||||
processSkinSettings(setting.name, setting.value)
|
||||
|
||||
local disp = ""
|
||||
if setting.type == "enum" then
|
||||
disp = string.format("%s: %s", setting.name, setting.options[setting.value])
|
||||
elseif setting.type == "int" then
|
||||
disp = string.format("%s: %d", setting.name, setting.value)
|
||||
elseif setting.type == "float" then
|
||||
disp = string.format("%s: %.2f", setting.name, setting.value)
|
||||
if setting.max == 1 and setting.min == 0 then --draw slider
|
||||
disp = setting.name .. ": "
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
local width = diagWidth - 20 - xmax
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(xmax + 5, 20)
|
||||
gfx.LineTo(xmax + 5 + width, 20)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
gfx.BeginPath()
|
||||
gfx.MoveTo(xmax + 5, 20)
|
||||
gfx.LineTo(xmax + 5 + width * setting.value, 20)
|
||||
gfx.StrokeColor(255,127,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Stroke()
|
||||
end
|
||||
elseif setting.type == "button" then
|
||||
disp = string.format("%s", setting.name)
|
||||
local xmin, ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(-2, 3, 4+xmax-xmin, 28)
|
||||
gfx.FillColor(0, 64, 128)
|
||||
if si == SettingsDiag.currentSetting then
|
||||
gfx.StrokeColor(255, 127, 0)
|
||||
else
|
||||
gfx.StrokeColor(0,127,255)
|
||||
end
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
else
|
||||
disp = string.format("%s:", setting.name)
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(xmax + 5, 5, 20,20)
|
||||
gfx.FillColor(255, 127, 0, setting.value and 255 or 0)
|
||||
gfx.StrokeColor(0,127,255)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
end
|
||||
gfx.Text(disp, 0 ,0)
|
||||
if si == SettingsDiag.currentSetting then
|
||||
local setting_name = setting.name .. ":"
|
||||
if setting.type == "button" then
|
||||
setting_name = setting.name
|
||||
end
|
||||
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, setting_name)
|
||||
ymax = ymax + settingHeight * (si - 1)
|
||||
if xmax ~= prevSettingStroke.x or ymax ~= prevSettingStroke.y then
|
||||
settingsStrokeAnimation.x:restart(settingStroke.x, xmax, 0.1)
|
||||
settingsStrokeAnimation.y:restart(settingStroke.y, ymax, 0.1)
|
||||
end
|
||||
|
||||
prevSettingStroke.x = xmax
|
||||
prevSettingStroke.y = ymax
|
||||
end
|
||||
gfx.Translate(0, settingHeight)
|
||||
end
|
||||
|
||||
|
||||
gfx.Restore() --draw current tab end
|
||||
prevTab = SettingsDiag.currentTab
|
||||
prevVis = visible
|
||||
|
||||
end
|
800
scripts/json.lua
|
@ -1,400 +1,400 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2019 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.1" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2019 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.1" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
local window = {
|
||||
isPortrait = false,
|
||||
resX = 0,
|
||||
resY = 0,
|
||||
scale = 1,
|
||||
w = 0,
|
||||
h = 0,
|
||||
|
||||
set = function(this, doScale)
|
||||
local resX, resY = game.GetResolution();
|
||||
|
||||
if ((this.resX ~= resX) or (this.resY ~= this.resY)) then
|
||||
this.isPortrait = resY > resX;
|
||||
this.w = (this.isPortrait and 1080) or 1920;
|
||||
this.h = this.w * (resY / resX);
|
||||
this.scale = resX / this.w;
|
||||
|
||||
this.resX = resX;
|
||||
this.resY = resY;
|
||||
end
|
||||
|
||||
if (doScale) then gfx.Scale(this.scale, this.scale); end
|
||||
end,
|
||||
};
|
||||
|
||||
local wheel = {
|
||||
cache = { w = 0, h = 0 },
|
||||
visibleSongs = 11,
|
||||
margin = 13,
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = 0,
|
||||
h = {
|
||||
song = 0,
|
||||
total = 0,
|
||||
},
|
||||
|
||||
setSizes = function(this)
|
||||
if ((this.cache.w ~= window.w) or (this.cache.h ~= window.h)) then
|
||||
local marginTotal = this.margin * (this.visibleSongs - 1);
|
||||
|
||||
this.x = window.w / 2;
|
||||
this.y = 0;
|
||||
this.w = window.w / 2;
|
||||
this.h.total = window.h - marginTotal;
|
||||
this.h.song = this.h.total / this.visibleSongs;
|
||||
|
||||
this.cache.w = window.w;
|
||||
this.cache.h = window.h;
|
||||
end
|
||||
end,
|
||||
};
|
||||
|
||||
local displaying = {};
|
||||
local jacketCache = {};
|
||||
|
||||
local currDiff = 1;
|
||||
local currSong = 1;
|
||||
|
||||
local jacketFallback = gfx.CreateSkinImage('song_select/loading.png', 0);
|
||||
|
||||
local getJacket = function(diff)
|
||||
if ((not jacketCache[diff.jacketPath])
|
||||
or (jacketCache[diff.jacketPath] == jacketFallback)) then
|
||||
jacketCache[diff.jacketPath] = gfx.LoadImageJob(
|
||||
diff.jacketPath,
|
||||
jacketFallback,
|
||||
500,
|
||||
500
|
||||
);
|
||||
end
|
||||
|
||||
return jacketCache[diff.jacketPath];
|
||||
end
|
||||
|
||||
local setDisplaying = function()
|
||||
local songs = songwheel.songs;
|
||||
local enoughSongs = #songs >= wheel.visibleSongs;
|
||||
|
||||
displaying[5] = songs[currSong] or {};
|
||||
|
||||
for i = 1, 4 do
|
||||
if (enoughSongs) then
|
||||
displaying[5 - i] = songs[currSong - i] or songs[currSong + #songs - i];
|
||||
else
|
||||
displaying[5 - i] = songs[currSong - i] or {};
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 3 do
|
||||
if (enoughSongs) then
|
||||
displaying[5 + i] = songs[currSong + i] or songs[currSong - #songs + i];
|
||||
else
|
||||
displaying[5 + i] = songs[currSong + i] or {};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local renderWheel = function()
|
||||
local margin = wheel.margin;
|
||||
local x = wheel.x;
|
||||
local y = wheel.y;
|
||||
local w = wheel.w;
|
||||
local h = wheel.h.song;
|
||||
|
||||
for i, song in ipairs(displaying) do
|
||||
local isSelected = i == 5;
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0, 0, 0, (isSelected and 200) or 100);
|
||||
gfx.Rect(x, y, w, h);
|
||||
gfx.Fill();
|
||||
|
||||
if (song and song.difficulties) then
|
||||
local jacket = getJacket(song.difficulties[currDiff] or song.difficulties[1]);
|
||||
|
||||
if (jacket) then
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(x, y, h, h, jacket, (isSelected and 1) or 0.5, 0);
|
||||
end
|
||||
end
|
||||
|
||||
y = y + h + margin;
|
||||
end
|
||||
end
|
||||
|
||||
render = function(dt)
|
||||
window:set(true);
|
||||
|
||||
wheel:setSizes();
|
||||
|
||||
setDisplaying();
|
||||
|
||||
renderWheel();
|
||||
|
||||
gfx.ForceRender();
|
||||
end
|
||||
|
||||
set_index = function(newSong)
|
||||
currSong = newSong;
|
||||
end
|
||||
|
||||
set_diff = function(newDiff)
|
||||
currDiff = newDiff;
|
||||
end
|
||||
|
||||
songs_changed = function(withAll)
|
||||
if (not withAll) then return; end
|
||||
end
|
|
@ -1,675 +0,0 @@
|
|||
easing = require("easing")
|
||||
|
||||
gfx.LoadSkinFont("rounded-mplus-1c-bold.ttf")
|
||||
|
||||
game.LoadSkinSample("cursor_song")
|
||||
game.LoadSkinSample("cursor_difficulty")
|
||||
|
||||
local resx, resy = game.GetResolution()
|
||||
|
||||
local levelFont = ImageFont.new("font-level", "0123456789")
|
||||
local diffFont = ImageFont.new("diff_num", "0123456789")
|
||||
local bpmFont = ImageFont.new("number", "0123456789.") -- FIXME: font-default
|
||||
local desw, desh;
|
||||
local resx, resy;
|
||||
local portrait;
|
||||
local scale;
|
||||
|
||||
function ResetLayoutInformation()
|
||||
resx, resy = game.GetResolution()
|
||||
portrait = resy > resx
|
||||
desw = portrait and 1080 or 1920
|
||||
desh = desw * (resy / resx)
|
||||
scale = resx / desw
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
ResetLayoutInformation()
|
||||
end
|
||||
|
||||
-- Grades
|
||||
---------
|
||||
local noGrade = Image.skin("song_select/grade/nograde.png")
|
||||
local grades = {
|
||||
{["min"] = 9900000, ["image"] = Image.skin("song_select/grade/s.png")},
|
||||
{["min"] = 9800000, ["image"] = Image.skin("song_select/grade/aaap.png")},
|
||||
{["min"] = 9700000, ["image"] = Image.skin("song_select/grade/aaa.png")},
|
||||
{["min"] = 9500000, ["image"] = Image.skin("song_select/grade/aap.png")},
|
||||
{["min"] = 9300000, ["image"] = Image.skin("song_select/grade/aa.png")},
|
||||
{["min"] = 9000000, ["image"] = Image.skin("song_select/grade/ap.png")},
|
||||
{["min"] = 8700000, ["image"] = Image.skin("song_select/grade/a.png")},
|
||||
{["min"] = 7500000, ["image"] = Image.skin("song_select/grade/b.png")},
|
||||
{["min"] = 6500000, ["image"] = Image.skin("song_select/grade/c.png")},
|
||||
{["min"] = 0, ["image"] = Image.skin("song_select/grade/d.png")},
|
||||
}
|
||||
|
||||
function lookup_grade_image(difficulty)
|
||||
local gradeImage = noGrade
|
||||
if difficulty.scores[1] ~= nil then
|
||||
local highScore = difficulty.scores[1]
|
||||
for i, v in ipairs(grades) do
|
||||
if highScore.score >= v.min then
|
||||
gradeImage = v.image
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return { image = gradeImage, flicker = (gradeImage == grades[1].image) }
|
||||
end
|
||||
|
||||
-- Medals
|
||||
---------
|
||||
local noMedal = Image.skin("song_select/medal/nomedal.png")
|
||||
local medals = {
|
||||
Image.skin("song_select/medal/played.png"),
|
||||
Image.skin("song_select/medal/clear.png"),
|
||||
Image.skin("song_select/medal/hard.png"),
|
||||
Image.skin("song_select/medal/uc.png"),
|
||||
Image.skin("song_select/medal/puc.png")
|
||||
}
|
||||
|
||||
function lookup_medal_image(difficulty)
|
||||
local medalImage = noMedal
|
||||
local flicker = false
|
||||
if difficulty.scores[1] ~= nil then
|
||||
if difficulty.topBadge ~= 0 then
|
||||
medalImage = medals[difficulty.topBadge]
|
||||
if difficulty.topBadge >= 3 then -- hard
|
||||
flicker = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return { image = medalImage, flicker = flicker }
|
||||
end
|
||||
|
||||
-- Lookup difficulty
|
||||
function lookup_difficulty(diffs, diff)
|
||||
local diffIndex = nil
|
||||
for i, v in ipairs(diffs) do
|
||||
if v.difficulty + 1 == diff then
|
||||
diffIndex = i
|
||||
end
|
||||
end
|
||||
local difficulty = nil
|
||||
if diffIndex ~= nil then
|
||||
difficulty = diffs[diffIndex]
|
||||
end
|
||||
return difficulty
|
||||
end
|
||||
|
||||
-- JacketCache class
|
||||
--------------------
|
||||
JacketCache = {}
|
||||
JacketCache.new = function()
|
||||
local this = {
|
||||
cache = {},
|
||||
images = {
|
||||
loading = Image.skin("song_select/loading.png"),
|
||||
}
|
||||
}
|
||||
setmetatable(this, {__index = JacketCache})
|
||||
return this
|
||||
end
|
||||
|
||||
JacketCache.get = function(this, path)
|
||||
local jacket = this.cache[path]
|
||||
if not jacket or jacket == this.images.loading.image then
|
||||
jacket = gfx.LoadImageJob(path, this.images.loading.image)
|
||||
this.cache[path] = jacket
|
||||
end
|
||||
return Image.wrap(jacket)
|
||||
end
|
||||
|
||||
|
||||
-- SongData class
|
||||
-----------------
|
||||
SongData = {}
|
||||
SongData.new = function(jacketCache)
|
||||
local this = {
|
||||
selectedIndex = 1,
|
||||
selectedDifficulty = 0,
|
||||
memo = Memo.new(),
|
||||
jacketCache = jacketCache,
|
||||
images = {
|
||||
dataBg = Image.skin("song_select/data_bg.png"),
|
||||
fg = Image.skin("song_select/fg.png"),
|
||||
cursor = Image.skin("song_select/level_cursor.png"),
|
||||
none = Image.skin("song_select/level/none.png"),
|
||||
difficulties = {
|
||||
Image.skin("song_select/level/novice.png"),
|
||||
Image.skin("song_select/level/advanced.png"),
|
||||
Image.skin("song_select/level/exhaust.png"),
|
||||
Image.skin("song_select/level/maximum.png"),
|
||||
Image.skin("song_select/level/infinite.png"),
|
||||
Image.skin("song_select/level/gravity.png"),
|
||||
Image.skin("song_select/level/heavenly.png"),
|
||||
Image.skin("song_select/level/vivid.png")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
setmetatable(this, {__index = SongData})
|
||||
return this
|
||||
end
|
||||
|
||||
SongData.render = function(this, deltaTime)
|
||||
local song = songwheel.songs[this.selectedIndex]
|
||||
if not song then return end
|
||||
|
||||
-- Lookup difficulty
|
||||
local diff = song.difficulties[this.selectedDifficulty]
|
||||
if diff == nil then diff = song.difficulties[1] end
|
||||
|
||||
-- Draw the background
|
||||
this.images.dataBg:draw({ x = desw / 2, y = desh / 2, w = 1080 ,h = 1920})
|
||||
|
||||
-- Draw the jacket
|
||||
local jacket = this.jacketCache:get(diff.jacketPath)
|
||||
jacket:draw({ x = 97, y = 326, w = 346, h = 346, anchor_h = Image.ANCHOR_LEFT, anchor_v = Image.ANCHOR_TOP })
|
||||
|
||||
-- Draw the title
|
||||
local title = this.memo:memoize(string.format("title_%s", song.id), function ()
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf")
|
||||
return gfx.CreateLabel(song.title, 24, 0)
|
||||
end)
|
||||
gfx.FillColor(255, 255, 255, 255)
|
||||
gfx.DrawLabel(title, 32, desh / 2 - 4, 390)
|
||||
|
||||
-- Draw the artist
|
||||
local artist = this.memo:memoize(string.format("artist_%s", song.id), function ()
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf")
|
||||
return gfx.CreateLabel(song.artist, 24, 0)
|
||||
end)
|
||||
gfx.FillColor(255, 255, 255, 255)
|
||||
gfx.DrawLabel(artist, 32, desh / 2 + 42, 390)
|
||||
|
||||
-- Draw the effector
|
||||
local effector = this.memo:memoize(string.format("eff_%s_%s", song.id, diff.id), function ()
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf")
|
||||
return gfx.CreateLabel(diff.effector, 16, 0)
|
||||
end)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BASELINE)
|
||||
gfx.FillColor(255, 255, 255, 255)
|
||||
gfx.DrawLabel(effector, 260, desh / 2 + 208, 320)
|
||||
|
||||
-- Draw the illustrator
|
||||
if diff.illustrator then
|
||||
local illustrator = this.memo:memoize(string.format("ill_%s_%s", song.id, diff.id), function ()
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf")
|
||||
return gfx.CreateLabel(diff.illustrator, 16, 0)
|
||||
end)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BASELINE)
|
||||
gfx.FillColor(255, 255, 255, 255)
|
||||
gfx.DrawLabel(illustrator, 260, desh / 2 + 238, 320)
|
||||
end
|
||||
|
||||
-- Draw the bpm
|
||||
gfx.LoadSkinFont("Digital-Serial-Bold.ttf")
|
||||
gfx.FontSize(24)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BASELINE)
|
||||
gfx.FillColor(255, 255, 255, 255)
|
||||
gfx.Text(song.bpm, 75, desh / 2 - 34)
|
||||
|
||||
this:draw_cursor(diff.difficulty)
|
||||
|
||||
-- Draw the hi-score
|
||||
local hiScore = diff.scores[1]
|
||||
if hiScore then
|
||||
-- FIXME: large / small font
|
||||
local scoreText = string.format("%08d", hiScore.score)
|
||||
levelFont:draw(scoreText, 362, 220, 1, gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_MIDDLE)
|
||||
-- local scoreHiText = string.format("%04d", math.floor(hiScore.score / 1000))
|
||||
-- levelFont:draw(scoreHiText, 362, 220, 1, gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_MIDDLE)
|
||||
-- local scoreLoText = string.format("%04d", hiScore.score % 1000)
|
||||
-- bpmFont:draw(scoreLoText, 470, 220, 1, gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_MIDDLE)
|
||||
end
|
||||
|
||||
-- Draw the grade and medal
|
||||
local grade = lookup_grade_image(diff)
|
||||
grade.image:draw({ x = desw / 2 - 157, y = desh / 2 - 162, scale = 0.85, alpha = grade.flicker and glowState and 0.9 or 1 })
|
||||
local medal = lookup_medal_image(diff)
|
||||
medal.image:draw({ x = desw / 2 - 72, y = desh / 2 - 199, scale = 0.86, alpha = medal.flicker and glowState and 0.9 or 1})
|
||||
|
||||
for i = 1, 4 do
|
||||
local d = lookup_difficulty(song.difficulties, i)
|
||||
this:draw_difficulty(i - 1, d, jacket)
|
||||
end
|
||||
end
|
||||
|
||||
SongData.draw_title_artist = function(this, label, x, y, maxWidth)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BASELINE)
|
||||
gfx.FillColor(55, 55, 55, 64)
|
||||
gfx.DrawLabel(label, x + 2, y + 2, maxWidth)
|
||||
gfx.FillColor(55, 55, 55, 255)
|
||||
gfx.DrawLabel(label, x, y, maxWidth)
|
||||
end
|
||||
|
||||
SongData.set_index = function(this, newIndex)
|
||||
this.selectedIndex = newIndex
|
||||
end
|
||||
|
||||
SongData.draw_cursor = function(this, index)
|
||||
local x = 98
|
||||
local y = desh / 2 + 133
|
||||
|
||||
-- Draw the cursor
|
||||
this.images.cursor:draw({ x = x + index * 115, y = y, scale = 0.85 })
|
||||
end
|
||||
|
||||
SongData.set_difficulty = function(this, newDiff)
|
||||
this.selectedDifficulty = newDiff
|
||||
end
|
||||
|
||||
SongData.draw_difficulty = function(this, index, diff, jacket)
|
||||
local x = 98
|
||||
local y = desh / 2 + 135
|
||||
|
||||
-- Draw the jacket icon
|
||||
local jacket = this.jacketCache.images.loading
|
||||
if diff ~= nil then jacket = this.jacketCache:get(diff.jacketPath) end
|
||||
|
||||
if diff == nil then
|
||||
this.images.none:draw({ x = x + index * 115, y = y - 600, scale = 0.78})
|
||||
else
|
||||
-- Draw the background
|
||||
this.images.difficulties[diff.difficulty + 1]:draw({ x = x + index * 115, y = y, scale = 0.78})
|
||||
-- Draw the level
|
||||
local levelText = string.format("%02d", diff.level)
|
||||
diffFont:draw(levelText, x + index * 115, y - 20, 1, gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_MIDDLE)
|
||||
end
|
||||
end
|
||||
|
||||
-- SongTable class
|
||||
------------------
|
||||
SongTable = {}
|
||||
SongTable.new = function(jacketCache)
|
||||
local this = {
|
||||
cols = 1,
|
||||
rows = 11,
|
||||
selectedIndex = 1,
|
||||
selectedDifficulty = 0,
|
||||
rowOffset = 0, -- song index offset of top-left song in page
|
||||
cursorPos = 0, -- cursor position in page [0..cols * rows)
|
||||
displayCursorPos = 0,
|
||||
cursorAnim = 0,
|
||||
cursorAnimTotal = 0.1,
|
||||
memo = Memo.new(),
|
||||
jacketCache = jacketCache,
|
||||
images = {
|
||||
matchingBg = Image.skin("song_select/matching_bg.png"),
|
||||
scoreBg = Image.skin("song_select/score_bg.png"),
|
||||
force = Image.skin("song_select/force.png"),
|
||||
cursor = Image.skin("song_select/cursor.png"),
|
||||
cursorText = Image.skin("song_select/cursor_text.png"),
|
||||
cursorDiamond = Image.skin("song_select/cursor_diamond.png"),
|
||||
cursorDiamondWire = Image.skin("song_select/cursor_diamond_wire.png"),
|
||||
plates = {
|
||||
Image.skin("song_select/plate/novice.png"),
|
||||
Image.skin("song_select/plate/advanced.png"),
|
||||
Image.skin("song_select/plate/exhaust.png"),
|
||||
Image.skin("song_select/plate/maximum.png"),
|
||||
Image.skin("song_select/plate/infinite.png"),
|
||||
Image.skin("song_select/plate/gravity.png"),
|
||||
Image.skin("song_select/plate/heavenly.png"),
|
||||
Image.skin("song_select/plate/vivid.png")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
setmetatable(this, {__index = SongTable})
|
||||
return this
|
||||
end
|
||||
|
||||
SongTable.calc_cursor_point = function(this, pos)
|
||||
local col = pos % this.cols
|
||||
local row = math.floor((pos) / this.cols)
|
||||
local x = desw * 0.75 + col * this.images.cursor.w
|
||||
local y = 0 + row * this.images.cursor.h
|
||||
return x, y
|
||||
end
|
||||
|
||||
SongTable.set_index = function(this, newIndex)
|
||||
if newIndex ~= this.selectedIndex then
|
||||
game.PlaySample("cursor_song")
|
||||
end
|
||||
|
||||
local delta = newIndex - this.selectedIndex
|
||||
if delta < -1 or delta > 1 then
|
||||
local newOffset = newIndex - 1
|
||||
this.rowOffset = math.floor((newIndex - 1) / this.cols) * this.cols
|
||||
this.cursorPos = (newIndex - 1) - this.rowOffset
|
||||
this.displayCursorPos = this.cursorPos
|
||||
else
|
||||
local newCursorPos = this.cursorPos + delta
|
||||
|
||||
if newCursorPos < 0 then
|
||||
-- scroll up
|
||||
this.rowOffset = this.rowOffset - this.cols
|
||||
if this.rowOffset < 0 then
|
||||
-- this.rowOffset = math.floor(#songwheel.songs / this.cols)
|
||||
end
|
||||
newCursorPos = newCursorPos + this.cols
|
||||
elseif newCursorPos >= this.cols * this.rows then
|
||||
-- scroll down
|
||||
this.rowOffset = this.rowOffset + this.cols
|
||||
newCursorPos = newCursorPos - this.cols
|
||||
else
|
||||
-- no scroll, move cursor in page
|
||||
end
|
||||
if this.cursorAnim > 0 then
|
||||
this.displayCursorPos = easing.outQuad(0.5 - this.cursorAnim, this.displayCursorPos, this.cursorPos - this.displayCursorPos, 0.5)
|
||||
end
|
||||
this.cursorPos = newCursorPos
|
||||
this.cursorAnim = this.cursorAnimTotal
|
||||
end
|
||||
this.selectedIndex = newIndex
|
||||
end
|
||||
|
||||
SongTable.set_difficulty = function(this, newDiff)
|
||||
if newDiff ~= this.selectedDifficulty then
|
||||
game.PlaySample("cursor_difficulty")
|
||||
end
|
||||
this.selectedDifficulty = newDiff
|
||||
end
|
||||
|
||||
SongTable.render = function(this, deltaTime)
|
||||
this:draw_songs()
|
||||
this:draw_cursor(deltaTime)
|
||||
end
|
||||
|
||||
SongTable.draw_songs = function(this)
|
||||
for i = 1, this.cols * this.rows do
|
||||
if this.rowOffset + i <= #songwheel.songs then
|
||||
this:draw_song(i - 1, this.rowOffset + i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Draw the song plate
|
||||
SongTable.draw_song = function(this, pos, songIndex)
|
||||
local song = songwheel.songs[songIndex]
|
||||
if not song then return end
|
||||
|
||||
-- Lookup difficulty
|
||||
local diff = song.difficulties[this.selectedDifficulty]
|
||||
if diff == nil then diff = song.difficulties[1] end
|
||||
|
||||
local x, y = this:calc_cursor_point(pos)
|
||||
x = x + 4
|
||||
y = y + 16
|
||||
|
||||
-- Draw the jacket
|
||||
local jacket = this.jacketCache:get(diff.jacketPath)
|
||||
jacket:draw({ x = x - 24, y = y - 21, w = 122, h = 122 })
|
||||
|
||||
-- Draw the background
|
||||
gfx.FillColor(255, 255, 255)
|
||||
this.images.scoreBg:draw({ x = x + 72, y = y + 16 })
|
||||
if diff.force and diff.force > 0 then
|
||||
this.images.matchingBg:draw({ x = x + 72, y = y - 62 })
|
||||
end
|
||||
this.images.plates[diff.difficulty + 1]:draw({ x = x, y = y })
|
||||
|
||||
-- Draw the title
|
||||
local title = this.memo:memoize(string.format("title_%s", song.id), function ()
|
||||
gfx.LoadSkinFont("rounded-mplus-1c-bold.ttf")
|
||||
return gfx.CreateLabel(song.title, 14, 0)
|
||||
end)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BASELINE)
|
||||
gfx.DrawLabel(title, x - 22, y + 53, 125)
|
||||
|
||||
-- Draw the grade and medal
|
||||
local grade = lookup_grade_image(diff)
|
||||
grade.image:draw({ x = x + 78, y = y - 23, alpha = grade.flicker and glowState and 0.9 or 1 })
|
||||
|
||||
local medal = lookup_medal_image(diff)
|
||||
medal.image:draw({ x = x + 78, y = y + 10, alpha = medal.flicker and glowState and 0.9 or 1 })
|
||||
|
||||
-- Draw the level
|
||||
local levelText = string.format("%02d", diff.level)
|
||||
levelFont:draw(levelText, x + 72, y + 56, 1, gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_MIDDLE)
|
||||
|
||||
-- Draw the volforce
|
||||
--if diff.force and diff.force > 0 then
|
||||
--local forceText = string.format("%d", math.floor(diff.force * 100))
|
||||
--bpmFont:draw(forceText, x + , y - 60, 1, gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_MIDDLE)
|
||||
--end
|
||||
|
||||
--if diff.forceInTotal then
|
||||
--this.images.force:draw({x = x - 75, y = y - 60, w = 59, h = 59 })
|
||||
--end
|
||||
end
|
||||
|
||||
-- Draw the song cursor
|
||||
SongTable.draw_cursor = function(this, deltaTime)
|
||||
gfx.Save()
|
||||
|
||||
local pos = this.displayCursorPos
|
||||
if this.cursorAnim > 0 then
|
||||
this.cursorAnim = this.cursorAnim - deltaTime
|
||||
if this.cursorAnim <= 0 then
|
||||
this.displayCursorPos = this.cursorPos
|
||||
pos = this.cursorPos
|
||||
else
|
||||
pos = easing.outQuad(this.cursorAnimTotal - this.cursorAnim, this.displayCursorPos, this.cursorPos - this.displayCursorPos, this.cursorAnimTotal)
|
||||
end
|
||||
end
|
||||
|
||||
local x, y = this:calc_cursor_point(pos)
|
||||
gfx.FillColor(255, 255, 255)
|
||||
|
||||
local t = currentTime % 1
|
||||
|
||||
-- scroll text
|
||||
gfx.Scissor(
|
||||
x - this.images.cursor.w / 2, y - (this.images.cursor.h - 30) / 2,
|
||||
this.images.cursor.w, this.images.cursor.h - 30)
|
||||
local offset = (currentTime * 50) % 290
|
||||
local alpha = glowState and 0.8 or 1
|
||||
this.images.cursorText:draw({ x = x + 96, y = y + offset, alpha = alpha })
|
||||
this.images.cursorText:draw({ x = x + 96, y = y - 290 + offset, alpha = alpha })
|
||||
this.images.cursorText:draw({ x = x - 96, y = y + offset, alpha = alpha })
|
||||
this.images.cursorText:draw({ x = x - 96, y = y - 290 + offset, alpha = alpha })
|
||||
gfx.ResetScissor()
|
||||
|
||||
-- diamong wireframe
|
||||
local h = (this.images.cursorDiamondWire.h * 1.5) * easing.outQuad(t * 2, 0, 1, 1)
|
||||
this.images.cursorDiamondWire:draw({ x = x, y = y, w = this.images.cursorDiamondWire.w * 1.5, h = h, alpha = 0.5 })
|
||||
|
||||
-- ghost cursor
|
||||
alpha = easing.outSine(t, 1, -1, 1)
|
||||
h = this.images.cursor.h * easing.outSine(t, 0, 1, 1)
|
||||
this.images.cursor:draw({ x = x, y = y, h = h, alpha = alpha })
|
||||
|
||||
-- concrete cursor
|
||||
-- local w = this.images.cursor.w * easing.outSine(t, 1, 0.05, 0.5)
|
||||
this.images.cursor:draw({ x = x, y = y, alpha = glowState and 0.8 or 1 })
|
||||
|
||||
-- diamond knot
|
||||
gfx.GlobalCompositeOperation(gfx.BLEND_OP_LIGHTER)
|
||||
this.images.cursorDiamond:draw({ x = x + 100, y = y, alpha = 1 })
|
||||
this.images.cursorDiamond:draw({ x = x - 100, y = y, alpha = 1 })
|
||||
|
||||
local s = this.images.cursorDiamond.w / 1.5
|
||||
this.images.cursorDiamond:draw({ x = x + 90 + easing.outQuad(t, 0, -4, 0.5), y = y, w = s, h = s, alpha = 0.5 })
|
||||
this.images.cursorDiamond:draw({ x = x - 90 - easing.outQuad(t, 0, -4, 0.5), y = y, w = s, h = s, alpha = 0.5 })
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
-- main
|
||||
-------
|
||||
|
||||
local jacketCache = JacketCache.new()
|
||||
local songData = SongData.new(jacketCache)
|
||||
local songTable = SongTable.new(jacketCache)
|
||||
|
||||
glowState = false
|
||||
currentTime = 0
|
||||
|
||||
-- Callback
|
||||
get_page_size = function()
|
||||
return 12
|
||||
end
|
||||
|
||||
searchIndex = 1
|
||||
soffset = 0
|
||||
searchText = gfx.CreateLabel("", 5, 0)
|
||||
|
||||
draw_search = function(x,y,w,h)
|
||||
soffset = soffset + (searchIndex) - (songwheel.searchInputActive and 0 or 1)
|
||||
if searchIndex ~= (songwheel.searchInputActive and 0 or 1) then
|
||||
game.PlaySample("woosh")
|
||||
end
|
||||
searchIndex = songwheel.searchInputActive and 0 or 1
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgfade = 1 - (searchIndex + soffset)
|
||||
--if not songwheel.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",songwheel.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("segoeui.ttf");
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE);
|
||||
gfx.DrawLabel(searchText, xpos+10,y+(h/2), w-20)
|
||||
end
|
||||
|
||||
-- Callback
|
||||
function render(deltaTime)
|
||||
ResetLayoutInformation()
|
||||
|
||||
if ((math.floor(currentTime * 1000) % 100) < 50) then
|
||||
glowState = false
|
||||
else
|
||||
glowState = true
|
||||
end
|
||||
|
||||
local xshift = (resx - desw * scale) / 2
|
||||
local yshift = (resy - desh * scale) / 2
|
||||
|
||||
gfx.Translate(xshift, yshift)
|
||||
--gfx.Scale(scale, scale)
|
||||
|
||||
songData:render(deltaTime)
|
||||
songTable:render(deltaTime)
|
||||
|
||||
--if totalForce then
|
||||
--local forceText = string.format("%.2f", totalForce)
|
||||
-- gfx.SetImageTint(255, 254, 2)
|
||||
--bpmFont:draw(forceText, 140, 353, 1, gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_MIDDLE)
|
||||
--end
|
||||
|
||||
-- Draw the search status
|
||||
if songwheel.searchStatus then
|
||||
gfx.BeginPath()
|
||||
gfx.LoadSkinFont("segoeui.ttf")
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(20)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM)
|
||||
gfx.Text(songwheel.searchStatus, 3, desh)
|
||||
end
|
||||
|
||||
soffset = soffset * 0.8
|
||||
draw_search(120, 5, 600, 40)
|
||||
end
|
||||
|
||||
-- Callback
|
||||
set_index = function(newIndex)
|
||||
songData:set_index(newIndex)
|
||||
songTable:set_index(newIndex)
|
||||
end
|
||||
|
||||
-- Callback
|
||||
set_diff = function(newDiff)
|
||||
songData:set_difficulty(newDiff)
|
||||
songTable:set_difficulty(newDiff)
|
||||
end
|
||||
|
||||
-- force calculation
|
||||
--------------------
|
||||
totalForce = nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
calculate_force = function(diff)
|
||||
if #diff.scores < 1 then
|
||||
return 0
|
||||
end
|
||||
local score = diff.scores[1]
|
||||
local badgeRate = badgeRates[diff.topBadge]
|
||||
local gradeRate
|
||||
for i, v in ipairs(gradeRates) do
|
||||
if score.score >= v.min then
|
||||
gradeRate = v.rate
|
||||
break
|
||||
end
|
||||
end
|
||||
return math.floor((diff.level * 2) * (score.score / 10000000) * gradeRate * badgeRate) / 100
|
||||
end
|
||||
|
||||
-- callback
|
||||
songs_changed = function(withAll)
|
||||
if (not withAll) then return end
|
||||
local diffsById = {}
|
||||
local diffs = {}
|
||||
for i = 1, #songwheel.allSongs do
|
||||
local song = songwheel.allSongs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
diff.force = calculate_force(diff)
|
||||
table.insert(diffs, diff)
|
||||
diffsById[diff.id] = diff
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(diffs, function (l, r)
|
||||
return l.force > r.force
|
||||
end)
|
||||
|
||||
totalForce = 0
|
||||
for i = 1, 50 do
|
||||
if diffs[i] then
|
||||
totalForce = totalForce + diffs[i].force
|
||||
diffs[i].forceInTotal = true
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #songwheel.songs do
|
||||
local song = songwheel.songs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
local newDiff = diffsById[diff.id]
|
||||
song.difficulties[j] = newDiff
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,894 +0,0 @@
|
|||
--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 effector = 0
|
||||
local searchText = gfx.CreateLabel("",5,0)
|
||||
local searchIndex = 1
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local showGuide = game.GetSkinSetting("show_guide")
|
||||
local legendTable = {
|
||||
{["labelSingleLine"] = gfx.CreateLabel("DIFFICULTY SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("DIFFICULTY\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-left.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-right.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("FILTER MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("FILTER\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-L.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("SORT MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SORT\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-R.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC MODS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nMODS",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("common/grades/D.png", 0)},
|
||||
{["max"] = 7999999, ["image"] = gfx.CreateSkinImage("common/grades/C.png", 0)},
|
||||
{["max"] = 8699999, ["image"] = gfx.CreateSkinImage("common/grades/B.png", 0)},
|
||||
{["max"] = 8999999, ["image"] = gfx.CreateSkinImage("common/grades/A.png", 0)},
|
||||
{["max"] = 9299999, ["image"] = gfx.CreateSkinImage("common/grades/A+.png", 0)},
|
||||
{["max"] = 9499999, ["image"] = gfx.CreateSkinImage("common/grades/AA.png", 0)},
|
||||
{["max"] = 9699999, ["image"] = gfx.CreateSkinImage("common/grades/AA+.png", 0)},
|
||||
{["max"] = 9799999, ["image"] = gfx.CreateSkinImage("common/grades/AAA.png", 0)},
|
||||
{["max"] = 9899999, ["image"] = gfx.CreateSkinImage("common/grades/AAA+.png", 0)},
|
||||
{["max"] = 99999999, ["image"] = gfx.CreateSkinImage("common/grades/S.png", 0)}
|
||||
}
|
||||
|
||||
local badges = {
|
||||
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 foreground = gfx.CreateSkinImage("song_select/fg.png", 0);
|
||||
|
||||
local recordCache = {}
|
||||
|
||||
gfx.LoadSkinFont("dfmarugoth.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, loadJacket)
|
||||
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, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["artist"] then
|
||||
songCache[song.id]["artist"] = gfx.CreateLabel(song.artist, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["bpm"] then
|
||||
songCache[song.id]["bpm"] = gfx.CreateLabel(string.format("%s",song.bpm), 12, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["effector"] then
|
||||
songCache[song.id]["effector"] = gfx.CreateLabel(string.format("BPM: %s",song.bpm), 20, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["jacket"] then
|
||||
songCache[song.id]["jacket"] = { }
|
||||
end
|
||||
|
||||
for i = 1, #song.difficulties do
|
||||
songCache[song.id]["jacket"][i] = gfx.LoadImageJob(song.difficulties[i].jacketPath, jacketFallback, 200, 200)
|
||||
end
|
||||
end
|
||||
|
||||
function record_handler_factory(hash)
|
||||
return (function(res)
|
||||
if res.statusCode == 42 then
|
||||
recordCache[hash] = {good=false, reason="Untracked"}
|
||||
elseif res.statusCode == 20 and res.body ~= nil then
|
||||
recordCache[hash] = {good=true, record=res.body.record}
|
||||
elseif res.statusCode == 44 then
|
||||
recordCache[hash] = {good=true, record=nil}
|
||||
else
|
||||
recordCache[hash] = {good=false, reason="Failed"}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function get_record(hash)
|
||||
if recordCache[hash] then return recordCache[hash] end
|
||||
|
||||
recordCache[hash] = {good=false, reason="Loading..."}
|
||||
|
||||
IR.Record(hash, record_handler_factory(hash))
|
||||
|
||||
return recordCache[hash]
|
||||
end
|
||||
|
||||
function log_table(table)
|
||||
str = "{"
|
||||
for k, v in pairs(table) do
|
||||
str = str .. k .. ": "
|
||||
|
||||
t = type(v)
|
||||
|
||||
if t == "table" then
|
||||
str = str .. log_table(v)
|
||||
elseif t == "string" then
|
||||
str = str .. "\"" .. v .. "\""
|
||||
elseif t == "boolean" then
|
||||
if v then
|
||||
str = str .. "true"
|
||||
else
|
||||
str = str .. "false"
|
||||
end
|
||||
else
|
||||
str = str .. v
|
||||
end
|
||||
|
||||
str = str .. ", "
|
||||
end
|
||||
|
||||
return str .. "}"
|
||||
end
|
||||
|
||||
draw_scores_ir = 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/4), y+(h/2))
|
||||
gfx.FastText("IR RECORD", x + (3/4 * w), y + (h/2))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+xOffset,y+h/2,w/2-(xOffset*2),h/2)
|
||||
gfx.FillColor(30,30,30,10)
|
||||
gfx.StrokeColor(0,128,255)
|
||||
gfx.StrokeWidth(1)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x + xOffset + w/2,y+h/2,w/2-(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)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
if difficulty.topBadge ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w/2-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/4),y+(h/4)*3,w/2)
|
||||
end
|
||||
|
||||
irRecord = get_record(difficulty.hash)
|
||||
|
||||
if not irRecord.good then
|
||||
recordLabel = gfx.CreateLabel(irRecord.reason, 40, 0)
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
elseif irRecord.record == nil then --record not set, but can be tracked
|
||||
recordLabel = gfx.CreateLabel(string.format("%08d", 0), 40, 0)
|
||||
gfx.FillColor(170, 170, 170)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
else
|
||||
|
||||
recordScoreLabel = gfx.CreateLabel(string.format("%08d", irRecord.record.score), 26, 0)
|
||||
recordPlayerLabel = gfx.CreateLabel(irRecord.record.username, 26, 0)
|
||||
|
||||
if irRecord.record.lamp ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[irRecord.record.lamp], 1, 0)
|
||||
end
|
||||
|
||||
for i,v in ipairs(grades) do
|
||||
if v.max > irRecord.record.score then
|
||||
gfx.BeginPath()
|
||||
iw,ih = gfx.ImageSize(v.image)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset+w/2, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordPlayerLabel, x+(w * 3/4),y+(h/4)*2.55,w/2)
|
||||
gfx.DrawLabel(recordScoreLabel, x+(w * 3/4),y+(h/4)*3.45,w/2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_scores = function(difficulty, x, y, w, h)
|
||||
if IRData.Active then return draw_scores_ir(difficulty, x, y, w, h) end
|
||||
|
||||
-- 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.BeginPath()
|
||||
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_BOTTOM + gfx.TEXT_ALIGN_LEFT);
|
||||
gfx.DrawLabel(scoreLabel, x/11,y/1.48,w*2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_song = function(song, x, y, w, h, selected)
|
||||
local diffIndex = math.min(selectedDiff, #song.difficulties)
|
||||
local difficulty = song.difficulties[diffIndex]
|
||||
local clearLampR = 255
|
||||
local clearLampG = 255
|
||||
local clearLampB = 255
|
||||
local clearLampA = 100
|
||||
|
||||
if difficulty ~= nil then
|
||||
if difficulty.scores[1] ~= nil then
|
||||
if difficulty.topBadge == 1 then -- fail/played
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 2 then -- clear
|
||||
clearLampR = 25
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 3 then -- hard clear
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 255
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 4 then -- full combo
|
||||
clearLampR = 255
|
||||
clearLampG = 100
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 5 then -- perfect
|
||||
clearLampR = 255
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_or_create_cache(song)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1,y+1, w-2, h-2)
|
||||
gfx.FillColor(220,220,220)
|
||||
gfx.StrokeColor(0,8,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
if songCache[song.id]["jacket"][diffIndex] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+2, y+2, h-4, h-4, songCache[song.id]["jacket"][diffIndex], 1, 0)
|
||||
end
|
||||
|
||||
-- Song title
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1, y + h - h/4 - 1, w-2, h/4)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], (x)+h/2 + 4, y + h - 7, -1)
|
||||
--gfx.DrawLabel(songCache[song.id]["artist"], x+10, y + 50, w-10)
|
||||
|
||||
-- Song difficulty
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x - 1, y + h-h/2 - 4, h/2, h/2)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BOTTOM)
|
||||
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
gfx.FastText(song.difficulties[selectedDiff].level, x + h/4, y + h - 10)
|
||||
else
|
||||
gfx.FastText(song.difficulties[selectedDiff - 1].level, x + h/4, y + h - 10)
|
||||
end
|
||||
|
||||
|
||||
-- CLEAN THIS SHIT UP
|
||||
local diff_long = ""
|
||||
local diff_short = ""
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
if (song.difficulties[selectedDiff].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
else
|
||||
if (song.difficulties[selectedDiff - 1].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FontSize(8)
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
gfx.FastText(diff_long, x + h/4, y + h - 7)
|
||||
|
||||
local seldiff = nil
|
||||
if song.difficulties[selectedDiff] ~= nil then
|
||||
seldiff = selectedDiff
|
||||
else
|
||||
seldiff = selectedDiff - 1
|
||||
end
|
||||
|
||||
if song.difficulties[seldiff].topBadge ~= 0 then
|
||||
if song.difficulties[seldiff].scores[1] ~= nil then
|
||||
local highScore = song.difficulties[seldiff].scores[1]
|
||||
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 + w/1.45, y + h/8 + 2, (h/1.5-14), h/1.5-14, v.image, 1, 0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x + w/2, y + h/8, (h/1.5-10), h/1.5-10, badges[song.difficulties[seldiff].topBadge], 1, 0)
|
||||
end
|
||||
end
|
||||
|
||||
draw_diff_icon = function(diff, x, y, w, h, selected)
|
||||
local shrinkX = w/4
|
||||
local shrinkY = h/4
|
||||
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.FontSize(28)
|
||||
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
|
||||
local diffSpacingOffset = (diffWidth*0.82)*(selectedDiff-1)
|
||||
for i = math.max(selectedDiff - 3, 1), math.max(selectedDiff - 1,1) do
|
||||
local diff = diffs[i]
|
||||
local xpos = (x + ((w/2 - diffWidth/2) + (-0.8*diffWidth))) - ((diffWidth*0.82)*(i-selectedDiff+1))
|
||||
if i ~= selectedDiff then
|
||||
draw_diff_icon(diff, xpos, y, diffWidth, diffHeight, false)
|
||||
end
|
||||
end
|
||||
|
||||
--after selected
|
||||
for i = math.min(selectedDiff + 3, diffCount), selectedDiff + 1,-1 do
|
||||
local diff = diffs[i]
|
||||
local xpos = (x + ((w/2 - diffWidth/2) + (-0.8*diffWidth))) + ((diffWidth*0.82)*(i-1))
|
||||
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) + (-0.8*diffWidth))
|
||||
draw_diff_icon(diff, (xpos*0.9)+diffSpacingOffset, y, diffWidth, diffHeight, true)
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,128,255)
|
||||
gfx.Fill()
|
||||
gfx.BeginPath()
|
||||
gfx.Fill()
|
||||
gfx.ResetScissor()
|
||||
draw_cursor((x + (w/5.7))*(selectedDiff^1.085), y +diffHeight/2, timer * math.pi, diffHeight / 1.5)
|
||||
end
|
||||
|
||||
draw_selected = function(song, x, y, w, h)
|
||||
check_or_create_cache(song)
|
||||
-- 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/32)
|
||||
yPadding = math.floor(h/32)
|
||||
xMargin = math.floor(w/34)
|
||||
yMargin = math.floor(h/32)
|
||||
width = ((w/2)-(xMargin))
|
||||
height = (h-(yMargin*2))
|
||||
xpos = x+xMargin/2
|
||||
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,100)
|
||||
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/3)
|
||||
local imageXPos = ((width/2) - (imageSize/2)) + x+xMargin
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
--Unless its portrait widesreen..
|
||||
imageSize = math.floor((height/8)*1.58)
|
||||
imageXPos = (x+w)/16+(xMargin*0.8)
|
||||
end
|
||||
if not songCache[song.id][selectedDiff] or songCache[song.id][selectedDiff] == jacketFallback then
|
||||
songCache[song.id][selectedDiff] = gfx.LoadImageJob(diff.jacketPath, jacketFallback, 200,200)
|
||||
end
|
||||
|
||||
if songCache[song.id][selectedDiff] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(imageXPos, y+yMargin*4.45+yPadding, imageSize, imageSize, songCache[song.id][selectedDiff], 1, 0)
|
||||
end
|
||||
-- difficulty should take up 1/6 of height, full width, and be centered
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
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/1.5,(ypos*10.3+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
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+xPadding/2, y+yMargin*15+yPadding, width)
|
||||
gfx.FontSize(40)
|
||||
gfx.DrawLabel(songCache[song.id]["artist"], xpos+xPadding/2, y+yMargin*15.8+yPadding, width)
|
||||
gfx.FontSize(10)
|
||||
gfx.DrawLabel(songCache[song.id]["bpm"], xpos+xPadding*2, y+yMargin*14.42+yPadding, width-imageSize)
|
||||
gfx.FastText(string.format("%s", diff.effector), xpos+xPadding*7.5, y+yMargin*18.87+yPadding)
|
||||
else
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+10, (height/10)*6, width-20)
|
||||
gfx.FontSize(30)
|
||||
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)
|
||||
gfx.FastText(string.format("%s", diff.effector),xpos+10, (height/10)*6 + 115)
|
||||
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/2
|
||||
end
|
||||
local height = math.floor((h/wheelSize)*1.75)
|
||||
|
||||
for i = math.max(selectedIndex - wheelSize/2, 1), math.max(selectedIndex - 1,0) do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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, #songwheel.songs), selectedIndex + 1,-1 do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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 + width
|
||||
local offsetY = (ioffset/2) * ( height - (wheelSize/2*((1)*aspectFloat)))
|
||||
local ypos = y+((h/2 - height/2) - (ioffset) - offsetY)
|
||||
draw_song(songwheel.songs[selectedIndex], xpos, ypos, width, height, true)
|
||||
-- cursor
|
||||
gfx.BeginPath()
|
||||
local ypos = y+((h/2 - height/2))
|
||||
gfx.Rect(xpos, ypos, width, height)
|
||||
gfx.FillColor(0,0,0,0)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(3)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
return songwheel.songs[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) - (songwheel.searchInputActive and 0 or 1)
|
||||
if searchIndex ~= (songwheel.searchInputActive and 0 or 1) then
|
||||
game.PlaySample("woosh")
|
||||
end
|
||||
searchIndex = songwheel.searchInputActive and 0 or 1
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgfade = 1 - (searchIndex + soffset)
|
||||
--if not songwheel.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",songwheel.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)
|
||||
timer = (timer + deltaTime)
|
||||
timer = timer % 2
|
||||
resx,resy = game.GetResolution();
|
||||
adjustScreen(resx,resy);
|
||||
gfx.BeginPath();
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf");
|
||||
gfx.FontSize(40);
|
||||
gfx.FillColor(255,255,255);
|
||||
if songwheel.songs[1] ~= nil then
|
||||
--draw songwheel 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,resy)
|
||||
else
|
||||
local song = draw_songwheel(fifthX*2,0,fifthX*3,fullY)
|
||||
--render selected song information
|
||||
draw_selected(song, 0,0,fifthX*2,(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
|
||||
gfx.BeginPath();
|
||||
gfx.TextAlign(TEXT_ALIGN_CENTER + TEXT_ALIGN_MIDDLE);
|
||||
gfx.ImageRect(0, 0, resx, resy, foreground, 1, 0);
|
||||
|
||||
--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 songwheel.searchStatus then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Text(songwheel.searchStatus, 3, 3)
|
||||
end
|
||||
if totalForce then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM)
|
||||
local forceText = string.format("Force: %.2f", totalForce)
|
||||
gfx.Text(forceText, 0, fullY)
|
||||
end
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
gfx.ResetTransform()
|
||||
gfx.ForceRender()
|
||||
end
|
||||
|
||||
set_index = function(newIndex)
|
||||
if newIndex ~= selectedIndex then
|
||||
game.PlaySample("menu_click")
|
||||
end
|
||||
ioffset = ioffset + selectedIndex - newIndex
|
||||
selectedIndex = newIndex
|
||||
end;
|
||||
|
||||
set_diff = function(newDiff)
|
||||
if newDiff ~= selectedDiff then
|
||||
game.PlaySample("click-02")
|
||||
end
|
||||
doffset = doffset + selectedDiff - newDiff
|
||||
selectedDiff = newDiff
|
||||
end;
|
||||
|
||||
-- force calculation
|
||||
--------------------
|
||||
totalForce = nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
calculate_force = function(diff)
|
||||
if #diff.scores < 1 then
|
||||
return 0
|
||||
end
|
||||
local score = diff.scores[1]
|
||||
local badgeRate = badgeRates[diff.topBadge]
|
||||
local gradeRate
|
||||
for i, v in ipairs(gradeRates) do
|
||||
if score.score >= v.min then
|
||||
gradeRate = v.rate
|
||||
break
|
||||
end
|
||||
end
|
||||
return math.floor((diff.level * 2) * (score.score / 10000000) * gradeRate * badgeRate) / 100
|
||||
end
|
||||
|
||||
songs_changed = function(withAll)
|
||||
if not withAll then return end
|
||||
|
||||
recordCache = {}
|
||||
|
||||
local diffs = {}
|
||||
for i = 1, #songwheel.allSongs do
|
||||
local song = songwheel.allSongs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
diff.force = calculate_force(diff)
|
||||
table.insert(diffs, diff)
|
||||
end
|
||||
end
|
||||
table.sort(diffs, function (l, r)
|
||||
return l.force > r.force
|
||||
end)
|
||||
totalForce = 0
|
||||
for i = 1, 50 do
|
||||
if diffs[i] then
|
||||
totalForce = totalForce + diffs[i].force
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,894 +0,0 @@
|
|||
--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 effector = 0
|
||||
local searchText = gfx.CreateLabel("",5,0)
|
||||
local searchIndex = 1
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local showGuide = game.GetSkinSetting("show_guide")
|
||||
local legendTable = {
|
||||
{["labelSingleLine"] = gfx.CreateLabel("DIFFICULTY SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("DIFFICULTY\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-left.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-right.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("FILTER MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("FILTER\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-L.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("SORT MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SORT\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-R.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC MODS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nMODS",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("common/grades/D.png", 0)},
|
||||
{["max"] = 7999999, ["image"] = gfx.CreateSkinImage("common/grades/C.png", 0)},
|
||||
{["max"] = 8699999, ["image"] = gfx.CreateSkinImage("common/grades/B.png", 0)},
|
||||
{["max"] = 8999999, ["image"] = gfx.CreateSkinImage("common/grades/A.png", 0)},
|
||||
{["max"] = 9299999, ["image"] = gfx.CreateSkinImage("common/grades/A+.png", 0)},
|
||||
{["max"] = 9499999, ["image"] = gfx.CreateSkinImage("common/grades/AA.png", 0)},
|
||||
{["max"] = 9699999, ["image"] = gfx.CreateSkinImage("common/grades/AA+.png", 0)},
|
||||
{["max"] = 9799999, ["image"] = gfx.CreateSkinImage("common/grades/AAA.png", 0)},
|
||||
{["max"] = 9899999, ["image"] = gfx.CreateSkinImage("common/grades/AAA+.png", 0)},
|
||||
{["max"] = 99999999, ["image"] = gfx.CreateSkinImage("common/grades/S.png", 0)}
|
||||
}
|
||||
|
||||
local badges = {
|
||||
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 foreground = gfx.CreateSkinImage("song_select/fg.png", 0);
|
||||
|
||||
local recordCache = {}
|
||||
|
||||
gfx.LoadSkinFont("dfmarugoth.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, loadJacket)
|
||||
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, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["artist"] then
|
||||
songCache[song.id]["artist"] = gfx.CreateLabel(song.artist, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["bpm"] then
|
||||
songCache[song.id]["bpm"] = gfx.CreateLabel(string.format("%s",song.bpm), 12, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["effector"] then
|
||||
songCache[song.id]["effector"] = gfx.CreateLabel(string.format("BPM: %s",song.bpm), 20, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["jacket"] then
|
||||
songCache[song.id]["jacket"] = { }
|
||||
end
|
||||
|
||||
for i = 1, #song.difficulties do
|
||||
songCache[song.id]["jacket"][i] = gfx.LoadImageJob(song.difficulties[i].jacketPath, jacketFallback, 200, 200)
|
||||
end
|
||||
end
|
||||
|
||||
function record_handler_factory(hash)
|
||||
return (function(res)
|
||||
if res.statusCode == 42 then
|
||||
recordCache[hash] = {good=false, reason="Untracked"}
|
||||
elseif res.statusCode == 20 and res.body ~= nil then
|
||||
recordCache[hash] = {good=true, record=res.body.record}
|
||||
elseif res.statusCode == 44 then
|
||||
recordCache[hash] = {good=true, record=nil}
|
||||
else
|
||||
recordCache[hash] = {good=false, reason="Failed"}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function get_record(hash)
|
||||
if recordCache[hash] then return recordCache[hash] end
|
||||
|
||||
recordCache[hash] = {good=false, reason="Loading..."}
|
||||
|
||||
IR.Record(hash, record_handler_factory(hash))
|
||||
|
||||
return recordCache[hash]
|
||||
end
|
||||
|
||||
function log_table(table)
|
||||
str = "{"
|
||||
for k, v in pairs(table) do
|
||||
str = str .. k .. ": "
|
||||
|
||||
t = type(v)
|
||||
|
||||
if t == "table" then
|
||||
str = str .. log_table(v)
|
||||
elseif t == "string" then
|
||||
str = str .. "\"" .. v .. "\""
|
||||
elseif t == "boolean" then
|
||||
if v then
|
||||
str = str .. "true"
|
||||
else
|
||||
str = str .. "false"
|
||||
end
|
||||
else
|
||||
str = str .. v
|
||||
end
|
||||
|
||||
str = str .. ", "
|
||||
end
|
||||
|
||||
return str .. "}"
|
||||
end
|
||||
|
||||
draw_scores_ir = 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/4), y+(h/2))
|
||||
gfx.FastText("IR RECORD", x + (3/4 * w), y + (h/2))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+xOffset,y+h/2,w/2-(xOffset*2),h/2)
|
||||
gfx.FillColor(30,30,30,10)
|
||||
gfx.StrokeColor(0,128,255)
|
||||
gfx.StrokeWidth(1)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x + xOffset + w/2,y+h/2,w/2-(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)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
if difficulty.topBadge ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w/2-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/4),y+(h/4)*3,w/2)
|
||||
end
|
||||
|
||||
irRecord = get_record(difficulty.hash)
|
||||
|
||||
if not irRecord.good then
|
||||
recordLabel = gfx.CreateLabel(irRecord.reason, 40, 0)
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
elseif irRecord.record == nil then --record not set, but can be tracked
|
||||
recordLabel = gfx.CreateLabel(string.format("%08d", 0), 40, 0)
|
||||
gfx.FillColor(170, 170, 170)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
else
|
||||
|
||||
recordScoreLabel = gfx.CreateLabel(string.format("%08d", irRecord.record.score), 26, 0)
|
||||
recordPlayerLabel = gfx.CreateLabel(irRecord.record.username, 26, 0)
|
||||
|
||||
if irRecord.record.lamp ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[irRecord.record.lamp], 1, 0)
|
||||
end
|
||||
|
||||
for i,v in ipairs(grades) do
|
||||
if v.max > irRecord.record.score then
|
||||
gfx.BeginPath()
|
||||
iw,ih = gfx.ImageSize(v.image)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset+w/2, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordPlayerLabel, x+(w * 3/4),y+(h/4)*2.55,w/2)
|
||||
gfx.DrawLabel(recordScoreLabel, x+(w * 3/4),y+(h/4)*3.45,w/2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_scores = function(difficulty, x, y, w, h)
|
||||
if IRData.Active then return draw_scores_ir(difficulty, x, y, w, h) end
|
||||
|
||||
-- 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.BeginPath()
|
||||
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_BOTTOM + gfx.TEXT_ALIGN_LEFT);
|
||||
gfx.DrawLabel(scoreLabel, x/11,y/1.48,w*2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_song = function(song, x, y, w, h, selected)
|
||||
local diffIndex = math.min(selectedDiff, #song.difficulties)
|
||||
local difficulty = song.difficulties[diffIndex]
|
||||
local clearLampR = 255
|
||||
local clearLampG = 255
|
||||
local clearLampB = 255
|
||||
local clearLampA = 100
|
||||
|
||||
if difficulty ~= nil then
|
||||
if difficulty.scores[1] ~= nil then
|
||||
if difficulty.topBadge == 1 then -- fail/played
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 2 then -- clear
|
||||
clearLampR = 25
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 3 then -- hard clear
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 255
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 4 then -- full combo
|
||||
clearLampR = 255
|
||||
clearLampG = 100
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 5 then -- perfect
|
||||
clearLampR = 255
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_or_create_cache(song)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1,y+1, w-2, h-2)
|
||||
gfx.FillColor(220,220,220)
|
||||
gfx.StrokeColor(0,8,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
if songCache[song.id]["jacket"][diffIndex] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+2, y+2, h-4, h-4, songCache[song.id]["jacket"][diffIndex], 1, 0)
|
||||
end
|
||||
|
||||
-- Song title
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1, y + h - h/4 - 1, w-2, h/4)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], (x)+h/2 + 4, y + h - 7, -1)
|
||||
--gfx.DrawLabel(songCache[song.id]["artist"], x+10, y + 50, w-10)
|
||||
|
||||
-- Song difficulty
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x - 1, y + h-h/2 - 4, h/2, h/2)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BOTTOM)
|
||||
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
gfx.FastText(song.difficulties[selectedDiff].level, x + h/4, y + h - 10)
|
||||
else
|
||||
gfx.FastText(song.difficulties[selectedDiff - 1].level, x + h/4, y + h - 10)
|
||||
end
|
||||
|
||||
|
||||
-- CLEAN THIS SHIT UP
|
||||
local diff_long = ""
|
||||
local diff_short = ""
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
if (song.difficulties[selectedDiff].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
else
|
||||
if (song.difficulties[selectedDiff - 1].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FontSize(8)
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
gfx.FastText(diff_long, x + h/4, y + h - 7)
|
||||
|
||||
local seldiff = nil
|
||||
if song.difficulties[selectedDiff] ~= nil then
|
||||
seldiff = selectedDiff
|
||||
else
|
||||
seldiff = selectedDiff - 1
|
||||
end
|
||||
|
||||
if song.difficulties[seldiff].topBadge ~= 0 then
|
||||
if song.difficulties[seldiff].scores[1] ~= nil then
|
||||
local highScore = song.difficulties[seldiff].scores[1]
|
||||
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 + w/1.45, y + h/8 + 2, (h/1.5-14), h/1.5-14, v.image, 1, 0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x + w/2, y + h/8, (h/1.5-10), h/1.5-10, badges[song.difficulties[seldiff].topBadge], 1, 0)
|
||||
end
|
||||
end
|
||||
|
||||
draw_diff_icon = function(diff, x, y, w, h, selected)
|
||||
local shrinkX = w/4
|
||||
local shrinkY = h/4
|
||||
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.FontSize(28)
|
||||
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
|
||||
local diffSpacingOffset = (diffWidth*0.82)*(selectedDiff-1)
|
||||
for i = math.max(selectedDiff - 3, 1), math.max(selectedDiff - 1,1) do
|
||||
local diff = diffs[i]
|
||||
local xpos = (x + ((w/2 - diffWidth/2) + (-0.8*diffWidth))) - ((diffWidth*0.82)*(i-selectedDiff+1))
|
||||
if i ~= selectedDiff then
|
||||
draw_diff_icon(diff, xpos, y, diffWidth, diffHeight, false)
|
||||
end
|
||||
end
|
||||
|
||||
--after selected
|
||||
for i = math.min(selectedDiff + 3, diffCount), selectedDiff + 1,-1 do
|
||||
local diff = diffs[i]
|
||||
local xpos = (x + ((w/2 - diffWidth/2) + (-0.8*diffWidth))) + ((diffWidth*0.82)*(i-1))
|
||||
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) + (-0.8*diffWidth))
|
||||
draw_diff_icon(diff, (xpos*0.9)+diffSpacingOffset, y, diffWidth, diffHeight, true)
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(0,128,255)
|
||||
gfx.Fill()
|
||||
gfx.BeginPath()
|
||||
gfx.Fill()
|
||||
gfx.ResetScissor()
|
||||
draw_cursor((x + (w/5.7))*(selectedDiff^1.085), y +diffHeight/2, timer * math.pi, diffHeight / 1.5)
|
||||
end
|
||||
|
||||
draw_selected = function(song, x, y, w, h)
|
||||
check_or_create_cache(song)
|
||||
-- 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/32)
|
||||
yPadding = math.floor(h/32)
|
||||
xMargin = math.floor(w/34)
|
||||
yMargin = math.floor(h/32)
|
||||
width = ((w/2)-(xMargin))
|
||||
height = (h-(yMargin*2))
|
||||
xpos = x+xMargin/2
|
||||
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,100)
|
||||
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/3)
|
||||
local imageXPos = ((width/2) - (imageSize/2)) + x+xMargin
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
--Unless its portrait widesreen..
|
||||
imageSize = math.floor((height/8)*1.58)
|
||||
imageXPos = (x+w)/16+(xMargin*0.8)
|
||||
end
|
||||
if not songCache[song.id][selectedDiff] or songCache[song.id][selectedDiff] == jacketFallback then
|
||||
songCache[song.id][selectedDiff] = gfx.LoadImageJob(diff.jacketPath, jacketFallback, 200,200)
|
||||
end
|
||||
|
||||
if songCache[song.id][selectedDiff] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(imageXPos, y+yMargin*4.45+yPadding, imageSize, imageSize, songCache[song.id][selectedDiff], 1, 0)
|
||||
end
|
||||
-- difficulty should take up 1/6 of height, full width, and be centered
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
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/1.5,(ypos*10.3+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
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+xPadding/2, y+yMargin*15+yPadding, width)
|
||||
gfx.FontSize(40)
|
||||
gfx.DrawLabel(songCache[song.id]["artist"], xpos+xPadding/2, y+yMargin*15.8+yPadding, width)
|
||||
gfx.FontSize(10)
|
||||
gfx.DrawLabel(songCache[song.id]["bpm"], xpos+xPadding*2, y+yMargin*14.42+yPadding, width-imageSize)
|
||||
gfx.FastText(string.format("%s", diff.effector), xpos+xPadding*7.5, y+yMargin*18.87+yPadding)
|
||||
else
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+10, (height/10)*6, width-20)
|
||||
gfx.FontSize(30)
|
||||
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)
|
||||
gfx.FastText(string.format("%s", diff.effector),xpos+10, (height/10)*6 + 115)
|
||||
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/2
|
||||
end
|
||||
local height = math.floor((h/wheelSize)*1.75)
|
||||
|
||||
for i = math.max(selectedIndex - wheelSize/2, 1), math.max(selectedIndex - 1,0) do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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, #songwheel.songs), selectedIndex + 1,-1 do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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 + width
|
||||
local offsetY = (ioffset/2) * ( height - (wheelSize/2*((1)*aspectFloat)))
|
||||
local ypos = y+((h/2 - height/2) - (ioffset) - offsetY)
|
||||
draw_song(songwheel.songs[selectedIndex], xpos, ypos, width, height, true)
|
||||
-- cursor
|
||||
gfx.BeginPath()
|
||||
local ypos = y+((h/2 - height/2))
|
||||
gfx.Rect(xpos, ypos, width, height)
|
||||
gfx.FillColor(0,0,0,0)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(3)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
return songwheel.songs[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) - (songwheel.searchInputActive and 0 or 1)
|
||||
if searchIndex ~= (songwheel.searchInputActive and 0 or 1) then
|
||||
game.PlaySample("woosh")
|
||||
end
|
||||
searchIndex = songwheel.searchInputActive and 0 or 1
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgfade = 1 - (searchIndex + soffset)
|
||||
--if not songwheel.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",songwheel.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)
|
||||
timer = (timer + deltaTime)
|
||||
timer = timer % 2
|
||||
resx,resy = game.GetResolution();
|
||||
adjustScreen(resx,resy);
|
||||
gfx.BeginPath();
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf");
|
||||
gfx.FontSize(40);
|
||||
gfx.FillColor(255,255,255);
|
||||
if songwheel.songs[1] ~= nil then
|
||||
--draw songwheel 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,resy)
|
||||
else
|
||||
local song = draw_songwheel(fifthX*2,0,fifthX*3,fullY)
|
||||
--render selected song information
|
||||
draw_selected(song, 0,0,fifthX*2,(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
|
||||
gfx.BeginPath();
|
||||
gfx.TextAlign(TEXT_ALIGN_CENTER + TEXT_ALIGN_MIDDLE);
|
||||
gfx.ImageRect(0, 0, resx, resy, foreground, 1, 0);
|
||||
|
||||
--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 songwheel.searchStatus then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Text(songwheel.searchStatus, 3, 3)
|
||||
end
|
||||
if totalForce then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM)
|
||||
local forceText = string.format("Force: %.2f", totalForce)
|
||||
gfx.Text(forceText, 0, fullY)
|
||||
end
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
gfx.ResetTransform()
|
||||
gfx.ForceRender()
|
||||
end
|
||||
|
||||
set_index = function(newIndex)
|
||||
if newIndex ~= selectedIndex then
|
||||
game.PlaySample("menu_click")
|
||||
end
|
||||
ioffset = ioffset + selectedIndex - newIndex
|
||||
selectedIndex = newIndex
|
||||
end;
|
||||
|
||||
set_diff = function(newDiff)
|
||||
if newDiff ~= selectedDiff then
|
||||
game.PlaySample("click-02")
|
||||
end
|
||||
doffset = doffset + selectedDiff - newDiff
|
||||
selectedDiff = newDiff
|
||||
end;
|
||||
|
||||
-- force calculation
|
||||
--------------------
|
||||
totalForce = nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
calculate_force = function(diff)
|
||||
if #diff.scores < 1 then
|
||||
return 0
|
||||
end
|
||||
local score = diff.scores[1]
|
||||
local badgeRate = badgeRates[diff.topBadge]
|
||||
local gradeRate
|
||||
for i, v in ipairs(gradeRates) do
|
||||
if score.score >= v.min then
|
||||
gradeRate = v.rate
|
||||
break
|
||||
end
|
||||
end
|
||||
return math.floor((diff.level * 2) * (score.score / 10000000) * gradeRate * badgeRate) / 100
|
||||
end
|
||||
|
||||
songs_changed = function(withAll)
|
||||
if not withAll then return end
|
||||
|
||||
recordCache = {}
|
||||
|
||||
local diffs = {}
|
||||
for i = 1, #songwheel.allSongs do
|
||||
local song = songwheel.allSongs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
diff.force = calculate_force(diff)
|
||||
table.insert(diffs, diff)
|
||||
end
|
||||
end
|
||||
table.sort(diffs, function (l, r)
|
||||
return l.force > r.force
|
||||
end)
|
||||
totalForce = 0
|
||||
for i = 1, 50 do
|
||||
if diffs[i] then
|
||||
totalForce = totalForce + diffs[i].force
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,897 +0,0 @@
|
|||
--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 effector = 0
|
||||
local searchText = gfx.CreateLabel("",5,0)
|
||||
local searchIndex = 1
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local showGuide = game.GetSkinSetting("show_guide")
|
||||
local legendTable = {
|
||||
{["labelSingleLine"] = gfx.CreateLabel("DIFFICULTY SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("DIFFICULTY\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-left.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-right.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("FILTER MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("FILTER\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-L.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("SORT MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SORT\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-R.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC MODS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nMODS",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("common/grades/D.png", 0)},
|
||||
{["max"] = 7999999, ["image"] = gfx.CreateSkinImage("common/grades/C.png", 0)},
|
||||
{["max"] = 8699999, ["image"] = gfx.CreateSkinImage("common/grades/B.png", 0)},
|
||||
{["max"] = 8999999, ["image"] = gfx.CreateSkinImage("common/grades/A.png", 0)},
|
||||
{["max"] = 9299999, ["image"] = gfx.CreateSkinImage("common/grades/A+.png", 0)},
|
||||
{["max"] = 9499999, ["image"] = gfx.CreateSkinImage("common/grades/AA.png", 0)},
|
||||
{["max"] = 9699999, ["image"] = gfx.CreateSkinImage("common/grades/AA+.png", 0)},
|
||||
{["max"] = 9799999, ["image"] = gfx.CreateSkinImage("common/grades/AAA.png", 0)},
|
||||
{["max"] = 9899999, ["image"] = gfx.CreateSkinImage("common/grades/AAA+.png", 0)},
|
||||
{["max"] = 99999999, ["image"] = gfx.CreateSkinImage("common/grades/S.png", 0)}
|
||||
}
|
||||
|
||||
local badges = {
|
||||
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 recordCache = {}
|
||||
|
||||
gfx.LoadSkinFont("dfmarugoth.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, loadJacket)
|
||||
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, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["artist"] then
|
||||
songCache[song.id]["artist"] = gfx.CreateLabel(song.artist, 25, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["bpm"] then
|
||||
songCache[song.id]["bpm"] = gfx.CreateLabel(string.format("BPM: %s",song.bpm), 20, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["effector"] then
|
||||
songCache[song.id]["effector"] = gfx.CreateLabel(string.format("BPM: %s",song.bpm), 20, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["jacket"] and loadJacket then
|
||||
songCache[song.id]["jacket"] = gfx.CreateImage(song.difficulties[1].jacketPath, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function record_handler_factory(hash)
|
||||
return (function(res)
|
||||
if res.statusCode == 42 then
|
||||
recordCache[hash] = {good=false, reason="Untracked"}
|
||||
elseif res.statusCode == 20 and res.body ~= nil then
|
||||
recordCache[hash] = {good=true, record=res.body.record}
|
||||
elseif res.statusCode == 44 then
|
||||
recordCache[hash] = {good=true, record=nil}
|
||||
else
|
||||
recordCache[hash] = {good=false, reason="Failed"}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function get_record(hash)
|
||||
if recordCache[hash] then return recordCache[hash] end
|
||||
|
||||
recordCache[hash] = {good=false, reason="Loading..."}
|
||||
|
||||
IR.Record(hash, record_handler_factory(hash))
|
||||
|
||||
return recordCache[hash]
|
||||
end
|
||||
|
||||
function log_table(table)
|
||||
str = "{"
|
||||
for k, v in pairs(table) do
|
||||
str = str .. k .. ": "
|
||||
|
||||
t = type(v)
|
||||
|
||||
if t == "table" then
|
||||
str = str .. log_table(v)
|
||||
elseif t == "string" then
|
||||
str = str .. "\"" .. v .. "\""
|
||||
elseif t == "boolean" then
|
||||
if v then
|
||||
str = str .. "true"
|
||||
else
|
||||
str = str .. "false"
|
||||
end
|
||||
else
|
||||
str = str .. v
|
||||
end
|
||||
|
||||
str = str .. ", "
|
||||
end
|
||||
|
||||
return str .. "}"
|
||||
end
|
||||
|
||||
draw_scores_ir = 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/4), y+(h/2))
|
||||
gfx.FastText("IR RECORD", x + (3/4 * w), y + (h/2))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+xOffset,y+h/2,w/2-(xOffset*2),h/2)
|
||||
gfx.FillColor(30,30,30,10)
|
||||
gfx.StrokeColor(0,128,255)
|
||||
gfx.StrokeWidth(1)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x + xOffset + w/2,y+h/2,w/2-(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)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
if difficulty.topBadge ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w/2-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/4),y+(h/4)*3,w/2)
|
||||
end
|
||||
|
||||
irRecord = get_record(difficulty.hash)
|
||||
|
||||
if not irRecord.good then
|
||||
recordLabel = gfx.CreateLabel(irRecord.reason, 40, 0)
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
elseif irRecord.record == nil then --record not set, but can be tracked
|
||||
recordLabel = gfx.CreateLabel(string.format("%08d", 0), 40, 0)
|
||||
gfx.FillColor(170, 170, 170)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
else
|
||||
|
||||
recordScoreLabel = gfx.CreateLabel(string.format("%08d", irRecord.record.score), 26, 0)
|
||||
recordPlayerLabel = gfx.CreateLabel(irRecord.record.username, 26, 0)
|
||||
|
||||
if irRecord.record.lamp ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[irRecord.record.lamp], 1, 0)
|
||||
end
|
||||
|
||||
for i,v in ipairs(grades) do
|
||||
if v.max > irRecord.record.score then
|
||||
gfx.BeginPath()
|
||||
iw,ih = gfx.ImageSize(v.image)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset+w/2, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordPlayerLabel, x+(w * 3/4),y+(h/4)*2.55,w/2)
|
||||
gfx.DrawLabel(recordScoreLabel, x+(w * 3/4),y+(h/4)*3.45,w/2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_scores = function(difficulty, x, y, w, h)
|
||||
if IRData.Active then return draw_scores_ir(difficulty, x, y, w, h) end
|
||||
|
||||
-- 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)
|
||||
local difficulty = song.difficulties[selectedDiff]
|
||||
local clearLampR = 255
|
||||
local clearLampG = 255
|
||||
local clearLampB = 255
|
||||
local clearLampA = 100
|
||||
|
||||
if difficulty ~= nil then
|
||||
if difficulty.scores[1] ~= nil then
|
||||
if difficulty.topBadge == 1 then -- fail/played
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 2 then -- clear
|
||||
clearLampR = 25
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 3 then -- hard clear
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 255
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 4 then -- full combo
|
||||
clearLampR = 255
|
||||
clearLampG = 100
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 5 then -- perfect
|
||||
clearLampR = 255
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_or_create_cache(song)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1,y+1, w-2, h-2)
|
||||
gfx.FillColor(220,220,220)
|
||||
gfx.StrokeColor(0,8,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
if not songCache[song.id][1] or songCache[song.id][1] == jacketFallback then
|
||||
songCache[song.id][1] = gfx.LoadImageJob(song.difficulties[1].jacketPath, jacketFallback, 200,200)
|
||||
end
|
||||
if songCache[song.id][1] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+2, y+2, h-4, h-4, songCache[song.id][1], 1, 0)
|
||||
end
|
||||
|
||||
-- Song title
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1, y + h - h/4 - 1, w-2, h/4)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], (x)+h/2 + 4, y + h - 7, -1)
|
||||
--gfx.DrawLabel(songCache[song.id]["artist"], x+10, y + 50, w-10)
|
||||
|
||||
-- Song difficulty
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x - 1, y + h-h/2 - 4, h/2, h/2)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BOTTOM)
|
||||
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
gfx.FastText(song.difficulties[selectedDiff].level, x + h/4, y + h - 10)
|
||||
else
|
||||
gfx.FastText(song.difficulties[selectedDiff - 1].level, x + h/4, y + h - 10)
|
||||
end
|
||||
|
||||
|
||||
-- CLEAN THIS SHIT UP
|
||||
local diff_long = ""
|
||||
local diff_short = ""
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
if (song.difficulties[selectedDiff].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
else
|
||||
if (song.difficulties[selectedDiff - 1].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff - 1].difficulty == 3) then
|
||||
diff_long = "INFINITE"
|
||||
diff_short = "INF"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FontSize(8)
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
gfx.FastText(diff_long, x + h/4, y + h - 7)
|
||||
|
||||
local seldiff = nil
|
||||
if song.difficulties[selectedDiff] ~= nil then
|
||||
seldiff = selectedDiff
|
||||
else
|
||||
seldiff = selectedDiff - 1
|
||||
end
|
||||
|
||||
if song.difficulties[seldiff].topBadge ~= 0 then
|
||||
if song.difficulties[seldiff].scores[1] ~= nil then
|
||||
local highScore = song.difficulties[seldiff].scores[1]
|
||||
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 + w/1.45, y + h/8 + 2, (h/1.5-14), h/1.5-14, v.image, 1, 0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x + w/2, y + h/8, (h/1.5-10), h/1.5-10, badges[song.difficulties[seldiff].topBadge], 1, 0)
|
||||
end
|
||||
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)
|
||||
-- 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/32)
|
||||
yPadding = math.floor(h/32)
|
||||
xMargin = math.floor(w/34)
|
||||
yMargin = math.floor(h/32)
|
||||
width = ((w/2)-(xMargin))
|
||||
height = (h-(yMargin*2))
|
||||
xpos = x+xMargin/2
|
||||
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,100)
|
||||
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/3)
|
||||
local imageXPos = ((width/2) - (imageSize/2)) + x+xMargin
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
--Unless its portrait widesreen..
|
||||
imageSize = math.floor((height/8)*2)
|
||||
imageXPos = x+xMargin
|
||||
end
|
||||
if not songCache[song.id][selectedDiff] or songCache[song.id][selectedDiff] == jacketFallback then
|
||||
songCache[song.id][selectedDiff] = gfx.LoadImageJob(diff.jacketPath, jacketFallback, 200,200)
|
||||
end
|
||||
|
||||
if songCache[song.id][selectedDiff] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(imageXPos, y+yMargin+yPadding, imageSize, imageSize, songCache[song.id][selectedDiff], 1, 0)
|
||||
end
|
||||
-- difficulty should take up 1/6 of height, full width, and be centered
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
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
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+xPadding+imageSize, y+yMargin+yPadding, width-imageSize-20)
|
||||
gfx.FontSize(30)
|
||||
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)
|
||||
else
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+10, (height/10)*6, width-20)
|
||||
gfx.FontSize(30)
|
||||
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)
|
||||
gfx.FastText(string.format("Effector: %s", diff.effector),xpos+10, (height/10)*6 + 115)
|
||||
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/2
|
||||
end
|
||||
local height = math.floor((h/wheelSize)*1.75)
|
||||
|
||||
for i = math.max(selectedIndex - wheelSize/2, 1), math.max(selectedIndex - 1,0) do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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, #songwheel.songs), selectedIndex + 1,-1 do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.05)
|
||||
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 + width
|
||||
local offsetY = (ioffset/2) * ( height - (wheelSize/2*((1)*aspectFloat)))
|
||||
local ypos = y+((h/2 - height/2) - (ioffset) - offsetY)
|
||||
draw_song(songwheel.songs[selectedIndex], xpos, ypos, width, height, true)
|
||||
-- cursor
|
||||
gfx.BeginPath()
|
||||
local ypos = y+((h/2 - height/2))
|
||||
gfx.Rect(xpos, ypos, width, height)
|
||||
gfx.FillColor(0,0,0,0)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(3)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
return songwheel.songs[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) - (songwheel.searchInputActive and 0 or 1)
|
||||
if searchIndex ~= (songwheel.searchInputActive and 0 or 1) then
|
||||
game.PlaySample("woosh")
|
||||
end
|
||||
searchIndex = songwheel.searchInputActive and 0 or 1
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgfade = 1 - (searchIndex + soffset)
|
||||
--if not songwheel.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",songwheel.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)
|
||||
timer = (timer + deltaTime)
|
||||
timer = timer % 2
|
||||
resx,resy = game.GetResolution();
|
||||
adjustScreen(resx,resy);
|
||||
gfx.BeginPath();
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf");
|
||||
gfx.FontSize(40);
|
||||
gfx.FillColor(255,255,255);
|
||||
if songwheel.songs[1] ~= nil then
|
||||
--draw songwheel 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,resy)
|
||||
else
|
||||
local song = draw_songwheel(fifthX*2,0,fifthX*3,fullY)
|
||||
--render selected song information
|
||||
draw_selected(song, 0,0,fifthX*2,(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 songwheel.searchStatus then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Text(songwheel.searchStatus, 3, 3)
|
||||
end
|
||||
if totalForce then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM)
|
||||
local forceText = string.format("Force: %.2f", totalForce)
|
||||
gfx.Text(forceText, 0, fullY)
|
||||
end
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
gfx.ResetTransform()
|
||||
gfx.ForceRender()
|
||||
end
|
||||
|
||||
set_index = function(newIndex)
|
||||
if newIndex ~= selectedIndex then
|
||||
game.PlaySample("menu_click")
|
||||
end
|
||||
ioffset = ioffset + selectedIndex - newIndex
|
||||
selectedIndex = newIndex
|
||||
end;
|
||||
|
||||
set_diff = function(newDiff)
|
||||
if newDiff ~= selectedDiff then
|
||||
game.PlaySample("click-02")
|
||||
end
|
||||
doffset = doffset + selectedDiff - newDiff
|
||||
selectedDiff = newDiff
|
||||
end;
|
||||
|
||||
-- force calculation
|
||||
--------------------
|
||||
totalForce = nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
calculate_force = function(diff)
|
||||
if #diff.scores < 1 then
|
||||
return 0
|
||||
end
|
||||
local score = diff.scores[1]
|
||||
local badgeRate = badgeRates[diff.topBadge]
|
||||
local gradeRate
|
||||
for i, v in ipairs(gradeRates) do
|
||||
if score.score >= v.min then
|
||||
gradeRate = v.rate
|
||||
break
|
||||
end
|
||||
end
|
||||
return math.floor((diff.level * 2) * (score.score / 10000000) * gradeRate * badgeRate) / 100
|
||||
end
|
||||
|
||||
songs_changed = function(withAll)
|
||||
if not withAll then return end
|
||||
|
||||
recordCache = {}
|
||||
|
||||
local diffs = {}
|
||||
for i = 1, #songwheel.allSongs do
|
||||
local song = songwheel.allSongs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
diff.force = calculate_force(diff)
|
||||
table.insert(diffs, diff)
|
||||
end
|
||||
end
|
||||
table.sort(diffs, function (l, r)
|
||||
return l.force > r.force
|
||||
end)
|
||||
totalForce = 0
|
||||
for i = 1, 50 do
|
||||
if diffs[i] then
|
||||
totalForce = totalForce + diffs[i].force
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,945 +0,0 @@
|
|||
-- game.Log("Something went wrong!", game.LOGGER_ERROR)
|
||||
|
||||
--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 effector = 0
|
||||
local searchText = gfx.CreateLabel("",5,0)
|
||||
local searchIndex = 1
|
||||
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
local showGuide = game.GetSkinSetting("show_guide")
|
||||
local legendTable = {
|
||||
{["labelSingleLine"] = gfx.CreateLabel("DIFFICULTY SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("DIFFICULTY\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-left.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC SELECT",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nSELECT",16, 0), ["image"] = gfx.CreateSkinImage("legend/knob-right.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("FILTER MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("FILTER\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-L.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("SORT MUSIC",16, 0), ["labelMultiLine"] = gfx.CreateLabel("SORT\nMUSIC",16, 0), ["image"] = gfx.CreateSkinImage("legend/FX-R.png", 0)},
|
||||
{["labelSingleLine"] = gfx.CreateLabel("MUSIC MODS",16, 0), ["labelMultiLine"] = gfx.CreateLabel("MUSIC\nMODS",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("common/grades/D.png", 0)},
|
||||
{["max"] = 7999999, ["image"] = gfx.CreateSkinImage("common/grades/C.png", 0)},
|
||||
{["max"] = 8699999, ["image"] = gfx.CreateSkinImage("common/grades/B.png", 0)},
|
||||
{["max"] = 8999999, ["image"] = gfx.CreateSkinImage("common/grades/A.png", 0)},
|
||||
{["max"] = 9299999, ["image"] = gfx.CreateSkinImage("common/grades/A+.png", 0)},
|
||||
{["max"] = 9499999, ["image"] = gfx.CreateSkinImage("common/grades/AA.png", 0)},
|
||||
{["max"] = 9699999, ["image"] = gfx.CreateSkinImage("common/grades/AA+.png", 0)},
|
||||
{["max"] = 9799999, ["image"] = gfx.CreateSkinImage("common/grades/AAA.png", 0)},
|
||||
{["max"] = 9899999, ["image"] = gfx.CreateSkinImage("common/grades/AAA+.png", 0)},
|
||||
{["max"] = 99999999, ["image"] = gfx.CreateSkinImage("common/grades/S.png", 0)}
|
||||
}
|
||||
|
||||
local badges = {
|
||||
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 difficultyNumbers = {
|
||||
[0] = gfx.CreateSkinImage("diff_num/0.png", 0),
|
||||
[1] = gfx.CreateSkinImage("diff_num/1.png", 0),
|
||||
[2] = gfx.CreateSkinImage("diff_num/2.png", 0),
|
||||
[3] = gfx.CreateSkinImage("diff_num/3.png", 0),
|
||||
[4] = gfx.CreateSkinImage("diff_num/4.png", 0),
|
||||
[5] = gfx.CreateSkinImage("diff_num/5.png", 0),
|
||||
[6] = gfx.CreateSkinImage("diff_num/6.png", 0),
|
||||
[7] = gfx.CreateSkinImage("diff_num/7.png", 0),
|
||||
[8] = gfx.CreateSkinImage("diff_num/8.png", 0),
|
||||
[9] = gfx.CreateSkinImage("diff_num/9.png", 0),
|
||||
};
|
||||
|
||||
local difficultyNameOverlays = {
|
||||
[0] = gfx.CreateSkinImage("song_select/level/novice.png", 0),
|
||||
[1] = gfx.CreateSkinImage("song_select/level/advanced.png", 0),
|
||||
[2] = gfx.CreateSkinImage("song_select/level/exhaust.png", 0),
|
||||
[3] = gfx.CreateSkinImage("song_select/level/maximum.png", 0),
|
||||
[4] = gfx.CreateSkinImage("song_select/level/maximum.png", 0),
|
||||
[5] = gfx.CreateSkinImage("song_select/level/maximum.png", 0),
|
||||
[6] = gfx.CreateSkinImage("song_select/level/maximum.png", 0),
|
||||
[7] = gfx.CreateSkinImage("song_select/level/maximum.png", 0),
|
||||
}
|
||||
|
||||
local difficultyLevelCursor = gfx.CreateSkinImage("song_select/level_cursor.png", 0);
|
||||
|
||||
local foreground = gfx.CreateSkinImage("song_select/fg.png", 0);
|
||||
|
||||
local datapanel = gfx.CreateSkinImage("song_select/data_bg.png", 0);
|
||||
|
||||
local recordCache = {}
|
||||
|
||||
gfx.LoadSkinFont("dfmarugoth.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, loadJacket)
|
||||
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, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["artist"] then
|
||||
songCache[song.id]["artist"] = gfx.CreateLabel(song.artist, 14, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["bpm"] then
|
||||
songCache[song.id]["bpm"] = gfx.CreateLabel(string.format("%s",song.bpm), 12, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["effector"] then
|
||||
songCache[song.id]["effector"] = gfx.CreateLabel(string.format("BPM: %s",song.bpm), 20, 0)
|
||||
end
|
||||
|
||||
if not songCache[song.id]["jacket"] then
|
||||
songCache[song.id]["jacket"] = { }
|
||||
end
|
||||
|
||||
for i = 1, #song.difficulties do
|
||||
songCache[song.id]["jacket"][i] = gfx.LoadImageJob(song.difficulties[i].jacketPath, jacketFallback, 400, 400)
|
||||
end
|
||||
end
|
||||
|
||||
function record_handler_factory(hash)
|
||||
return (function(res)
|
||||
if res.statusCode == 42 then
|
||||
recordCache[hash] = {good=false, reason="Untracked"}
|
||||
elseif res.statusCode == 20 and res.body ~= nil then
|
||||
recordCache[hash] = {good=true, record=res.body.record}
|
||||
elseif res.statusCode == 44 then
|
||||
recordCache[hash] = {good=true, record=nil}
|
||||
else
|
||||
recordCache[hash] = {good=false, reason="Failed"}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function get_record(hash)
|
||||
if recordCache[hash] then return recordCache[hash] end
|
||||
|
||||
recordCache[hash] = {good=false, reason="Loading..."}
|
||||
|
||||
IR.Record(hash, record_handler_factory(hash))
|
||||
|
||||
return recordCache[hash]
|
||||
end
|
||||
|
||||
function log_table(table)
|
||||
str = "{"
|
||||
for k, v in pairs(table) do
|
||||
str = str .. k .. ": "
|
||||
|
||||
t = type(v)
|
||||
|
||||
if t == "table" then
|
||||
str = str .. log_table(v)
|
||||
elseif t == "string" then
|
||||
str = str .. "\"" .. v .. "\""
|
||||
elseif t == "boolean" then
|
||||
if v then
|
||||
str = str .. "true"
|
||||
else
|
||||
str = str .. "false"
|
||||
end
|
||||
else
|
||||
str = str .. v
|
||||
end
|
||||
|
||||
str = str .. ", "
|
||||
end
|
||||
|
||||
return str .. "}"
|
||||
end
|
||||
|
||||
draw_scores_ir = 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/4), y+(h/2))
|
||||
gfx.FastText("IR RECORD", x + (3/4 * w), y + (h/2))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+xOffset,y+h/2,w/2-(xOffset*2),h/2)
|
||||
gfx.FillColor(30,30,30,10)
|
||||
gfx.StrokeColor(0,128,255)
|
||||
gfx.StrokeWidth(1)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x + xOffset + w/2,y+h/2,w/2-(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)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
if difficulty.topBadge ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w/2-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/4),y+(h/4)*3,w/2)
|
||||
end
|
||||
|
||||
irRecord = get_record(difficulty.hash)
|
||||
|
||||
if not irRecord.good then
|
||||
recordLabel = gfx.CreateLabel(irRecord.reason, 40, 0)
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
elseif irRecord.record == nil then --record not set, but can be tracked
|
||||
recordLabel = gfx.CreateLabel(string.format("%08d", 0), 40, 0)
|
||||
gfx.FillColor(170, 170, 170)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordLabel, x+(w * 3/4),y+(h/4)*3,w/2)
|
||||
else
|
||||
|
||||
recordScoreLabel = gfx.CreateLabel(string.format("%08d", irRecord.record.score), 26, 0)
|
||||
recordPlayerLabel = gfx.CreateLabel(irRecord.record.username, 26, 0)
|
||||
|
||||
if irRecord.record.lamp ~= 0 then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+xOffset+w-h/2, y+h/2 +5, (h/2-10), h/2-10, badges[irRecord.record.lamp], 1, 0)
|
||||
end
|
||||
|
||||
for i,v in ipairs(grades) do
|
||||
if v.max > irRecord.record.score then
|
||||
gfx.BeginPath()
|
||||
iw,ih = gfx.ImageSize(v.image)
|
||||
iarr = ih / iw
|
||||
oldheight = h/2 - 10
|
||||
newheight = iarr * (h/2-10)
|
||||
centreoffset = (oldheight - newheight)/2 + 3 -- +3 is stupid but ehhh
|
||||
gfx.ImageRect(x+xOffset+w/2, y+h/2 + centreoffset, oldheight, newheight, v.image, 1, 0) --this is nasty but it works for me
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FillColor(255, 255, 255)
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER);
|
||||
gfx.DrawLabel(recordPlayerLabel, x+(w * 3/4),y+(h/4)*2.55,w/2)
|
||||
gfx.DrawLabel(recordScoreLabel, x+(w * 3/4),y+(h/4)*3.45,w/2)
|
||||
end
|
||||
end
|
||||
|
||||
draw_scores = function(difficulty, x, y, w, h)
|
||||
if IRData.Active then return draw_scores_ir(difficulty, x, y, w, h) end
|
||||
|
||||
-- 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.BeginPath()
|
||||
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_BOTTOM + gfx.TEXT_ALIGN_LEFT);
|
||||
gfx.DrawLabel(scoreLabel, x/11,y/1.48,w*2)
|
||||
end
|
||||
end
|
||||
|
||||
function deep_to_string(t)
|
||||
local tType = type(t);
|
||||
if (tType ~= "table") then
|
||||
return tostring(t);
|
||||
end
|
||||
|
||||
local result = "{";
|
||||
|
||||
for k, v in next, t do
|
||||
local kType = type(k);
|
||||
local vType = type(v);
|
||||
|
||||
local keyString = deep_to_string(k);
|
||||
local valueString = deep_to_string(v);
|
||||
|
||||
if (#result > 1) then
|
||||
result = result .. ";";
|
||||
end
|
||||
|
||||
result = result .. keyString .. "=" .. valueString;
|
||||
end
|
||||
|
||||
return result .. "}";
|
||||
end
|
||||
|
||||
draw_song = function(song, x, y, w, h, selected)
|
||||
-- game.Log("draw_song", game.LOGGER_ERROR);
|
||||
|
||||
local diffIndex = math.min(selectedDiff, #song.difficulties)
|
||||
local difficulty = song.difficulties[diffIndex]
|
||||
local clearLampR = 255
|
||||
local clearLampG = 255
|
||||
local clearLampB = 255
|
||||
local clearLampA = 100
|
||||
|
||||
if difficulty ~= nil then
|
||||
-- game.Log(deep_to_string(difficulty), game.LOGGER_ERROR);
|
||||
if difficulty.scores[1] ~= nil then
|
||||
if difficulty.topBadge == 1 then -- fail/played
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 2 then -- clear
|
||||
clearLampR = 25
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 3 then -- hard clear
|
||||
clearLampR = 255
|
||||
clearLampG = 25
|
||||
clearLampB = 255
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 4 then -- full combo
|
||||
clearLampR = 255
|
||||
clearLampG = 100
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
if difficulty.topBadge == 5 then -- perfect
|
||||
clearLampR = 255
|
||||
clearLampG = 255
|
||||
clearLampB = 25
|
||||
clearLampA = 200
|
||||
end
|
||||
end
|
||||
end
|
||||
-- game.Log(" past difficulty check", game.LOGGER_ERROR);
|
||||
|
||||
check_or_create_cache(song)
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1,y+1, w-2, h-2)
|
||||
gfx.FillColor(220,220,220)
|
||||
gfx.StrokeColor(0,8,0)
|
||||
gfx.StrokeWidth(2)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
gfx.FillColor(255,255,255)
|
||||
if songCache[song.id]["jacket"][diffIndex] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x+2, y+2, h-4, h-4, songCache[song.id]["jacket"][diffIndex], 1, 0)
|
||||
end
|
||||
|
||||
-- Song title
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x+1, y + h - h/4 - 1, w-2, h/4)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], (x)+h/2 + 4, y + h - 7, -1)
|
||||
--gfx.DrawLabel(songCache[song.id]["artist"], x+10, y + 50, w-10)
|
||||
|
||||
-- Song difficulty
|
||||
gfx.BeginPath()
|
||||
gfx.Rect(x - 1, y + h-h/2 - 4, h/2, h/2)
|
||||
gfx.FillColor(0,0,0,200)
|
||||
gfx.Fill()
|
||||
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
gfx.FontSize(28)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_BOTTOM)
|
||||
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
gfx.FastText(song.difficulties[selectedDiff].level, x + h/4, y + h - 10)
|
||||
else
|
||||
--gfx.FastText(song.difficulties[selectedDiff - 1].level, x + h/4, y + h - 10)
|
||||
end
|
||||
|
||||
-- CLEAN THIS SHIT UP
|
||||
local diff_long = ""
|
||||
local diff_short = ""
|
||||
if (song.difficulties[selectedDiff] ~= nil) then
|
||||
if (song.difficulties[selectedDiff].difficulty == 0) then
|
||||
diff_long = "NOVICE"
|
||||
diff_short = "NOV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 1) then
|
||||
diff_long = "ADVANCED"
|
||||
diff_short = "ADV"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 2) then
|
||||
diff_long = "EXHAUST"
|
||||
diff_short = "EXH"
|
||||
elseif (song.difficulties[selectedDiff].difficulty == 3) then
|
||||
diff_long = "MAXIMUM"
|
||||
diff_short = "MXM"
|
||||
else
|
||||
diff_long = "UNKNOWN"
|
||||
diff_short = "???"
|
||||
end
|
||||
end
|
||||
|
||||
gfx.FontSize(8)
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
gfx.FastText(diff_long, x + h/4, y + h - 7)
|
||||
|
||||
if (false) then
|
||||
|
||||
local seldiff = nil
|
||||
if song.difficulties[selectedDiff] ~= nil then
|
||||
seldiff = selectedDiff
|
||||
else
|
||||
seldiff = selectedDiff
|
||||
end
|
||||
|
||||
if song.difficulties[seldiff].topBadge ~= 0 then
|
||||
if song.difficulties[seldiff].scores[1] ~= nil then
|
||||
local highScore = song.difficulties[seldiff].scores[1]
|
||||
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 + w/1.45, y + h/8 + 2, (h/1.5-14), h/1.5-14, v.image, 1, 0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x + w/2, y + h/8, (h/1.5-10), h/1.5-10, badges[song.difficulties[seldiff].topBadge], 1, 0)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
draw_diff_icon = function(diff, x, y, w, h, selected)
|
||||
local difficultyIndex = diff.difficulty;
|
||||
|
||||
local image = difficultyNameOverlays[difficultyIndex];
|
||||
|
||||
local imgx, imgy = gfx.ImageSize(image);
|
||||
local aspect = imgx / imgy;
|
||||
|
||||
h = h * 98 / 112;
|
||||
|
||||
local wa = h * aspect;
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(x - wa / 2, y - h / 2, wa, h, image, 1, 0);
|
||||
|
||||
local level = diff.level;
|
||||
|
||||
local firstDigit = difficultyNumbers[math.max(0, math.floor(level / 10))];
|
||||
local secondDigit = difficultyNumbers[level % 10];
|
||||
|
||||
h = h * 0.475;
|
||||
|
||||
imgx, imgy = gfx.ImageSize(firstDigit);
|
||||
aspect = imgx / imgy;
|
||||
|
||||
wa = h * aspect;
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(x - wa, y - h / 2, wa, h, firstDigit, 1, 0);
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(x, y - h / 2, wa, h, secondDigit, 1, 0);
|
||||
end
|
||||
|
||||
draw_cursor = function(x, y, h)
|
||||
local imgx, imgy = gfx.ImageSize(difficultyLevelCursor);
|
||||
local aspect = imgx / imgy;
|
||||
|
||||
local w = h * aspect;
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(x - w / 2, y - h / 2, w, h, difficultyLevelCursor, 1, 0);
|
||||
end
|
||||
|
||||
draw_diffs = function(diffs, x, y, w, h)
|
||||
local diffWidth = w / 5
|
||||
local diffHeight = diffWidth
|
||||
|
||||
for i = 1, #diffs do
|
||||
local diff = diffs[i]
|
||||
|
||||
local xPos = x + w * (i - 0.5) / 4;
|
||||
local yPos = y + h / 2;
|
||||
|
||||
if (i == selectedDiff) then
|
||||
draw_cursor(xPos, yPos, diffHeight);
|
||||
end
|
||||
|
||||
draw_diff_icon(diff, xPos, yPos, diffWidth, diffHeight, i == selectedDiff);
|
||||
end
|
||||
end
|
||||
|
||||
draw_selected = function(song, x, y, w, h)
|
||||
check_or_create_cache(song)
|
||||
-- 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/32)
|
||||
yPadding = math.floor(h/32)
|
||||
xMargin = math.floor(w/34)
|
||||
yMargin = math.floor(h/32)
|
||||
width = ((w/2)-(xMargin))
|
||||
height = (h-(yMargin*2))
|
||||
xpos = x+xMargin/2
|
||||
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,100)
|
||||
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/3)
|
||||
local imageXPos = ((width/2) - (imageSize/2)) + x+xMargin
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
--Unless its portrait widesreen..
|
||||
imageSize = math.floor((height/8)*1.58)
|
||||
imageXPos = (x+w)/16+(xMargin*0.8)
|
||||
end
|
||||
if not songCache[song.id][selectedDiff] or songCache[song.id][selectedDiff] == jacketFallback then
|
||||
songCache[song.id][selectedDiff] = gfx.LoadImageJob(diff.jacketPath, jacketFallback, 200,200)
|
||||
end
|
||||
|
||||
if songCache[song.id][selectedDiff] then
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(imageXPos, y+yMargin*4.45+yPadding, imageSize, imageSize, songCache[song.id][selectedDiff], 1, 0)
|
||||
end
|
||||
-- difficulty should take up 1/6 of height, full width, and be centered
|
||||
gfx.LoadSkinFont("commext.ttf")
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
--difficulty wheel should be right below the jacketImage, and the same width as
|
||||
--the jacketImage
|
||||
local diffPanelWidth = w * 0.4275;
|
||||
local diffPanelHeight = diffPanelWidth / 4;
|
||||
draw_diffs(song.difficulties, (w / 2 - diffPanelWidth) / 2, y + 0.5687583444592 * h - diffPanelHeight / 2, diffPanelWidth, diffPanelHeight)
|
||||
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
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf")
|
||||
if aspectRatio == "PortraitWidescreen" then
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+xPadding/2, y+yMargin*15+yPadding, width)
|
||||
gfx.FontSize(40)
|
||||
gfx.DrawLabel(songCache[song.id]["artist"], xpos+xPadding/2, y+yMargin*15.8+yPadding, width)
|
||||
gfx.FontSize(10)
|
||||
gfx.DrawLabel(songCache[song.id]["bpm"], xpos+xPadding*2, y+yMargin*14.42+yPadding, width-imageSize)
|
||||
gfx.FastText(string.format("%s", diff.effector), xpos+xPadding*7.5, y+yMargin*18.87+yPadding)
|
||||
else
|
||||
gfx.FontSize(40)
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.DrawLabel(songCache[song.id]["title"], xpos+10, (height/10)*6, width-20)
|
||||
gfx.FontSize(30)
|
||||
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)
|
||||
gfx.FastText(string.format("%s", diff.effector),xpos+10, (height/10)*6 + 115)
|
||||
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/2
|
||||
end
|
||||
local height = math.floor((h/wheelSize)*1.75)
|
||||
|
||||
for i = math.max(selectedIndex - wheelSize/2, 1), math.max(selectedIndex - 1,0) do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.0)
|
||||
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, #songwheel.songs), selectedIndex + 1,-1 do
|
||||
local song = songwheel.songs[i]
|
||||
local xpos = x + width
|
||||
local offsetY = (selectedIndex - i + ioffset/2) * ( height * 1.0)
|
||||
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 + width
|
||||
local offsetY = (ioffset/2) * ( height - (wheelSize/2*((1)*aspectFloat)))
|
||||
local ypos = y+((h/2 - height/2) - (ioffset) - offsetY)
|
||||
draw_song(songwheel.songs[selectedIndex], xpos, ypos, width, height, true)
|
||||
-- cursor
|
||||
gfx.BeginPath()
|
||||
local ypos = y+((h/2 - height/2))
|
||||
gfx.Rect(xpos, ypos, width, height)
|
||||
gfx.FillColor(0,0,0,0)
|
||||
gfx.StrokeColor(255,128,0)
|
||||
gfx.StrokeWidth(3)
|
||||
gfx.Fill()
|
||||
gfx.Stroke()
|
||||
|
||||
return songwheel.songs[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) - (songwheel.searchInputActive and 0 or 1)
|
||||
if searchIndex ~= (songwheel.searchInputActive and 0 or 1) then
|
||||
game.PlaySample("woosh")
|
||||
end
|
||||
searchIndex = songwheel.searchInputActive and 0 or 1
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgfade = 1 - (searchIndex + soffset)
|
||||
--if not songwheel.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",songwheel.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();
|
||||
-- game.Log("res :: " .. resx .. "," .. resy, game.LOGGER_ERROR);
|
||||
adjustScreen(resx,resy);
|
||||
gfx.BeginPath();
|
||||
gfx.LoadSkinFont("dfmarugoth.ttf");
|
||||
gfx.FontSize(40);
|
||||
gfx.FillColor(255,255,255);
|
||||
gfx.ImageRect(0, 0, resx, resy, datapanel, 1, 0);
|
||||
if songwheel.songs[1] ~= nil then
|
||||
--draw songwheel 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,resy)
|
||||
else
|
||||
local song = draw_songwheel(0,0,fullX,fullY)
|
||||
--render selected song information
|
||||
draw_selected(song, 0,0,fullX,resy)
|
||||
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
|
||||
gfx.BeginPath();
|
||||
gfx.TextAlign(TEXT_ALIGN_CENTER + TEXT_ALIGN_MIDDLE);
|
||||
gfx.ImageRect(0, 0, resx, resy, foreground, 1, 0);
|
||||
|
||||
--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 songwheel.searchStatus then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
|
||||
gfx.Text(songwheel.searchStatus, 3, 3)
|
||||
end
|
||||
if totalForce then
|
||||
gfx.BeginPath()
|
||||
gfx.FillColor(255,255,255)
|
||||
gfx.FontSize(20);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_BOTTOM)
|
||||
local forceText = string.format("Force: %.2f", totalForce)
|
||||
gfx.Text(forceText, 0, fullY)
|
||||
end
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
gfx.ResetTransform()
|
||||
gfx.ForceRender()
|
||||
end
|
||||
|
||||
set_index = function(newIndex)
|
||||
if newIndex ~= selectedIndex then
|
||||
game.PlaySample("menu_click")
|
||||
end
|
||||
ioffset = ioffset + selectedIndex - newIndex
|
||||
selectedIndex = newIndex
|
||||
end;
|
||||
|
||||
set_diff = function(newDiff)
|
||||
if newDiff ~= selectedDiff then
|
||||
game.PlaySample("click-02")
|
||||
end
|
||||
doffset = doffset + selectedDiff - newDiff
|
||||
selectedDiff = newDiff
|
||||
end;
|
||||
|
||||
-- force calculation
|
||||
--------------------
|
||||
totalForce = nil
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
calculate_force = function(diff)
|
||||
if #diff.scores < 1 then
|
||||
return 0
|
||||
end
|
||||
local score = diff.scores[1]
|
||||
local badgeRate = badgeRates[diff.topBadge]
|
||||
local gradeRate
|
||||
for i, v in ipairs(gradeRates) do
|
||||
if score.score >= v.min then
|
||||
gradeRate = v.rate
|
||||
break
|
||||
end
|
||||
end
|
||||
return math.floor((diff.level * 2) * (score.score / 10000000) * gradeRate * badgeRate) / 100
|
||||
end
|
||||
|
||||
songs_changed = function(withAll)
|
||||
if not withAll then return end
|
||||
|
||||
recordCache = {}
|
||||
|
||||
local diffs = {}
|
||||
for i = 1, #songwheel.allSongs do
|
||||
local song = songwheel.allSongs[i]
|
||||
for j = 1, #song.difficulties do
|
||||
local diff = song.difficulties[j]
|
||||
diff.force = calculate_force(diff)
|
||||
table.insert(diffs, diff)
|
||||
end
|
||||
end
|
||||
table.sort(diffs, function (l, r)
|
||||
return l.force > r.force
|
||||
end)
|
||||
totalForce = 0
|
||||
for i = 1, 50 do
|
||||
if diffs[i] then
|
||||
totalForce = totalForce + diffs[i].force
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,243 +1,243 @@
|
|||
require('common')
|
||||
local Easing = require('common.easing');
|
||||
|
||||
local resx, resy = game.GetResolution()
|
||||
local desw, desh = 1080, 1920
|
||||
|
||||
-- AUDIO
|
||||
game.LoadSkinSample('sort_wheel/enter.wav');
|
||||
game.LoadSkinSample('sort_wheel/leave.wav');
|
||||
|
||||
-- IMAGES
|
||||
local panelBgImage = gfx.CreateSkinImage('song_select/sort_wheel/bg.png', 0)
|
||||
local activeItemBgImage = gfx.CreateSkinImage(
|
||||
'song_select/sort_wheel/active_bg.png', 0)
|
||||
local titleTextImage =
|
||||
gfx.CreateSkinImage('song_select/sort_wheel/title.png', 0)
|
||||
|
||||
local selection = 1;
|
||||
local renderedButtonLabels = {}
|
||||
|
||||
local FONT_SIZE = 32;
|
||||
local MARGIN = 16;
|
||||
local SUB_FONT_SIZE = 26;
|
||||
local SUB_MARGIN = 8;
|
||||
|
||||
local SORT_ORDER_LABEL_TEXTS = {
|
||||
{
|
||||
label = 'Title',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}, {label = 'Score', asc = 'Worst to best', dsc = 'Best to worst'},
|
||||
{label = 'Date', asc = 'Oldest to newest', dsc = 'Newest to oldest'},
|
||||
{label = 'Badge', asc = 'None to D to S', dsc = 'S to D to None'}, {
|
||||
label = 'Artist',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}, {
|
||||
label = 'Effector',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}
|
||||
}
|
||||
|
||||
local transitionEnterReverse = false;
|
||||
local transitionEnterScale = 0;
|
||||
local transitionEnterOffsetX = 0;
|
||||
|
||||
local previousActiveState = false;
|
||||
|
||||
-- Window variables
|
||||
local resX, resY
|
||||
|
||||
-- Aspect Ratios
|
||||
local landscapeWidescreenRatio = 16 / 9
|
||||
local landscapeStandardRatio = 4 / 3
|
||||
local portraitWidescreenRatio = 9 / 16 --+ 0.0035
|
||||
|
||||
-- Portrait sizes
|
||||
local fullX, fullY
|
||||
|
||||
local resolutionChange = function(x, y)
|
||||
resX = x
|
||||
resY = y
|
||||
fullX = portraitWidescreenRatio * y
|
||||
fullY = y
|
||||
|
||||
game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR);
|
||||
end
|
||||
|
||||
function tableContains(table, value)
|
||||
for i, v in ipairs(table) do if v == value then return true end end
|
||||
|
||||
return false;
|
||||
end
|
||||
|
||||
function drawButton(i, f, x, y)
|
||||
local spaceAfter = (FONT_SIZE + MARGIN)
|
||||
|
||||
local sortOrder = 'asc';
|
||||
if (string.find(f, 'v')) then sortOrder = 'dsc' end
|
||||
|
||||
local label = f:gsub(' ^', '')
|
||||
label = label:gsub(' v', '')
|
||||
|
||||
if (string.find(sorts[selection], label) and sorts[selection] ~= f) then
|
||||
-- If there is a button active with the same label, but different sort order, don't render this one
|
||||
return 0;
|
||||
else
|
||||
-- If there is no active button with this label, if one with a label was already rendered, don't render this one
|
||||
if (tableContains(renderedButtonLabels, label)) then return 0; end
|
||||
table.insert(renderedButtonLabels, label);
|
||||
end
|
||||
|
||||
if (i == selection) then
|
||||
local ascLabelText = 'Ascending'
|
||||
local dscLabelText = 'Descending'
|
||||
|
||||
for i, obj in ipairs(SORT_ORDER_LABEL_TEXTS) do
|
||||
if (obj.label == label) then
|
||||
ascLabelText = obj.asc;
|
||||
dscLabelText = obj.dsc;
|
||||
end
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x - 182, y - 38, 365, 82, activeItemBgImage, 1, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
if sortOrder == 'asc' then
|
||||
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 - 31, 300, 67,
|
||||
activeItemBgImage, 1, 0)
|
||||
elseif sortOrder == 'dsc' then
|
||||
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 +
|
||||
SUB_FONT_SIZE + SUB_MARGIN - 31, 300, 67,
|
||||
activeItemBgImage, 1, 0)
|
||||
end
|
||||
|
||||
gfx.Save()
|
||||
gfx.FontSize(SUB_FONT_SIZE)
|
||||
gfx.Text(ascLabelText, x, y + FONT_SIZE + SUB_MARGIN * 2);
|
||||
gfx.Text(dscLabelText, x,
|
||||
y + FONT_SIZE + SUB_MARGIN * 2 + SUB_FONT_SIZE + SUB_MARGIN);
|
||||
gfx.Restore()
|
||||
|
||||
spaceAfter = spaceAfter + SUB_FONT_SIZE * 2 + SUB_MARGIN * 4;
|
||||
end
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.Text(label, x, y);
|
||||
|
||||
return spaceAfter;
|
||||
end
|
||||
|
||||
function tickTransitions(deltaTime)
|
||||
-- ENTRY TRANSITION
|
||||
if transitionEnterReverse then
|
||||
if transitionEnterScale > 0 then
|
||||
transitionEnterScale = transitionEnterScale - deltaTime / 0.25 -- transition should last for that time in seconds
|
||||
else
|
||||
transitionEnterScale = 0
|
||||
end
|
||||
else
|
||||
if transitionEnterScale < 1 then
|
||||
transitionEnterScale = transitionEnterScale + deltaTime / 0.25 -- transition should last for that time in seconds
|
||||
else
|
||||
transitionEnterScale = 1
|
||||
end
|
||||
end
|
||||
|
||||
transitionEnterOffsetX = Easing.inOutQuad(1 - transitionEnterScale) * 416
|
||||
end
|
||||
|
||||
local drawSortWheel = function (x,y,w,h, deltaTime)
|
||||
gfx.Scissor(x,y,w,h);
|
||||
gfx.Translate(x,y);
|
||||
gfx.Scale(w/1080, h/1920);
|
||||
gfx.Scissor(0,0,1080,1920);
|
||||
|
||||
-- Draw the dark overlay above song wheel
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0, 0, 0, math.floor(transitionEnterScale * 192));
|
||||
gfx.Rect(0, 0, 1080, 1920);
|
||||
gfx.Fill();
|
||||
|
||||
-- Draw the panel background
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(desw - 416 + transitionEnterOffsetX, 0, 416, desh,
|
||||
panelBgImage, 1, 0)
|
||||
|
||||
gfx.LoadSkinFont("Digital-Serial-Bold.ttf");
|
||||
gfx.FontSize(FONT_SIZE);
|
||||
gfx.FillColor(255, 255, 255, 255);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
|
||||
|
||||
gfx.GlobalAlpha(transitionEnterScale)
|
||||
|
||||
-- Starting position of the first sort option
|
||||
local x = 889 + transitionEnterOffsetX;
|
||||
local y = desh / 2 - -- Center point
|
||||
(#sorts / 2 / 2) * (FONT_SIZE + MARGIN) - -- Space taken up by half the sort options (we remove the duplicate one)
|
||||
((SUB_FONT_SIZE * 2 + SUB_MARGIN * 4) / 2); -- Space for taken by order options
|
||||
|
||||
-- Draw the title image
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x - 72, y - 27, 144, 54, titleTextImage, 1, 0)
|
||||
y = y + (54 + MARGIN)
|
||||
|
||||
-- Draw all the sorting options
|
||||
for i, f in ipairs(sorts) do
|
||||
local spaceAfter = drawButton(i, f, x, y);
|
||||
y = y + spaceAfter;
|
||||
end
|
||||
end
|
||||
|
||||
function setSkinSetting()
|
||||
for i, f in ipairs(sorts) do
|
||||
if i == selection then
|
||||
local label = f:gsub(' ^', '')
|
||||
label = label:gsub(' v', '')
|
||||
|
||||
game.SetSkinSetting('_songWheelActiveSortOptionLabel', label);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function render(deltaTime, shown)
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
renderedButtonLabels = {};
|
||||
|
||||
|
||||
if (shown ~= previousActiveState) then
|
||||
if (shown) then
|
||||
game.PlaySample('sort_wheel/enter.wav');
|
||||
else
|
||||
game.PlaySample('sort_wheel/leave.wav');
|
||||
end
|
||||
|
||||
previousActiveState = shown;
|
||||
end
|
||||
|
||||
-- detect resolution change
|
||||
local resx, resy = game.GetResolution();
|
||||
if resx ~= resX or resy ~= resY then
|
||||
resolutionChange(resx, resy)
|
||||
end
|
||||
|
||||
gfx.GlobalAlpha(1)
|
||||
|
||||
if not shown then
|
||||
transitionEnterReverse = true
|
||||
if (transitionEnterScale > 0) then drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime) end
|
||||
else
|
||||
transitionEnterReverse = false
|
||||
drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
|
||||
end
|
||||
tickTransitions(deltaTime)
|
||||
setSkinSetting();
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function set_selection(index) selection = index end
|
||||
require('common')
|
||||
local Easing = require('common.easing');
|
||||
|
||||
local resx, resy = game.GetResolution()
|
||||
local desw, desh = 1080, 1920
|
||||
|
||||
-- AUDIO
|
||||
game.LoadSkinSample('sort_wheel/enter.wav');
|
||||
game.LoadSkinSample('sort_wheel/leave.wav');
|
||||
|
||||
-- IMAGES
|
||||
local panelBgImage = gfx.CreateSkinImage('song_select/sort_wheel/bg.png', 0)
|
||||
local activeItemBgImage = gfx.CreateSkinImage(
|
||||
'song_select/sort_wheel/active_bg.png', 0)
|
||||
local titleTextImage =
|
||||
gfx.CreateSkinImage('song_select/sort_wheel/title.png', 0)
|
||||
|
||||
local selection = 1;
|
||||
local renderedButtonLabels = {}
|
||||
|
||||
local FONT_SIZE = 32;
|
||||
local MARGIN = 16;
|
||||
local SUB_FONT_SIZE = 26;
|
||||
local SUB_MARGIN = 8;
|
||||
|
||||
local SORT_ORDER_LABEL_TEXTS = {
|
||||
{
|
||||
label = 'Title',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}, {label = 'Score', asc = 'Worst to best', dsc = 'Best to worst'},
|
||||
{label = 'Date', asc = 'Oldest to newest', dsc = 'Newest to oldest'},
|
||||
{label = 'Badge', asc = 'None to D to S', dsc = 'S to D to None'}, {
|
||||
label = 'Artist',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}, {
|
||||
label = 'Effector',
|
||||
asc = '# to A to Z to かな to 漢字',
|
||||
dsc = '漢字 to かな to Z to A to #'
|
||||
}
|
||||
}
|
||||
|
||||
local transitionEnterReverse = false;
|
||||
local transitionEnterScale = 0;
|
||||
local transitionEnterOffsetX = 0;
|
||||
|
||||
local previousActiveState = false;
|
||||
|
||||
-- Window variables
|
||||
local resX, resY
|
||||
|
||||
-- Aspect Ratios
|
||||
local landscapeWidescreenRatio = 16 / 9
|
||||
local landscapeStandardRatio = 4 / 3
|
||||
local portraitWidescreenRatio = 9 / 16 --+ 0.0035
|
||||
|
||||
-- Portrait sizes
|
||||
local fullX, fullY
|
||||
|
||||
local resolutionChange = function(x, y)
|
||||
resX = x
|
||||
resY = y
|
||||
fullX = portraitWidescreenRatio * y
|
||||
fullY = y
|
||||
|
||||
game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR);
|
||||
end
|
||||
|
||||
function tableContains(table, value)
|
||||
for i, v in ipairs(table) do if v == value then return true end end
|
||||
|
||||
return false;
|
||||
end
|
||||
|
||||
function drawButton(i, f, x, y)
|
||||
local spaceAfter = (FONT_SIZE + MARGIN)
|
||||
|
||||
local sortOrder = 'asc';
|
||||
if (string.find(f, 'v')) then sortOrder = 'dsc' end
|
||||
|
||||
local label = f:gsub(' ^', '')
|
||||
label = label:gsub(' v', '')
|
||||
|
||||
if (string.find(sorts[selection], label) and sorts[selection] ~= f) then
|
||||
-- If there is a button active with the same label, but different sort order, don't render this one
|
||||
return 0;
|
||||
else
|
||||
-- If there is no active button with this label, if one with a label was already rendered, don't render this one
|
||||
if (tableContains(renderedButtonLabels, label)) then return 0; end
|
||||
table.insert(renderedButtonLabels, label);
|
||||
end
|
||||
|
||||
if (i == selection) then
|
||||
local ascLabelText = 'Ascending'
|
||||
local dscLabelText = 'Descending'
|
||||
|
||||
for i, obj in ipairs(SORT_ORDER_LABEL_TEXTS) do
|
||||
if (obj.label == label) then
|
||||
ascLabelText = obj.asc;
|
||||
dscLabelText = obj.dsc;
|
||||
end
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x - 182, y - 38, 365, 82, activeItemBgImage, 1, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
if sortOrder == 'asc' then
|
||||
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 - 31, 300, 67,
|
||||
activeItemBgImage, 1, 0)
|
||||
elseif sortOrder == 'dsc' then
|
||||
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 +
|
||||
SUB_FONT_SIZE + SUB_MARGIN - 31, 300, 67,
|
||||
activeItemBgImage, 1, 0)
|
||||
end
|
||||
|
||||
gfx.Save()
|
||||
gfx.FontSize(SUB_FONT_SIZE)
|
||||
gfx.Text(ascLabelText, x, y + FONT_SIZE + SUB_MARGIN * 2);
|
||||
gfx.Text(dscLabelText, x,
|
||||
y + FONT_SIZE + SUB_MARGIN * 2 + SUB_FONT_SIZE + SUB_MARGIN);
|
||||
gfx.Restore()
|
||||
|
||||
spaceAfter = spaceAfter + SUB_FONT_SIZE * 2 + SUB_MARGIN * 4;
|
||||
end
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.Text(label, x, y);
|
||||
|
||||
return spaceAfter;
|
||||
end
|
||||
|
||||
function tickTransitions(deltaTime)
|
||||
-- ENTRY TRANSITION
|
||||
if transitionEnterReverse then
|
||||
if transitionEnterScale > 0 then
|
||||
transitionEnterScale = transitionEnterScale - deltaTime / 0.25 -- transition should last for that time in seconds
|
||||
else
|
||||
transitionEnterScale = 0
|
||||
end
|
||||
else
|
||||
if transitionEnterScale < 1 then
|
||||
transitionEnterScale = transitionEnterScale + deltaTime / 0.25 -- transition should last for that time in seconds
|
||||
else
|
||||
transitionEnterScale = 1
|
||||
end
|
||||
end
|
||||
|
||||
transitionEnterOffsetX = Easing.inOutQuad(1 - transitionEnterScale) * 416
|
||||
end
|
||||
|
||||
local drawSortWheel = function (x,y,w,h, deltaTime)
|
||||
gfx.Scissor(x,y,w,h);
|
||||
gfx.Translate(x,y);
|
||||
gfx.Scale(w/1080, h/1920);
|
||||
gfx.Scissor(0,0,1080,1920);
|
||||
|
||||
-- Draw the dark overlay above song wheel
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0, 0, 0, math.floor(transitionEnterScale * 192));
|
||||
gfx.Rect(0, 0, 1080, 1920);
|
||||
gfx.Fill();
|
||||
|
||||
-- Draw the panel background
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(desw - 416 + transitionEnterOffsetX, 0, 416, desh,
|
||||
panelBgImage, 1, 0)
|
||||
|
||||
gfx.LoadSkinFont("Digital-Serial-Bold.ttf");
|
||||
gfx.FontSize(FONT_SIZE);
|
||||
gfx.FillColor(255, 255, 255, 255);
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
|
||||
|
||||
gfx.GlobalAlpha(transitionEnterScale)
|
||||
|
||||
-- Starting position of the first sort option
|
||||
local x = 889 + transitionEnterOffsetX;
|
||||
local y = desh / 2 - -- Center point
|
||||
(#sorts / 2 / 2) * (FONT_SIZE + MARGIN) - -- Space taken up by half the sort options (we remove the duplicate one)
|
||||
((SUB_FONT_SIZE * 2 + SUB_MARGIN * 4) / 2); -- Space for taken by order options
|
||||
|
||||
-- Draw the title image
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(x - 72, y - 27, 144, 54, titleTextImage, 1, 0)
|
||||
y = y + (54 + MARGIN)
|
||||
|
||||
-- Draw all the sorting options
|
||||
for i, f in ipairs(sorts) do
|
||||
local spaceAfter = drawButton(i, f, x, y);
|
||||
y = y + spaceAfter;
|
||||
end
|
||||
end
|
||||
|
||||
function setSkinSetting()
|
||||
for i, f in ipairs(sorts) do
|
||||
if i == selection then
|
||||
local label = f:gsub(' ^', '')
|
||||
label = label:gsub(' v', '')
|
||||
|
||||
game.SetSkinSetting('_songWheelActiveSortOptionLabel', label);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function render(deltaTime, shown)
|
||||
gfx.Save()
|
||||
gfx.ResetTransform()
|
||||
renderedButtonLabels = {};
|
||||
|
||||
|
||||
if (shown ~= previousActiveState) then
|
||||
if (shown) then
|
||||
game.PlaySample('sort_wheel/enter.wav');
|
||||
else
|
||||
game.PlaySample('sort_wheel/leave.wav');
|
||||
end
|
||||
|
||||
previousActiveState = shown;
|
||||
end
|
||||
|
||||
-- detect resolution change
|
||||
local resx, resy = game.GetResolution();
|
||||
if resx ~= resX or resy ~= resY then
|
||||
resolutionChange(resx, resy)
|
||||
end
|
||||
|
||||
gfx.GlobalAlpha(1)
|
||||
|
||||
if not shown then
|
||||
transitionEnterReverse = true
|
||||
if (transitionEnterScale > 0) then drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime) end
|
||||
else
|
||||
transitionEnterReverse = false
|
||||
drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
|
||||
end
|
||||
tickTransitions(deltaTime)
|
||||
setSkinSetting();
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
function set_selection(index) selection = index end
|
||||
|
|
|
@ -1,214 +1,214 @@
|
|||
|
||||
local common = require('common.common');
|
||||
local Numbers = require('common.numbers')
|
||||
|
||||
game.LoadSkinSample('song_transition_screen/transition_enter.wav');
|
||||
|
||||
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
|
||||
|
||||
local bgImage = gfx.CreateSkinImage("songtransition/bg.png", 0)
|
||||
local glowOverlayImage = gfx.CreateSkinImage("songtransition/glowy.png", 0)
|
||||
local frameOverlayImage = gfx.CreateSkinImage("songtransition/frames.png", 0)
|
||||
|
||||
local albumBgImage = gfx.CreateSkinImage("songtransition/album_crop.png", 0)
|
||||
local infoOverlayPanel = gfx.CreateSkinImage("songtransition/info_panels_crop.png", 0)
|
||||
|
||||
local linkedHexagonsImage = gfx.CreateSkinImage("songtransition/linked_hexagons_crop.png", 0)
|
||||
local hexagonImages = {
|
||||
gfx.CreateSkinImage("songtransition/hex1.png", 0),
|
||||
gfx.CreateSkinImage("songtransition/hex2.png", 0)
|
||||
}
|
||||
|
||||
local difficultyNumbers;
|
||||
|
||||
local difficultyLabelImages = {
|
||||
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),
|
||||
}
|
||||
|
||||
local timer = 0
|
||||
local transitionProgress = 0;
|
||||
local outProgress = 0
|
||||
|
||||
local flickerTime = 0.050 --seconds (50ms)
|
||||
|
||||
-- Window variables
|
||||
local resX, resY = game.GetResolution()
|
||||
|
||||
-- Aspect Ratios
|
||||
local landscapeWidescreenRatio = 16 / 9
|
||||
local landscapeStandardRatio = 4 / 3
|
||||
local portraitWidescreenRatio = 9 / 16
|
||||
|
||||
-- Portrait sizes
|
||||
local fullX, fullY
|
||||
local desw = 1080
|
||||
local desh = 1920
|
||||
|
||||
local noJacket = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
|
||||
local wasEnterSfxPlayed = false;
|
||||
|
||||
function resetLayoutInformation()
|
||||
resx, resy = game.GetResolution()
|
||||
scale = resx / desw
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
if not wasEnterSfxPlayed then
|
||||
common.stopMusic();
|
||||
game.PlaySample('song_transition_screen/transition_enter.wav');
|
||||
wasEnterSfxPlayed = true;
|
||||
end
|
||||
if not difficultyNumbers then
|
||||
difficultyNumbers = Numbers.load_number_image('diff_num')
|
||||
end
|
||||
|
||||
local x_offset = (resX - fullX) / 2
|
||||
local y_offset = 0
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
|
||||
gfx.Rect(0, 0, resX, resY)
|
||||
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
|
||||
gfx.Fill()
|
||||
|
||||
gfx.Translate(x_offset, y_offset);
|
||||
gfx.Scale(fullX / 1080, fullY / 1920);
|
||||
gfx.Scissor(0, 0, 1080, 1920);
|
||||
|
||||
render_screen();
|
||||
|
||||
transitionProgress = transitionProgress + deltaTime * 0.2
|
||||
transitionProgress = math.min(transitionProgress,1)
|
||||
|
||||
if transitionProgress < 0.25 then
|
||||
local whiteAlpha = math.max(0, (1-transitionProgress/0.25))
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(255,255,255,math.floor(255*whiteAlpha));
|
||||
gfx.Rect(0,0,desw,desh);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath();
|
||||
end
|
||||
|
||||
if transitionProgress > 0.85 then
|
||||
local blackAlpha = math.min(1, ((transitionProgress-0.85)/0.15))
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0,0,0,math.floor(255*blackAlpha));
|
||||
gfx.Rect(0,0,desw,desh);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath();
|
||||
end
|
||||
|
||||
timer = timer + deltaTime
|
||||
return transitionProgress >= 1
|
||||
end
|
||||
|
||||
function render_out(deltaTime)
|
||||
outProgress = outProgress + deltaTime * 0.2
|
||||
outProgress = math.min(outProgress, 1)
|
||||
|
||||
timer = timer + deltaTime
|
||||
return outProgress >= 1;
|
||||
end
|
||||
|
||||
function sign(x)
|
||||
return x>0 and 1 or x<0 and -1 or 0
|
||||
end
|
||||
|
||||
function render_screen()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, 1080, 1920, bgImage,1,0);
|
||||
|
||||
if transitionProgress < 0.35 then
|
||||
local hex1alpha = math.max(0, (1-transitionProgress/0.35))
|
||||
local hex2alpha = math.max(0, (1-transitionProgress/0.3))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0, desw, desh, hexagonImages[1], hex1alpha, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0, desw, desh, hexagonImages[2], hex2alpha, 0)
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0,1080,1920,frameOverlayImage,1,0);
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, 1080, 1920, glowOverlayImage,1,0);
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(37.5, 1074, 1180*0.85, 343*0.85, infoOverlayPanel, 1, 0);
|
||||
|
||||
if (timer % flickerTime) < (flickerTime / 2) then --flicker with 20Hz (50ms), 50% duty cycle
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(37.5, 1074, 1180*0.85, 189*0.85, linkedHexagonsImage, 0.1, 0);
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(10, 195.5, 1060, 1015, albumBgImage,1,0);
|
||||
|
||||
local jacket = song.jacket == 0 and noJacket or song.jacket
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(235, 385, 608, 608, jacket, 1, 0)
|
||||
gfx.ClosePath();
|
||||
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
|
||||
gfx.FontSize(55)
|
||||
gfx.Text(song.title,desw/2, 1114)
|
||||
|
||||
gfx.FontSize(30)
|
||||
gfx.Text(song.artist, desw/2 , 1182)
|
||||
|
||||
|
||||
local EFFECTOR_LABEL_Y = 1288
|
||||
local ILLUSTRATOR_LABEL_Y = 1347
|
||||
|
||||
gfx.FontSize(22)
|
||||
|
||||
gfx.Text(song.effector, desw/2+70 , EFFECTOR_LABEL_Y-1)
|
||||
gfx.Text(song.illustrator, desw/2+70 , ILLUSTRATOR_LABEL_Y-3)
|
||||
|
||||
-- Draw song diff level
|
||||
gfx.BeginPath();
|
||||
Numbers.draw_number(933, 1140, 1.0, song.level, 2, difficultyNumbers, false, 1, 1)
|
||||
|
||||
-- Draw song diff label (NOV/ADV/EXH/MXM/etc.)
|
||||
gfx.BeginPath();
|
||||
local diffLabelImage = difficultyLabelImages[song.difficulty+1];
|
||||
local diffLabelW, diffLabelH = gfx.ImageSize(diffLabelImage);
|
||||
gfx.ImageRect(952-diffLabelW/2, 1154-diffLabelH/2, diffLabelW, diffLabelH, diffLabelImage,1,0);
|
||||
gfx.ClosePath();
|
||||
|
||||
gfx.Save();
|
||||
gfx.FontSize(24)
|
||||
gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.BeginPath();
|
||||
|
||||
gfx.Text('BPM', 127, 1140)
|
||||
gfx.Text(song.bpm, 127, 1167)
|
||||
|
||||
-- temp ref overlay
|
||||
-- gfx.BeginPath()
|
||||
-- gfx.ImageRect(0, 0, 1080, 1920, refBgImage,0.5,0);
|
||||
|
||||
gfx.ClosePath();
|
||||
gfx.Restore();
|
||||
end
|
||||
|
||||
function reset()
|
||||
transitionProgress = 0
|
||||
resX, resY = game.GetResolution()
|
||||
fullX = portraitWidescreenRatio * resY
|
||||
fullY = resY
|
||||
outProgress = 0
|
||||
wasEnterSfxPlayed = false;
|
||||
|
||||
local common = require('common.common');
|
||||
local Numbers = require('common.numbers')
|
||||
|
||||
game.LoadSkinSample('song_transition_screen/transition_enter.wav');
|
||||
|
||||
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
|
||||
|
||||
local bgImage = gfx.CreateSkinImage("songtransition/bg.png", 0)
|
||||
local glowOverlayImage = gfx.CreateSkinImage("songtransition/glowy.png", 0)
|
||||
local frameOverlayImage = gfx.CreateSkinImage("songtransition/frames.png", 0)
|
||||
|
||||
local albumBgImage = gfx.CreateSkinImage("songtransition/album_crop.png", 0)
|
||||
local infoOverlayPanel = gfx.CreateSkinImage("songtransition/info_panels_crop.png", 0)
|
||||
|
||||
local linkedHexagonsImage = gfx.CreateSkinImage("songtransition/linked_hexagons_crop.png", 0)
|
||||
local hexagonImages = {
|
||||
gfx.CreateSkinImage("songtransition/hex1.png", 0),
|
||||
gfx.CreateSkinImage("songtransition/hex2.png", 0)
|
||||
}
|
||||
|
||||
local difficultyNumbers;
|
||||
|
||||
local difficultyLabelImages = {
|
||||
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),
|
||||
}
|
||||
|
||||
local timer = 0
|
||||
local transitionProgress = 0;
|
||||
local outProgress = 0
|
||||
|
||||
local flickerTime = 0.050 --seconds (50ms)
|
||||
|
||||
-- Window variables
|
||||
local resX, resY = game.GetResolution()
|
||||
|
||||
-- Aspect Ratios
|
||||
local landscapeWidescreenRatio = 16 / 9
|
||||
local landscapeStandardRatio = 4 / 3
|
||||
local portraitWidescreenRatio = 9 / 16
|
||||
|
||||
-- Portrait sizes
|
||||
local fullX, fullY
|
||||
local desw = 1080
|
||||
local desh = 1920
|
||||
|
||||
local noJacket = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||
|
||||
local wasEnterSfxPlayed = false;
|
||||
|
||||
function resetLayoutInformation()
|
||||
resx, resy = game.GetResolution()
|
||||
scale = resx / desw
|
||||
end
|
||||
|
||||
function render(deltaTime)
|
||||
if not wasEnterSfxPlayed then
|
||||
common.stopMusic();
|
||||
game.PlaySample('song_transition_screen/transition_enter.wav');
|
||||
wasEnterSfxPlayed = true;
|
||||
end
|
||||
if not difficultyNumbers then
|
||||
difficultyNumbers = Numbers.load_number_image('diff_num')
|
||||
end
|
||||
|
||||
local x_offset = (resX - fullX) / 2
|
||||
local y_offset = 0
|
||||
|
||||
gfx.BeginPath()
|
||||
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
|
||||
gfx.Rect(0, 0, resX, resY)
|
||||
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
|
||||
gfx.Fill()
|
||||
|
||||
gfx.Translate(x_offset, y_offset);
|
||||
gfx.Scale(fullX / 1080, fullY / 1920);
|
||||
gfx.Scissor(0, 0, 1080, 1920);
|
||||
|
||||
render_screen();
|
||||
|
||||
transitionProgress = transitionProgress + deltaTime * 0.2
|
||||
transitionProgress = math.min(transitionProgress,1)
|
||||
|
||||
if transitionProgress < 0.25 then
|
||||
local whiteAlpha = math.max(0, (1-transitionProgress/0.25))
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(255,255,255,math.floor(255*whiteAlpha));
|
||||
gfx.Rect(0,0,desw,desh);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath();
|
||||
end
|
||||
|
||||
if transitionProgress > 0.85 then
|
||||
local blackAlpha = math.min(1, ((transitionProgress-0.85)/0.15))
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(0,0,0,math.floor(255*blackAlpha));
|
||||
gfx.Rect(0,0,desw,desh);
|
||||
gfx.Fill();
|
||||
gfx.ClosePath();
|
||||
end
|
||||
|
||||
timer = timer + deltaTime
|
||||
return transitionProgress >= 1
|
||||
end
|
||||
|
||||
function render_out(deltaTime)
|
||||
outProgress = outProgress + deltaTime * 0.2
|
||||
outProgress = math.min(outProgress, 1)
|
||||
|
||||
timer = timer + deltaTime
|
||||
return outProgress >= 1;
|
||||
end
|
||||
|
||||
function sign(x)
|
||||
return x>0 and 1 or x<0 and -1 or 0
|
||||
end
|
||||
|
||||
function render_screen()
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, 1080, 1920, bgImage,1,0);
|
||||
|
||||
if transitionProgress < 0.35 then
|
||||
local hex1alpha = math.max(0, (1-transitionProgress/0.35))
|
||||
local hex2alpha = math.max(0, (1-transitionProgress/0.3))
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0, desw, desh, hexagonImages[1], hex1alpha, 0)
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0, desw, desh, hexagonImages[2], hex2alpha, 0)
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0,0,1080,1920,frameOverlayImage,1,0);
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(0, 0, 1080, 1920, glowOverlayImage,1,0);
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(37.5, 1074, 1180*0.85, 343*0.85, infoOverlayPanel, 1, 0);
|
||||
|
||||
if (timer % flickerTime) < (flickerTime / 2) then --flicker with 20Hz (50ms), 50% duty cycle
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(37.5, 1074, 1180*0.85, 189*0.85, linkedHexagonsImage, 0.1, 0);
|
||||
end
|
||||
|
||||
gfx.BeginPath()
|
||||
gfx.ImageRect(10, 195.5, 1060, 1015, albumBgImage,1,0);
|
||||
|
||||
local jacket = song.jacket == 0 and noJacket or song.jacket
|
||||
gfx.BeginPath();
|
||||
gfx.ImageRect(235, 385, 608, 608, jacket, 1, 0)
|
||||
gfx.ClosePath();
|
||||
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
|
||||
gfx.FontSize(55)
|
||||
gfx.Text(song.title,desw/2, 1114)
|
||||
|
||||
gfx.FontSize(30)
|
||||
gfx.Text(song.artist, desw/2 , 1182)
|
||||
|
||||
|
||||
local EFFECTOR_LABEL_Y = 1288
|
||||
local ILLUSTRATOR_LABEL_Y = 1347
|
||||
|
||||
gfx.FontSize(22)
|
||||
|
||||
gfx.Text(song.effector, desw/2+70 , EFFECTOR_LABEL_Y-1)
|
||||
gfx.Text(song.illustrator, desw/2+70 , ILLUSTRATOR_LABEL_Y-3)
|
||||
|
||||
-- Draw song diff level
|
||||
gfx.BeginPath();
|
||||
Numbers.draw_number(933, 1140, 1.0, song.level, 2, difficultyNumbers, false, 1, 1)
|
||||
|
||||
-- Draw song diff label (NOV/ADV/EXH/MXM/etc.)
|
||||
gfx.BeginPath();
|
||||
local diffLabelImage = difficultyLabelImages[song.difficulty+1];
|
||||
local diffLabelW, diffLabelH = gfx.ImageSize(diffLabelImage);
|
||||
gfx.ImageRect(952-diffLabelW/2, 1154-diffLabelH/2, diffLabelW, diffLabelH, diffLabelImage,1,0);
|
||||
gfx.ClosePath();
|
||||
|
||||
gfx.Save();
|
||||
gfx.FontSize(24)
|
||||
gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
|
||||
gfx.BeginPath();
|
||||
|
||||
gfx.Text('BPM', 127, 1140)
|
||||
gfx.Text(song.bpm, 127, 1167)
|
||||
|
||||
-- temp ref overlay
|
||||
-- gfx.BeginPath()
|
||||
-- gfx.ImageRect(0, 0, 1080, 1920, refBgImage,0.5,0);
|
||||
|
||||
gfx.ClosePath();
|
||||
gfx.Restore();
|
||||
end
|
||||
|
||||
function reset()
|
||||
transitionProgress = 0
|
||||
resX, resY = game.GetResolution()
|
||||
fullX = portraitWidescreenRatio * resY
|
||||
fullY = resY
|
||||
outProgress = 0
|
||||
wasEnterSfxPlayed = false;
|
||||
end
|
|
@ -1,215 +0,0 @@
|
|||
local mposx = 0;
|
||||
local mposy = 0;
|
||||
local hovered = nil;
|
||||
local cursorIndex = 1
|
||||
local buttonWidth = 250;
|
||||
local buttonHeight = 50;
|
||||
local buttonBorder = 2;
|
||||
local label = -1;
|
||||
|
||||
local gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test")
|
||||
gfx.GradientColors(0,127,255,255,0,128,255,0)
|
||||
local gradient = gfx.LinearGradient(0,0,0,1)
|
||||
local bgPattern = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX + gfx.IMAGE_REPEATY)
|
||||
local bgAngle = 0.5
|
||||
local bgPaint = gfx.ImagePattern(0,0, 256,256, bgAngle, bgPattern, 1.0)
|
||||
local bgPatternTimer = 0
|
||||
local cursorYs = {}
|
||||
local buttons = nil
|
||||
local resx, resy = game.GetResolution();
|
||||
|
||||
view_update = function()
|
||||
if package.config:sub(1,1) == '\\' then --windows
|
||||
updateUrl, updateVersion = game.UpdateAvailable()
|
||||
os.execute("start " .. updateUrl)
|
||||
else --unix
|
||||
--TODO: Mac solution
|
||||
os.execute("xdg-open " .. updateUrl)
|
||||
end
|
||||
end
|
||||
|
||||
mouse_clipped = function(x,y,w,h)
|
||||
return mposx > x and mposy > y and mposx < x+w and mposy < y+h;
|
||||
end;
|
||||
|
||||
draw_button = function(button, x, y)
|
||||
local name = button[1]
|
||||
local rx = x - (buttonWidth / 2);
|
||||
local ty = y - (buttonHeight / 2);
|
||||
gfx.BeginPath();
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
|
||||
|
||||
gfx.FontSize(40);
|
||||
|
||||
if mouse_clipped(rx,ty, buttonWidth, buttonHeight) then
|
||||
hovered = button[2];
|
||||
r, b_g, b_b, b_a = game.GetSkinSetting("col_test")
|
||||
gfx.FillColor(0, 125, 255);
|
||||
gfx.Text(name, x+1, y+1);
|
||||
gfx.Text(name, x-1, y+1);
|
||||
gfx.Text(name, x+1, y-1);
|
||||
gfx.Text(name, x-1, y-1);
|
||||
end
|
||||
gfx.FillColor(255,255,255);
|
||||
gfx.Text(name, x, y);
|
||||
return buttonHeight + 5
|
||||
end;
|
||||
|
||||
function updateGradient()
|
||||
gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test")
|
||||
if gr_r == nil then return end
|
||||
gfx.GradientColors(gr_r,gr_g,gr_b,gr_a,0,128,255,0)
|
||||
--gradient = gfx.LinearGradient(0,0,0,1)
|
||||
end
|
||||
|
||||
function updatePattern(dt)
|
||||
bgPatternTimer = (bgPatternTimer + dt) % 1.0
|
||||
local bgx = math.cos(bgAngle) * (bgPatternTimer * 256)
|
||||
local bgy = math.sin(bgAngle) * (bgPatternTimer * 256)
|
||||
gfx.UpdateImagePattern(bgPaint, bgx, bgy, 256, 256, bgAngle, 1.0)
|
||||
end
|
||||
|
||||
function setButtons()
|
||||
if buttons == nil then
|
||||
buttons = {}
|
||||
buttons[1] = {"Start", Menu.Start}
|
||||
buttons[2] = {"Multiplayer", Menu.Multiplayer}
|
||||
buttons[3] = {"Challenges", Menu.Challenges}
|
||||
buttons[4] = {"Get Songs", Menu.DLScreen}
|
||||
buttons[5] = {"Settings", Menu.Settings}
|
||||
buttons[6] = {"Exit", Menu.Exit}
|
||||
end
|
||||
end
|
||||
|
||||
local renderY = resy/2
|
||||
function draw_cursor(x,y,deltaTime)
|
||||
gfx.Save()
|
||||
gfx.BeginPath()
|
||||
|
||||
local size = 8
|
||||
|
||||
renderY = renderY - (renderY - y) * deltaTime * 30
|
||||
|
||||
gfx.MoveTo(x-size,renderY-size)
|
||||
gfx.LineTo(x,renderY)
|
||||
gfx.LineTo(x-size,renderY+size)
|
||||
|
||||
gfx.StrokeWidth(3)
|
||||
gfx.StrokeColor(255,255,255)
|
||||
gfx.Stroke()
|
||||
|
||||
gfx.Restore()
|
||||
end
|
||||
|
||||
|
||||
function sign(x)
|
||||
return x>0 and 1 or x<0 and -1 or 0
|
||||
end
|
||||
|
||||
function roundToZero(x)
|
||||
if x<0 then return math.ceil(x)
|
||||
elseif x>0 then return math.floor(x)
|
||||
else return 0 end
|
||||
end
|
||||
|
||||
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 lastKnobs = nil
|
||||
local knobProgress = 0
|
||||
function handle_controller()
|
||||
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) > 1 then
|
||||
cursorIndex = (((cursorIndex - 1) + roundToZero(knobProgress)) % #buttons) + 1
|
||||
knobProgress = knobProgress - roundToZero(knobProgress)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
render = function(deltaTime)
|
||||
setButtons()
|
||||
updateGradient()
|
||||
updatePattern(deltaTime)
|
||||
resx,resy = game.GetResolution();
|
||||
mposx,mposy = game.GetMousePos();
|
||||
gfx.Scale(resx, resy / 3)
|
||||
gfx.Rect(0,0,1,1)
|
||||
gfx.FillPaint(gradient)
|
||||
gfx.Fill()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.Scale(0.5,0.5)
|
||||
gfx.Rect(0,0,resx * 2,resy * 2)
|
||||
gfx.GlobalCompositeOperation(gfx.BLEND_OP_DESTINATION_IN)
|
||||
gfx.FillPaint(bgPaint)
|
||||
gfx.Fill()
|
||||
gfx.ResetTransform()
|
||||
gfx.BeginPath()
|
||||
gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER)
|
||||
|
||||
cursorGet = 1
|
||||
buttonY = resy / 2;
|
||||
hovered = nil;
|
||||
|
||||
gfx.LoadSkinFont("NotoSans-Regular.ttf");
|
||||
|
||||
for i=1,#buttons do
|
||||
cursorYs[i] = buttonY
|
||||
buttonY = buttonY + draw_button(buttons[i], resx / 2, buttonY);
|
||||
if hovered == buttons[i][2] then
|
||||
cursorIndex = i
|
||||
end
|
||||
end
|
||||
|
||||
handle_controller()
|
||||
|
||||
draw_cursor(resx/2 - 100, cursorYs[cursorIndex], deltaTime)
|
||||
|
||||
gfx.BeginPath();
|
||||
gfx.FillColor(255,255,255);
|
||||
gfx.FontSize(120);
|
||||
if label == -1 then
|
||||
label = gfx.CreateLabel("ExperimentalGear ALPHA 1.8.7 ''README.TXT''", 120, 0);
|
||||
end
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
|
||||
gfx.DrawLabel(label, resx / 2, resy / 2 - 200, resx-40);
|
||||
updateUrl, updateVersion = game.UpdateAvailable()
|
||||
if updateUrl then
|
||||
gfx.BeginPath()
|
||||
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
|
||||
gfx.FontSize(30)
|
||||
gfx.Text(string.format("Version %s is now available", updateVersion), 5, resy - buttonHeight - 10)
|
||||
draw_button({"View", view_update}, buttonWidth / 2 + 5, resy - buttonHeight / 2 - 5);
|
||||
draw_button({"Update", Menu.Update}, buttonWidth * 1.5 + 15, resy - buttonHeight / 2 - 5)
|
||||
end
|
||||
end;
|
||||
|
||||
mouse_pressed = function(button)
|
||||
if hovered then
|
||||
hovered()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function button_pressed(button)
|
||||
if button == game.BUTTON_STA then
|
||||
buttons[cursorIndex][2]()
|
||||
elseif button == game.BUTTON_BCK then
|
||||
Menu.Exit()
|
||||
end
|
||||
end
|
|
@ -42,10 +42,10 @@ local settingsLabelImage = gfx.CreateSkinImage(
|
|||
'titlescreen/labels/settings.png', 0);
|
||||
local exitLabelImage = gfx.CreateSkinImage('titlescreen/labels/exit.png', 0);
|
||||
|
||||
local creww = game.GetSkinSetting("single_idol")
|
||||
local crew = game.GetSkinSetting("single_idol")
|
||||
|
||||
-- ANIMS
|
||||
local idolAnimation = gfx.LoadSkinAnimation('crew/anim/'..creww, 1 / 30, 0, true);
|
||||
local idolAnimation = gfx.LoadSkinAnimation('crew/anim/'..crew, 1 / 30, 0, true);
|
||||
|
||||
-- AUDIO
|
||||
game.LoadSkinSample('titlescreen/bgm.wav');
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = fsTex.x;
|
||||
|
||||
if (x < 0.0 || x > 1.0)
|
||||
{
|
||||
target = vec4(0);
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 mainColor = texture(mainTex, vec2(x,fsTex.y));
|
||||
target = vec4(0.0, 0.0, 0.0, mainColor.a);
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = fsTex.x;
|
||||
|
||||
if (x < 0.0 || x > 1.0)
|
||||
{
|
||||
target = vec4(0);
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 mainColor = texture(mainTex, vec2(x,fsTex.y));
|
||||
target = vec4(0.0, 0.0, 0.0, mainColor.a);
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
}
|
|
@ -1,26 +1,26 @@
|
|||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
uniform vec4 lCol;
|
||||
uniform vec4 rCol;
|
||||
uniform float hidden;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 mainColor = texture(mainTex, fsTex.xy);
|
||||
vec4 col = mainColor;
|
||||
|
||||
if(fsTex.y > hidden * 1.0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
col.xyz = vec3(0.);
|
||||
col.a = col.a > 0.0 ? 0.3 : 0.0;
|
||||
}
|
||||
target = col;
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
uniform vec4 lCol;
|
||||
uniform vec4 rCol;
|
||||
uniform float hidden;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 mainColor = texture(mainTex, fsTex.xy);
|
||||
vec4 col = mainColor;
|
||||
|
||||
if(fsTex.y > hidden * 1.0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
col.xyz = vec3(0.);
|
||||
col.a = col.a > 0.0 ? 0.3 : 0.0;
|
||||
}
|
||||
target = col;
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
}
|
|
@ -1,53 +1,53 @@
|
|||
#ifdef EMBEDDED
|
||||
varying vec2 fsTex;
|
||||
#else
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
#endif
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
uniform float hiddenCutoff;
|
||||
uniform float hiddenFadeWindow;
|
||||
uniform float suddenCutoff;
|
||||
uniform float suddenFadeWindow;
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef EMBEDDED
|
||||
target = vec4(0.0);
|
||||
#else
|
||||
target = texture(mainTex, vec2(fsTex.x, fsTex.y * 2.0));
|
||||
|
||||
float off = 1.0 - (fsTex.y * 2.0);
|
||||
|
||||
if(hiddenCutoff < suddenCutoff)
|
||||
{
|
||||
float hiddenCutoffFade = hiddenCutoff - hiddenFadeWindow;
|
||||
if (off > hiddenCutoffFade && off < hiddenCutoff) {
|
||||
target.a = target.a * max(0.0, (hiddenCutoff - off) / hiddenFadeWindow);
|
||||
}
|
||||
|
||||
if (off < suddenCutoff && off > hiddenCutoff) {
|
||||
target.a = 0.0;
|
||||
}
|
||||
|
||||
float suddenCutoffFade = suddenCutoff + suddenFadeWindow;
|
||||
if (off < suddenCutoffFade && off > suddenCutoff) {
|
||||
target.a = target.a * max(0.0, (off - suddenCutoff) / suddenFadeWindow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float hiddenCutoffFade = hiddenCutoff + hiddenFadeWindow;
|
||||
if (off > hiddenCutoff) {
|
||||
target.a = target.a * max(0.0, (hiddenCutoffFade - off) / hiddenFadeWindow);
|
||||
}
|
||||
|
||||
float suddenCutoffFade = suddenCutoff - suddenFadeWindow;
|
||||
if (off < suddenCutoff) {
|
||||
target.a = target.a * max(0.0, (off - suddenCutoffFade) / suddenFadeWindow);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef EMBEDDED
|
||||
varying vec2 fsTex;
|
||||
#else
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=1) in vec2 fsTex;
|
||||
layout(location=0) out vec4 target;
|
||||
#endif
|
||||
|
||||
uniform sampler2D mainTex;
|
||||
uniform float hiddenCutoff;
|
||||
uniform float hiddenFadeWindow;
|
||||
uniform float suddenCutoff;
|
||||
uniform float suddenFadeWindow;
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef EMBEDDED
|
||||
target = vec4(0.0);
|
||||
#else
|
||||
target = texture(mainTex, vec2(fsTex.x, fsTex.y * 2.0));
|
||||
|
||||
float off = 1.0 - (fsTex.y * 2.0);
|
||||
|
||||
if(hiddenCutoff < suddenCutoff)
|
||||
{
|
||||
float hiddenCutoffFade = hiddenCutoff - hiddenFadeWindow;
|
||||
if (off > hiddenCutoffFade && off < hiddenCutoff) {
|
||||
target.a = target.a * max(0.0, (hiddenCutoff - off) / hiddenFadeWindow);
|
||||
}
|
||||
|
||||
if (off < suddenCutoff && off > hiddenCutoff) {
|
||||
target.a = 0.0;
|
||||
}
|
||||
|
||||
float suddenCutoffFade = suddenCutoff + suddenFadeWindow;
|
||||
if (off < suddenCutoffFade && off > suddenCutoff) {
|
||||
target.a = target.a * max(0.0, (off - suddenCutoff) / suddenFadeWindow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float hiddenCutoffFade = hiddenCutoff + hiddenFadeWindow;
|
||||
if (off > hiddenCutoff) {
|
||||
target.a = target.a * max(0.0, (hiddenCutoffFade - off) / hiddenFadeWindow);
|
||||
}
|
||||
|
||||
float suddenCutoffFade = suddenCutoff - suddenFadeWindow;
|
||||
if (off < suddenCutoff) {
|
||||
target.a = target.a * max(0.0, (off - suddenCutoffFade) / suddenFadeWindow);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,25 +1,25 @@
|
|||
#ifdef EMBEDDED
|
||||
attribute vec2 inPos;
|
||||
attribute vec2 inTex;
|
||||
varying vec2 fsTex;
|
||||
#else
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
#endif
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
#ifdef EMBEDDED
|
||||
attribute vec2 inPos;
|
||||
attribute vec2 inTex;
|
||||
varying vec2 fsTex;
|
||||
#else
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
layout(location=0) in vec2 inPos;
|
||||
layout(location=1) in vec2 inTex;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location=1) out vec2 fsTex;
|
||||
#endif
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 camera;
|
||||
uniform mat4 world;
|
||||
|
||||
void main()
|
||||
{
|
||||
fsTex = inTex;
|
||||
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
|
||||
}
|
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,15 @@
|
|||
CUSTOM CREW CREATOR
|
||||
|
||||
Must have:
|
||||
|
||||
Art Program with layers (Paint.NET and GIMP are both FREE)
|
||||
Basic Knowledge of the program
|
||||
Custom crew assets
|
||||
|
||||
1: Bottom-most layer is frame_glow.png
|
||||
2: Middle layer is your custom crew member
|
||||
3: Third layer is frame_metal.png
|
||||
|
||||
Clean up so no part of your custom crew is outside of frame_metal.png.
|
||||
|
||||
EXPORT AS .PNG FILE!
|
After Width: | Height: | Size: 129 B |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 268 KiB |
Before Width: | Height: | Size: 16 KiB |