normalize line endings

This commit is contained in:
Hersi 2022-02-20 17:27:27 +01:00
parent 6b4eac23e2
commit d0c2932c5b
25 changed files with 6327 additions and 6326 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -1,36 +1,36 @@
# ExperimentalGear skin for USC # ExperimentalGear skin for USC
Project Starter: GSK Bladez Project Starter: GSK Bladez
## Coding ## Coding
- [REDACTED] - [REDACTED]
- Hersi - Hersi
- fdigl - fdigl
- Hoshikara - Hoshikara
- GSK Bladez - GSK Bladez
- Local - Local
## Graphics ## Graphics
- GSK Bladez - GSK Bladez
- Neardayo - Neardayo
- YellowBird - YellowBird
- Dengekiko - Dengekiko
## Translation ## Translation
- GSK Bladez - GSK Bladez
- Neardayo - Neardayo
- RealFD - RealFD
## Misc. Help ## Misc. Help
- Neardayo - Neardayo
- DDX - DDX
- GM*DEO - GM*DEO
## Beta Testing ## Beta Testing
- Gam - Gam
- TealStar - TealStar
- Dengikiko - Dengikiko
- Adamyes - Adamyes
- Gio - Gio
- Mattadome - Mattadome

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,86 +1,86 @@
local newFormatTemplate = { local newFormatTemplate = {
{ {
Bg={ Bg={
Base={ Base={
Tex={{"",""}}, Tex={{"",""}},
Tilt=false, Tilt=false,
OffsetY=0.17, OffsetY=0.17,
Anim=true, Anim=true,
ScaleSoft=false, ScaleSoft=false,
ClampTiling=false, ClampTiling=false,
}, },
Overlay={ -- is meant to be displayed overtop Base Overlay={ -- is meant to be displayed overtop Base
Tex="", Tex="",
Float=true, Float=true,
FloatFactor=2.0, FloatFactor=2.0,
OffsetY=0.12, OffsetY=0.12,
FlashEffect=true, FlashEffect=true,
Tilt=true, Tilt=true,
}, },
Layer={ Layer={
Tex={{"",""}}, Tex={{"",""}},
ScaleHard=false, ScaleHard=false,
Brighten=0.6, Brighten=0.6,
}, },
u={ u={
Pivot=0.36 Pivot=0.36
}, },
}, },
Center={ Center={
Tex={{"",""}}, Tex={{"",""}},
u={ u={
Scale=2.8, Scale=2.8,
Pulse=true, Pulse=true,
Float=true, Float=true,
FloatFactor=0.5, FloatFactor=0.5,
FloatXFactor=0, FloatXFactor=0,
FloatRotationFactor=0, FloatRotationFactor=0,
FadeEffect=true, FadeEffect=true,
Tilt=false, Tilt=false,
Anim=true, Anim=true,
OffsetY=-0.1, OffsetY=-0.1,
Glow=false, Glow=false,
Rotate=false, Rotate=false,
}, },
LayerEffect={ LayerEffect={
Tex="", Tex="",
Fade=true, Fade=true,
Rotate=true, Rotate=true,
RotateSpeed=-2.0, RotateSpeed=-2.0,
DodgeBlend=true, DodgeBlend=true,
Glow=true, Glow=true,
Alpha=0.7, Alpha=0.7,
Scale=0.5, Scale=0.5,
}, },
}, },
Tunnel={ -- todo: give option to do sth with blend mode Tunnel={ -- todo: give option to do sth with blend mode
Tex={{"",""}}, Tex={{"",""}},
u={ u={
Stretch=0.3, Stretch=0.3,
ScaleX=0.8, ScaleX=0.8,
ScaleY=0.9, ScaleY=0.9,
Fog=10.0, Fog=10.0,
FlashEffect=true, FlashEffect=true,
VortexEffect=false, VortexEffect=false,
VortexFactor=1.0, VortexFactor=1.0,
DodgeBlend=false DodgeBlend=false
} }
}, },
Particle={ Particle={
Tex={{"",""}}, Tex={{"",""}},
u={ u={
Speed=0.3, Speed=0.3,
OffsetY=-0.02, OffsetY=-0.02,
Amount=3, Amount=3,
Scale=1.0, Scale=1.0,
ExtraRotation=0.125 ExtraRotation=0.125
}, },
}, },
luaParticleEffect={ luaParticleEffect={
particles={ particles={
{"star-particle.png", 32} {"star-particle.png", 32}
} }
}, },
speed=0.6, speed=0.6,
} }
} }

View File

@ -1,68 +1,68 @@
{ {
"User information": {"type": "label"}, "User information": {"type": "label"},
"username": { "username": {
"type": "text", "type": "text",
"label": "Username (max 8 characters)", "label": "Username (max 8 characters)",
"default": "GUEST" "default": "GUEST"
}, },
"separator_a": {}, "separator_a": {},
"MSG": { "MSG": {
"type": "text", "type": "text",
"label": "Message (max 8 characters)", "label": "Message (max 8 characters)",
"default": "Hellooooooo" "default": "Hellooooooo"
}, },
"separator_b": {}, "separator_b": {},
"Animations": {"type": "label"}, "Animations": {"type": "label"},
"animations_affectWithBPM": { "animations_affectWithBPM": {
"type": "bool", "type": "bool",
"label": "Affect speed of some animations with the current song's BPM", "label": "Affect speed of some animations with the current song's BPM",
"default": false "default": false
}, },
"separator_c": {}, "separator_c": {},
"Crew": { "type": "label" }, "Crew": { "type": "label" },
"single_idol": { "single_idol": {
"label": "!!!ALWAYS MATCH THE NAME!!!", "label": "!!!ALWAYS MATCH THE NAME!!!",
"type": "text", "type": "text",
"default": "nothing" "default": "nothing"
}, },
"words": { "words": {
"type": "selection", "type": "selection",
"label": "Language", "label": "Language",
"default": "EN", "default": "EN",
"values": ["EN", "DE", "SK", "test2"] "values": ["EN", "DE", "SK", "test2"]
}, },
"separator_d": {}, "separator_d": {},
"Audio": { "type": "label" }, "Audio": { "type": "label" },
"audio_systemVoice": { "audio_systemVoice": {
"label": "Turn on Rasis", "label": "Turn on Rasis",
"type": "bool", "type": "bool",
"default": false "default": false
}, },
"separator_e": {}, "separator_e": {},
"Gameplay": { "type": "label" }, "Gameplay": { "type": "label" },
"gameplay_ucDifferentColor": { "gameplay_ucDifferentColor": {
"label": "Use different colors for UC and PUC chain numbers", "label": "Use different colors for UC and PUC chain numbers",
"type": "bool", "type": "bool",
"default": false "default": false
}, },
"separator_f": {}, "separator_f": {},
"Debug": { "type": "label" }, "Debug": { "type": "label" },
"debug_showInformation": { "debug_showInformation": {
"label": "Show debug information (sometimes in the middle of the screen when you're playing)", "label": "Show debug information (sometimes in the middle of the screen when you're playing)",
"type": "bool", "type": "bool",
"default": false "default": false
} }
} }

View File

