local love = love local step = 1.0 / 120 local sitelenpona local text local marble local wave local particles local audio local recorder local DetectCollision local OnImpact local OnVictory local BeatScore local Draw local ExtrapolateBeatScore local _ExtrapolateBeatScore local state state = { Reset = function() state.beat = {} state.startTime = love.timer.getTime() state.currentBeat = 1 state.timeToSimulate = 0.0 end, finishTime = 0.0, timeToSimulate = 0.0, isGameStarted = false, lastBeatScore = 0.0, currentBeat = 1, startTime = 0.0, beat = { t = nil, mu = nil, }, } local function UpdateWindowTransform( w, h ) local d = math.min( w, h ) local r = love.graphics.get local tf = love.math.newTransform() local size = 3 tf:translate( w / 2, h / 2) tf:scale( d / size, -d / size ) transform = tf end OnImpact = function( impact ) if not impact then return end local score = BeatScore( impact.t ) --DEBUG state.lastBeatScore = score local pass = false if score > 1.0 then pass = true state.currentBeat = state.currentBeat + 1 if state.currentBeat >= 120 then return OnVictory() end end local x, y = math.cos(impact.th), math.sin(impact.th) particles:setPosition( impact.r * x, impact.r * y ) particles:setEmissionArea( "normal", 0.1, 0.2, impact.th, true ) particles:emit( 50 * score * score ) audio.OnImpact( impact, score, pass, state.currentBeat ) marble.OnImpact( impact ) wave.OnImpact( impact, state.currentBeat ) end local _OnImpact = OnImpact local function NewGame() love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink. OnImpact = _OnImpact ExtrapolateBeatScore = _ExtrapolateBeatScore love.draw = Draw particles:reset() state.Reset() marble.Reset() wave.Reset() text.Reset() end function love.load() UpdateWindowTransform( love.graphics.getDimensions() ) do--particle system setup particles = love.graphics.newParticleSystem( love.graphics.newImage( "prideflag.png" ), 1024) particles:setSizes( 0.00015, 0.0001, 0.00018 ) particles:setSizeVariation( 1 ) particles:setRadialAcceleration( 0, 0.5 ) particles:setSpeed( 0.2, 1 ) particles:setLinearDamping( 1, 10 ) particles:setRelativeRotation( true ) particles:setEmitterLifetime( -1 ) particles:setParticleLifetime( 0, 15 ) particles:setSpread( 0.05 ) particles:setColors( 1, 1, 1, 1, 91 / 255, 206 / 255, 250 / 255, 1, 245 / 255, 169 / 255, 184 / 255, 1, 1,1,1,0 ) end sitelenpona = assert( require "sitelenpona" ) text = assert( require "text" ) marble = assert( require "marble" ) wave = assert( require "wave" ) DetectCollision = assert ( require "collision" ) audio = assert( require "audio" ) recorder = assert( require "recorder" ) return NewGame() end ExtrapolateBeatScore = function( ) local t = love.timer.getTime() local beat = state.beat if not beat.t then return 2.0 end if not beat.mu then return 2.0 end if beat.mu < 0.001 then return 2.0 end t = 1.1 * math.sin( 0.5 * math.pi * ( t - beat.t ) / beat.mu ) return t * t * t * t end _ExtrapolateBeatScore = ExtrapolateBeatScore BeatScore = function( t ) local beat = state.beat --Base case 1: first tap. if not beat.t then beat.t = t return 2.0 end local dt = t - beat.t beat.t = t --Base case 2: second tap. if not beat.mu then beat.mu = dt return 2.0 end local score = 1.1 * math.sin( 0.5 * math.pi * dt / beat.mu ) score = score * score * score * score --General case: update average beat length. --Debounce: max BPM 200 if dt > 60.0 / 200.0 then local WEIGHT = 0.75 --High number makes the last beat more significant. local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt beat.mu = mu else score = 0 end return score end OnVictory = function() local totalTime = love.timer.getTime() - state.startTime OnImpact = function() end love.draw = function() love.graphics.setColor( 1, 1, 1, 1 ) text.Draw( 120 ) love.graphics.printf( "your.time:\n"..totalTime, 0, 0.5 * love.graphics.getHeight(), love.graphics.getWidth(), "center" ) marble.Draw() end marble.OnVictory() love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue. end Draw = function() local score = ExtrapolateBeatScore() love.graphics.push( "transform" ) love.graphics.applyTransform( transform ) wave.Draw( score ) love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 ) love.graphics.draw(particles, 0, 0) --[[if debugRenderImpact then love.graphics.setLineWidth( 0.01 ) love.graphics.setColor( 1, 0, 0, 0.5 ) --Red: Incoming love.graphics.line( debugRenderImpact.xi, debugRenderImpact.yi, debugRenderImpact.xf, debugRenderImpact.yf) love.graphics.setColor( 0, 1, 0, 0.5 ) --Green: Normal love.graphics.line( debugRenderImpact.xi, debugRenderImpact.yi, debugRenderImpact.xn, debugRenderImpact.yn) love.graphics.setColor( 0, 0, 1, 0.5 ) -- Blue: Outgoing love.graphics.line( debugRenderImpact.xi, debugRenderImpact.yi, debugRenderImpact.vxout, debugRenderImpact.vyout) end]] love.graphics.pop() sitelenpona.Draw( text.words[state.currentBeat] ) marble.Draw() text.Draw( state.currentBeat ) end function love.update( dt ) audio.Update( ExtrapolateBeatScore(), state.currentBeat ) particles:update( dt ) dt = dt + state.timeToSimulate --Physics tick. while dt > step do recorder.Update( marble.GetAcceleration() ) --For savegames. marble.Integrate( step ) wave.Integrate( step ) OnImpact( DetectCollision( marble.Current(), marble.Next(), wave.Current(), wave.Next() )) marble.Update() wave.Update() dt = dt - step end state.timeToSimulate = dt end function love.keypressed( key, code, isRepeat ) if key == "escape" then return love.event.quit() end if key == "return" then return OnVictory() end if key == "space" then return NewGame() end return marble.OnKey() end function love.keyreleased( key, code ) return marble.OnKey() end function love.resize( w, h ) UpdateWindowTransform( w, h ) if marble then marble.Resize() end end function love.mousepressed( x, y, button, istouch, presses ) return NewGame() end