From 85f50b25374da6a2effda61264db8cfdde13ac2d Mon Sep 17 00:00:00 2001 From: entar Date: Sat, 31 May 2025 15:22:04 +0700 Subject: [PATCH] Fixed quitting playstate --- main.lua | 1 + modules/states/playstate.lua | 581 ++++++++++++++++++----------------- 2 files changed, 308 insertions(+), 274 deletions(-) diff --git a/main.lua b/main.lua index 2c27b3e..c2e46b3 100644 --- a/main.lua +++ b/main.lua @@ -38,6 +38,7 @@ function love.update(dt) curState = state(curSong, curDiff) curState.quit = function() curState = nil + stateLoaded = false myTypes.destroyAllSprites() end curState.load() diff --git a/modules/states/playstate.lua b/modules/states/playstate.lua index ce3a907..8f8833b 100644 --- a/modules/states/playstate.lua +++ b/modules/states/playstate.lua @@ -1,324 +1,357 @@ return function(songName, songDifficulty) - local state = {} + local state = {} -- Returns needed functions for the state to work after loading it properly - local Height = 0 - local Width = 0 + -- I NEED THEM IMPORTS + local myMath = require("modules.math") + local myTypes = require("modules.types") + local conductor = require("modules.conductor") + local json = require("modules.json") + local files = require("modules.files") + local logger = require("modules.logging") + local socket = require("socket") + local logging= require("modules.logging") + -- I NEED THEM IMPORTS - local myMath = require("modules.math") - local myTypes = require("modules.types") - local conductor = require("modules.conductor") - local json = require("modules.json") - local files = require("modules.files") - local logger = require("modules.logging") - local socket = require("socket") - local logging= require("modules.logging") + local startTime = 0 - local newVector2 = myTypes.Vector2(10, 10) + local chartString = files.read_file(string.format("charts/%s/%s-%s.json", songName, songName, songDifficulty)) + if not chartString then + error("Chart couldn't be loaded!") + end - local startTime = 0 + local chart = json.parse(chartString).song - local chartString = files.read_file(string.format("charts/%s/%s-%s.json", songName, songName, songDifficulty)) + local inst = love.audio.newSource(string.format("songs/%s/Inst.ogg", chart.song), "stream") + local voices = love.audio.newSource(string.format("songs/%s/Voices.ogg", chart.song), "stream") - if not chartString then - error("Chart couldn't be loaded!") - end - local chart = json.parse(chartString).song + local miss = love.audio.newSource("sounds/missnote1.ogg", "static") - local inst = love.audio.newSource(string.format("songs/%s/Inst.ogg", chart.song), "stream") - local voices = love.audio.newSource(string.format("songs/%s/Voices.ogg", chart.song), "stream") - local miss = love.audio.newSource("sounds/missnote1.ogg", "static") + conductor:setBpm(chart.bpm) + conductor:mapBpmChanges(chart) - conductor:setBpm(chart.bpm) - conductor:mapBpmChanges(chart) + local step = 0 + local beat = 0 - local step = 0 - local beat = 0 + local zoom = 1 - local playing = false + local playing = false - local characters = {} - local stage = json.parse(files.read_file("stages/stage.json")) - local unspawnedNotes = {} - local notes = {} + local stage = json.parse(files.read_file("stages/stage.json")) - local directions = { - "LEFT", - "DOWN", - "UP", - "RIGHT" - } + local unspawnedNotes = {} + local notes = {} - local pressed = { - false, - false, - false, - false - } - local holded = { - false, - false, - false, - false - } + local characters = {} - local receptors = { - } + local directions = { + "LEFT", + "DOWN", + "UP", + "RIGHT" + } - local keyBinds = {} + local pressed = { + false, + false, + false, + false, + false + } + local holded = { + false, + false, + false, + false + } - local function quit() - inst:stop() - inst:release() + local receptors = {} - voices:stop() - voices:release() + local keyBinds = {} -- loaded from settings.json, if anything's wrong then check your settings.json - miss:stop() - miss:release() + local paused = false - state.quit() - end + local pauseTime = 0 -- the global amount of time the song has been paused + local pauseStart = 0 -- the start of the latest pause (for pauseTime calculation) - local zoom = 1 + local function quit() + playing = false - local function checkNote(dir) - if pressed[dir] or holded[dir] then - pressed[dir] = false - holded[dir] = true - if receptors[dir].animation ~= string.lower(directions[dir]).." confirm" then - receptors[dir]:PlayAnimation(string.lower(directions[dir]).." press", 25, false) - end - return -- You dont check if you are already holding and not just pressing - end - pressed[dir] = true - for index, note in next, notes do - if note.position - conductor.songPosition < 200 then - if note.mustPress and not note.pressed and note.direction == dir then - characters.bf:PlayAnimation("sing"..directions[note.direction]) - note.pressed = true - receptors[dir]:PlayAnimation(string.lower(directions[dir]).." confirm", 25, false) - table.remove(notes, index) - note:destroy() - end - end - end - end + inst:stop() + inst:release() + inst = nil - local elapsed = 0 + voices:stop() + voices:release() + voices = nil - function state.update(dt) - if not playing then return end + miss:stop() + miss:release() - local currentTime = socket.gettime() + state.quit() + end - elapsed = (currentTime - startTime) * 1000 - - conductor.songPosition = elapsed - - local oldStep = step - local oldBeat = beat - - step = conductor:getStepRounded(elapsed) - beat = conductor:getBeatRounded(elapsed) - - if beat ~= oldBeat then - if beat % 2 == 0 then - -- gf:PlayAnimation("BF NOTE LEFT", 30, false) - for name, character in next, characters do - if not character.singing then - if name == "gf" then - if beat % character.beats == 0 then - character:PlayAnimation("danceLeft") - end - else - character:PlayAnimation("idle") - end - end - end - if beat % 4 == 0 then - zoom = zoom + .1 + local function checkNote(dir) + if pressed[dir] or holded[dir] then + pressed[dir] = false + holded[dir] = true + if receptors[dir].animation ~= string.lower(directions[dir]).." confirm" then + receptors[dir]:PlayAnimation(string.lower(directions[dir]).." press", 25, false) + end + return -- You dont check if you are already holding and not just pressing + end + pressed[dir] = true + for index, note in next, notes do + if note.position - conductor.songPosition < 200 then + if note.mustPress and not note.pressed and note.direction == dir then + characters.bf:PlayAnimation("sing"..directions[note.direction]) + note.pressed = true + receptors[dir]:PlayAnimation(string.lower(directions[dir]).." confirm", 25, false) + table.remove(notes, index) + note:destroy() + end end end - end - - local section = chart.notes[math.floor(step / 16) + 1] - - if section.mustHitSection then - myTypes.cameraTarget = myTypes.Vector2(-stage.camera_boyfriend[1], -stage.camera_boyfriend[2]):Add(characters.bf.stageCamera:Negate()):Add(myTypes.Vector2(0, -200)) - else - myTypes.cameraTarget = myTypes.Vector2(stage.camera_opponent[1], stage.camera_opponent[2]):Add(characters.dad.stageCamera:Negate()):Add(myTypes.Vector2(0, -200)) - end - - myTypes.updateSprites(dt) - - if love.keyboard.isDown(keyBinds[1]) then - checkNote(1) - else - holded[1] = false - pressed[1] = false - end - if love.keyboard.isDown(keyBinds[2]) then - checkNote(2) - else - holded[2] = false - pressed[2] = false - end - if love.keyboard.isDown(keyBinds[3]) then - checkNote(3) - else - holded[3] = false - pressed[3] = false - end - if love.keyboard.isDown(keyBinds[4]) then - checkNote(4) - else - holded[4] = false - pressed[4] = false - end - - if love.keyboard.isDown("escape") then - state.quit() - end - - for index, note in next, notes do - if note.position - conductor.songPosition < 0 then - if note.mustPress then - note:destroy() - miss:stop() - miss:play() - characters.bf:PlayAnimation("sing"..directions[note.direction].."miss") - table.remove(notes, index) - end - end - end - - for name, character in next, characters do - if name ~= "gf" and character.sprite.animation ~= "idle" and character.sprite.ended then - character:PlayAnimation("idle") - end - end - - for index, note in next, unspawnedNotes do - if note.position - elapsed < 600 and note.mustPress then - note:spawn() - table.remove(unspawnedNotes, index) - table.insert(notes, note) - elseif note.position - elapsed < 600 and not note.mustPress then - table.remove(unspawnedNotes, index) - table.insert(notes, note) - end - end - - for index, note in next, notes do - if note.spawned then - note.sprite.position = myTypes.Vector2(400 + 65 * (note.direction - 1), note.position - elapsed - 100) - if note.position - elapsed < -150 then - note:destroy() - table.remove(notes, index) - end - else - if note.position - elapsed < 50 then - table.remove(notes, index) - if section.altAnim or note.altAnim then - characters.dad:PlayAnimation("sing"..directions[note.direction].."-alt") - else - characters.dad:PlayAnimation("sing"..directions[note.direction]) - end - note = nil - end - end - end - - for index, receptor in next, receptors do - if receptor.ended and receptor.animation ~= "arrow"..directions[index] and receptor.animation ~= "" then - receptor:PlayAnimation("arrow"..directions[index], 25, false) - end - end - - if not inst:isPlaying() then - state.quit() - end - - zoom = myMath.lerp(zoom, 1, .05) - end - - local mainCanvas = love.graphics.newCanvas(1920, 1080) - - function state.draw() - - love.graphics.setCanvas(mainCanvas) - - love.graphics.clear() - - love.graphics.circle("fill", 960, 59, 50000) - - myTypes.drawSprites() - - love.graphics.setCanvas() - - love.graphics.draw(mainCanvas, (love.graphics:getHeight() * (4/3) - (love.graphics:getHeight()* (4/3)) * zoom) / 2, (love.graphics:getHeight()- love.graphics:getHeight() * zoom) / 2, 0, ((love.graphics:getHeight() / 1080) * (4/3)) * zoom, (love.graphics:getHeight() / 1080) * zoom) - - end - - love.window.setMode(1280, 720, { fullscreen = false , resizable = true}) - - function state.load() - -- GF first so she is below other chars - if chart.gfVersion ~= "none" then - characters.gf = myTypes.character(chart.gfVersion) - characters.gf.stagePosition = myTypes.Vector2(stage.girlfriend[1], stage.girlfriend[2]) end - characters.bf = myTypes.character(chart.player1) - characters.bf.stagePosition = myTypes.Vector2(stage.boyfriend[1], stage.boyfriend[2]) + local elapsed = 0 - characters.dad = myTypes.character(chart.player2) - characters.dad.stagePosition = myTypes.Vector2(stage.opponent[1], stage.opponent[2]) + function state.update(dt) + if not playing then return end + -- playing isn't supposed to work like "paused", it's there to keep the game from working during loading - - for index, section in next, chart.notes do - for index, note in next, section.sectionNotes do - local newNote = myTypes.note(note, section.mustHitSection) - table.insert(unspawnedNotes, newNote) + if love.keyboard.isDown(keyBinds[1]) then + checkNote(1) + else + holded[1] = false + pressed[1] = false end + if love.keyboard.isDown(keyBinds[2]) then + checkNote(2) + else + holded[2] = false + pressed[2] = false + end + if love.keyboard.isDown(keyBinds[3]) then + checkNote(3) + else + holded[3] = false + pressed[3] = false + end + if love.keyboard.isDown(keyBinds[4]) then + checkNote(4) + else + holded[4] = false + pressed[4] = false + end + + if love.keyboard.isDown("escape") then + quit() + end + + if love.keyboard.isDown("space") then + if pressed[5] then goto evilContinue end + pressed[5] = true + paused = not paused + + if paused then + inst:pause() + voices:pause() + pauseStart = socket.gettime() * 1000 + else + inst:play() + voices:play() + pauseTime = pauseTime + (socket.gettime() * 1000 - pauseStart) + end + ::evilContinue:: + else + pressed[5] = false + end + + if paused then goto continue end -- if paused then skip this cycle + + local currentTime = socket.gettime() + + elapsed = (currentTime - startTime) * 1000 - pauseTime + + conductor.songPosition = elapsed + + local oldStep = step + local oldBeat = beat + + step = conductor:getStepRounded(elapsed) + beat = conductor:getBeatRounded(elapsed) + + if beat ~= oldBeat then + if beat % 2 == 0 then + -- gf:PlayAnimation("BF NOTE LEFT", 30, false) + for name, character in next, characters do + if not character.singing then + if name == "gf" then + if beat % character.beats == 0 then + character:PlayAnimation("danceLeft") + end + else + character:PlayAnimation("idle") + end + end + end + if beat % 4 == 0 then + zoom = zoom + .1 + end + end + end + + local section = chart.notes[math.floor(step / 16) + 1] + + if section.mustHitSection then + myTypes.cameraTarget = myTypes.Vector2(-stage.camera_boyfriend[1], -stage.camera_boyfriend[2]):Add(characters.bf.stageCamera:Negate()):Add(myTypes.Vector2(0, -200)) + else + myTypes.cameraTarget = myTypes.Vector2(stage.camera_opponent[1], stage.camera_opponent[2]):Add(characters.dad.stageCamera:Negate()):Add(myTypes.Vector2(0, -200)) + end + + myTypes.updateSprites(dt) + + for index, note in next, notes do + if note.position - conductor.songPosition < 0 then + if note.mustPress then + note:destroy() + miss:stop() + miss:play() + characters.bf:PlayAnimation("sing"..directions[note.direction].."miss") + table.remove(notes, index) + end + end + end + + for name, character in next, characters do + if name ~= "gf" and character.sprite.animation ~= "idle" and character.sprite.ended then + character:PlayAnimation("idle") + end + end + + for index, note in next, unspawnedNotes do + if note.position - elapsed < 600 and note.mustPress then + note:spawn() + table.remove(unspawnedNotes, index) + table.insert(notes, note) + elseif note.position - elapsed < 600 and not note.mustPress then + table.remove(unspawnedNotes, index) + table.insert(notes, note) + end + end + + for index, note in next, notes do + if note.spawned then + note.sprite.position = myTypes.Vector2(400 + 65 * (note.direction - 1), note.position - elapsed - 100) + if note.position - elapsed < -150 then + note:destroy() + table.remove(notes, index) + end + else + if note.position - elapsed < 50 then + table.remove(notes, index) + if section.altAnim or note.altAnim then + characters.dad:PlayAnimation("sing"..directions[note.direction].."-alt") + else + characters.dad:PlayAnimation("sing"..directions[note.direction]) + end + note = nil + end + end + end + + for index, receptor in next, receptors do + if receptor.ended and receptor.animation ~= "arrow"..directions[index] and receptor.animation ~= "" then + receptor:PlayAnimation("arrow"..directions[index], 25, false) + end + end + + if inst and not inst:isPlaying() then + quit() + end + + zoom = myMath.lerp(zoom, 1, .05) + + ::continue:: end - for i = 0, 3 do - local receptor = myTypes.Sprite("sprites/NOTE_assets.png", "sprites/NOTE_assets.json", false) - receptor:PlayAnimation("arrow"..directions[i+1], 25, false) + local mainCanvas = love.graphics.newCanvas(1920, 1080) - receptor.position = myTypes.Vector2(400 + (65 * i), 0) + function state.draw() - receptor.ui = true -- So it doesnt move with the camera. + love.graphics.setCanvas(mainCanvas) - receptors[i + 1] = receptor - end - state.loaded = true + love.graphics.clear() - myTypes.cameraTarget = myTypes.Vector2() + love.graphics.circle("fill", 960, 59, 50000) + + myTypes.drawSprites() + + love.graphics.setCanvas() + + love.graphics.draw(mainCanvas, (love.graphics:getHeight() * (4/3) - (love.graphics:getHeight()* (4/3)) * zoom) / 2, (love.graphics:getHeight()- love.graphics:getHeight() * zoom) / 2, 0, ((love.graphics:getHeight() / 1080) * (4/3)) * zoom, (love.graphics:getHeight() / 1080) * zoom) - local settings = json.parse(files.read_file("settings.json")) - if not settings then - error("Failed to load settings") end - keyBinds = settings.Keybinds + love.window.setMode(1280, 720, { fullscreen = false , resizable = true}) - end + function state.load() + -- GF first so she is below other chars + if chart.gfVersion ~= "none" then + characters.gf = myTypes.character(chart.gfVersion) + characters.gf.stagePosition = myTypes.Vector2(stage.girlfriend[1], stage.girlfriend[2]) + end - function state.finish() - inst:play() - voices:play() + characters.bf = myTypes.character(chart.player1) + characters.bf.stagePosition = myTypes.Vector2(stage.boyfriend[1], stage.boyfriend[2]) - while not inst:isPlaying() do - end --waiting till the song actually plays. + characters.dad = myTypes.character(chart.player2) + characters.dad.stagePosition = myTypes.Vector2(stage.opponent[1], stage.opponent[2]) - elapsed = 0 - playing = true + for index, section in next, chart.notes do + for index, note in next, section.sectionNotes do + local newNote = myTypes.note(note, section.mustHitSection) + table.insert(unspawnedNotes, newNote) + end + end - startTime = socket.gettime() - end + for i = 0, 3 do + local receptor = myTypes.Sprite("sprites/NOTE_assets.png", "sprites/NOTE_assets.json") + receptor:PlayAnimation("arrow"..directions[i+1], 25, false) - return state + receptor.position = myTypes.Vector2(400 + (65 * i), 0) + + receptor.ui = true -- So it doesnt move with the camera. + + receptors[i + 1] = receptor + end + state.loaded = true + + myTypes.cameraTarget = myTypes.Vector2() + + local settings = json.parse(files.read_file("settings.json")) + if not settings then + error("Failed to load settings") + end + + keyBinds = settings.Keybinds + + end + + function state.finish() + inst:play() + voices:play() + + while not inst:isPlaying() do + end --waiting till the song actually plays. + + elapsed = 0 + + playing = true + + startTime = socket.gettime() + end + + return state end