@ -1,302 +1,302 @@
local Easing = require("common.easing"); local Easing = require("common.easing");
local Footer = require("components.footer"); local Footer = require("components.footer");
local DiffRectangle = require('components.diff_rectangle'); local DiffRectangle = require('components.diff_rectangle');
local common = require('common.common'); local common = require('common.common');
local Numbers = require('common.numbers') local Numbers = require('common.numbers')
local VolforceWindow = require("components.volforceWindow") local VolforceWindow = require("components.volforceWindow")
-- Window variables -- Window variables
local resX, resY local resX, resY
-- Aspect Ratios -- Aspect Ratios
local landscapeWidescreenRatio = 16 / 9 local landscapeWidescreenRatio = 16 / 9
local landscapeStandardRatio = 4 / 3 local landscapeStandardRatio = 4 / 3
local portraitWidescreenRatio = 9 / 16 local portraitWidescreenRatio = 9 / 16
-- Portrait sizes -- Portrait sizes
local fullX, fullY local fullX, fullY
local desw = 1080 local desw = 1080
local desh = 1920 local desh = 1920
local function resolutionChange(x, y) local function resolutionChange(x, y)
resX = x resX = x
resY = y resY = y
fullX = portraitWidescreenRatio * y fullX = portraitWidescreenRatio * y
fullY = y fullY = y
end end
local bgSfxPlayed = false; local bgSfxPlayed = false;
local BAR_ALPHA = 191; local BAR_ALPHA = 191;
local HEADER_HEIGHT = 100 local HEADER_HEIGHT = 100
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY) local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
local resultBgImage = gfx.CreateSkinImage("challenge_result/bg.png", 0); local resultBgImage = gfx.CreateSkinImage("challenge_result/bg.png", 0);
local playerInfoOverlayBgImage = gfx.CreateSkinImage("challenge_result/player_info_overlay_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 headerTitleImage = gfx.CreateSkinImage("challenge_result/header/title.png", 0);
local username = game.GetSkinSetting("username"); local username = game.GetSkinSetting("username");
local appealCardImage = gfx.CreateSkinImage("crew/appeal_card.png", 0); local appealCardImage = gfx.CreateSkinImage("crew/appeal_card.png", 0);
local danBadgeImage = gfx.CreateSkinImage("dan/inf.png", 0); local danBadgeImage = gfx.CreateSkinImage("dan/inf.png", 0);
local crewImage = gfx.CreateSkinImage("crew/portrait.png", 0); local crewImage = gfx.CreateSkinImage("crew/portrait.png", 0);
local notchesImage = gfx.CreateSkinImage("challenge_result/notches.png", 0); local notchesImage = gfx.CreateSkinImage("challenge_result/notches.png", 0);
local trackBarsImage = gfx.CreateSkinImage("challenge_result/track_bars.png", 0); local trackBarsImage = gfx.CreateSkinImage("challenge_result/track_bars.png", 0);
local completionFailImage = gfx.CreateSkinImage("challenge_result/pass_states/fail.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 completionPassImage = gfx.CreateSkinImage("challenge_result/pass_states/pass.png", 0);
local irHeartbeatRequested = false; local irHeartbeatRequested = false;
local IRserverName = ""; local IRserverName = "";
local badgeImages = { local badgeImages = {
gfx.CreateSkinImage("song_select/medal/nomedal.png", 1), gfx.CreateSkinImage("song_select/medal/nomedal.png", 1),
gfx.CreateSkinImage("song_select/medal/played.png", 1), gfx.CreateSkinImage("song_select/medal/played.png", 1),
gfx.CreateSkinImage("song_select/medal/clear.png", 1), gfx.CreateSkinImage("song_select/medal/clear.png", 1),
gfx.CreateSkinImage("song_select/medal/hard.png", 1), gfx.CreateSkinImage("song_select/medal/hard.png", 1),
gfx.CreateSkinImage("song_select/medal/uc.png", 1), gfx.CreateSkinImage("song_select/medal/uc.png", 1),
gfx.CreateSkinImage("song_select/medal/puc.png", 1), gfx.CreateSkinImage("song_select/medal/puc.png", 1),
} }
local gradeImages = { local gradeImages = {
S = gfx.CreateSkinImage("common/grades/S.png", 0), S = gfx.CreateSkinImage("common/grades/S.png", 0),
AAA_P = gfx.CreateSkinImage("common/grades/AAA+.png", 0), AAA_P = gfx.CreateSkinImage("common/grades/AAA+.png", 0),
AAA = 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_P = gfx.CreateSkinImage("common/grades/AA+.png", 0),
AA = 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_P = gfx.CreateSkinImage("common/grades/A+.png", 0),
A = gfx.CreateSkinImage("common/grades/A.png", 0), A = gfx.CreateSkinImage("common/grades/A.png", 0),
B = gfx.CreateSkinImage("common/grades/B.png", 0), B = gfx.CreateSkinImage("common/grades/B.png", 0),
C = gfx.CreateSkinImage("common/grades/C.png", 0), C = gfx.CreateSkinImage("common/grades/C.png", 0),
D = gfx.CreateSkinImage("common/grades/D.png", 0), D = gfx.CreateSkinImage("common/grades/D.png", 0),
none = gfx.CreateSkinImage("common/grades/none.png", 0), none = gfx.CreateSkinImage("common/grades/none.png", 0),
} }
local percRequired = 1; local percRequired = 1;
local percGet = 0; local percGet = 0;
-- AUDIO -- AUDIO
game.LoadSkinSample("challenge_result.wav") game.LoadSkinSample("challenge_result.wav")
function resetLayoutInformation() function resetLayoutInformation()
resx, resy = game.GetResolution() resx, resy = game.GetResolution()
desw = 1080 desw = 1080
desh = 1920 desh = 1920
scale = resx / desw scale = resx / desw
end end
local function handleSfx() local function handleSfx()
if not bgSfxPlayed then if not bgSfxPlayed then
common.stopMusic(); common.stopMusic();
game.PlaySample("challenge_result.wav", true) game.PlaySample("challenge_result.wav", true)
bgSfxPlayed = true bgSfxPlayed = true
end end
if game.GetButton(game.BUTTON_STA) then if game.GetButton(game.BUTTON_STA) then
game.StopSample("challenge_result.wav") game.StopSample("challenge_result.wav")
end end
if game.GetButton(game.BUTTON_BCK) then if game.GetButton(game.BUTTON_BCK) then
game.StopSample("challenge_result.wav") game.StopSample("challenge_result.wav")
end end
end end
function drawBackground() function drawBackground()
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0, 0, desw, desh, resultBgImage, 1, 0); gfx.ImageRect(0, 0, desw, desh, resultBgImage, 1, 0);
end end
function drawHeader() function drawHeader()
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(0, 0, 0, BAR_ALPHA); gfx.FillColor(0, 0, 0, BAR_ALPHA);
gfx.Rect(0, 0, desw, HEADER_HEIGHT); gfx.Rect(0, 0, desw, HEADER_HEIGHT);
gfx.Fill(); gfx.Fill();
gfx.ClosePath() gfx.ClosePath()
gfx.ImageRect(desw / 2 - 209, HEADER_HEIGHT / 2 - 52, 419, 105, headerTitleImage, 1, 0) gfx.ImageRect(desw / 2 - 209, HEADER_HEIGHT / 2 - 52, 419, 105, headerTitleImage, 1, 0)
end end
function drawPlayerInfo() function drawPlayerInfo()
-- Draw crew -- Draw crew
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(460, 215, 522, 362, crewImage, 1, 0); gfx.ImageRect(460, 215, 522, 362, crewImage, 1, 0);
-- Draw the info bg -- Draw the info bg
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(300, 352, 374 * 0.85, 222 * 0.85, playerInfoOverlayBgImage, 1, 0); gfx.ImageRect(300, 352, 374 * 0.85, 222 * 0.85, playerInfoOverlayBgImage, 1, 0);
-- Draw appeal card -- Draw appeal card
gfx.BeginPath(); gfx.BeginPath();
gfx.ImageRect(145, 364, 103 * 1.25, 132 * 1.25, appealCardImage, 1, 0); gfx.ImageRect(145, 364, 103 * 1.25, 132 * 1.25, appealCardImage, 1, 0);
-- Draw description -- Draw description
gfx.FontSize(28) gfx.FontSize(28)
gfx.LoadSkinFont("Digital-Serial-Bold.ttf") gfx.LoadSkinFont("Digital-Serial-Bold.ttf")
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
gfx.Text("Hellooooooo", 310, 370); gfx.Text("Hellooooooo", 310, 370);
-- Draw username -- Draw username
gfx.FontSize(40) gfx.FontSize(40)
gfx.Text(username, 310, 413); gfx.Text(username, 310, 413);
-- Draw IR server name -- Draw IR server name
gfx.FontSize(28) gfx.FontSize(28)
gfx.Text(IRserverName, 310, 453); gfx.Text(IRserverName, 310, 453);
-- Draw dan badge -- Draw dan badge
gfx.BeginPath(); gfx.BeginPath();
gfx.ImageRect(311, 490, 107 * 1.25, 29 * 1.25, danBadgeImage, 1, 0); gfx.ImageRect(311, 490, 107 * 1.25, 29 * 1.25, danBadgeImage, 1, 0);
end end
local scoreNumber = Numbers.load_number_image("score_num"); local scoreNumber = Numbers.load_number_image("score_num");
function drawChartResult(deltaTime, x, y, chartResult) function drawChartResult(deltaTime, x, y, chartResult)
gfx.Save() gfx.Save()
gfx.LoadSkinFont('NotoSans-Regular.ttf') gfx.LoadSkinFont('NotoSans-Regular.ttf')
gfx.FontSize(28) gfx.FontSize(28)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
gfx.BeginPath() gfx.BeginPath()
gfx.GlobalAlpha(1); gfx.GlobalAlpha(1);
gfx.Text(chartResult.title, x+160,y+32); gfx.Text(chartResult.title, x+160,y+32);
DiffRectangle.render(deltaTime, x+287.5, y+67, 0.85, chartResult.difficulty, chartResult.level) DiffRectangle.render(deltaTime, x+287.5, y+67, 0.85, chartResult.difficulty, chartResult.level)
local score = chartResult.score or 0; 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 + 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) 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 gradeImageKey = string.gsub(chartResult.grade, '+', '_P');
local gradeImage = gradeImages[gradeImageKey] or gradeImages.D local gradeImage = gradeImages[gradeImageKey] or gradeImages.D
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(x+800, y+12, 79, 79, gradeImage, 1, 0) gfx.ImageRect(x+800, y+12, 79, 79, gradeImage, 1, 0)
if chartResult.badge then if chartResult.badge then
local badgeImage = badgeImages[chartResult.badge+1]; local badgeImage = badgeImages[chartResult.badge+1];
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(x+900, y+16, 79*1.05, 69*1.05, badgeImage, 1, 0) gfx.ImageRect(x+900, y+16, 79*1.05, 69*1.05, badgeImage, 1, 0)
end end
gfx.Restore() gfx.Restore()
end end
function drawScorePanelContent(deltaTime) function drawScorePanelContent(deltaTime)
-- game.Log("Drawing scores...", game.LOGGER_INFO) -- debug -- game.Log("Drawing scores...", game.LOGGER_INFO) -- debug
for i, chart in ipairs(result.charts) do for i, chart in ipairs(result.charts) do
-- if chart.score == nil then -- if chart.score == nil then
-- game.Log("Score does not exist? Quitting loop...", game.LOGGER_WARNING) -- game.Log("Score does not exist? Quitting loop...", game.LOGGER_WARNING)
-- break -- break
-- end -- end
drawChartResult(deltaTime, 0, 836+(165*(i-1)), chart); drawChartResult(deltaTime, 0, 836+(165*(i-1)), chart);
end end
end end
function drawDecorations() function drawDecorations()
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(118, 846.5, 43*0.855, 429*0.855, notchesImage, 1, 0) gfx.ImageRect(118, 846.5, 43*0.855, 429*0.855, notchesImage, 1, 0)
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(400, 807, 367*0.857, 429*0.857, trackBarsImage, 1, 0) gfx.ImageRect(400, 807, 367*0.857, 429*0.857, trackBarsImage, 1, 0)
end end
function drawCompletion() function drawCompletion()
local completitionImage = completionFailImage; local completitionImage = completionFailImage;
if result.passed then if result.passed then
completitionImage = completionPassImage; completitionImage = completionPassImage;
end end
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(63, 1331, 766*0.85, 130*0.85, completitionImage, 1, 0) 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) Numbers.draw_number(925, 1370, 1.0, percGet, 3, scoreNumber, true, 0.3, 1.12)
gfx.BeginPath(); gfx.BeginPath();
gfx.Rect(741, 1402, 278*math.min(1, percGet / percRequired), 6); gfx.Rect(741, 1402, 278*math.min(1, percGet / percRequired), 6);
gfx.FillColor(255, 128, 0, 255); gfx.FillColor(255, 128, 0, 255);
gfx.Fill() gfx.Fill()
end end
function result_set() function result_set()
local reqTextWords = common.splitString(result.requirement_text, ' '); local reqTextWords = common.splitString(result.requirement_text, ' ');
for index, word in ipairs(reqTextWords) do for index, word in ipairs(reqTextWords) do
if string.find(word, '%%') ~= nil then -- %% = %, because % is an escape char if string.find(word, '%%') ~= nil then -- %% = %, because % is an escape char
local percNumber = tonumber(string.gsub(word, '%%', ''), 10) local percNumber = tonumber(string.gsub(word, '%%', ''), 10)
percRequired = percNumber; percRequired = percNumber;
end end
end end
game.Log(percRequired, game.LOGGER_ERROR); game.Log(percRequired, game.LOGGER_ERROR);
local a = 0; local a = 0;
for i, chart in ipairs(result.charts) do for i, chart in ipairs(result.charts) do
a = a + chart.percent; a = a + chart.percent;
game.Log('#' .. i .. ' got ' .. chart.percent .. '% // ACC at ' .. a, game.LOGGER_ERROR); game.Log('#' .. i .. ' got ' .. chart.percent .. '% // ACC at ' .. a, game.LOGGER_ERROR);
end end
percGet = a / #result.charts; percGet = a / #result.charts;
end end
local IR_HeartbeatResponse = function(res) local IR_HeartbeatResponse = function(res)
if res.statusCode == IRData.States.Success then if res.statusCode == IRData.States.Success then
IRserverName = res.body.serverName .. " " .. res.body.irVersion; IRserverName = res.body.serverName .. " " .. res.body.irVersion;
else else
game.Log("Can't connect to IR!", game.LOGGER_WARNING) game.Log("Can't connect to IR!", game.LOGGER_WARNING)
end end
end end
local IR_Handle = function() local IR_Handle = function()
if not irHeartbeatRequested then if not irHeartbeatRequested then
IR.Heartbeat(IR_HeartbeatResponse) IR.Heartbeat(IR_HeartbeatResponse)
irHeartbeatRequested = true; irHeartbeatRequested = true;
end end
end end
local function drawResultScreen(x, y, w, h, deltaTime) local function drawResultScreen(x, y, w, h, deltaTime)
gfx.BeginPath() gfx.BeginPath()
gfx.Translate(x, y); gfx.Translate(x, y);
gfx.Scale(w / 1080, h / 1920); gfx.Scale(w / 1080, h / 1920);
gfx.Scissor(0, 0, 1080, 1920); gfx.Scissor(0, 0, 1080, 1920);
handleSfx() handleSfx()
IR_Handle() IR_Handle()
drawBackground() drawBackground()
drawDecorations() drawDecorations()
drawPlayerInfo() drawPlayerInfo()
drawScorePanelContent(deltaTime) drawScorePanelContent(deltaTime)
drawCompletion() drawCompletion()
drawHeader() drawHeader()
Footer.draw(deltaTime); Footer.draw(deltaTime);
gfx.ResetTransform() gfx.ResetTransform()
end end
function render(deltaTime) function render(deltaTime)
-- detect resolution change -- detect resolution change
local resx, resy = game.GetResolution() local resx, resy = game.GetResolution()
if resx ~= resX or resy ~= resY then if resx ~= resX or resy ~= resY then
resolutionChange(resx, resy) resolutionChange(resx, resy)
end end
gfx.BeginPath() gfx.BeginPath()
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage) local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
gfx.Rect(0, 0, resX, resY) gfx.Rect(0, 0, resX, resY)
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2)) gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
gfx.Fill() gfx.Fill()
drawResultScreen((resX - fullX) / 2, 0, fullX, fullY, deltaTime) drawResultScreen((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
end end

View File

@ -1,107 +1,107 @@
options = {} options = {}
yscale = 0.0 yscale = 0.0
selectedIndex = 0 selectedIndex = 0
resx, resy = game.GetResolution() resx, resy = game.GetResolution()
width = 500 width = 500
titleText = "" titleText = ""
scale = 1.0 scale = 1.0
function make_option(name) function make_option(name)
return function() return function()
menu.Confirm(name) menu.Confirm(name)
end end
end end
function open() function open()
yscale = 0.0 yscale = 0.0
options = {} options = {}
selectedIndex = 0 selectedIndex = 0
resx, resy = game.GetResolution() resx, resy = game.GetResolution()
index = 1 index = 1
if #dialog.collections == 0 then if #dialog.collections == 0 then
options[index] = {"Favourites", make_option("Favourites"), {255,255,255}} options[index] = {"Favourites", make_option("Favourites"), {255,255,255}}
end end
for i,value in ipairs(dialog.collections) do for i,value in ipairs(dialog.collections) do
options[i] = {value.name, make_option(value.name), {255,255,255}} options[i] = {value.name, make_option(value.name), {255,255,255}}
if value.exists then options[i][3] = {255,0,0} end if value.exists then options[i][3] = {255,0,0} end
end end
table.insert(options, {"New Collection", menu.ChangeState, {0, 255, 128}}) table.insert(options, {"New Collection", menu.ChangeState, {0, 255, 128}})
table.insert(options, {"Cancel", menu.Cancel, {200,200,200}}) table.insert(options, {"Cancel", menu.Cancel, {200,200,200}})
gfx.FontFace("fallback") gfx.FontFace("fallback")
gfx.FontSize(50) gfx.FontSize(50)
titleText = string.format("Add %s to collection:", dialog.title) titleText = string.format("Add %s to collection:", dialog.title)
xmi,ymi,xma,yma = gfx.TextBounds(0,0, titleText) xmi,ymi,xma,yma = gfx.TextBounds(0,0, titleText)
width = xma - xmi + 50 width = xma - xmi + 50
width = math.max(500, width) width = math.max(500, width)
scale = math.min(resy / 1280, 1.0) scale = math.min(resy / 1280, 1.0)
scale = math.min(scale, resx / width) scale = math.min(scale, resx / width)
end end
function render(deltaTime) function render(deltaTime)
if dialog.closing then if dialog.closing then
yscale = math.min(yscale - deltaTime * 4, 1.0) yscale = math.min(yscale - deltaTime * 4, 1.0)
else else
yscale = math.min(yscale + deltaTime * 4, 1.0) yscale = math.min(yscale + deltaTime * 4, 1.0)
end end
gfx.Translate(resx / 2, resy / 2) gfx.Translate(resx / 2, resy / 2)
gfx.Scale(scale, yscale * scale) gfx.Scale(scale, yscale * scale)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(-width/2, -250, width, 500) gfx.Rect(-width/2, -250, width, 500)
gfx.FillColor(50,50,50) gfx.FillColor(50,50,50)
gfx.Fill() gfx.Fill()
gfx.BeginPath() gfx.BeginPath()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.FontFace("fallback") gfx.FontFace("fallback")
gfx.FontSize(50) gfx.FontSize(50)
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_CENTER) gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_CENTER)
gfx.Text(titleText, 0, -240) gfx.Text(titleText, 0, -240)
if dialog.isTextEntry then if dialog.isTextEntry then
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER) gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(-width/2 + 20, -30, width - 40, 60) gfx.Rect(-width/2 + 20, -30, width - 40, 60)
gfx.FillColor(25,25,25) gfx.FillColor(25,25,25)
gfx.Fill() gfx.Fill()
gfx.BeginPath() gfx.BeginPath()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.Text(dialog.newName, 0, 0) gfx.Text(dialog.newName, 0, 0)
else else
local yshift = 20 local yshift = 20
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT) gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_LEFT)
for i, option in ipairs(options) do for i, option in ipairs(options) do
local y = yshift + 60 * ((i-1) - selectedIndex) local y = yshift + 60 * ((i-1) - selectedIndex)
if y > -190 and y < 220 then if y > -190 and y < 220 then
gfx.FillColor(option[3][1], option[3][2], option[3][3]) gfx.FillColor(option[3][1], option[3][2], option[3][3])
gfx.Text(option[1], 40 - width/2, y) gfx.Text(option[1], 40 - width/2, y)
end end
end end
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(20 - width/2, -10 + yshift) gfx.MoveTo(20 - width/2, -10 + yshift)
gfx.LineTo(30 - width/2, 0 + yshift) gfx.LineTo(30 - width/2, 0 + yshift)
gfx.LineTo(20 - width/2, 10 + yshift) gfx.LineTo(20 - width/2, 10 + yshift)
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.Fill() gfx.Fill()
end end
if dialog.closing == true and yscale <= 0.0 then if dialog.closing == true and yscale <= 0.0 then
return false return false
else else
return true return true
end end
end end
function button_pressed(button) function button_pressed(button)
if button == game.BUTTON_BCK then if button == game.BUTTON_BCK then
menu.Cancel() menu.Cancel()
elseif button == game.BUTTON_STA then elseif button == game.BUTTON_STA then
options[selectedIndex+1][2]() options[selectedIndex+1][2]()
end end
end end
function advance_selection(value) function advance_selection(value)
selectedIndex = (selectedIndex + value) % #options selectedIndex = (selectedIndex + value) % #options
end end

View File

