BIG big commit, tweak physics parameters. Fix beat detection bug.
Scale down particle sprite. Fix sitelen pona rendering, fix text rendering. Invert white border feedback, more intuitive semantics. &c. &c.
|
@ -1,2 +1,3 @@
|
||||||
build/
|
build/
|
||||||
|
screenshots/
|
||||||
cover.png
|
cover.png
|
41
audio.lua
|
@ -4,20 +4,26 @@ local failChimes = {}
|
||||||
local drones = {}
|
local drones = {}
|
||||||
local N = 8
|
local N = 8
|
||||||
|
|
||||||
|
love.audio.setDistanceModel( "exponentclamped" )
|
||||||
|
|
||||||
local function StartDrones()
|
local function StartDrones()
|
||||||
love.audio.setEffect( "droneReverb", {
|
love.audio.setEffect( "droneReverb", {
|
||||||
type = "reverb",
|
type = "reverb",
|
||||||
volume = 1.0, }
|
volume = 1.0,
|
||||||
|
decaytime = 20}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
drones.bass = love.audio.newSource("sounds/drone.flac", "stream")
|
drones.bass = love.audio.newSource("sounds/drone.flac", "stream")
|
||||||
drones.alto = love.audio.newSource("sounds/drone.flac", "stream")
|
drones.alto = love.audio.newSource("sounds/drone.flac", "stream")
|
||||||
drones.bass:setVolume( 0 )
|
drones.bass:setVolume( 0 )
|
||||||
drones.bass:setPitch( 0.25 )
|
drones.bass:setPitch( 0.125 )
|
||||||
drones.alto:setVolume( 0 )
|
drones.alto:setVolume( 0 )
|
||||||
drones.bass:setPitch( 0.5 )
|
drones.bass:setPitch( 0.25 )
|
||||||
drones.bass:setEffect( "droneReverb", true )
|
drones.bass:setEffect( "droneReverb", true )
|
||||||
|
drones.alto:setEffect( "droneReverb", true )
|
||||||
|
drones.bass:stop()
|
||||||
|
drones.alto:stop()
|
||||||
|
|
||||||
love.audio.play( drones.bass )
|
love.audio.play( drones.bass )
|
||||||
love.audio.play( drones.alto )
|
love.audio.play( drones.alto )
|
||||||
|
@ -72,6 +78,13 @@ local function OnImpact( impact, score, pass, level )
|
||||||
if not pass then
|
if not pass then
|
||||||
--drones.bass:seek( level )
|
--drones.bass:seek( level )
|
||||||
-- drones.alto:seek( level )
|
-- drones.alto:seek( level )
|
||||||
|
else
|
||||||
|
|
||||||
|
love.audio.setEffect( "passChimeReverb", {
|
||||||
|
type = "reverb",
|
||||||
|
volume = 1.0,
|
||||||
|
gain = 0.1 + level / 200} )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local th = impact.th
|
local th = impact.th
|
||||||
|
@ -81,6 +94,7 @@ local function OnImpact( impact, score, pass, level )
|
||||||
local chime = pass and GetNextPassChime() or GetNextFailChime()
|
local chime = pass and GetNextPassChime() or GetNextFailChime()
|
||||||
chime:setPitch( GetPitch( th ) )
|
chime:setPitch( GetPitch( th ) )
|
||||||
chime:setPosition( r * math.cos( th ), r * math.sin( th ) )
|
chime:setPosition( r * math.cos( th ), r * math.sin( th ) )
|
||||||
|
chime:setVolume( 0.1 + level / 200 )
|
||||||
local highgain = math.max( 0, math.min( score, 1.0 ) - (pass and 0.0 or 0.5))
|
local highgain = math.max( 0, math.min( score, 1.0 ) - (pass and 0.0 or 0.5))
|
||||||
chime:setFilter{
|
chime:setFilter{
|
||||||
type = "lowpass",
|
type = "lowpass",
|
||||||
|
@ -92,11 +106,22 @@ local function OnImpact( impact, score, pass, level )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function OnVictory( )
|
||||||
|
love.audio.setEffect( "passChimeReverb", {
|
||||||
|
type = "reverb",
|
||||||
|
volume = 1.0,
|
||||||
|
decaytime = 20,} )
|
||||||
|
end
|
||||||
|
|
||||||
--score is the hypothetical "beat score" that would be attained
|
--score is the hypothetical "beat score" that would be attained
|
||||||
--if an impact took place at this instant.
|
--if an impact took place at this instant.
|
||||||
local function Update( score )
|
local function Update( score, level )
|
||||||
drones.bass:setVolume( 0.5 * math.sqrt( score ) )
|
|
||||||
--drones.alto:setVolume( 0.3 * score )
|
if level > 2 then
|
||||||
|
drones.bass:setVolume( 0.5 * math.sqrt( score ) )
|
||||||
|
drones.alto:setVolume( 0.3 * score )
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Reset( )
|
local function Reset( )
|
||||||
|
@ -108,4 +133,4 @@ local function Reset( )
|
||||||
end
|
end
|
||||||
|
|
||||||
Reset()
|
Reset()
|
||||||
return { OnImpact = OnImpact, Update = Update, Reset = Reset}
|
return { OnImpact = OnImpact, Update = Update, Reset = Reset, OnVictory = OnVictory}
|
146
main.lua
|
@ -10,7 +10,10 @@ local recorder
|
||||||
local DetectCollision
|
local DetectCollision
|
||||||
local OnImpact
|
local OnImpact
|
||||||
local OnVictory
|
local OnVictory
|
||||||
|
local BeatScore
|
||||||
|
local Draw
|
||||||
|
local ExtrapolateBeatScore
|
||||||
|
local _ExtrapolateBeatScore
|
||||||
|
|
||||||
local state
|
local state
|
||||||
state = {
|
state = {
|
||||||
|
@ -22,9 +25,9 @@ state = {
|
||||||
state.timeToSimulate = 0.0
|
state.timeToSimulate = 0.0
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
finishTime = 0.0,
|
||||||
timeToSimulate = 0.0,
|
timeToSimulate = 0.0,
|
||||||
isGameStarted = false,
|
isGameStarted = false,
|
||||||
beatScoreThreshold = 1.0,
|
|
||||||
lastBeatScore = 0.0,
|
lastBeatScore = 0.0,
|
||||||
currentBeat = 1,
|
currentBeat = 1,
|
||||||
startTime = 0.0,
|
startTime = 0.0,
|
||||||
|
@ -47,12 +50,51 @@ local function UpdateWindowTransform( w, h )
|
||||||
transform = tf
|
transform = tf
|
||||||
end
|
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()
|
function love.load()
|
||||||
UpdateWindowTransform( love.graphics.getDimensions() )
|
UpdateWindowTransform( love.graphics.getDimensions() )
|
||||||
|
|
||||||
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
|
|
||||||
--love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.
|
|
||||||
|
|
||||||
do--particle system setup
|
do--particle system setup
|
||||||
particles = love.graphics.newParticleSystem(
|
particles = love.graphics.newParticleSystem(
|
||||||
love.graphics.newImage( "prideflag.png" ),
|
love.graphics.newImage( "prideflag.png" ),
|
||||||
|
@ -84,22 +126,23 @@ function love.load()
|
||||||
DetectCollision = assert ( require "collision" )
|
DetectCollision = assert ( require "collision" )
|
||||||
audio = assert( require "audio" )
|
audio = assert( require "audio" )
|
||||||
recorder = assert( require "recorder" )
|
recorder = assert( require "recorder" )
|
||||||
return state.Reset()
|
return NewGame()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ExtrapolateBeatScore( )
|
ExtrapolateBeatScore = function( )
|
||||||
local t = love.timer.getTime()
|
local t = love.timer.getTime()
|
||||||
local beat = state.beat
|
local beat = state.beat
|
||||||
if not beat.t then return 2.0 end
|
if not beat.t then return 2.0 end
|
||||||
if not beat.mu then return 2.0 end
|
if not beat.mu then return 2.0 end
|
||||||
if beat.mu < 0.001 then return 2.0 end
|
if beat.mu < 0.001 then return 2.0 end
|
||||||
|
|
||||||
t = 1.1 * math.sin( math.pi * ( t - beat.t ) / beat.mu )
|
t = 1.1 * math.sin( 0.5 * math.pi * ( t - beat.t ) / beat.mu )
|
||||||
return t * t * t * t
|
return t * t * t * t
|
||||||
end
|
end
|
||||||
|
_ExtrapolateBeatScore = ExtrapolateBeatScore
|
||||||
|
|
||||||
local function BeatScore( t )
|
BeatScore = function( t )
|
||||||
local beat = state.beat
|
local beat = state.beat
|
||||||
|
|
||||||
--Base case 1: first tap.
|
--Base case 1: first tap.
|
||||||
|
@ -117,73 +160,51 @@ local function BeatScore( t )
|
||||||
return 2.0
|
return 2.0
|
||||||
end
|
end
|
||||||
|
|
||||||
local score = 1.1 * math.sin( math.pi * dt / beat.mu )
|
local score = 1.1 * math.sin( 0.5 * math.pi * dt / beat.mu )
|
||||||
score = score * score * score * score
|
score = score * score * score * score
|
||||||
|
|
||||||
--General case: update average beat length.
|
--General case: update average beat length.
|
||||||
if dt > 0.2 then
|
|
||||||
|
--Debounce: max BPM 200
|
||||||
|
if dt > 60.0 / 200.0 then
|
||||||
|
|
||||||
local WEIGHT = 0.85 --High number makes the last beat more significant.
|
local WEIGHT = 0.75 --High number makes the last beat more significant.
|
||||||
local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt
|
local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt
|
||||||
beat.mu = mu
|
beat.mu = mu
|
||||||
|
|
||||||
|
else
|
||||||
|
score = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
return score
|
return score
|
||||||
end
|
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 )
|
|
||||||
end
|
|
||||||
local _OnImpact = OnImpact
|
|
||||||
|
|
||||||
|
|
||||||
local function NewGame()
|
|
||||||
OnImpact = _OnImpact
|
|
||||||
state.Reset()
|
|
||||||
marble.Reset()
|
|
||||||
wave.Reset()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
OnVictory = function()
|
OnVictory = function()
|
||||||
|
|
||||||
|
local totalTime = love.timer.getTime() - state.startTime
|
||||||
OnImpact = function() end
|
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
|
end
|
||||||
|
|
||||||
function love.draw()
|
Draw = function()
|
||||||
|
|
||||||
|
|
||||||
--[[love.graphics.setColor(1.0, 1.0, 1.0)
|
|
||||||
love.graphics.print( state.beat.mu or 0, 0)
|
|
||||||
love.graphics.print( state.beat.t or 0, 0, 10)
|
|
||||||
love.graphics.print( state.beatScoreThreshold, 0, 20)
|
|
||||||
love.graphics.print( state.lastBeatScore, 0, 30 )]]
|
|
||||||
|
|
||||||
local score = ExtrapolateBeatScore()
|
local score = ExtrapolateBeatScore()
|
||||||
|
|
||||||
|
|
||||||
love.graphics.push( "transform" )
|
love.graphics.push( "transform" )
|
||||||
love.graphics.applyTransform( transform )
|
love.graphics.applyTransform( transform )
|
||||||
|
|
||||||
|
@ -219,10 +240,9 @@ function love.draw()
|
||||||
end]]
|
end]]
|
||||||
|
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
|
sitelenpona.Draw( text.words[state.currentBeat] )
|
||||||
sitelenpona.Draw( text.tok[state.currentBeat] )
|
|
||||||
|
|
||||||
marble.Draw()
|
marble.Draw()
|
||||||
|
text.Draw( state.currentBeat )
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -231,7 +251,7 @@ end
|
||||||
|
|
||||||
function love.update( dt )
|
function love.update( dt )
|
||||||
|
|
||||||
audio.Update( ExtrapolateBeatScore() )
|
audio.Update( ExtrapolateBeatScore(), state.currentBeat )
|
||||||
particles:update( dt )
|
particles:update( dt )
|
||||||
dt = dt + state.timeToSimulate
|
dt = dt + state.timeToSimulate
|
||||||
|
|
||||||
|
@ -269,4 +289,8 @@ end
|
||||||
function love.resize( w, h )
|
function love.resize( w, h )
|
||||||
UpdateWindowTransform( w, h )
|
UpdateWindowTransform( w, h )
|
||||||
if marble then marble.Resize() end
|
if marble then marble.Resize() end
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.mousepressed( x, y, button, istouch, presses )
|
||||||
|
return NewGame()
|
||||||
end
|
end
|
84
marble.lua
|
@ -1,5 +1,6 @@
|
||||||
--Character controller. Renders point particle.
|
--Character controller. Renders point particle.
|
||||||
local love = love
|
local love = love
|
||||||
|
local marble = {}
|
||||||
local oldBuffer = love.graphics.newCanvas()
|
local oldBuffer = love.graphics.newCanvas()
|
||||||
local newBuffer = love.graphics.newCanvas()
|
local newBuffer = love.graphics.newCanvas()
|
||||||
local oldState, curState, newState
|
local oldState, curState, newState
|
||||||
|
@ -12,14 +13,14 @@ local function State( )
|
||||||
return { t = 0, x = 0, y = 0, dx = 0, dy = 0 }
|
return { t = 0, x = 0, y = 0, dx = 0, dy = 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
local function GetAcceleration( )
|
|
||||||
return ddx, ddy
|
|
||||||
end
|
|
||||||
|
|
||||||
local function Current() return curState end
|
function marble.Current() return curState end
|
||||||
local function Next() return newState end
|
|
||||||
|
|
||||||
local function Integrate( step )
|
function marble.Next() return newState end
|
||||||
|
|
||||||
|
function marble.GetAcceleration( ) return ddx, ddy end
|
||||||
|
|
||||||
|
function marble.Integrate( step )
|
||||||
newState.t = love.timer.getTime()
|
newState.t = love.timer.getTime()
|
||||||
newState.dx = (1.0 - FRICTION) * curState.dx + FRICTION * ddx
|
newState.dx = (1.0 - FRICTION) * curState.dx + FRICTION * ddx
|
||||||
newState.dy = (1.0 - FRICTION) * curState.dy + FRICTION * ddy
|
newState.dy = (1.0 - FRICTION) * curState.dy + FRICTION * ddy
|
||||||
|
@ -27,7 +28,7 @@ local function Integrate( step )
|
||||||
newState.y = curState.y + newState.dy * step * MAXSPEED
|
newState.y = curState.y + newState.dy * step * MAXSPEED
|
||||||
end
|
end
|
||||||
|
|
||||||
local function OnImpact( impact )
|
function marble.OnImpact( impact )
|
||||||
--Adjust current trajectory according to collision.
|
--Adjust current trajectory according to collision.
|
||||||
if not impact.dt then return end
|
if not impact.dt then return end
|
||||||
|
|
||||||
|
@ -66,14 +67,14 @@ local function OnImpact( impact )
|
||||||
curState.x, curState.y = x, y
|
curState.x, curState.y = x, y
|
||||||
curState.dx, curState.dy = vxout, vyout
|
curState.dx, curState.dy = vxout, vyout
|
||||||
|
|
||||||
debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy,
|
--[[debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy,
|
||||||
xn = 0.2 * unx + x, yn = 0.2 * uny + y,
|
xn = 0.2 * unx + x, yn = 0.2 * uny + y,
|
||||||
vxout = x + vxout, vyout = y + vyout}
|
vxout = x + vxout, vyout = y + vyout}]]
|
||||||
|
|
||||||
return Integrate( math.max( impact.dt , 1 / 120 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake.
|
return marble.Integrate( math.max( impact.dt , 1 / 120 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake.
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Update()
|
function marble.Update()
|
||||||
--Roll the log.
|
--Roll the log.
|
||||||
for k, v in pairs( oldState ) do
|
for k, v in pairs( oldState ) do
|
||||||
oldState[k] = curState[k]
|
oldState[k] = curState[k]
|
||||||
|
@ -83,7 +84,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function OnKey()
|
function marble.OnKey()
|
||||||
ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
|
ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
|
||||||
ddy = (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0)
|
ddy = (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0)
|
||||||
|
|
||||||
|
@ -92,11 +93,35 @@ local function OnKey()
|
||||||
ddx, ddy = ddx / n, ddy / n
|
ddx, ddy = ddx / n, ddy / n
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Draw()
|
function marble.OnVictory()
|
||||||
|
|
||||||
|
marble.Draw = function()
|
||||||
|
|
||||||
|
--Extrapolate forward for slightly smoother rendering.
|
||||||
|
local dt = love.timer.getTime() - curState.t
|
||||||
|
marble.Integrate( dt + 1 / 60.0 )
|
||||||
|
|
||||||
|
local xp, yp = transform:transformPoint( oldState.x, oldState.y )
|
||||||
|
local xc, yc = transform:transformPoint( curState.x, curState.y )
|
||||||
|
local xn, yn = transform:transformPoint( newState.x, newState.y )
|
||||||
|
|
||||||
|
love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255, 1.0 ) --Trans blue.
|
||||||
|
love.graphics.setLineJoin( "bevel" )
|
||||||
|
love.graphics.setLineStyle( "smooth" )
|
||||||
|
love.graphics.setLineWidth( 8.0 )
|
||||||
|
love.graphics.line( xp, yp, xc, yc, xn, yn)
|
||||||
|
love.graphics.circle( "fill", xn, yn, 4.0 )
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function marble.Draw()
|
||||||
|
|
||||||
--Extrapolate forward for slightly smoother rendering.
|
--Extrapolate forward for slightly smoother rendering.
|
||||||
local dt = love.timer.getTime() - curState.t
|
local dt = love.timer.getTime() - curState.t
|
||||||
Integrate( dt + 1 / 60.0 )
|
marble.Integrate( dt + 1 / 60.0 )
|
||||||
|
|
||||||
local xp, yp = transform:transformPoint( oldState.x, oldState.y )
|
local xp, yp = transform:transformPoint( oldState.x, oldState.y )
|
||||||
local xc, yc = transform:transformPoint( curState.x, curState.y )
|
local xc, yc = transform:transformPoint( curState.x, curState.y )
|
||||||
|
@ -130,12 +155,14 @@ local function Draw()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Impact( impact )
|
marble._Draw = marble.Draw
|
||||||
|
|
||||||
|
function marble.Impact( impact )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--Window resize.
|
--Window resize.
|
||||||
local function Resize()
|
function marble.Resize()
|
||||||
newBuffer = love.graphics.newCanvas()
|
newBuffer = love.graphics.newCanvas()
|
||||||
--TODO: render oldBuffer to new newBuffer, but scaled down.
|
--TODO: render oldBuffer to new newBuffer, but scaled down.
|
||||||
oldBuffer = love.graphics.newCanvas()
|
oldBuffer = love.graphics.newCanvas()
|
||||||
|
@ -143,25 +170,18 @@ local function Resize()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Reset()
|
function marble.Reset()
|
||||||
oldState, curState, newState = State(), State(), State()
|
oldState, curState, newState = State(), State(), State()
|
||||||
Resize()
|
marble.Resize()
|
||||||
|
marble.Draw = marble._Draw
|
||||||
|
end
|
||||||
|
|
||||||
|
function marble.Canvas()
|
||||||
|
return newBuffer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reset()
|
marble.Reset()
|
||||||
|
|
||||||
return {
|
return marble
|
||||||
Integrate = Integrate,
|
|
||||||
OnImpact = OnImpact,
|
|
||||||
Update = Update,
|
|
||||||
OnKey = OnKey,
|
|
||||||
Draw = Draw,
|
|
||||||
Impact = Impact,
|
|
||||||
Reset = Reset,
|
|
||||||
Resize = Resize,
|
|
||||||
Current = Current,
|
|
||||||
Next = Next,
|
|
||||||
GetAcceleration = GetAcceleration,
|
|
||||||
}
|
|
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 990 B |
After Width: | Height: | Size: 49 KiB |
|
@ -6,12 +6,32 @@ local info = love.filesystem.getInfo( "sitelenpona" )
|
||||||
for _, filename in ipairs(love.filesystem.getDirectoryItems( "sitelenpona" )) do
|
for _, filename in ipairs(love.filesystem.getDirectoryItems( "sitelenpona" )) do
|
||||||
sp[filename:gsub( ".png", "" )] = love.graphics.newImage( "sitelenpona/"..filename )
|
sp[filename:gsub( ".png", "" )] = love.graphics.newImage( "sitelenpona/"..filename )
|
||||||
end
|
end
|
||||||
--Render one glyph in the center.
|
--Render one or two glyphs in the center.
|
||||||
sp.Draw = function( str )
|
sp.Draw = function( t )
|
||||||
local w, h = love.graphics.getDimensions()
|
local cx, cy = love.graphics.getDimensions()
|
||||||
local x, y = 0.5 * w - 128, 0.5 * h - 128
|
cx, cy = 0.5 * cx, 0.5 * cy
|
||||||
love.graphics.setColor( 1.0, 1.0, 1.0, 0.5 )
|
love.graphics.setColor( 1.0, 1.0, 1.0, 0.5 )
|
||||||
love.graphics.draw( sp[str] or sp.q, x, y )
|
|
||||||
|
if #t == 1 then
|
||||||
|
love.graphics.draw( sp[t[1]] or sp.pilin, cx - 128, cy - 128 )
|
||||||
|
elseif #t == 2 then
|
||||||
|
love.graphics.draw( sp[t[1]] or sp.pilin, cx - 128, cy - 64, 0, 0.5, 0.5 )
|
||||||
|
love.graphics.draw( sp[t[2]] or sp.pilin, cx, cy - 64, 0, 0.5, 0.5 )
|
||||||
|
elseif #t == 3 then
|
||||||
|
love.graphics.draw( sp[t[1]] or sp.pilin, cx - 128, cy - 64, 0, 0.5, 0.5 )
|
||||||
|
love.graphics.draw( sp[t[1]] or sp.pilin, cx - 128, cy - 64, 0, 0.5, 0.5 )
|
||||||
|
love.graphics.draw( sp[t[2]] or sp.pilin, cx, cy - 64, 0, 0.5, 0.5 )
|
||||||
|
end
|
||||||
|
--[[ w in str:gmatch( "%a+") do
|
||||||
|
t[i] = w
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
for _, str in ipairs( t ) do
|
||||||
|
local w, h = love.graphics.getDimensions()
|
||||||
|
local x, y = 0.5 * w - 128, 0.5 * h - 128
|
||||||
|
|
||||||
|
love.graphics.draw( sp[str] or sp.pilin, x, y )
|
||||||
|
end]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 903 B |
79
text.lua
|
@ -1,17 +1,72 @@
|
||||||
--Load poems.
|
--Load poems.
|
||||||
local love = love
|
local love = love
|
||||||
local txt = {}
|
local feet = {}
|
||||||
|
local feetNL = {}
|
||||||
|
local words = {} --Sequence of tables.
|
||||||
|
local poem = {[0] = ""}
|
||||||
|
local poemLines = {[0] = 0}
|
||||||
|
|
||||||
local info = love.filesystem.getInfo( "text" )
|
local mt = { __index = function() return "linja" end }
|
||||||
for _, filename in ipairs(love.filesystem.getDirectoryItems( "text" )) do
|
|
||||||
local s = love.filesystem.read( "text/"..filename )
|
local s = love.filesystem.read( "text/tok.txt" )
|
||||||
local t = {}
|
local smallFont = love.graphics.setNewFont( "text/linja-sike.ttf", 12 )
|
||||||
local i = 1
|
local largeFont = love.graphics.setNewFont( "text/linja-sike.ttf", 32 )
|
||||||
for w in s:gmatch( "%a+") do
|
|
||||||
t[i] = w
|
local i = 1
|
||||||
i = i + 1
|
--Split string into feet.
|
||||||
end
|
for foot in s:gmatch( ".-%-") do
|
||||||
txt[filename:gsub( ".txt", "" )] = t
|
|
||||||
|
feet[i] = foot:gsub( "-", "" ):gsub( " $", "" ):gsub( "%c", "") --Remove hyphens, trailing spaces, newlines.
|
||||||
|
feetNL[i] = foot:gsub( "-", "" ):gsub(" ", "")--:gsub( "%.", " " ) --Remove hyphens, spaces, transform periods to spaces
|
||||||
|
|
||||||
|
|
||||||
|
poem[i] = poem[ i - 1 ]..feetNL[i]
|
||||||
|
poemLines[i] = poemLines[i - 1] + (feetNL[i]:match( "\n") and 1 or 0)
|
||||||
|
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return txt
|
--Split string into words.
|
||||||
|
i = 1
|
||||||
|
local isTe = true
|
||||||
|
for foot in s:gmatch( ".-%-" ) do
|
||||||
|
--Split foot into words.
|
||||||
|
local word = {}
|
||||||
|
local j = 1
|
||||||
|
|
||||||
|
for w in foot:gmatch( [["]] ) do
|
||||||
|
word[j] = isTe and "te" or "to"
|
||||||
|
isTe = not( isTe )
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for w in foot:gmatch( "%a+" ) do
|
||||||
|
word[j] = w
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
words[i] = word
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Draw( beat )
|
||||||
|
|
||||||
|
local foot = feet[ beat ]
|
||||||
|
|
||||||
|
|
||||||
|
love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
|
||||||
|
love.graphics.printf(
|
||||||
|
foot,
|
||||||
|
0, (9/10) * love.graphics.getHeight(),
|
||||||
|
love.graphics.getWidth(),
|
||||||
|
"center")
|
||||||
|
|
||||||
|
love.graphics.setColor(1.0, 1.0, 1.0, 0.5)
|
||||||
|
love.graphics.print( poem[beat], 0, (3 - poemLines[beat]) * 32 )
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local Reset = function() end
|
||||||
|
|
||||||
|
return { feet = feet, words = words, feetNL = feetNL, Reset = Reset, Draw = Draw }
|
173
text/tok.txt
|
@ -1,120 +1,53 @@
|
||||||
a
|
open -la mi -jan pi -toki-
|
||||||
akesi
|
ala -li mu -taso -li ken-
|
||||||
ala
|
ala -pana -toki -pilin-
|
||||||
alasa
|
toki -mu li -wawa -wawa
|
||||||
ale
|
nasa -la jan -ante -li ken-
|
||||||
anpa
|
awen -pona -tawa -mi li-
|
||||||
ante
|
awen -ala-sa e -pona.-
|
||||||
anu
|
moku -mi li -tan jan -pona-
|
||||||
awen
|
mama -mi li -pona -mute-
|
||||||
e
|
mama -tu li -lon. tu -ona-
|
||||||
en
|
o mi -taso. -wile -mute-
|
||||||
esun
|
li lon -sije-lo mi -ike.-
|
||||||
ijo
|
mute -la mi -pilin -nasa-
|
||||||
ike
|
a mi -wile -musi -mute-
|
||||||
ilo
|
wile -mi li -ni: ko -jelo-
|
||||||
insa
|
ni o -kama -tomo -wawa-
|
||||||
jaki
|
tomo -ni li -jo e -poki-
|
||||||
jan
|
poki l-a jan -mi li -lawa-
|
||||||
jelo
|
lawa -la jan -ni li -toki-
|
||||||
jo
|
"tomo -mi li -pona -mute-
|
||||||
kala
|
paka-la o -kama -ala-
|
||||||
kalama
|
weka -o ken -ala -kama"-
|
||||||
kama
|
wile -ala -mi la -ni li-
|
||||||
kasi
|
kama: -jan li -paka-la e-
|
||||||
ken
|
tomo -suli -pona -mi a-
|
||||||
kepeken
|
ni li -ike -tawa -pilin-
|
||||||
kili
|
jan li -ni tan -seme -ike-
|
||||||
kiwen
|
mi ken -ala -sona -pona-
|
||||||
ko
|
wile -toki -li lon -ala-
|
||||||
kon
|
la mi -tawa -mama -kiwen-
|
||||||
kule
|
|
||||||
kulupu
|
tenpo ni -mute li -kama li -weka la-
|
||||||
kute
|
sijelo -ike li -ante a -mute
|
||||||
la
|
pilin mi -ala li -awen kin -sama-
|
||||||
lape
|
wile mi su-li en wi-le mi li-li li-
|
||||||
laso
|
nasa lon -tenpo ni. -mi kama -suli-
|
||||||
lawa
|
toki pi jan mute li kama ike. jan
|
||||||
len
|
mute li wile e pakala taso
|
||||||
lete
|
lili li wile e sona e nasin
|
||||||
li
|
mute li wile e ni ala. nasa
|
||||||
lili
|
mi toki ala lon poka pi jan ni
|
||||||
linja
|
seme la mi wile alasa toki
|
||||||
lipu
|
jan ni la pilin mi li nasa mute
|
||||||
loje
|
jan ni la musi mi li musi ala
|
||||||
lon
|
seme la mi o lon poka jan ike
|
||||||
luka
|
jan ike ni li ken pona e ala
|
||||||
lukin
|
mute la ona li wile e tawa
|
||||||
lupa
|
ala la wile ona li lon pali
|
||||||
ma
|
mute la mi awen lon insa lawa
|
||||||
mama
|
lawa la mi toki mute e pilin
|
||||||
mani
|
lon lawa ala la mi lukin mute
|
||||||
meli
|
lukin li wile e musi e sona
|
||||||
mi
|
sitelen la m ken moku ni mute
|
||||||
mije
|
|
||||||
moku
|
|
||||||
moli
|
|
||||||
monsi
|
|
||||||
mu
|
|
||||||
mun
|
|
||||||
musi
|
|
||||||
mute
|
|
||||||
nanpa
|
|
||||||
nasa
|
|
||||||
nasin
|
|
||||||
nena
|
|
||||||
ni
|
|
||||||
nimi
|
|
||||||
noka
|
|
||||||
o
|
|
||||||
olin
|
|
||||||
ona
|
|
||||||
open
|
|
||||||
pakala
|
|
||||||
pali
|
|
||||||
palisa
|
|
||||||
pan
|
|
||||||
pana
|
|
||||||
pi
|
|
||||||
pilin
|
|
||||||
pimeja
|
|
||||||
pini
|
|
||||||
pipi
|
|
||||||
poka
|
|
||||||
poki
|
|
||||||
pona
|
|
||||||
pu
|
|
||||||
sama
|
|
||||||
seli
|
|
||||||
selo
|
|
||||||
seme
|
|
||||||
sewi
|
|
||||||
sijelo
|
|
||||||
sike
|
|
||||||
sin
|
|
||||||
sina
|
|
||||||
sinpin
|
|
||||||
sitelen
|
|
||||||
sona
|
|
||||||
soweli
|
|
||||||
suli
|
|
||||||
suno
|
|
||||||
supa
|
|
||||||
suwi
|
|
||||||
tan
|
|
||||||
taso
|
|
||||||
tawa
|
|
||||||
telo
|
|
||||||
tenpo
|
|
||||||
toki
|
|
||||||
tomo
|
|
||||||
tu
|
|
||||||
unpa
|
|
||||||
uta
|
|
||||||
utala
|
|
||||||
walo
|
|
||||||
wan
|
|
||||||
waso
|
|
||||||
wawa
|
|
||||||
weka
|
|
||||||
wile
|
|
34
wave.lua
|
@ -1,9 +1,9 @@
|
||||||
--Render and simulate 1D wave equation.
|
--Render and simulate 1D wave equation.
|
||||||
local love = love
|
local love = love
|
||||||
local N = 33
|
local N = 33
|
||||||
local SOUNDSPEED = 0.5
|
local SOUNDSPEED
|
||||||
local IMPULSESIZE = 5
|
local IMPULSESIZE
|
||||||
local DAMPING = 0.01
|
local DAMPING
|
||||||
|
|
||||||
|
|
||||||
local old, cur, new --States add beginning of last tick, current tick, and next tick respectively.
|
local old, cur, new --States add beginning of last tick, current tick, and next tick respectively.
|
||||||
|
@ -47,8 +47,9 @@ local shader = love.graphics.newShader([[
|
||||||
p.y = -p.y;
|
p.y = -p.y;
|
||||||
|
|
||||||
float r = r( atan(p.y, p.x) ) - length( p );
|
float r = r( atan(p.y, p.x) ) - length( p );
|
||||||
|
float q = float( r < 0.01 ) * clamp( 1.0 - score, 0.0, 1.0 ) ;
|
||||||
|
|
||||||
return vec4( (1.0 + float(score > 1.0) * r * 0.1) * color.xyz, float(r > -0.01)) ;
|
return vec4( q + (1.0 + clamp( score, 0.0, 1.0 ) * r * r * 0.2) * color.xyz, float(r > 0.0)) ;
|
||||||
}
|
}
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
@ -146,9 +147,15 @@ local function AliasedSinc( theta, x )
|
||||||
return n / d
|
return n / d
|
||||||
end
|
end
|
||||||
|
|
||||||
--Apply bandlimited impulse to wave.
|
--Apply bandlimited impulse to wave, adjust free parameters according to game state.
|
||||||
local function OnImpact( impact )
|
local function OnImpact( impact, level )
|
||||||
|
|
||||||
|
IMPULSESIZE = 10.0 * math.sqrt( level / 120.0 )
|
||||||
|
SOUNDSPEED = 50.0 - 35 * level / 120
|
||||||
|
DAMPING = 0.03 * ( 1.0 - 0.4 * level / 120 )
|
||||||
|
|
||||||
|
|
||||||
|
--Apply bandlimited impulse.
|
||||||
local r = cur.radii
|
local r = cur.radii
|
||||||
local theta = impact.th
|
local theta = impact.th
|
||||||
local magnitude = IMPULSESIZE * impact.speed
|
local magnitude = IMPULSESIZE * impact.speed
|
||||||
|
@ -272,11 +279,14 @@ end
|
||||||
Integrate = function( step )
|
Integrate = function( step )
|
||||||
|
|
||||||
for i = 1, N do
|
for i = 1, N do
|
||||||
|
|
||||||
local rxx = cur:SecondDerivative( math.pi * 2.0 * ( i - 1 ) / N )
|
local rxx = cur:SecondDerivative( math.pi * 2.0 * ( i - 1 ) / N )
|
||||||
|
|
||||||
local r = ( 1.0 - DAMPING ) * ( 2.0 * cur.radii[i] - old.radii[i] + step * step * SOUNDSPEED * rxx ) --Verlet
|
local r = ( 1.0 - DAMPING ) * ( 2.0 * cur.radii[i] - old.radii[i] + step * step * SOUNDSPEED * rxx ) --Verlet
|
||||||
+ DAMPING --Damping: oscillate toward 1.
|
+ DAMPING --Damping: oscillate toward 1.
|
||||||
if r > 1.5 then r = 1.5 end
|
|
||||||
if r < 0.5 then r = 0.5 end
|
--Avoid explosions.
|
||||||
|
r = math.max( 0.5, math.min( r, 4.0 ) )
|
||||||
new.radii[i] = r
|
new.radii[i] = r
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -290,10 +300,10 @@ end
|
||||||
|
|
||||||
local function Reset()
|
local function Reset()
|
||||||
|
|
||||||
SOUNDSPEED = 0.5
|
IMPULSESIZE = 1 / 10.0
|
||||||
IMPULSESIZE = 5
|
SOUNDSPEED = 5.0
|
||||||
DAMPING = 0.02
|
DAMPING = 0.1 / 1
|
||||||
|
|
||||||
old = Wave()
|
old = Wave()
|
||||||
cur = Wave()
|
cur = Wave()
|
||||||
new = Wave()
|
new = Wave()
|
||||||
|
|