your-own-drum/main.lua

375 lines
8.5 KiB
Lua

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 Update
local ExtrapolateBeatScore
local _ExtrapolateBeatScore
local state
state = {
Reset = function()
state.tick = 0
state.beat = {}
state.currentBeat = 1
state.timeToSimulate = 0.0
state.longestStreak = 0
state.currentStreak = 0
state.lastCollisionTick = 0
end,
finishTime = 0.0,
timeToSimulate = 0.0,
isGameStarted = false,
lastBeatScore = 0.0,
currentBeat = 1,
longestStreak = 0,
currentStreak = 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
state.lastCollisionTick = state.tick
local pass = false
if score > 1.0 then
pass = true
state.currentStreak = state.currentStreak + 1
if state.currentStreak > state.longestStreak then
state.longestStreak = state.currentStreak
end
state.currentBeat = state.currentBeat + 1
if state.currentBeat >= 119 then
return OnVictory()
end
else
state.currentStreak = 0
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, state.currentBeat )
wave.OnImpact( impact, state.currentBeat )
end
local _OnImpact = OnImpact
local function NewGame( demoName )
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
OnImpact = _OnImpact
ExtrapolateBeatScore = _ExtrapolateBeatScore
love.draw = Draw
love.update = Update
do --particle shit
particles:reset()
particles:setSizes( 0.0007, 0.0001, 0.0003 )
--particles:setSizes( 0.0007, 0.0001, 0.0003 )
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, 5 )
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,
245 / 255, 169 / 255, 184 / 255, 1,
1,1,1,0,
245 / 255, 169 / 255, 184 / 255, 1,
1,1,1,0)
end
state.Reset()
marble.Reset()
wave.Reset()
text.Reset()
audio.Reset()
recorder.Reset()
state.isDemo = demoName
if demoName then
recorder.Load( demoName )
end
end
function love.load()
UpdateWindowTransform( love.graphics.getDimensions() )
do--particle system setup
particles = love.graphics.newParticleSystem(
love.graphics.newImage( "prideflag.png" ),
2048)
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 = state.tick / 120.0
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
local pow = 1 / math.max( beat.mu, 0.2 )
t = 1.05 * math.sin( 0.5 * math.pi * ( t - beat.t ) / beat.mu )
return math.pow( t * t, pow )
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 pow = 1 / math.max( beat.mu, 0.1 )
local score = 1.1 * math.sin( 0.5 * math.pi * dt / beat.mu )
score = math.pow( score * score, pow )
--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()
if state.isDemo then
state.isDemo = false
else
recorder.Save()
end
particles:setParticleLifetime( 0, 30 )
particles:setSizes( 0.001, 0.0001, 0.0005 )
particles:setColors(
245 / 255, 169 / 255, 184 / 255, 1,
1, 1, 1, 1,
245 / 255, 169 / 255, 184 / 255, 1,
1, 1, 1, 0,
245 / 255, 169 / 255, 184 / 255, 1,
1, 1, 1, 0
)
particles:setPosition( 0, 0 )
particles:setEmissionArea( "normal", 0.5, 0.5, 0, true )
particles:emit( 500 )
particles:setEmissionArea( "normal", 0.01, 0.01, 0, true )
love.graphics.setCanvas( marble.Canvas() )
love.graphics.setCanvas()
local totalTime = state.tick / 120.0
OnImpact = function() end
love.update = function( dt )
while dt > step do
marble.Integrate( step )
wave.Integrate( step )
marble.Update()
wave.Update()
dt = dt - step
end
local marblePos = marble.Current()
particles:emit( 1 )
particles:update( dt )
particles:moveTo( marblePos.x, marblePos.y )
end
love.draw = function()
love.graphics.push( "transform" )
love.graphics.applyTransform( transform )
love.graphics.setColor( 1, 1, 1, 1 )
love.graphics.draw( particles )
love.graphics.pop()
text.Draw( 119 )
local score = state.longestStreak * state.longestStreak / totalTime
love.graphics.setColor( 1, 1, 1, 1 )
love.graphics.printf(
string.format( "time:\t%.2f\nstreak:\t%d\nscore:%.2f", totalTime, state.longestStreak, score):gsub( "%.", "," ),
0, 0.5 * love.graphics.getHeight() - 2.0* love.graphics.getFont():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 )
love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 )
love.graphics.draw(particles, 0, 0)
wave.Draw( score )
love.graphics.pop()
sitelenpona.Draw( text.words[state.currentBeat] )
marble.Draw()
text.Draw( state.currentBeat )
love.graphics.print( state.tick, 400, 400, 0, 1, 1 )
love.graphics.print( state.currentStreak, 400, 450, 0, 1, 1 )
love.graphics.print( state.lastCollisionTick, 400, 500, 0, 1, 1 )
end
Update = function( dt )
audio.Update( ExtrapolateBeatScore(), state.currentBeat )
particles:update( dt )
dt = dt + state.timeToSimulate
--Physics tick.
while dt > step do
state.tick = state.tick + 1
if state.isDemo then
local ddx, ddy = recorder.NextTick()
if ddx then marble.SetAcceleration( ddx, ddy )
else state.isDemo = false end
else
recorder.Update( marble.GetAcceleration() ) --For savegames.
end
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
_Update = Update
local function LoadGame() -- Load game screen.
local loadGame = assert( dofile "loadgame.lua" )
love.mousepressed = function( x, y, button, istouch, presses)
local demoName = loadGame( x, y, button, istouch, presses )
if demoName then return NewGame( demoName ) end
end
end
function love.keypressed( key, code, isRepeat )
if key == "escape" then return love.event.quit() end
if key == "space" then return NewGame() end
if code == "return" then return LoadGame() end --Play demo.
if state.isDemo then return end
return marble.OnKey( state.tick )
end
function love.keyreleased( key, code )
return marble.OnKey( state.tick )
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