@ -1,429 +1,429 @@
json = require "json" json = require "json"
local header = {} local header = {}
header["user-agent"] = "unnamed_sdvx_clone" header["user-agent"] = "unnamed_sdvx_clone"
local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0) local jacketFallback = gfx.CreateSkinImage("song_select/loading.png", 0)
local diffColors = {{50,50,127}, {50,127,50}, {127,50,50}, {127, 50, 127}} local diffColors = {{50,50,127}, {50,127,50}, {127,50,50}, {127, 50, 127}}
local entryW = 770 local entryW = 770
local entryH = 320 local entryH = 320
local resX,resY = game.GetResolution() local resX,resY = game.GetResolution()
local xCount = math.max(1, math.floor(resX / entryW)) local xCount = math.max(1, math.floor(resX / entryW))
local yCount = math.max(1, math.floor(resY / entryH)) local yCount = math.max(1, math.floor(resY / entryH))
local xOffset = (resX - xCount * entryW) / 2 local xOffset = (resX - xCount * entryW) / 2
local cursorPos = 0 local cursorPos = 0
local cursorPosX = 0 local cursorPosX = 0
local cursorPosY = 0 local cursorPosY = 0
local displayCursorPosX = 0 local displayCursorPosX = 0
local displayCursorPosY = 0 local displayCursorPosY = 0
local nextUrl = "https://ksm.dev/app/songs" local nextUrl = "https://ksm.dev/app/songs"
local screenState = 0 --0 = normal, 1 = level, 2 = sorting local screenState = 0 --0 = normal, 1 = level, 2 = sorting
local loading = true local loading = true
local downloaded = {} local downloaded = {}
local songs = {} local songs = {}
local selectedLevels = {} local selectedLevels = {}
local selectedSorting = "Uploaded" local selectedSorting = "Uploaded"
local lastPlaying = nil local lastPlaying = nil
for i = 1, 20 do for i = 1, 20 do
selectedLevels[i] = false selectedLevels[i] = false
end end
local cachepath = path.Absolute("skins/" .. game.GetSkin() .. "/nautica.json") local cachepath = path.Absolute("skins/" .. game.GetSkin() .. "/nautica.json")
local levelcursor = 0 local levelcursor = 0
local sortingcursor = 0 local sortingcursor = 0
local sortingOptions = {"Uploaded", "Oldest"} local sortingOptions = {"Uploaded", "Oldest"}
local needsReload = false local needsReload = false
function addsong(song) function addsong(song)
if song.jacket_url ~= nil then if song.jacket_url ~= nil then
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250) song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
else else
song.jacket = jacketFallback song.jacket = jacketFallback
end end
if downloaded[song.id] then if downloaded[song.id] then
song.status = "Downloaded" song.status = "Downloaded"
end end
table.insert(songs, song) table.insert(songs, song)
end end
local yOffset = 0 local yOffset = 0
local backgroundImage = gfx.CreateSkinImage("bg.png", 1); local backgroundImage = gfx.CreateSkinImage("bg.png", 1);
dlcache = io.open(cachepath, "r") dlcache = io.open(cachepath, "r")
if dlcache then if dlcache then
downloaded = json.decode(dlcache:read("*all")) downloaded = json.decode(dlcache:read("*all"))
dlcache:close() dlcache:close()
end end
function encodeURI(str) function encodeURI(str)
if (str) then if (str) then
str = string.gsub(str, "\n", "\r\n") str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^%w ])", str = string.gsub(str, "([^%w ])",
function (c) function (c)
local dontChange = "-/_:." local dontChange = "-/_:."
for i = 1, #dontChange do for i = 1, #dontChange do
if c == dontChange:sub(i,i) then return c end if c == dontChange:sub(i,i) then return c end
end end
return string.format ("%%%02X", string.byte(c)) return string.format ("%%%02X", string.byte(c))
end) end)
str = string.gsub(str, " ", "%%20") str = string.gsub(str, " ", "%%20")
end end
return str return str
end end
function gotSongsCallback(response) function gotSongsCallback(response)
if response.status ~= 200 then if response.status ~= 200 then
error() error()
return return
end end
local jsondata = json.decode(response.text) local jsondata = json.decode(response.text)
for i,song in ipairs(jsondata.data) do for i,song in ipairs(jsondata.data) do
addsong(song) addsong(song)
end end
nextUrl = jsondata.links.next nextUrl = jsondata.links.next
loading = false loading = false
end end
Http.GetAsync(nextUrl, header, gotSongsCallback) Http.GetAsync(nextUrl, header, gotSongsCallback)
function render_song(song, x,y) function render_song(song, x,y)
gfx.Save() gfx.Save()
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
gfx.Translate(x,y) gfx.Translate(x,y)
gfx.Scissor(0,0,750,300) gfx.Scissor(0,0,750,300)
gfx.BeginPath() gfx.BeginPath()
gfx.FillColor(0,0,0,140) gfx.FillColor(0,0,0,140)
gfx.Rect(0,0,750,300) gfx.Rect(0,0,750,300)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.FontSize(30) gfx.FontSize(30)
gfx.Text(song.title, 2,2) gfx.Text(song.title, 2,2)
gfx.FontSize(24) gfx.FontSize(24)
gfx.Text(song.artist, 2,26) gfx.Text(song.artist, 2,26)
if song.jacket_url ~= nil and song.jacket == jacketFallback then if song.jacket_url ~= nil and song.jacket == jacketFallback then
song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250) song.jacket = gfx.LoadWebImageJob(song.jacket_url, jacketFallback, 250, 250)
end end
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0, 50, 250, 250, song.jacket, 1, 0) gfx.ImageRect(0, 50, 250, 250, song.jacket, 1, 0)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(250,50,500,250) gfx.Rect(250,50,500,250)
gfx.FillColor(55,55,55,128) gfx.FillColor(55,55,55,128)
gfx.Fill() gfx.Fill()
for i, diff in ipairs(song.charts) do for i, diff in ipairs(song.charts) do
local col = diffColors[diff.difficulty] local col = diffColors[diff.difficulty]
local diffY = 50 + 250/4 * (diff.difficulty - 1) local diffY = 50 + 250/4 * (diff.difficulty - 1)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(250,diffY, 500, 250 / 4) gfx.Rect(250,diffY, 500, 250 / 4)
gfx.FillColor(col[1], col[2], col[3]) gfx.FillColor(col[1], col[2], col[3])
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.FontSize(40) gfx.FontSize(40)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE) 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) gfx.Text(string.format("%d Effected by %s", diff.level, diff.effector), 255, diffY + 250 / 8)
end end
if downloaded[song.id] then if downloaded[song.id] then
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(0,0,750,300) gfx.Rect(0,0,750,300)
gfx.FillColor(0,0,0,127) gfx.FillColor(0,0,0,127)
gfx.Fill() gfx.Fill()
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
gfx.FontSize(60) gfx.FontSize(60)
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.Text(downloaded[song.id], 375, 150) gfx.Text(downloaded[song.id], 375, 150)
elseif song.status then elseif song.status then
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(0,0,750,300) gfx.Rect(0,0,750,300)
gfx.FillColor(0,0,0,127) gfx.FillColor(0,0,0,127)
gfx.Fill() gfx.Fill()
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
gfx.FontSize(60) gfx.FontSize(60)
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.Text(song.status, 375, 150) gfx.Text(song.status, 375, 150)
end end
gfx.ResetScissor() gfx.ResetScissor()
gfx.Restore() gfx.Restore()
end end
function load_more() function load_more()
if nextUrl ~= nil and not loading then if nextUrl ~= nil and not loading then
Http.GetAsync(nextUrl, header, gotSongsCallback) Http.GetAsync(nextUrl, header, gotSongsCallback)
loading = true loading = true
end end
end end
function render_cursor() function render_cursor()
local x = displayCursorPosX * entryW local x = displayCursorPosX * entryW
local y = displayCursorPosY * entryH local y = displayCursorPosY * entryH
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(x,y,750,300) gfx.Rect(x,y,750,300)
gfx.StrokeColor(255,128,0) gfx.StrokeColor(255,128,0)
gfx.StrokeWidth(5) gfx.StrokeWidth(5)
gfx.Stroke() gfx.Stroke()
end end
function render_loading() function render_loading()
if not loading then return end if not loading then return end
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(resX, resY) gfx.MoveTo(resX, resY)
gfx.LineTo(resX - 350, resY) gfx.LineTo(resX - 350, resY)
gfx.LineTo(resX - 300, resY - 50) gfx.LineTo(resX - 300, resY - 50)
gfx.LineTo(resX, resY - 50) gfx.LineTo(resX, resY - 50)
gfx.ClosePath() gfx.ClosePath()
gfx.FillColor(33,33,33) gfx.FillColor(33,33,33)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM) gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
gfx.FontSize(70) gfx.FontSize(70)
gfx.Text("LOADING...", resX - 20, resY - 3) gfx.Text("LOADING...", resX - 20, resY - 3)
gfx.Restore() gfx.Restore()
end end
function render_hotkeys() function render_hotkeys()
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.FillColor(0,0,0,240) gfx.FillColor(0,0,0,240)
gfx.Rect(0,resY - 50, resX, 50) gfx.Rect(0,resY - 50, resX, 50)
gfx.Fill() gfx.Fill()
gfx.FontSize(30) gfx.FontSize(30)
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
gfx.Text("FXR: Sorting", resX/2 + 20, resY - 10) gfx.Text("FXR: Sorting", resX/2 + 20, resY - 10)
gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM) gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM)
gfx.Text("FXL: Levels", resX/2 - 20, resY - 10) gfx.Text("FXL: Levels", resX/2 - 20, resY - 10)
gfx.Restore() gfx.Restore()
end end
function render_info() function render_info()
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(0, resY) gfx.MoveTo(0, resY)
gfx.LineTo(350, resY) gfx.LineTo(350, resY)
gfx.LineTo(300, resY - 50) gfx.LineTo(300, resY - 50)
gfx.LineTo(0, resY - 50) gfx.LineTo(0, resY - 50)
gfx.ClosePath() gfx.ClosePath()
gfx.FillColor(33,33,33) gfx.FillColor(33,33,33)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_BOTTOM)
gfx.FontSize(70) gfx.FontSize(70)
gfx.Text("Nautica", 3, resY - 3) gfx.Text("Nautica", 3, resY - 3)
local xmin,ymin,xmax,ymax = gfx.TextBounds(3, resY - 3, "Nautica") local xmin,ymin,xmax,ymax = gfx.TextBounds(3, resY - 3, "Nautica")
gfx.FontSize(20) gfx.FontSize(20)
gfx.Text("https://ksm.dev/", xmax + 13, resY - 3) gfx.Text("https://ksm.dev/", xmax + 13, resY - 3)
gfx.Restore() gfx.Restore()
end end
function render(deltaTime) function render(deltaTime)
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0, 0, resX, resY, backgroundImage, 1, 0); gfx.ImageRect(0, 0, resX, resY, backgroundImage, 1, 0);
gfx.LoadSkinFont("NotoSans-Regular.ttf"); gfx.LoadSkinFont("NotoSans-Regular.ttf");
displayCursorPosX = displayCursorPosX - (displayCursorPosX - cursorPosX) * deltaTime * 10 displayCursorPosX = displayCursorPosX - (displayCursorPosX - cursorPosX) * deltaTime * 10
displayCursorPosY = displayCursorPosY - (displayCursorPosY - cursorPosY) * deltaTime * 10 displayCursorPosY = displayCursorPosY - (displayCursorPosY - cursorPosY) * deltaTime * 10
if displayCursorPosY - yOffset > yCount - 1 then --scrolling down if displayCursorPosY - yOffset > yCount - 1 then --scrolling down
yOffset = yOffset - (yOffset - displayCursorPosY) - yCount + 1 yOffset = yOffset - (yOffset - displayCursorPosY) - yCount + 1
elseif displayCursorPosY - yOffset < 0 then elseif displayCursorPosY - yOffset < 0 then
yOffset = yOffset - (yOffset - displayCursorPosY) yOffset = yOffset - (yOffset - displayCursorPosY)
end end
gfx.Translate(xOffset, 50 - yOffset * entryH) gfx.Translate(xOffset, 50 - yOffset * entryH)
for i, song in ipairs(songs) do for i, song in ipairs(songs) do
if math.abs(cursorPos - i) <= xCount * yCount + xCount then if math.abs(cursorPos - i) <= xCount * yCount + xCount then
i = i - 1 i = i - 1
local x = entryW * (i % xCount) local x = entryW * (i % xCount)
local y = math.floor(i / xCount) * entryH local y = math.floor(i / xCount) * entryH
render_song(song, x, y) render_song(song, x, y)
if math.abs(#songs - i) < 4 then load_more() end if math.abs(#songs - i) < 4 then load_more() end
end end
end end
render_cursor() render_cursor()
if needsReload then reload_songs() end if needsReload then reload_songs() end
if screenState == 1 then render_level_filters() if screenState == 1 then render_level_filters()
elseif screenState == 2 then render_sorting_selection() elseif screenState == 2 then render_sorting_selection()
end end
render_hotkeys() render_hotkeys()
render_loading() render_loading()
render_info() render_info()
end end
function archive_callback(entries, id) function archive_callback(entries, id)
game.Log("Listing entries for " .. id, 0) game.Log("Listing entries for " .. id, 0)
local songsfolder = dlScreen.GetSongsPath() local songsfolder = dlScreen.GetSongsPath()
res = {} res = {}
folders = { songsfolder .. "/nautica/" } folders = { songsfolder .. "/nautica/" }
local hasFolder = false local hasFolder = false
for i, entry in ipairs(entries) do for i, entry in ipairs(entries) do
for j = 1, #entry do for j = 1, #entry do
if entry:sub(j,j) == '/' then if entry:sub(j,j) == '/' then
hasFolder = true hasFolder = true
table.insert(folders, songsfolder .. "/nautica/" .. entry:sub(1,j)) table.insert(folders, songsfolder .. "/nautica/" .. entry:sub(1,j))
end end
end end
game.Log(entry, 0) game.Log(entry, 0)
res[entry] = songsfolder .. "/nautica/" .. entry res[entry] = songsfolder .. "/nautica/" .. entry
end end
if not hasFolder then if not hasFolder then
for i, entry in ipairs(entries) do for i, entry in ipairs(entries) do
res[entry] = songsfolder .. "/nautica/" .. id .. "/" .. entry res[entry] = songsfolder .. "/nautica/" .. id .. "/" .. entry
end end
table.insert(folders, songsfolder .. "/nautica/" .. id .. "/") table.insert(folders, songsfolder .. "/nautica/" .. id .. "/")
end end
downloaded[id] = "Downloaded" downloaded[id] = "Downloaded"
res[".folders"] = table.concat(folders, "|") res[".folders"] = table.concat(folders, "|")
return res return res
end end
function reload_songs() function reload_songs()
needsReload = true needsReload = true
if loading then return end if loading then return end
local useLevels = false local useLevels = false
local levelarr = {} local levelarr = {}
for i,value in ipairs(selectedLevels) do for i,value in ipairs(selectedLevels) do
if value then if value then
useLevels = true useLevels = true
table.insert(levelarr, i) table.insert(levelarr, i)
end end
end end
nextUrl = string.format("https://ksm.dev/app/songs?sort=%s", selectedSorting:lower()) nextUrl = string.format("https://ksm.dev/app/songs?sort=%s", selectedSorting:lower())
if useLevels then if useLevels then
nextUrl = nextUrl .. "&levels=" .. table.concat(levelarr, ",") nextUrl = nextUrl .. "&levels=" .. table.concat(levelarr, ",")
end end
songs = {} songs = {}
cursorPos = 0 cursorPos = 0
cursorPosX = 0 cursorPosX = 0
cursorPosY = 0 cursorPosY = 0
displayCursorPosX = 0 displayCursorPosX = 0
displayCursorPosY = 0 displayCursorPosY = 0
load_more() load_more()
game.Log(nextUrl, 0) game.Log(nextUrl, 0)
needsReload = false needsReload = false
end end
function button_pressed(button) function button_pressed(button)
if button == game.BUTTON_STA then if button == game.BUTTON_STA then
if screenState == 0 then if screenState == 0 then
local song = songs[cursorPos + 1] local song = songs[cursorPos + 1]
if song == nil then return end if song == nil then return end
dlScreen.DownloadArchive(encodeURI(song.cdn_download_url), header, song.id, archive_callback) dlScreen.DownloadArchive(encodeURI(song.cdn_download_url), header, song.id, archive_callback)
downloaded[song.id] = "Downloading..." downloaded[song.id] = "Downloading..."
elseif screenState == 1 then elseif screenState == 1 then
if selectedLevels[levelcursor + 1] then if selectedLevels[levelcursor + 1] then
selectedLevels[levelcursor + 1] = false selectedLevels[levelcursor + 1] = false
else else
selectedLevels[levelcursor + 1] = true selectedLevels[levelcursor + 1] = true
end end
reload_songs() reload_songs()
elseif screenState == 2 then elseif screenState == 2 then
selectedSorting = sortingOptions[sortingcursor + 1] selectedSorting = sortingOptions[sortingcursor + 1]
reload_songs() reload_songs()
end end
elseif button == game.BUTTON_BTA then elseif button == game.BUTTON_BTA then
if screenState == 0 then if screenState == 0 then
local song = songs[cursorPos + 1] local song = songs[cursorPos + 1]
if song == nil then return end if song == nil then return end
dlScreen.PlayPreview(encodeURI(song.preview_url), header, song.id) dlScreen.PlayPreview(encodeURI(song.preview_url), header, song.id)
song.status = "Playing" song.status = "Playing"
if lastPlaying ~=nil then if lastPlaying ~=nil then
lastPlaying.status = nil lastPlaying.status = nil
end end
lastPlaying = song lastPlaying = song
end end
elseif button == game.BUTTON_FXL then elseif button == game.BUTTON_FXL then
if screenState ~= 1 then if screenState ~= 1 then
screenState = 1 screenState = 1
else else
screenState = 0 screenState = 0
end end
elseif button == game.BUTTON_FXR then elseif button == game.BUTTON_FXR then
if screenState ~= 2 then if screenState ~= 2 then
screenState = 2 screenState = 2
else else
screenState = 0 screenState = 0
end end
end end
end end
function key_pressed(key) function key_pressed(key)
if key == 27 then --escape pressed if key == 27 then --escape pressed
dlcache = io.open(cachepath, "w") dlcache = io.open(cachepath, "w")
dlcache:write(json.encode(downloaded)) dlcache:write(json.encode(downloaded))
dlcache:close() dlcache:close()
dlScreen.Exit() dlScreen.Exit()
end end
end end
function advance_selection(steps) function advance_selection(steps)
if screenState == 0 and #songs > 0 then if screenState == 0 and #songs > 0 then
cursorPos = (cursorPos + steps) % #songs cursorPos = (cursorPos + steps) % #songs
cursorPosX = cursorPos % xCount cursorPosX = cursorPos % xCount
cursorPosY = math.floor(cursorPos / xCount) cursorPosY = math.floor(cursorPos / xCount)
if cursorPos > #songs - 6 then if cursorPos > #songs - 6 then
load_more() load_more()
end end
elseif screenState == 1 then elseif screenState == 1 then
levelcursor = (levelcursor + steps) % 20 levelcursor = (levelcursor + steps) % 20
elseif screenState == 2 then elseif screenState == 2 then
sortingcursor = (sortingcursor + steps) % #sortingOptions sortingcursor = (sortingcursor + steps) % #sortingOptions
end end
end end
function render_level_filters() function render_level_filters()
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(0,0, resX, resY) gfx.Rect(0,0, resX, resY)
gfx.FillColor(0,0,0,200) gfx.FillColor(0,0,0,200)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
gfx.FontSize(60) gfx.FontSize(60)
gfx.Text("Level filters:", 10, 10) gfx.Text("Level filters:", 10, 10)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(resX/2 - 30, resY/2 - 22, 60, 44) gfx.Rect(resX/2 - 30, resY/2 - 22, 60, 44)
gfx.StrokeColor(255,128,0) gfx.StrokeColor(255,128,0)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Stroke() gfx.Stroke()
gfx.FontSize(40) gfx.FontSize(40)
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
for i = 1, 20 do for i = 1, 20 do
y = (resY/2) + (i - (levelcursor + 1)) * 40 y = (resY/2) + (i - (levelcursor + 1)) * 40
if selectedLevels[i] then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end if selectedLevels[i] then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
gfx.Text(tostring(i), resX/2, y) gfx.Text(tostring(i), resX/2, y)
end end
gfx.Restore() gfx.Restore()
end end
function render_sorting_selection() function render_sorting_selection()
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(0,0, resX, resY) gfx.Rect(0,0, resX, resY)
gfx.FillColor(0,0,0,200) gfx.FillColor(0,0,0,200)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
gfx.FontSize(60) gfx.FontSize(60)
gfx.Text("Sorting method:", 10, 10) gfx.Text("Sorting method:", 10, 10)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(resX/2 - 75, resY/2 - 22, 150, 44) gfx.Rect(resX/2 - 75, resY/2 - 22, 150, 44)
gfx.StrokeColor(255,128,0) gfx.StrokeColor(255,128,0)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Stroke() gfx.Stroke()
gfx.FontSize(40) gfx.FontSize(40)
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
for i, opt in ipairs(sortingOptions) do for i, opt in ipairs(sortingOptions) do
y = (resY/2) + (i - (sortingcursor + 1)) * 40 y = (resY/2) + (i - (sortingcursor + 1)) * 40
if selectedSorting == opt then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end if selectedSorting == opt then gfx.FillColor(255,255,255) else gfx.FillColor(127,127,127) end
gfx.Text(opt, resX/2, y) gfx.Text(opt, resX/2, y)
end end
gfx.Restore() gfx.Restore()
end end

View File

@ -1,247 +1,247 @@
function clamp(x, min, max) function clamp(x, min, max)
if x < min then if x < min then
x = min x = min
end end
if x > max then if x > max then
x = max x = max
end end
return x return x
end end
function smootherstep(edge0, edge1, x) function smootherstep(edge0, edge1, x)
-- Scale, and clamp x to 0..1 range -- Scale, and clamp x to 0..1 range
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0) x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
-- Evaluate polynomial -- Evaluate polynomial
return x * x * x * (x * (x * 6 - 15) + 10) return x * x * x * (x * (x * 6 - 15) + 10)
end end
function to_range(val, start, stop) function to_range(val, start, stop)
return start + (stop - start) * val return start + (stop - start) * val
end end
Animation = { Animation = {
start = 0, start = 0,
stop = 0, stop = 0,
progress = 0, progress = 0,
duration = 1, duration = 1,
smoothStart = false smoothStart = false
} }
function Animation:new(o) function Animation:new(o)
o = o or {} o = o or {}
setmetatable(o, self) setmetatable(o, self)
self.__index = self self.__index = self
return o return o
end end
function Animation:restart(start, stop, duration) function Animation:restart(start, stop, duration)
self.progress = 0 self.progress = 0
self.start = start self.start = start
self.stop = stop self.stop = stop
self.duration = duration self.duration = duration
end end
function Animation:tick(deltaTime) function Animation:tick(deltaTime)
self.progress = math.min(1, self.progress + deltaTime / self.duration) self.progress = math.min(1, self.progress + deltaTime / self.duration)
if self.progress == 1 then return self.stop end if self.progress == 1 then return self.stop end
if self.smoothStart then if self.smoothStart then
return to_range(smootherstep(0, 1, self.progress), self.start, self.stop) return to_range(smootherstep(0, 1, self.progress), self.start, self.stop)
else else
return to_range(smootherstep(-1, 1, self.progress) * 2 - 1, self.start, self.stop) return to_range(smootherstep(-1, 1, self.progress) * 2 - 1, self.start, self.stop)
end end
end end
local yScale = Animation:new() local yScale = Animation:new()
local diagWidth = 600 local diagWidth = 600
local diagHeight = 400 local diagHeight = 400
local tabStroke = {start=0, stop=1} local tabStroke = {start=0, stop=1}
local tabStrokeAnimation = {start=Animation:new(), stop=Animation:new()} local tabStrokeAnimation = {start=Animation:new(), stop=Animation:new()}
local settingsStrokeAnimation = {x=Animation:new(), y=Animation:new()} local settingsStrokeAnimation = {x=Animation:new(), y=Animation:new()}
local prevTab = -1 local prevTab = -1
local prevSettingStroke = {x=0, y=0} local prevSettingStroke = {x=0, y=0}
local settingStroke = {x=0, y=0} local settingStroke = {x=0, y=0}
local prevVis = false local prevVis = false
function processSkinSettings() function processSkinSettings()
for ti, tab in ipairs(SettingsDiag.tabs) do for ti, tab in ipairs(SettingsDiag.tabs) do
for si, setting in ipairs(tab.settings) do for si, setting in ipairs(tab.settings) do
if (tab.name == 'Game') then if (tab.name == 'Game') then
if (setting.name == 'Gauge') then if (setting.name == 'Gauge') then
game.SetSkinSetting('_gaugeType', setting.value); game.SetSkinSetting('_gaugeType', setting.value);
end end
if (setting.name == 'Backup Gauge') then if (setting.name == 'Backup Gauge') then
game.SetSkinSetting('_gaugeARS', setting.value and 1 or 0); game.SetSkinSetting('_gaugeARS', setting.value and 1 or 0);
end end
end end
end end
end end
end end
function render(deltaTime, visible) function render(deltaTime, visible)
if visible and not prevVis then if visible and not prevVis then
yScale:restart(0, 1, 0.25) yScale:restart(0, 1, 0.25)
elseif not visible and prevVis then elseif not visible and prevVis then
yScale:restart(1, 0, 0.25) yScale:restart(1, 0, 0.25)
end end
processSkinSettings() processSkinSettings()
if not visible and yScale:tick(0) < 0.05 then return end if not visible and yScale:tick(0) < 0.05 then return end
local posX = SettingsDiag.posX or 0.5 local posX = SettingsDiag.posX or 0.5
local posY = SettingsDiag.posY 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_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." local message_2 = "Use FX keys to navigate tabs. Use arrow keys to navigate and modify settings."
resX, resY = game.GetResolution() resX, resY = game.GetResolution()
local scale = resY / 1080 local scale = resY / 1080
gfx.ResetTransform() gfx.ResetTransform()
gfx.Translate(math.floor(diagWidth/2 + posX*(resX-diagWidth)), math.floor(diagHeight/2 + posY*(resY-diagHeight))) gfx.Translate(math.floor(diagWidth/2 + posX*(resX-diagWidth)), math.floor(diagHeight/2 + posY*(resY-diagHeight)))
gfx.Scale(scale, scale) gfx.Scale(scale, scale)
gfx.Scale(1.0, smootherstep(0, 1, yScale:tick(deltaTime))) gfx.Scale(1.0, smootherstep(0, 1, yScale:tick(deltaTime)))
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(-diagWidth/2, -diagHeight/2, diagWidth, diagHeight) gfx.Rect(-diagWidth/2, -diagHeight/2, diagWidth, diagHeight)
gfx.FillColor(50,50,50) gfx.FillColor(50,50,50)
gfx.Fill() gfx.Fill()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
gfx.FontSize(20) gfx.FontSize(20)
local m_xmin, m_ymin, m_xmax, m_ymax = gfx.TextBounds(0, 0, message_1) 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) 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) 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) gfx.Text(message_2, diagWidth/2 - m_xmax, diagHeight/2 - m_ymax)
tabStroke.start = tabStrokeAnimation.start:tick(deltaTime) tabStroke.start = tabStrokeAnimation.start:tick(deltaTime)
tabStroke.stop = tabStrokeAnimation.stop:tick(deltaTime) tabStroke.stop = tabStrokeAnimation.stop:tick(deltaTime)
settingStroke.x = settingsStrokeAnimation.x:tick(deltaTime) settingStroke.x = settingsStrokeAnimation.x:tick(deltaTime)
settingStroke.y = settingsStrokeAnimation.y:tick(deltaTime) settingStroke.y = settingsStrokeAnimation.y:tick(deltaTime)
local tabBarHeight = 0 local tabBarHeight = 0
local nextTabX = 5 local nextTabX = 5
gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT) gfx.TextAlign(gfx.TEXT_ALIGN_TOP + gfx.TEXT_ALIGN_LEFT)
gfx.FontSize(35) gfx.FontSize(35)
gfx.Save() --draw tab bar gfx.Save() --draw tab bar
gfx.Translate(-diagWidth / 2, -diagHeight / 2) gfx.Translate(-diagWidth / 2, -diagHeight / 2)
for ti, tab in ipairs(SettingsDiag.tabs) do for ti, tab in ipairs(SettingsDiag.tabs) do
local xmin,ymin, xmax,ymax = gfx.TextBounds(nextTabX, 5, tab.name) local xmin,ymin, xmax,ymax = gfx.TextBounds(nextTabX, 5, tab.name)
if ti == SettingsDiag.currentTab and SettingsDiag.currentTab ~= prevTab then if ti == SettingsDiag.currentTab and SettingsDiag.currentTab ~= prevTab then
tabStrokeAnimation.start:restart(tabStroke.start, nextTabX, 0.1) tabStrokeAnimation.start:restart(tabStroke.start, nextTabX, 0.1)
tabStrokeAnimation.stop:restart(tabStroke.stop, xmax, 0.1) tabStrokeAnimation.stop:restart(tabStroke.stop, xmax, 0.1)
end end
tabBarHeight = math.max(tabBarHeight, ymax + 5) tabBarHeight = math.max(tabBarHeight, ymax + 5)
gfx.Text(tab.name, nextTabX, 5) gfx.Text(tab.name, nextTabX, 5)
nextTabX = xmax + 10 nextTabX = xmax + 10
end end
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(0, tabBarHeight) gfx.MoveTo(0, tabBarHeight)
gfx.LineTo(diagWidth, tabBarHeight) gfx.LineTo(diagWidth, tabBarHeight)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.StrokeColor(0,127,255) gfx.StrokeColor(0,127,255)
gfx.Stroke() gfx.Stroke()
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(tabStroke.start, tabBarHeight) gfx.MoveTo(tabStroke.start, tabBarHeight)
gfx.LineTo(tabStroke.stop, tabBarHeight) gfx.LineTo(tabStroke.stop, tabBarHeight)
gfx.StrokeColor(255, 127, 0) gfx.StrokeColor(255, 127, 0)
gfx.Stroke() gfx.Stroke()
gfx.Restore() --draw tab bar end gfx.Restore() --draw tab bar end
gfx.FontSize(30) gfx.FontSize(30)
gfx.Save() --draw current tab gfx.Save() --draw current tab
gfx.Translate(-diagWidth / 2, -diagHeight / 2) gfx.Translate(-diagWidth / 2, -diagHeight / 2)
gfx.Translate(5, tabBarHeight + 5) gfx.Translate(5, tabBarHeight + 5)
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(0, settingStroke.y) gfx.MoveTo(0, settingStroke.y)
gfx.LineTo(settingStroke.x, settingStroke.y) gfx.LineTo(settingStroke.x, settingStroke.y)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.StrokeColor(255, 127, 0) gfx.StrokeColor(255, 127, 0)
gfx.Stroke() gfx.Stroke()
local settingHeight = 30 local settingHeight = 30
local tab = SettingsDiag.tabs[SettingsDiag.currentTab] local tab = SettingsDiag.tabs[SettingsDiag.currentTab]
for si, setting in ipairs(tab.settings) do for si, setting in ipairs(tab.settings) do
processSkinSettings(setting.name, setting.value) processSkinSettings(setting.name, setting.value)
local disp = "" local disp = ""
if setting.type == "enum" then if setting.type == "enum" then
disp = string.format("%s: %s", setting.name, setting.options[setting.value]) disp = string.format("%s: %s", setting.name, setting.options[setting.value])
elseif setting.type == "int" then elseif setting.type == "int" then
disp = string.format("%s: %d", setting.name, setting.value) disp = string.format("%s: %d", setting.name, setting.value)
elseif setting.type == "float" then elseif setting.type == "float" then
disp = string.format("%s: %.2f", setting.name, setting.value) disp = string.format("%s: %.2f", setting.name, setting.value)
if setting.max == 1 and setting.min == 0 then --draw slider if setting.max == 1 and setting.min == 0 then --draw slider
disp = setting.name .. ": " disp = setting.name .. ": "
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp) local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
local width = diagWidth - 20 - xmax local width = diagWidth - 20 - xmax
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(xmax + 5, 20) gfx.MoveTo(xmax + 5, 20)
gfx.LineTo(xmax + 5 + width, 20) gfx.LineTo(xmax + 5 + width, 20)
gfx.StrokeColor(0,127,255) gfx.StrokeColor(0,127,255)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Stroke() gfx.Stroke()
gfx.BeginPath() gfx.BeginPath()
gfx.MoveTo(xmax + 5, 20) gfx.MoveTo(xmax + 5, 20)
gfx.LineTo(xmax + 5 + width * setting.value, 20) gfx.LineTo(xmax + 5 + width * setting.value, 20)
gfx.StrokeColor(255,127,0) gfx.StrokeColor(255,127,0)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Stroke() gfx.Stroke()
end end
elseif setting.type == "button" then elseif setting.type == "button" then
disp = string.format("%s", setting.name) disp = string.format("%s", setting.name)
local xmin, ymin, xmax,ymax = gfx.TextBounds(0, 0, disp) local xmin, ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(-2, 3, 4+xmax-xmin, 28) gfx.Rect(-2, 3, 4+xmax-xmin, 28)
gfx.FillColor(0, 64, 128) gfx.FillColor(0, 64, 128)
if si == SettingsDiag.currentSetting then if si == SettingsDiag.currentSetting then
gfx.StrokeColor(255, 127, 0) gfx.StrokeColor(255, 127, 0)
else else
gfx.StrokeColor(0,127,255) gfx.StrokeColor(0,127,255)
end end
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Fill() gfx.Fill()
gfx.Stroke() gfx.Stroke()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
else else
disp = string.format("%s:", setting.name) disp = string.format("%s:", setting.name)
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp) local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, disp)
gfx.BeginPath() gfx.BeginPath()
gfx.Rect(xmax + 5, 5, 20,20) gfx.Rect(xmax + 5, 5, 20,20)
gfx.FillColor(255, 127, 0, setting.value and 255 or 0) gfx.FillColor(255, 127, 0, setting.value and 255 or 0)
gfx.StrokeColor(0,127,255) gfx.StrokeColor(0,127,255)
gfx.StrokeWidth(2) gfx.StrokeWidth(2)
gfx.Fill() gfx.Fill()
gfx.Stroke() gfx.Stroke()
gfx.FillColor(255,255,255) gfx.FillColor(255,255,255)
end end
gfx.Text(disp, 0 ,0) gfx.Text(disp, 0 ,0)
if si == SettingsDiag.currentSetting then if si == SettingsDiag.currentSetting then
local setting_name = setting.name .. ":" local setting_name = setting.name .. ":"
if setting.type == "button" then if setting.type == "button" then
setting_name = setting.name setting_name = setting.name
end end
local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, setting_name) local xmin,ymin, xmax,ymax = gfx.TextBounds(0, 0, setting_name)
ymax = ymax + settingHeight * (si - 1) ymax = ymax + settingHeight * (si - 1)
if xmax ~= prevSettingStroke.x or ymax ~= prevSettingStroke.y then if xmax ~= prevSettingStroke.x or ymax ~= prevSettingStroke.y then
settingsStrokeAnimation.x:restart(settingStroke.x, xmax, 0.1) settingsStrokeAnimation.x:restart(settingStroke.x, xmax, 0.1)
settingsStrokeAnimation.y:restart(settingStroke.y, ymax, 0.1) settingsStrokeAnimation.y:restart(settingStroke.y, ymax, 0.1)
end end
prevSettingStroke.x = xmax prevSettingStroke.x = xmax
prevSettingStroke.y = ymax prevSettingStroke.y = ymax
end end
gfx.Translate(0, settingHeight) gfx.Translate(0, settingHeight)
end end
gfx.Restore() --draw current tab end gfx.Restore() --draw current tab end
prevTab = SettingsDiag.currentTab prevTab = SettingsDiag.currentTab
prevVis = visible prevVis = visible
end end

View File

@ -1,400 +1,400 @@
-- --
-- json.lua -- json.lua
-- --
-- Copyright (c) 2019 rxi -- Copyright (c) 2019 rxi
-- --
-- Permission is hereby granted, free of charge, to any person obtaining a copy of -- 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 -- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to -- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -- 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 -- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions: -- so, subject to the following conditions:
-- --
-- The above copyright notice and this permission notice shall be included in all -- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software. -- copies or substantial portions of the Software.
-- --
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- 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 -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE. -- SOFTWARE.
-- --
local json = { _version = "0.1.1" } local json = { _version = "0.1.1" }
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Encode -- Encode
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local encode local encode
local escape_char_map = { local escape_char_map = {
[ "\\" ] = "\\\\", [ "\\" ] = "\\\\",
[ "\"" ] = "\\\"", [ "\"" ] = "\\\"",
[ "\b" ] = "\\b", [ "\b" ] = "\\b",
[ "\f" ] = "\\f", [ "\f" ] = "\\f",
[ "\n" ] = "\\n", [ "\n" ] = "\\n",
[ "\r" ] = "\\r", [ "\r" ] = "\\r",
[ "\t" ] = "\\t", [ "\t" ] = "\\t",
} }
local escape_char_map_inv = { [ "\\/" ] = "/" } local escape_char_map_inv = { [ "\\/" ] = "/" }
for k, v in pairs(escape_char_map) do for k, v in pairs(escape_char_map) do
escape_char_map_inv[v] = k escape_char_map_inv[v] = k
end end
local function escape_char(c) local function escape_char(c)
return escape_char_map[c] or string.format("\\u%04x", c:byte()) return escape_char_map[c] or string.format("\\u%04x", c:byte())
end end
local function encode_nil(val) local function encode_nil(val)
return "null" return "null"
end end
local function encode_table(val, stack) local function encode_table(val, stack)
local res = {} local res = {}
stack = stack or {} stack = stack or {}
-- Circular reference? -- Circular reference?
if stack[val] then error("circular reference") end if stack[val] then error("circular reference") end
stack[val] = true stack[val] = true
if rawget(val, 1) ~= nil or next(val) == nil then if rawget(val, 1) ~= nil or next(val) == nil then
-- Treat as array -- check keys are valid and it is not sparse -- Treat as array -- check keys are valid and it is not sparse
local n = 0 local n = 0
for k in pairs(val) do for k in pairs(val) do
if type(k) ~= "number" then if type(k) ~= "number" then
error("invalid table: mixed or invalid key types") error("invalid table: mixed or invalid key types")
end end
n = n + 1 n = n + 1
end end
if n ~= #val then if n ~= #val then
error("invalid table: sparse array") error("invalid table: sparse array")
end end
-- Encode -- Encode
for i, v in ipairs(val) do for i, v in ipairs(val) do
table.insert(res, encode(v, stack)) table.insert(res, encode(v, stack))
end end
stack[val] = nil stack[val] = nil
return "[" .. table.concat(res, ",") .. "]" return "[" .. table.concat(res, ",") .. "]"
else else
-- Treat as an object -- Treat as an object
for k, v in pairs(val) do for k, v in pairs(val) do
if type(k) ~= "string" then if type(k) ~= "string" then
error("invalid table: mixed or invalid key types") error("invalid table: mixed or invalid key types")
end end
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
end end
stack[val] = nil stack[val] = nil
return "{" .. table.concat(res, ",") .. "}" return "{" .. table.concat(res, ",") .. "}"
end end
end end
local function encode_string(val) local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end end
local function encode_number(val) local function encode_number(val)
-- Check for NaN, -inf and inf -- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'") error("unexpected number value '" .. tostring(val) .. "'")
end end
return string.format("%.14g", val) return string.format("%.14g", val)
end end
local type_func_map = { local type_func_map = {
[ "nil" ] = encode_nil, [ "nil" ] = encode_nil,
[ "table" ] = encode_table, [ "table" ] = encode_table,
[ "string" ] = encode_string, [ "string" ] = encode_string,
[ "number" ] = encode_number, [ "number" ] = encode_number,
[ "boolean" ] = tostring, [ "boolean" ] = tostring,
} }
encode = function(val, stack) encode = function(val, stack)
local t = type(val) local t = type(val)
local f = type_func_map[t] local f = type_func_map[t]
if f then if f then
return f(val, stack) return f(val, stack)
end end
error("unexpected type '" .. t .. "'") error("unexpected type '" .. t .. "'")
end end
function json.encode(val) function json.encode(val)
return ( encode(val) ) return ( encode(val) )
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Decode -- Decode
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local parse local parse
local function create_set(...) local function create_set(...)
local res = {} local res = {}
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
res[ select(i, ...) ] = true res[ select(i, ...) ] = true
end end
return res return res
end end
local space_chars = create_set(" ", "\t", "\r", "\n") local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_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 escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null") local literals = create_set("true", "false", "null")
local literal_map = { local literal_map = {
[ "true" ] = true, [ "true" ] = true,
[ "false" ] = false, [ "false" ] = false,
[ "null" ] = nil, [ "null" ] = nil,
} }
local function next_char(str, idx, set, negate) local function next_char(str, idx, set, negate)
for i = idx, #str do for i = idx, #str do
if set[str:sub(i, i)] ~= negate then if set[str:sub(i, i)] ~= negate then
return i return i
end end
end end
return #str + 1 return #str + 1
end end
local function decode_error(str, idx, msg) local function decode_error(str, idx, msg)
local line_count = 1 local line_count = 1
local col_count = 1 local col_count = 1
for i = 1, idx - 1 do for i = 1, idx - 1 do
col_count = col_count + 1 col_count = col_count + 1
if str:sub(i, i) == "\n" then if str:sub(i, i) == "\n" then
line_count = line_count + 1 line_count = line_count + 1
col_count = 1 col_count = 1
end end
end end
error( string.format("%s at line %d col %d", msg, line_count, col_count) ) error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end end
local function codepoint_to_utf8(n) local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = math.floor local f = math.floor
if n <= 0x7f then if n <= 0x7f then
return string.char(n) return string.char(n)
elseif n <= 0x7ff then elseif n <= 0x7ff then
return string.char(f(n / 64) + 192, n % 64 + 128) return string.char(f(n / 64) + 192, n % 64 + 128)
elseif n <= 0xffff then elseif n <= 0xffff then
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
elseif n <= 0x10ffff then elseif n <= 0x10ffff then
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
f(n % 4096 / 64) + 128, n % 64 + 128) f(n % 4096 / 64) + 128, n % 64 + 128)
end end
error( string.format("invalid unicode codepoint '%x'", n) ) error( string.format("invalid unicode codepoint '%x'", n) )
end end
local function parse_unicode_escape(s) local function parse_unicode_escape(s)
local n1 = tonumber( s:sub(3, 6), 16 ) local n1 = tonumber( s:sub(3, 6), 16 )
local n2 = tonumber( s:sub(9, 12), 16 ) local n2 = tonumber( s:sub(9, 12), 16 )
-- Surrogate pair? -- Surrogate pair?
if n2 then if n2 then
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else else
return codepoint_to_utf8(n1) return codepoint_to_utf8(n1)
end end
end end
local function parse_string(str, i) local function parse_string(str, i)
local has_unicode_escape = false local has_unicode_escape = false
local has_surrogate_escape = false local has_surrogate_escape = false
local has_escape = false local has_escape = false
local last local last
for j = i + 1, #str do for j = i + 1, #str do
local x = str:byte(j) local x = str:byte(j)
if x < 32 then if x < 32 then
decode_error(str, j, "control character in string") decode_error(str, j, "control character in string")
end end
if last == 92 then -- "\\" (escape char) if last == 92 then -- "\\" (escape char)
if x == 117 then -- "u" (unicode escape sequence) if x == 117 then -- "u" (unicode escape sequence)
local hex = str:sub(j + 1, j + 5) local hex = str:sub(j + 1, j + 5)
if not hex:find("%x%x%x%x") then if not hex:find("%x%x%x%x") then
decode_error(str, j, "invalid unicode escape in string") decode_error(str, j, "invalid unicode escape in string")
end end
if hex:find("^[dD][89aAbB]") then if hex:find("^[dD][89aAbB]") then
has_surrogate_escape = true has_surrogate_escape = true
else else
has_unicode_escape = true has_unicode_escape = true
end end
else else
local c = string.char(x) local c = string.char(x)
if not escape_chars[c] then if not escape_chars[c] then
decode_error(str, j, "invalid escape char '" .. c .. "' in string") decode_error(str, j, "invalid escape char '" .. c .. "' in string")
end end
has_escape = true has_escape = true
end end
last = nil last = nil
elseif x == 34 then -- '"' (end of string) elseif x == 34 then -- '"' (end of string)
local s = str:sub(i + 1, j - 1) local s = str:sub(i + 1, j - 1)
if has_surrogate_escape then if has_surrogate_escape then
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape) s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
end end
if has_unicode_escape then if has_unicode_escape then
s = s:gsub("\\u....", parse_unicode_escape) s = s:gsub("\\u....", parse_unicode_escape)
end end
if has_escape then if has_escape then
s = s:gsub("\\.", escape_char_map_inv) s = s:gsub("\\.", escape_char_map_inv)
end end
return s, j + 1 return s, j + 1
else else
last = x last = x
end end
end end
decode_error(str, i, "expected closing quote for string") decode_error(str, i, "expected closing quote for string")
end end
local function parse_number(str, i) local function parse_number(str, i)
local x = next_char(str, i, delim_chars) local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1) local s = str:sub(i, x - 1)
local n = tonumber(s) local n = tonumber(s)
if not n then if not n then
decode_error(str, i, "invalid number '" .. s .. "'") decode_error(str, i, "invalid number '" .. s .. "'")
end end
return n, x return n, x
end end
local function parse_literal(str, i) local function parse_literal(str, i)
local x = next_char(str, i, delim_chars) local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1) local word = str:sub(i, x - 1)
if not literals[word] then if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'") decode_error(str, i, "invalid literal '" .. word .. "'")
end end
return literal_map[word], x return literal_map[word], x
end end
local function parse_array(str, i) local function parse_array(str, i)
local res = {} local res = {}
local n = 1 local n = 1
i = i + 1 i = i + 1
while 1 do while 1 do
local x local x
i = next_char(str, i, space_chars, true) i = next_char(str, i, space_chars, true)
-- Empty / end of array? -- Empty / end of array?
if str:sub(i, i) == "]" then if str:sub(i, i) == "]" then
i = i + 1 i = i + 1
break break
end end
-- Read token -- Read token
x, i = parse(str, i) x, i = parse(str, i)
res[n] = x res[n] = x
n = n + 1 n = n + 1
-- Next token -- Next token
i = next_char(str, i, space_chars, true) i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i) local chr = str:sub(i, i)
i = i + 1 i = i + 1
if chr == "]" then break end if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end end
return res, i return res, i
end end
local function parse_object(str, i) local function parse_object(str, i)
local res = {} local res = {}
i = i + 1 i = i + 1
while 1 do while 1 do
local key, val local key, val
i = next_char(str, i, space_chars, true) i = next_char(str, i, space_chars, true)
-- Empty / end of object? -- Empty / end of object?
if str:sub(i, i) == "}" then if str:sub(i, i) == "}" then
i = i + 1 i = i + 1
break break
end end
-- Read key -- Read key
if str:sub(i, i) ~= '"' then if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key") decode_error(str, i, "expected string for key")
end end
key, i = parse(str, i) key, i = parse(str, i)
-- Read ':' delimiter -- Read ':' delimiter
i = next_char(str, i, space_chars, true) i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key") decode_error(str, i, "expected ':' after key")
end end
i = next_char(str, i + 1, space_chars, true) i = next_char(str, i + 1, space_chars, true)
-- Read value -- Read value
val, i = parse(str, i) val, i = parse(str, i)
-- Set -- Set
res[key] = val res[key] = val
-- Next token -- Next token
i = next_char(str, i, space_chars, true) i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i) local chr = str:sub(i, i)
i = i + 1 i = i + 1
if chr == "}" then break end if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end end
return res, i return res, i
end end
local char_func_map = { local char_func_map = {
[ '"' ] = parse_string, [ '"' ] = parse_string,
[ "0" ] = parse_number, [ "0" ] = parse_number,
[ "1" ] = parse_number, [ "1" ] = parse_number,
[ "2" ] = parse_number, [ "2" ] = parse_number,
[ "3" ] = parse_number, [ "3" ] = parse_number,
[ "4" ] = parse_number, [ "4" ] = parse_number,
[ "5" ] = parse_number, [ "5" ] = parse_number,
[ "6" ] = parse_number, [ "6" ] = parse_number,
[ "7" ] = parse_number, [ "7" ] = parse_number,
[ "8" ] = parse_number, [ "8" ] = parse_number,
[ "9" ] = parse_number, [ "9" ] = parse_number,
[ "-" ] = parse_number, [ "-" ] = parse_number,
[ "t" ] = parse_literal, [ "t" ] = parse_literal,
[ "f" ] = parse_literal, [ "f" ] = parse_literal,
[ "n" ] = parse_literal, [ "n" ] = parse_literal,
[ "[" ] = parse_array, [ "[" ] = parse_array,
[ "{" ] = parse_object, [ "{" ] = parse_object,
} }
parse = function(str, idx) parse = function(str, idx)
local chr = str:sub(idx, idx) local chr = str:sub(idx, idx)
local f = char_func_map[chr] local f = char_func_map[chr]
if f then if f then
return f(str, idx) return f(str, idx)
end end
decode_error(str, idx, "unexpected character '" .. chr .. "'") decode_error(str, idx, "unexpected character '" .. chr .. "'")
end end
function json.decode(str) function json.decode(str)
if type(str) ~= "string" then if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str)) error("expected argument of type string, got " .. type(str))
end end
local res, idx = parse(str, next_char(str, 1, space_chars, true)) local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true) idx = next_char(str, idx, space_chars, true)
if idx <= #str then if idx <= #str then
decode_error(str, idx, "trailing garbage") decode_error(str, idx, "trailing garbage")
end end
return res return res
end end
return json return json

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,149 +1,149 @@
local window = { local window = {
isPortrait = false, isPortrait = false,
resX = 0, resX = 0,
resY = 0, resY = 0,
scale = 1, scale = 1,
w = 0, w = 0,
h = 0, h = 0,
set = function(this, doScale) set = function(this, doScale)
local resX, resY = game.GetResolution(); local resX, resY = game.GetResolution();
if ((this.resX ~= resX) or (this.resY ~= this.resY)) then if ((this.resX ~= resX) or (this.resY ~= this.resY)) then
this.isPortrait = resY > resX; this.isPortrait = resY > resX;
this.w = (this.isPortrait and 1080) or 1920; this.w = (this.isPortrait and 1080) or 1920;
this.h = this.w * (resY / resX); this.h = this.w * (resY / resX);
this.scale = resX / this.w; this.scale = resX / this.w;
this.resX = resX; this.resX = resX;
this.resY = resY; this.resY = resY;
end end
if (doScale) then gfx.Scale(this.scale, this.scale); end if (doScale) then gfx.Scale(this.scale, this.scale); end
end, end,
}; };
local wheel = { local wheel = {
cache = { w = 0, h = 0 }, cache = { w = 0, h = 0 },
visibleSongs = 11, visibleSongs = 11,
margin = 13, margin = 13,
x = 0, x = 0,
y = 0, y = 0,
w = 0, w = 0,
h = { h = {
song = 0, song = 0,
total = 0, total = 0,
}, },
setSizes = function(this) setSizes = function(this)
if ((this.cache.w ~= window.w) or (this.cache.h ~= window.h)) then if ((this.cache.w ~= window.w) or (this.cache.h ~= window.h)) then
local marginTotal = this.margin * (this.visibleSongs - 1); local marginTotal = this.margin * (this.visibleSongs - 1);
this.x = window.w / 2; this.x = window.w / 2;
this.y = 0; this.y = 0;
this.w = window.w / 2; this.w = window.w / 2;
this.h.total = window.h - marginTotal; this.h.total = window.h - marginTotal;
this.h.song = this.h.total / this.visibleSongs; this.h.song = this.h.total / this.visibleSongs;
this.cache.w = window.w; this.cache.w = window.w;
this.cache.h = window.h; this.cache.h = window.h;
end end
end, end,
}; };
local displaying = {}; local displaying = {};
local jacketCache = {}; local jacketCache = {};
local currDiff = 1; local currDiff = 1;
local currSong = 1; local currSong = 1;
local jacketFallback = gfx.CreateSkinImage('song_select/loading.png', 0); local jacketFallback = gfx.CreateSkinImage('song_select/loading.png', 0);
local getJacket = function(diff) local getJacket = function(diff)
if ((not jacketCache[diff.jacketPath]) if ((not jacketCache[diff.jacketPath])
or (jacketCache[diff.jacketPath] == jacketFallback)) then or (jacketCache[diff.jacketPath] == jacketFallback)) then
jacketCache[diff.jacketPath] = gfx.LoadImageJob( jacketCache[diff.jacketPath] = gfx.LoadImageJob(
diff.jacketPath, diff.jacketPath,
jacketFallback, jacketFallback,
500, 500,
500 500
); );
end end
return jacketCache[diff.jacketPath]; return jacketCache[diff.jacketPath];
end end
local setDisplaying = function() local setDisplaying = function()
local songs = songwheel.songs; local songs = songwheel.songs;
local enoughSongs = #songs >= wheel.visibleSongs; local enoughSongs = #songs >= wheel.visibleSongs;
displaying[5] = songs[currSong] or {}; displaying[5] = songs[currSong] or {};
for i = 1, 4 do for i = 1, 4 do
if (enoughSongs) then if (enoughSongs) then
displaying[5 - i] = songs[currSong - i] or songs[currSong + #songs - i]; displaying[5 - i] = songs[currSong - i] or songs[currSong + #songs - i];
else else
displaying[5 - i] = songs[currSong - i] or {}; displaying[5 - i] = songs[currSong - i] or {};
end end
end end
for i = 1, 3 do for i = 1, 3 do
if (enoughSongs) then if (enoughSongs) then
displaying[5 + i] = songs[currSong + i] or songs[currSong - #songs + i]; displaying[5 + i] = songs[currSong + i] or songs[currSong - #songs + i];
else else
displaying[5 + i] = songs[currSong + i] or {}; displaying[5 + i] = songs[currSong + i] or {};
end end
end end
end end
local renderWheel = function() local renderWheel = function()
local margin = wheel.margin; local margin = wheel.margin;
local x = wheel.x; local x = wheel.x;
local y = wheel.y; local y = wheel.y;
local w = wheel.w; local w = wheel.w;
local h = wheel.h.song; local h = wheel.h.song;
for i, song in ipairs(displaying) do for i, song in ipairs(displaying) do
local isSelected = i == 5; local isSelected = i == 5;
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(0, 0, 0, (isSelected and 200) or 100); gfx.FillColor(0, 0, 0, (isSelected and 200) or 100);
gfx.Rect(x, y, w, h); gfx.Rect(x, y, w, h);
gfx.Fill(); gfx.Fill();
if (song and song.difficulties) then if (song and song.difficulties) then
local jacket = getJacket(song.difficulties[currDiff] or song.difficulties[1]); local jacket = getJacket(song.difficulties[currDiff] or song.difficulties[1]);
if (jacket) then if (jacket) then
gfx.BeginPath(); gfx.BeginPath();
gfx.ImageRect(x, y, h, h, jacket, (isSelected and 1) or 0.5, 0); gfx.ImageRect(x, y, h, h, jacket, (isSelected and 1) or 0.5, 0);
end end
end end
y = y + h + margin; y = y + h + margin;
end end
end end
render = function(dt) render = function(dt)
window:set(true); window:set(true);
wheel:setSizes(); wheel:setSizes();
setDisplaying(); setDisplaying();
renderWheel(); renderWheel();
gfx.ForceRender(); gfx.ForceRender();
end end
set_index = function(newSong) set_index = function(newSong)
currSong = newSong; currSong = newSong;
end end
set_diff = function(newDiff) set_diff = function(newDiff)
currDiff = newDiff; currDiff = newDiff;
end end
songs_changed = function(withAll) songs_changed = function(withAll)
if (not withAll) then return; end if (not withAll) then return; end
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,243 +1,243 @@
require('common') require('common')
local Easing = require('common.easing'); local Easing = require('common.easing');
local resx, resy = game.GetResolution() local resx, resy = game.GetResolution()
local desw, desh = 1080, 1920 local desw, desh = 1080, 1920
-- AUDIO -- AUDIO
game.LoadSkinSample('sort_wheel/enter.wav'); game.LoadSkinSample('sort_wheel/enter.wav');
game.LoadSkinSample('sort_wheel/leave.wav'); game.LoadSkinSample('sort_wheel/leave.wav');
-- IMAGES -- IMAGES
local panelBgImage = gfx.CreateSkinImage('song_select/sort_wheel/bg.png', 0) local panelBgImage = gfx.CreateSkinImage('song_select/sort_wheel/bg.png', 0)
local activeItemBgImage = gfx.CreateSkinImage( local activeItemBgImage = gfx.CreateSkinImage(
'song_select/sort_wheel/active_bg.png', 0) 'song_select/sort_wheel/active_bg.png', 0)
local titleTextImage = local titleTextImage =
gfx.CreateSkinImage('song_select/sort_wheel/title.png', 0) gfx.CreateSkinImage('song_select/sort_wheel/title.png', 0)
local selection = 1; local selection = 1;
local renderedButtonLabels = {} local renderedButtonLabels = {}
local FONT_SIZE = 32; local FONT_SIZE = 32;
local MARGIN = 16; local MARGIN = 16;
local SUB_FONT_SIZE = 26; local SUB_FONT_SIZE = 26;
local SUB_MARGIN = 8; local SUB_MARGIN = 8;
local SORT_ORDER_LABEL_TEXTS = { local SORT_ORDER_LABEL_TEXTS = {
{ {
label = 'Title', label = 'Title',
asc = '# to A to Z to かな to 漢字', asc = '# to A to Z to かな to 漢字',
dsc = '漢字 to かな to Z to A to #' dsc = '漢字 to かな to Z to A to #'
}, {label = 'Score', asc = 'Worst to best', dsc = 'Best to worst'}, }, {label = 'Score', asc = 'Worst to best', dsc = 'Best to worst'},
{label = 'Date', asc = 'Oldest to newest', dsc = 'Newest to oldest'}, {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 = 'Badge', asc = 'None to D to S', dsc = 'S to D to None'}, {
label = 'Artist', label = 'Artist',
asc = '# to A to Z to かな to 漢字', asc = '# to A to Z to かな to 漢字',
dsc = '漢字 to かな to Z to A to #' dsc = '漢字 to かな to Z to A to #'
}, { }, {
label = 'Effector', label = 'Effector',
asc = '# to A to Z to かな to 漢字', asc = '# to A to Z to かな to 漢字',
dsc = '漢字 to かな to Z to A to #' dsc = '漢字 to かな to Z to A to #'
} }
} }
local transitionEnterReverse = false; local transitionEnterReverse = false;
local transitionEnterScale = 0; local transitionEnterScale = 0;
local transitionEnterOffsetX = 0; local transitionEnterOffsetX = 0;
local previousActiveState = false; local previousActiveState = false;
-- Window variables -- Window variables
local resX, resY local resX, resY
-- Aspect Ratios -- Aspect Ratios
local landscapeWidescreenRatio = 16 / 9 local landscapeWidescreenRatio = 16 / 9
local landscapeStandardRatio = 4 / 3 local landscapeStandardRatio = 4 / 3
local portraitWidescreenRatio = 9 / 16 --+ 0.0035 local portraitWidescreenRatio = 9 / 16 --+ 0.0035
-- Portrait sizes -- Portrait sizes
local fullX, fullY local fullX, fullY
local resolutionChange = function(x, y) local resolutionChange = function(x, y)
resX = x resX = x
resY = y resY = y
fullX = portraitWidescreenRatio * y fullX = portraitWidescreenRatio * y
fullY = y fullY = y
game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR); game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR);
end end
function tableContains(table, value) function tableContains(table, value)
for i, v in ipairs(table) do if v == value then return true end end for i, v in ipairs(table) do if v == value then return true end end
return false; return false;
end end
function drawButton(i, f, x, y) function drawButton(i, f, x, y)
local spaceAfter = (FONT_SIZE + MARGIN) local spaceAfter = (FONT_SIZE + MARGIN)
local sortOrder = 'asc'; local sortOrder = 'asc';
if (string.find(f, 'v')) then sortOrder = 'dsc' end if (string.find(f, 'v')) then sortOrder = 'dsc' end
local label = f:gsub(' ^', '') local label = f:gsub(' ^', '')
label = label:gsub(' v', '') label = label:gsub(' v', '')
if (string.find(sorts[selection], label) and sorts[selection] ~= f) then 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 -- If there is a button active with the same label, but different sort order, don't render this one
return 0; return 0;
else else
-- If there is no active button with this label, if one with a label was already rendered, don't render this one -- 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 if (tableContains(renderedButtonLabels, label)) then return 0; end
table.insert(renderedButtonLabels, label); table.insert(renderedButtonLabels, label);
end end
if (i == selection) then if (i == selection) then
local ascLabelText = 'Ascending' local ascLabelText = 'Ascending'
local dscLabelText = 'Descending' local dscLabelText = 'Descending'
for i, obj in ipairs(SORT_ORDER_LABEL_TEXTS) do for i, obj in ipairs(SORT_ORDER_LABEL_TEXTS) do
if (obj.label == label) then if (obj.label == label) then
ascLabelText = obj.asc; ascLabelText = obj.asc;
dscLabelText = obj.dsc; dscLabelText = obj.dsc;
end end
end end
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(x - 182, y - 38, 365, 82, activeItemBgImage, 1, 0) gfx.ImageRect(x - 182, y - 38, 365, 82, activeItemBgImage, 1, 0)
gfx.BeginPath() gfx.BeginPath()
if sortOrder == 'asc' then if sortOrder == 'asc' then
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 - 31, 300, 67, gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 - 31, 300, 67,
activeItemBgImage, 1, 0) activeItemBgImage, 1, 0)
elseif sortOrder == 'dsc' then elseif sortOrder == 'dsc' then
gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 + gfx.ImageRect(x - 150, y + FONT_SIZE + SUB_MARGIN * 2 +
SUB_FONT_SIZE + SUB_MARGIN - 31, 300, 67, SUB_FONT_SIZE + SUB_MARGIN - 31, 300, 67,
activeItemBgImage, 1, 0) activeItemBgImage, 1, 0)
end end
gfx.Save() gfx.Save()
gfx.FontSize(SUB_FONT_SIZE) gfx.FontSize(SUB_FONT_SIZE)
gfx.Text(ascLabelText, x, y + FONT_SIZE + SUB_MARGIN * 2); gfx.Text(ascLabelText, x, y + FONT_SIZE + SUB_MARGIN * 2);
gfx.Text(dscLabelText, x, gfx.Text(dscLabelText, x,
y + FONT_SIZE + SUB_MARGIN * 2 + SUB_FONT_SIZE + SUB_MARGIN); y + FONT_SIZE + SUB_MARGIN * 2 + SUB_FONT_SIZE + SUB_MARGIN);
gfx.Restore() gfx.Restore()
spaceAfter = spaceAfter + SUB_FONT_SIZE * 2 + SUB_MARGIN * 4; spaceAfter = spaceAfter + SUB_FONT_SIZE * 2 + SUB_MARGIN * 4;
end end
gfx.BeginPath(); gfx.BeginPath();
gfx.Text(label, x, y); gfx.Text(label, x, y);
return spaceAfter; return spaceAfter;
end end
function tickTransitions(deltaTime) function tickTransitions(deltaTime)
-- ENTRY TRANSITION -- ENTRY TRANSITION
if transitionEnterReverse then if transitionEnterReverse then
if transitionEnterScale > 0 then if transitionEnterScale > 0 then
transitionEnterScale = transitionEnterScale - deltaTime / 0.25 -- transition should last for that time in seconds transitionEnterScale = transitionEnterScale - deltaTime / 0.25 -- transition should last for that time in seconds
else else
transitionEnterScale = 0 transitionEnterScale = 0
end end
else else
if transitionEnterScale < 1 then if transitionEnterScale < 1 then
transitionEnterScale = transitionEnterScale + deltaTime / 0.25 -- transition should last for that time in seconds transitionEnterScale = transitionEnterScale + deltaTime / 0.25 -- transition should last for that time in seconds
else else
transitionEnterScale = 1 transitionEnterScale = 1
end end
end end
transitionEnterOffsetX = Easing.inOutQuad(1 - transitionEnterScale) * 416 transitionEnterOffsetX = Easing.inOutQuad(1 - transitionEnterScale) * 416
end end
local drawSortWheel = function (x,y,w,h, deltaTime) local drawSortWheel = function (x,y,w,h, deltaTime)
gfx.Scissor(x,y,w,h); gfx.Scissor(x,y,w,h);
gfx.Translate(x,y); gfx.Translate(x,y);
gfx.Scale(w/1080, h/1920); gfx.Scale(w/1080, h/1920);
gfx.Scissor(0,0,1080,1920); gfx.Scissor(0,0,1080,1920);
-- Draw the dark overlay above song wheel -- Draw the dark overlay above song wheel
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(0, 0, 0, math.floor(transitionEnterScale * 192)); gfx.FillColor(0, 0, 0, math.floor(transitionEnterScale * 192));
gfx.Rect(0, 0, 1080, 1920); gfx.Rect(0, 0, 1080, 1920);
gfx.Fill(); gfx.Fill();
-- Draw the panel background -- Draw the panel background
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(desw - 416 + transitionEnterOffsetX, 0, 416, desh, gfx.ImageRect(desw - 416 + transitionEnterOffsetX, 0, 416, desh,
panelBgImage, 1, 0) panelBgImage, 1, 0)
gfx.LoadSkinFont("Digital-Serial-Bold.ttf"); gfx.LoadSkinFont("Digital-Serial-Bold.ttf");
gfx.FontSize(FONT_SIZE); gfx.FontSize(FONT_SIZE);
gfx.FillColor(255, 255, 255, 255); gfx.FillColor(255, 255, 255, 255);
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
gfx.GlobalAlpha(transitionEnterScale) gfx.GlobalAlpha(transitionEnterScale)
-- Starting position of the first sort option -- Starting position of the first sort option
local x = 889 + transitionEnterOffsetX; local x = 889 + transitionEnterOffsetX;
local y = desh / 2 - -- Center point 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) (#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 ((SUB_FONT_SIZE * 2 + SUB_MARGIN * 4) / 2); -- Space for taken by order options
-- Draw the title image -- Draw the title image
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(x - 72, y - 27, 144, 54, titleTextImage, 1, 0) gfx.ImageRect(x - 72, y - 27, 144, 54, titleTextImage, 1, 0)
y = y + (54 + MARGIN) y = y + (54 + MARGIN)
-- Draw all the sorting options -- Draw all the sorting options
for i, f in ipairs(sorts) do for i, f in ipairs(sorts) do
local spaceAfter = drawButton(i, f, x, y); local spaceAfter = drawButton(i, f, x, y);
y = y + spaceAfter; y = y + spaceAfter;
end end
end end
function setSkinSetting() function setSkinSetting()
for i, f in ipairs(sorts) do for i, f in ipairs(sorts) do
if i == selection then if i == selection then
local label = f:gsub(' ^', '') local label = f:gsub(' ^', '')
label = label:gsub(' v', '') label = label:gsub(' v', '')
game.SetSkinSetting('_songWheelActiveSortOptionLabel', label); game.SetSkinSetting('_songWheelActiveSortOptionLabel', label);
end end
end end
end end
function render(deltaTime, shown) function render(deltaTime, shown)
gfx.Save() gfx.Save()
gfx.ResetTransform() gfx.ResetTransform()
renderedButtonLabels = {}; renderedButtonLabels = {};
if (shown ~= previousActiveState) then if (shown ~= previousActiveState) then
if (shown) then if (shown) then
game.PlaySample('sort_wheel/enter.wav'); game.PlaySample('sort_wheel/enter.wav');
else else
game.PlaySample('sort_wheel/leave.wav'); game.PlaySample('sort_wheel/leave.wav');
end end
previousActiveState = shown; previousActiveState = shown;
end end
-- detect resolution change -- detect resolution change
local resx, resy = game.GetResolution(); local resx, resy = game.GetResolution();
if resx ~= resX or resy ~= resY then if resx ~= resX or resy ~= resY then
resolutionChange(resx, resy) resolutionChange(resx, resy)
end end
gfx.GlobalAlpha(1) gfx.GlobalAlpha(1)
if not shown then if not shown then
transitionEnterReverse = true transitionEnterReverse = true
if (transitionEnterScale > 0) then drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime) end if (transitionEnterScale > 0) then drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime) end
else else
transitionEnterReverse = false transitionEnterReverse = false
drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime) drawSortWheel((resX - fullX) / 2, 0, fullX, fullY, deltaTime)
end end
tickTransitions(deltaTime) tickTransitions(deltaTime)
setSkinSetting(); setSkinSetting();
gfx.Restore() gfx.Restore()
end end
function set_selection(index) selection = index end function set_selection(index) selection = index end

View File

@ -1,214 +1,214 @@
local common = require('common.common'); local common = require('common.common');
local Numbers = require('common.numbers') local Numbers = require('common.numbers')
game.LoadSkinSample('song_transition_screen/transition_enter.wav'); game.LoadSkinSample('song_transition_screen/transition_enter.wav');
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY) local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
local bgImage = gfx.CreateSkinImage("songtransition/bg.png", 0) local bgImage = gfx.CreateSkinImage("songtransition/bg.png", 0)
local glowOverlayImage = gfx.CreateSkinImage("songtransition/glowy.png", 0) local glowOverlayImage = gfx.CreateSkinImage("songtransition/glowy.png", 0)
local frameOverlayImage = gfx.CreateSkinImage("songtransition/frames.png", 0) local frameOverlayImage = gfx.CreateSkinImage("songtransition/frames.png", 0)
local albumBgImage = gfx.CreateSkinImage("songtransition/album_crop.png", 0) local albumBgImage = gfx.CreateSkinImage("songtransition/album_crop.png", 0)
local infoOverlayPanel = gfx.CreateSkinImage("songtransition/info_panels_crop.png", 0) local infoOverlayPanel = gfx.CreateSkinImage("songtransition/info_panels_crop.png", 0)
local linkedHexagonsImage = gfx.CreateSkinImage("songtransition/linked_hexagons_crop.png", 0) local linkedHexagonsImage = gfx.CreateSkinImage("songtransition/linked_hexagons_crop.png", 0)
local hexagonImages = { local hexagonImages = {
gfx.CreateSkinImage("songtransition/hex1.png", 0), gfx.CreateSkinImage("songtransition/hex1.png", 0),
gfx.CreateSkinImage("songtransition/hex2.png", 0) gfx.CreateSkinImage("songtransition/hex2.png", 0)
} }
local difficultyNumbers; local difficultyNumbers;
local difficultyLabelImages = { local difficultyLabelImages = {
gfx.CreateSkinImage("songtransition/difficulty_labels/nov.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/nov.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/adv.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/adv.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/exh.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/exh.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/mxm.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/mxm.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/inf.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/inf.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/grv.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/grv.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/hvn.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/hvn.png", 0),
gfx.CreateSkinImage("songtransition/difficulty_labels/vvd.png", 0), gfx.CreateSkinImage("songtransition/difficulty_labels/vvd.png", 0),
} }
local timer = 0 local timer = 0
local transitionProgress = 0; local transitionProgress = 0;
local outProgress = 0 local outProgress = 0
local flickerTime = 0.050 --seconds (50ms) local flickerTime = 0.050 --seconds (50ms)
-- Window variables -- Window variables
local resX, resY = game.GetResolution() local resX, resY = game.GetResolution()
-- Aspect Ratios -- Aspect Ratios
local landscapeWidescreenRatio = 16 / 9 local landscapeWidescreenRatio = 16 / 9
local landscapeStandardRatio = 4 / 3 local landscapeStandardRatio = 4 / 3
local portraitWidescreenRatio = 9 / 16 local portraitWidescreenRatio = 9 / 16
-- Portrait sizes -- Portrait sizes
local fullX, fullY local fullX, fullY
local desw = 1080 local desw = 1080
local desh = 1920 local desh = 1920
local noJacket = gfx.CreateSkinImage("song_select/loading.png", 0) local noJacket = gfx.CreateSkinImage("song_select/loading.png", 0)
local wasEnterSfxPlayed = false; local wasEnterSfxPlayed = false;
function resetLayoutInformation() function resetLayoutInformation()
resx, resy = game.GetResolution() resx, resy = game.GetResolution()
scale = resx / desw scale = resx / desw
end end
function render(deltaTime) function render(deltaTime)
if not wasEnterSfxPlayed then if not wasEnterSfxPlayed then
common.stopMusic(); common.stopMusic();
game.PlaySample('song_transition_screen/transition_enter.wav'); game.PlaySample('song_transition_screen/transition_enter.wav');
wasEnterSfxPlayed = true; wasEnterSfxPlayed = true;
end end
if not difficultyNumbers then if not difficultyNumbers then
difficultyNumbers = Numbers.load_number_image('diff_num') difficultyNumbers = Numbers.load_number_image('diff_num')
end end
local x_offset = (resX - fullX) / 2 local x_offset = (resX - fullX) / 2
local y_offset = 0 local y_offset = 0
gfx.BeginPath() gfx.BeginPath()
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage) local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
gfx.Rect(0, 0, resX, resY) gfx.Rect(0, 0, resX, resY)
gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2)) gfx.FillPaint(gfx.ImagePattern(0, 0, bgImageWidth, bgImageHeight, 0, backgroundImage, 0.2))
gfx.Fill() gfx.Fill()
gfx.Translate(x_offset, y_offset); gfx.Translate(x_offset, y_offset);
gfx.Scale(fullX / 1080, fullY / 1920); gfx.Scale(fullX / 1080, fullY / 1920);
gfx.Scissor(0, 0, 1080, 1920); gfx.Scissor(0, 0, 1080, 1920);
render_screen(); render_screen();
transitionProgress = transitionProgress + deltaTime * 0.2 transitionProgress = transitionProgress + deltaTime * 0.2
transitionProgress = math.min(transitionProgress,1) transitionProgress = math.min(transitionProgress,1)
if transitionProgress < 0.25 then if transitionProgress < 0.25 then
local whiteAlpha = math.max(0, (1-transitionProgress/0.25)) local whiteAlpha = math.max(0, (1-transitionProgress/0.25))
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(255,255,255,math.floor(255*whiteAlpha)); gfx.FillColor(255,255,255,math.floor(255*whiteAlpha));
gfx.Rect(0,0,desw,desh); gfx.Rect(0,0,desw,desh);
gfx.Fill(); gfx.Fill();
gfx.ClosePath(); gfx.ClosePath();
end end
if transitionProgress > 0.85 then if transitionProgress > 0.85 then
local blackAlpha = math.min(1, ((transitionProgress-0.85)/0.15)) local blackAlpha = math.min(1, ((transitionProgress-0.85)/0.15))
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(0,0,0,math.floor(255*blackAlpha)); gfx.FillColor(0,0,0,math.floor(255*blackAlpha));
gfx.Rect(0,0,desw,desh); gfx.Rect(0,0,desw,desh);
gfx.Fill(); gfx.Fill();
gfx.ClosePath(); gfx.ClosePath();
end end
timer = timer + deltaTime timer = timer + deltaTime
return transitionProgress >= 1 return transitionProgress >= 1
end end
function render_out(deltaTime) function render_out(deltaTime)
outProgress = outProgress + deltaTime * 0.2 outProgress = outProgress + deltaTime * 0.2
outProgress = math.min(outProgress, 1) outProgress = math.min(outProgress, 1)
timer = timer + deltaTime timer = timer + deltaTime
return outProgress >= 1; return outProgress >= 1;
end end
function sign(x) function sign(x)
return x>0 and 1 or x<0 and -1 or 0 return x>0 and 1 or x<0 and -1 or 0
end end
function render_screen() function render_screen()
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0, 0, 1080, 1920, bgImage,1,0); gfx.ImageRect(0, 0, 1080, 1920, bgImage,1,0);
if transitionProgress < 0.35 then if transitionProgress < 0.35 then
local hex1alpha = math.max(0, (1-transitionProgress/0.35)) local hex1alpha = math.max(0, (1-transitionProgress/0.35))
local hex2alpha = math.max(0, (1-transitionProgress/0.3)) local hex2alpha = math.max(0, (1-transitionProgress/0.3))
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0,0, desw, desh, hexagonImages[1], hex1alpha, 0) gfx.ImageRect(0,0, desw, desh, hexagonImages[1], hex1alpha, 0)
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0,0, desw, desh, hexagonImages[2], hex2alpha, 0) gfx.ImageRect(0,0, desw, desh, hexagonImages[2], hex2alpha, 0)
end end
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0,0,1080,1920,frameOverlayImage,1,0); gfx.ImageRect(0,0,1080,1920,frameOverlayImage,1,0);
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(0, 0, 1080, 1920, glowOverlayImage,1,0); gfx.ImageRect(0, 0, 1080, 1920, glowOverlayImage,1,0);
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(37.5, 1074, 1180*0.85, 343*0.85, infoOverlayPanel, 1, 0); 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 if (timer % flickerTime) < (flickerTime / 2) then --flicker with 20Hz (50ms), 50% duty cycle
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(37.5, 1074, 1180*0.85, 189*0.85, linkedHexagonsImage, 0.1, 0); gfx.ImageRect(37.5, 1074, 1180*0.85, 189*0.85, linkedHexagonsImage, 0.1, 0);
end end
gfx.BeginPath() gfx.BeginPath()
gfx.ImageRect(10, 195.5, 1060, 1015, albumBgImage,1,0); gfx.ImageRect(10, 195.5, 1060, 1015, albumBgImage,1,0);
local jacket = song.jacket == 0 and noJacket or song.jacket local jacket = song.jacket == 0 and noJacket or song.jacket
gfx.BeginPath(); gfx.BeginPath();
gfx.ImageRect(235, 385, 608, 608, jacket, 1, 0) gfx.ImageRect(235, 385, 608, 608, jacket, 1, 0)
gfx.ClosePath(); gfx.ClosePath();
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
gfx.FontSize(55) gfx.FontSize(55)
gfx.Text(song.title,desw/2, 1114) gfx.Text(song.title,desw/2, 1114)
gfx.FontSize(30) gfx.FontSize(30)
gfx.Text(song.artist, desw/2 , 1182) gfx.Text(song.artist, desw/2 , 1182)
local EFFECTOR_LABEL_Y = 1288 local EFFECTOR_LABEL_Y = 1288
local ILLUSTRATOR_LABEL_Y = 1347 local ILLUSTRATOR_LABEL_Y = 1347
gfx.FontSize(22) gfx.FontSize(22)
gfx.Text(song.effector, desw/2+70 , EFFECTOR_LABEL_Y-1) gfx.Text(song.effector, desw/2+70 , EFFECTOR_LABEL_Y-1)
gfx.Text(song.illustrator, desw/2+70 , ILLUSTRATOR_LABEL_Y-3) gfx.Text(song.illustrator, desw/2+70 , ILLUSTRATOR_LABEL_Y-3)
-- Draw song diff level -- Draw song diff level
gfx.BeginPath(); gfx.BeginPath();
Numbers.draw_number(933, 1140, 1.0, song.level, 2, difficultyNumbers, false, 1, 1) Numbers.draw_number(933, 1140, 1.0, song.level, 2, difficultyNumbers, false, 1, 1)
-- Draw song diff label (NOV/ADV/EXH/MXM/etc.) -- Draw song diff label (NOV/ADV/EXH/MXM/etc.)
gfx.BeginPath(); gfx.BeginPath();
local diffLabelImage = difficultyLabelImages[song.difficulty+1]; local diffLabelImage = difficultyLabelImages[song.difficulty+1];
local diffLabelW, diffLabelH = gfx.ImageSize(diffLabelImage); local diffLabelW, diffLabelH = gfx.ImageSize(diffLabelImage);
gfx.ImageRect(952-diffLabelW/2, 1154-diffLabelH/2, diffLabelW, diffLabelH, diffLabelImage,1,0); gfx.ImageRect(952-diffLabelW/2, 1154-diffLabelH/2, diffLabelW, diffLabelH, diffLabelImage,1,0);
gfx.ClosePath(); gfx.ClosePath();
gfx.Save(); gfx.Save();
gfx.FontSize(24) gfx.FontSize(24)
gfx.LoadSkinFont('Digital-Serial-Bold.ttf') gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE)
gfx.BeginPath(); gfx.BeginPath();
gfx.Text('BPM', 127, 1140) gfx.Text('BPM', 127, 1140)
gfx.Text(song.bpm, 127, 1167) gfx.Text(song.bpm, 127, 1167)
-- temp ref overlay -- temp ref overlay
-- gfx.BeginPath() -- gfx.BeginPath()
-- gfx.ImageRect(0, 0, 1080, 1920, refBgImage,0.5,0); -- gfx.ImageRect(0, 0, 1080, 1920, refBgImage,0.5,0);
gfx.ClosePath(); gfx.ClosePath();
gfx.Restore(); gfx.Restore();
end end
function reset() function reset()
transitionProgress = 0 transitionProgress = 0
resX, resY = game.GetResolution() resX, resY = game.GetResolution()
fullX = portraitWidescreenRatio * resY fullX = portraitWidescreenRatio * resY
fullY = resY fullY = resY
outProgress = 0 outProgress = 0
wasEnterSfxPlayed = false; wasEnterSfxPlayed = false;
end end

View File

@ -1,215 +1,215 @@
local mposx = 0; local mposx = 0;
local mposy = 0; local mposy = 0;
local hovered = nil; local hovered = nil;
local cursorIndex = 1 local cursorIndex = 1
local buttonWidth = 250; local buttonWidth = 250;
local buttonHeight = 50; local buttonHeight = 50;
local buttonBorder = 2; local buttonBorder = 2;
local label = -1; local label = -1;
local gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test") local gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test")
gfx.GradientColors(0,127,255,255,0,128,255,0) gfx.GradientColors(0,127,255,255,0,128,255,0)
local gradient = gfx.LinearGradient(0,0,0,1) local gradient = gfx.LinearGradient(0,0,0,1)
local bgPattern = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX + gfx.IMAGE_REPEATY) local bgPattern = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX + gfx.IMAGE_REPEATY)
local bgAngle = 0.5 local bgAngle = 0.5
local bgPaint = gfx.ImagePattern(0,0, 256,256, bgAngle, bgPattern, 1.0) local bgPaint = gfx.ImagePattern(0,0, 256,256, bgAngle, bgPattern, 1.0)
local bgPatternTimer = 0 local bgPatternTimer = 0
local cursorYs = {} local cursorYs = {}
local buttons = nil local buttons = nil
local resx, resy = game.GetResolution(); local resx, resy = game.GetResolution();
view_update = function() view_update = function()
if package.config:sub(1,1) == '\\' then --windows if package.config:sub(1,1) == '\\' then --windows
updateUrl, updateVersion = game.UpdateAvailable() updateUrl, updateVersion = game.UpdateAvailable()
os.execute("start " .. updateUrl) os.execute("start " .. updateUrl)
else --unix else --unix
--TODO: Mac solution --TODO: Mac solution
os.execute("xdg-open " .. updateUrl) os.execute("xdg-open " .. updateUrl)
end end
end end
mouse_clipped = function(x,y,w,h) mouse_clipped = function(x,y,w,h)
return mposx > x and mposy > y and mposx < x+w and mposy < y+h; return mposx > x and mposy > y and mposx < x+w and mposy < y+h;
end; end;
draw_button = function(button, x, y) draw_button = function(button, x, y)
local name = button[1] local name = button[1]
local rx = x - (buttonWidth / 2); local rx = x - (buttonWidth / 2);
local ty = y - (buttonHeight / 2); local ty = y - (buttonHeight / 2);
gfx.BeginPath(); gfx.BeginPath();
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
gfx.FontSize(40); gfx.FontSize(40);
if mouse_clipped(rx,ty, buttonWidth, buttonHeight) then if mouse_clipped(rx,ty, buttonWidth, buttonHeight) then
hovered = button[2]; hovered = button[2];
r, b_g, b_b, b_a = game.GetSkinSetting("col_test") r, b_g, b_b, b_a = game.GetSkinSetting("col_test")
gfx.FillColor(0, 125, 255); 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);
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 end
gfx.FillColor(255,255,255); gfx.FillColor(255,255,255);
gfx.Text(name, x, y); gfx.Text(name, x, y);
return buttonHeight + 5 return buttonHeight + 5
end; end;
function updateGradient() function updateGradient()
gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test") gr_r, gr_g, gr_b, gr_a = game.GetSkinSetting("col_test")
if gr_r == nil then return end if gr_r == nil then return end
gfx.GradientColors(gr_r,gr_g,gr_b,gr_a,0,128,255,0) gfx.GradientColors(gr_r,gr_g,gr_b,gr_a,0,128,255,0)
--gradient = gfx.LinearGradient(0,0,0,1) --gradient = gfx.LinearGradient(0,0,0,1)
end end
function updatePattern(dt) function updatePattern(dt)
bgPatternTimer = (bgPatternTimer + dt) % 1.0 bgPatternTimer = (bgPatternTimer + dt) % 1.0
local bgx = math.cos(bgAngle) * (bgPatternTimer * 256) local bgx = math.cos(bgAngle) * (bgPatternTimer * 256)
local bgy = math.sin(bgAngle) * (bgPatternTimer * 256) local bgy = math.sin(bgAngle) * (bgPatternTimer * 256)
gfx.UpdateImagePattern(bgPaint, bgx, bgy, 256, 256, bgAngle, 1.0) gfx.UpdateImagePattern(bgPaint, bgx, bgy, 256, 256, bgAngle, 1.0)
end end
function setButtons() function setButtons()
if buttons == nil then if buttons == nil then
buttons = {} buttons = {}
buttons[1] = {"Start", Menu.Start} buttons[1] = {"Start", Menu.Start}
buttons[2] = {"Multiplayer", Menu.Multiplayer} buttons[2] = {"Multiplayer", Menu.Multiplayer}
buttons[3] = {"Challenges", Menu.Challenges} buttons[3] = {"Challenges", Menu.Challenges}
buttons[4] = {"Get Songs", Menu.DLScreen} buttons[4] = {"Get Songs", Menu.DLScreen}
buttons[5] = {"Settings", Menu.Settings} buttons[5] = {"Settings", Menu.Settings}
buttons[6] = {"Exit", Menu.Exit} buttons[6] = {"Exit", Menu.Exit}
end end
end end
local renderY = resy/2 local renderY = resy/2
function draw_cursor(x,y,deltaTime) function draw_cursor(x,y,deltaTime)
gfx.Save() gfx.Save()
gfx.BeginPath() gfx.BeginPath()
local size = 8 local size = 8
renderY = renderY - (renderY - y) * deltaTime * 30 renderY = renderY - (renderY - y) * deltaTime * 30
gfx.MoveTo(x-size,renderY-size) gfx.MoveTo(x-size,renderY-size)
gfx.LineTo(x,renderY) gfx.LineTo(x,renderY)
gfx.LineTo(x-size,renderY+size) gfx.LineTo(x-size,renderY+size)
gfx.StrokeWidth(3) gfx.StrokeWidth(3)
gfx.StrokeColor(255,255,255) gfx.StrokeColor(255,255,255)
gfx.Stroke() gfx.Stroke()
gfx.Restore() gfx.Restore()
end end
function sign(x) function sign(x)
return x>0 and 1 or x<0 and -1 or 0 return x>0 and 1 or x<0 and -1 or 0
end end
function roundToZero(x) function roundToZero(x)
if x<0 then return math.ceil(x) if x<0 then return math.ceil(x)
elseif x>0 then return math.floor(x) elseif x>0 then return math.floor(x)
else return 0 end else return 0 end
end end
function deltaKnob(delta) function deltaKnob(delta)
if math.abs(delta) > 1.5 * math.pi then if math.abs(delta) > 1.5 * math.pi then
return delta + 2 * math.pi * sign(delta) * -1 return delta + 2 * math.pi * sign(delta) * -1
end end
return delta return delta
end end
local lastKnobs = nil local lastKnobs = nil
local knobProgress = 0 local knobProgress = 0
function handle_controller() function handle_controller()
if lastKnobs == nil then if lastKnobs == nil then
lastKnobs = {game.GetKnob(0), game.GetKnob(1)} lastKnobs = {game.GetKnob(0), game.GetKnob(1)}
else else
local newKnobs = {game.GetKnob(0), game.GetKnob(1)} local newKnobs = {game.GetKnob(0), game.GetKnob(1)}
knobProgress = knobProgress - deltaKnob(lastKnobs[1] - newKnobs[1]) * 1.2 knobProgress = knobProgress - deltaKnob(lastKnobs[1] - newKnobs[1]) * 1.2
knobProgress = knobProgress - deltaKnob(lastKnobs[2] - newKnobs[2]) * 1.2 knobProgress = knobProgress - deltaKnob(lastKnobs[2] - newKnobs[2]) * 1.2
lastKnobs = newKnobs lastKnobs = newKnobs
if math.abs(knobProgress) > 1 then if math.abs(knobProgress) > 1 then
cursorIndex = (((cursorIndex - 1) + roundToZero(knobProgress)) % #buttons) + 1 cursorIndex = (((cursorIndex - 1) + roundToZero(knobProgress)) % #buttons) + 1
knobProgress = knobProgress - roundToZero(knobProgress) knobProgress = knobProgress - roundToZero(knobProgress)
end end
end end
end end
render = function(deltaTime) render = function(deltaTime)
setButtons() setButtons()
updateGradient() updateGradient()
updatePattern(deltaTime) updatePattern(deltaTime)
resx,resy = game.GetResolution(); resx,resy = game.GetResolution();
mposx,mposy = game.GetMousePos(); mposx,mposy = game.GetMousePos();
gfx.Scale(resx, resy / 3) gfx.Scale(resx, resy / 3)
gfx.Rect(0,0,1,1) gfx.Rect(0,0,1,1)
gfx.FillPaint(gradient) gfx.FillPaint(gradient)
gfx.Fill() gfx.Fill()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.Scale(0.5,0.5) gfx.Scale(0.5,0.5)
gfx.Rect(0,0,resx * 2,resy * 2) gfx.Rect(0,0,resx * 2,resy * 2)
gfx.GlobalCompositeOperation(gfx.BLEND_OP_DESTINATION_IN) gfx.GlobalCompositeOperation(gfx.BLEND_OP_DESTINATION_IN)
gfx.FillPaint(bgPaint) gfx.FillPaint(bgPaint)
gfx.Fill() gfx.Fill()
gfx.ResetTransform() gfx.ResetTransform()
gfx.BeginPath() gfx.BeginPath()
gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER) gfx.GlobalCompositeOperation(gfx.BLEND_OP_SOURCE_OVER)
cursorGet = 1 cursorGet = 1
buttonY = resy / 2; buttonY = resy / 2;
hovered = nil; hovered = nil;
gfx.LoadSkinFont("NotoSans-Regular.ttf"); gfx.LoadSkinFont("NotoSans-Regular.ttf");
for i=1,#buttons do for i=1,#buttons do
cursorYs[i] = buttonY cursorYs[i] = buttonY
buttonY = buttonY + draw_button(buttons[i], resx / 2, buttonY); buttonY = buttonY + draw_button(buttons[i], resx / 2, buttonY);
if hovered == buttons[i][2] then if hovered == buttons[i][2] then
cursorIndex = i cursorIndex = i
end end
end end
handle_controller() handle_controller()
draw_cursor(resx/2 - 100, cursorYs[cursorIndex], deltaTime) draw_cursor(resx/2 - 100, cursorYs[cursorIndex], deltaTime)
gfx.BeginPath(); gfx.BeginPath();
gfx.FillColor(255,255,255); gfx.FillColor(255,255,255);
gfx.FontSize(120); gfx.FontSize(120);
if label == -1 then if label == -1 then
label = gfx.CreateLabel("ExperimentalGear ALPHA 1.8.7 ''README.TXT''", 120, 0); label = gfx.CreateLabel("ExperimentalGear ALPHA 1.8.7 ''README.TXT''", 120, 0);
end end
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE);
gfx.DrawLabel(label, resx / 2, resy / 2 - 200, resx-40); gfx.DrawLabel(label, resx / 2, resy / 2 - 200, resx-40);
updateUrl, updateVersion = game.UpdateAvailable() updateUrl, updateVersion = game.UpdateAvailable()
if updateUrl then if updateUrl then
gfx.BeginPath() gfx.BeginPath()
gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT) gfx.TextAlign(gfx.TEXT_ALIGN_BOTTOM + gfx.TEXT_ALIGN_LEFT)
gfx.FontSize(30) gfx.FontSize(30)
gfx.Text(string.format("Version %s is now available", updateVersion), 5, resy - buttonHeight - 10) 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({"View", view_update}, buttonWidth / 2 + 5, resy - buttonHeight / 2 - 5);
draw_button({"Update", Menu.Update}, buttonWidth * 1.5 + 15, resy - buttonHeight / 2 - 5) draw_button({"Update", Menu.Update}, buttonWidth * 1.5 + 15, resy - buttonHeight / 2 - 5)
end end
end; end;
mouse_pressed = function(button) mouse_pressed = function(button)
if hovered then if hovered then
hovered() hovered()
end end
return 0 return 0
end end
function button_pressed(button) function button_pressed(button)
if button == game.BUTTON_STA then if button == game.BUTTON_STA then
buttons[cursorIndex][2]() buttons[cursorIndex][2]()
elseif button == game.BUTTON_BCK then elseif button == game.BUTTON_BCK then
Menu.Exit() Menu.Exit()
end end
end end

View File

@ -1,21 +1,21 @@
#version 330 #version 330
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=1) in vec2 fsTex; layout(location=1) in vec2 fsTex;
layout(location=0) out vec4 target; layout(location=0) out vec4 target;
uniform sampler2D mainTex; uniform sampler2D mainTex;
void main() void main()
{ {
float x = fsTex.x; float x = fsTex.x;
if (x < 0.0 || x > 1.0) if (x < 0.0 || x > 1.0)
{ {
target = vec4(0); target = vec4(0);
return; return;
} }
vec4 mainColor = texture(mainTex, vec2(x,fsTex.y)); vec4 mainColor = texture(mainTex, vec2(x,fsTex.y));
target = vec4(0.0, 0.0, 0.0, mainColor.a); target = vec4(0.0, 0.0, 0.0, mainColor.a);
} }

View File

@ -1,20 +1,20 @@
#version 330 #version 330
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=0) in vec2 inPos; layout(location=0) in vec2 inPos;
layout(location=1) in vec2 inTex; layout(location=1) in vec2 inTex;
out gl_PerVertex out gl_PerVertex
{ {
vec4 gl_Position; vec4 gl_Position;
}; };
layout(location=1) out vec2 fsTex; layout(location=1) out vec2 fsTex;
uniform mat4 proj; uniform mat4 proj;
uniform mat4 camera; uniform mat4 camera;
uniform mat4 world; uniform mat4 world;
void main() void main()
{ {
fsTex = inTex; fsTex = inTex;
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1); gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
} }

View File

@ -1,26 +1,26 @@
#version 330 #version 330
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=1) in vec2 fsTex; layout(location=1) in vec2 fsTex;
layout(location=0) out vec4 target; layout(location=0) out vec4 target;
uniform sampler2D mainTex; uniform sampler2D mainTex;
uniform vec4 lCol; uniform vec4 lCol;
uniform vec4 rCol; uniform vec4 rCol;
uniform float hidden; uniform float hidden;
void main() void main()
{ {
vec4 mainColor = texture(mainTex, fsTex.xy); vec4 mainColor = texture(mainTex, fsTex.xy);
vec4 col = mainColor; vec4 col = mainColor;
if(fsTex.y > hidden * 1.0) if(fsTex.y > hidden * 1.0)
{ {
} }
else else
{ {
col.xyz = vec3(0.); col.xyz = vec3(0.);
col.a = col.a > 0.0 ? 0.3 : 0.0; col.a = col.a > 0.0 ? 0.3 : 0.0;
} }
target = col; target = col;
} }

View File

@ -1,20 +1,20 @@
#version 330 #version 330
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=0) in vec2 inPos; layout(location=0) in vec2 inPos;
layout(location=1) in vec2 inTex; layout(location=1) in vec2 inTex;
out gl_PerVertex out gl_PerVertex
{ {
vec4 gl_Position; vec4 gl_Position;
}; };
layout(location=1) out vec2 fsTex; layout(location=1) out vec2 fsTex;
uniform mat4 proj; uniform mat4 proj;
uniform mat4 camera; uniform mat4 camera;
uniform mat4 world; uniform mat4 world;
void main() void main()
{ {
fsTex = inTex; fsTex = inTex;
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1); gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
} }

View File

@ -1,53 +1,53 @@
#ifdef EMBEDDED #ifdef EMBEDDED
varying vec2 fsTex; varying vec2 fsTex;
#else #else
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=1) in vec2 fsTex; layout(location=1) in vec2 fsTex;
layout(location=0) out vec4 target; layout(location=0) out vec4 target;
#endif #endif
uniform sampler2D mainTex; uniform sampler2D mainTex;
uniform float hiddenCutoff; uniform float hiddenCutoff;
uniform float hiddenFadeWindow; uniform float hiddenFadeWindow;
uniform float suddenCutoff; uniform float suddenCutoff;
uniform float suddenFadeWindow; uniform float suddenFadeWindow;
void main() void main()
{ {
#ifdef EMBEDDED #ifdef EMBEDDED
target = vec4(0.0); target = vec4(0.0);
#else #else
target = texture(mainTex, vec2(fsTex.x, fsTex.y * 2.0)); target = texture(mainTex, vec2(fsTex.x, fsTex.y * 2.0));
float off = 1.0 - (fsTex.y * 2.0); float off = 1.0 - (fsTex.y * 2.0);
if(hiddenCutoff < suddenCutoff) if(hiddenCutoff < suddenCutoff)
{ {
float hiddenCutoffFade = hiddenCutoff - hiddenFadeWindow; float hiddenCutoffFade = hiddenCutoff - hiddenFadeWindow;
if (off > hiddenCutoffFade && off < hiddenCutoff) { if (off > hiddenCutoffFade && off < hiddenCutoff) {
target.a = target.a * max(0.0, (hiddenCutoff - off) / hiddenFadeWindow); target.a = target.a * max(0.0, (hiddenCutoff - off) / hiddenFadeWindow);
} }
if (off < suddenCutoff && off > hiddenCutoff) { if (off < suddenCutoff && off > hiddenCutoff) {
target.a = 0.0; target.a = 0.0;
} }
float suddenCutoffFade = suddenCutoff + suddenFadeWindow; float suddenCutoffFade = suddenCutoff + suddenFadeWindow;
if (off < suddenCutoffFade && off > suddenCutoff) { if (off < suddenCutoffFade && off > suddenCutoff) {
target.a = target.a * max(0.0, (off - suddenCutoff) / suddenFadeWindow); target.a = target.a * max(0.0, (off - suddenCutoff) / suddenFadeWindow);
} }
} }
else else
{ {
float hiddenCutoffFade = hiddenCutoff + hiddenFadeWindow; float hiddenCutoffFade = hiddenCutoff + hiddenFadeWindow;
if (off > hiddenCutoff) { if (off > hiddenCutoff) {
target.a = target.a * max(0.0, (hiddenCutoffFade - off) / hiddenFadeWindow); target.a = target.a * max(0.0, (hiddenCutoffFade - off) / hiddenFadeWindow);
} }
float suddenCutoffFade = suddenCutoff - suddenFadeWindow; float suddenCutoffFade = suddenCutoff - suddenFadeWindow;
if (off < suddenCutoff) { if (off < suddenCutoff) {
target.a = target.a * max(0.0, (off - suddenCutoffFade) / suddenFadeWindow); target.a = target.a * max(0.0, (off - suddenCutoffFade) / suddenFadeWindow);
} }
} }
#endif #endif
} }

View File

@ -1,25 +1,25 @@
#ifdef EMBEDDED #ifdef EMBEDDED
attribute vec2 inPos; attribute vec2 inPos;
attribute vec2 inTex; attribute vec2 inTex;
varying vec2 fsTex; varying vec2 fsTex;
#else #else
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
layout(location=0) in vec2 inPos; layout(location=0) in vec2 inPos;
layout(location=1) in vec2 inTex; layout(location=1) in vec2 inTex;
out gl_PerVertex out gl_PerVertex
{ {
vec4 gl_Position; vec4 gl_Position;
}; };
layout(location=1) out vec2 fsTex; layout(location=1) out vec2 fsTex;
#endif #endif
uniform mat4 proj; uniform mat4 proj;
uniform mat4 camera; uniform mat4 camera;
uniform mat4 world; uniform mat4 world;
void main() void main()
{ {
fsTex = inTex; fsTex = inTex;
gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1); gl_Position = proj * camera * world * vec4(inPos.xy, 0, 1);
} }

View File

@ -1,15 +1,15 @@
CUSTOM CREW CREATOR CUSTOM CREW CREATOR
Must have: Must have:
Art Program with layers (Paint.NET and GIMP are both FREE) Art Program with layers (Paint.NET and GIMP are both FREE)
Basic Knowledge of the program Basic Knowledge of the program
Custom crew assets Custom crew assets
1: Bottom-most layer is frame_glow.png 1: Bottom-most layer is frame_glow.png
2: Middle layer is your custom crew member 2: Middle layer is your custom crew member
3: Third layer is frame_metal.png 3: Third layer is frame_metal.png
Clean up so no part of your custom crew is outside of frame_metal.png. Clean up so no part of your custom crew is outside of frame_metal.png.
EXPORT AS .PNG FILE! EXPORT AS .PNG FILE!