Skip to content

Instantly share code, notes, and snippets.

@tarrouye
Last active August 29, 2015 14:17
Show Gist options
  • Save tarrouye/e3eb73282d7590b503f2 to your computer and use it in GitHub Desktop.
Save tarrouye/e3eb73282d7590b503f2 to your computer and use it in GitHub Desktop.
Codea GAMEJAM 2015 Entry
--# Main
-- Wrinkle
displayMode(FULLSCREEN)
function setup()
makeAssets()
highscore = readLocalData("highscore", 0)
reset()
end
function lose()
lost = true
score = math.round(score)
if score > highscore then
saveLocalData("highscore", score)
highscore = score
end
end
function reset()
wrinkle = Wrinkle()
objects = Objects()
multiplier, score = 1, 0
speedmod = 1
lost = false
invincible = false
end
function draw()
background(255, 255, 255, 255)
if not lost then
objects:setSpeedMod(speedmod)
objects:draw()
wrinkle:draw()
for i = 1, #wrinkle.points - 1 do
local p, p2 = wrinkle.points[i], wrinkle.points[i+1]
for j, k in pairs(objects.objs) do
for v, o in ipairs(k) do
if not o.hit and intersectsCircle(o.position, o:halfSize(), p, p2) then
o:die()
if o.kills then
if not invincible then
lose()
end
goto rdone
end
if powerup then
tween.stop(powerup)
end
score = score + (o.bonus or 0)
speedmod = (o.speedMod or 1)
invincible = (o.invincibilizes or false)
wrinkle.colour = o.colour
powerup = tween.delay(o.lasts, function()
invincible = false
speedmod = 1
wrinkle.colour = wrinkle.ocolour
end)
goto done
end
end
end
end
::done::
multiplier = math.max(#wrinkle.points - 1, 0)
score = score + (multiplier / 60)
smooth()
fill(wrinkle.colour.r, wrinkle.colour.g, wrinkle.colour.b, 127) fontSize(HEIGHT / 2)
text(math.round(score), WIDTH / 2, HEIGHT / 2)
fill(255, 0, 0, 127) fontSize(HEIGHT / 8)
text("x" .. multiplier, WIDTH / 2, HEIGHT * 7.5/8)
else
fill(0) fontSize(HEIGHT / 8)
text("game over\n\nscore: " .. score .. "\n\nhighscore: " .. highscore, WIDTH / 2, HEIGHT / 2)
end
::rdone::
end
function touched(t)
if not lost then
wrinkle:touched(t)
else
if t.state == ENDED then
reset()
end
end
end
--# Objects
Objects = class()
function Objects:init()
self.m = mesh()
self.m.texture = assets.object
self.objs = {}
self.types = {}
for k, v in pairsByKeys(subclasses(Object)) do
table.insert(self.types, v)
self.objs[v.id] = {}
end
self:spawnObj()
end
function Objects:spawnObj()
local possibilities = {}
for i, t in ipairs(self.types) do
for j = 1, t.probability do
table.insert(possibilities, t)
end
end
local kind = possibilities[math.random(1, #possibilities)]
local dead
for i, obj in ipairs(self.objs[kind.id]) do
if not obj.alive then
dead = obj
break
end
end
if dead then
dead:undeactivate()
else
local speed = math.random(5, 10)
local size = WIDTH / 20
local new = kind(speed, size)
new.mId = self.m:addRect(0, 0, 0, 0, 0)
table.insert(self.objs[kind.id], new)
end
self.lastSpawnTime = ElapsedTime
self.nextSpawn = math.random(15, 35) / 100
end
function Objects:setSpeedMod(s)
for v, kind in pairs(self.objs) do
for i, obj in ipairs(kind) do
obj.speedModifier = s
end
end
end
function Objects:draw()
-- Spawn Objects
if self.lastSpawnTime + self.nextSpawn < ElapsedTime then
self:spawnObj()
end
-- Update mesh
for v, kind in pairs(self.objs) do
for i, obj in ipairs(kind) do
obj:update()
self.m:setRect(obj.mId, obj:getX(), obj:getY(), obj:getSize(), obj:getSize(), obj.angle)
self.m:setRectColor(obj.mId, obj.colour.r, obj.colour.g, obj.colour.b, obj.alpha or 255)
end
end
self.m:draw()
end
function Objects:touched(touch)
end
--# Object
Object = class()
function Object:init(speed, size)
self.speed = speed
self.size = size
self.speedModifier = 1
self.scale = 1
self.angle = 0
self.alpha = 255
self.alive = true
self:randomDirAndPos()
end
function Object:randomDirAndPos()
local dirs = { vec2(-1,0), vec2(1,0), vec2(0,-1), vec2(0,1), vec2(1,1), vec2(-1,-1) }
self.direction = dirs[math.random(1, #dirs)]
local trx = WIDTH / 2 + ((WIDTH / 2 + self:halfSize()) * (self.direction.x * -1))
local try = HEIGHT / 2 + ((HEIGHT / 2 + self:halfSize()) * (self.direction.y * -1))
local ranx = math.max(math.min(math.random(0, WIDTH), WIDTH - self:halfSize()), self:halfSize())
local rany = math.max(math.min(math.random(0, HEIGHT), HEIGHT - self:halfSize()), self:halfSize())
if self.direction.x == 0 then
self.position = vec2(ranx, try)
else
self.position = vec2(trx, rany)
end
end
function Object:update()
if self.alive then
local move = self.direction * self.speed * self.speedModifier
self.position = vec2(self:getX() + move.x, self:getY() + move.y)
if self:offscreen() then
self:deactivate()
end
end
end
function Object:deactivate()
self.alive = false
self:randomDirAndPos()
end
function Object:undeactivate()
self.alive = true
self.hit = false
end
function Object:die()
if self.hit or (not self.alive) then return end
self.hit = true
self.speed = 0
self.fadeAlpha = 255
tween(0.25, self, { scale = 1.5 }, tween.easing.quadOut, function()
tween(0.2, self, { scale = 0, alpha = 0 }, tween.easing.quadIn, function() self:deactivate() end)
end)
end
function Object:offscreen()
return (self.position.x > WIDTH + self:halfSize() or self.position.x < -self:halfSize()
or self.position.y > HEIGHT + self:halfSize() or self.position.y < -self:halfSize())
end
function Object:getSize()
return self.size * self.scale
end
function Object:getX()
return self.position.x
end
function Object:getY()
return self.position.y
end
function Object:halfSize()
return self:getSize() / 2
end
function Object:pointInside(p)
return (p:dist(self.position) <= self:halfSize())
end
--# Types
-- Enemy
Enemy = class(Object)
Enemy.id = "enemy"
Enemy.colour = color(255, 0, 0)
Enemy.probability = 20
Enemy.kills = true
function Enemy:init(...)
Object.init(self, ...)
end
-- Bonus points
Bonus = class(Object)
Bonus.id = "points"
Bonus.colour = color(0, 255, 0)
Bonus.probability = 1
Bonus.bonus = 50
Bonus.lasts = 0.001
function Bonus:init(...)
Object.init(self, ...)
end
-- Shield
Shield = class(Object)
Shield.id = "shield"
Shield.colour = color(0, 204, 255, 255)
Shield.probability = 5
Shield.lasts = 1
Shield.invincibilizes = true
function Shield:init(...)
Object.init(self, ...)
end
-- Slowdown
Slow = class(Object)
Slow.id = "slow"
Slow.colour = color(127, 0, 255)
Slow.probability = 5
Slow.lasts = 2
Slow.speedMod = 0.5
function Slow:init(...)
Object.init(self, ...)
end
-- Speedup
Fast = class(Object)
Fast.id = "fast"
Fast.colour = color(245, 0, 255, 255)
Fast.probability = 5
Fast.lasts = 2
Fast.speedMod = 2
function Fast:init(...)
Object.init(self, ...)
end
--# Wrinkle
Wrinkle = class()
function Wrinkle:init()
self.points = {}
self.trailing = {}
self.locked = false
self.moving = false
self.colour = color(245, 255, 0, 255) --color(math.random(0,255), math.random(0,255), math.random(0,255))
self.ocolour = self.colour
end
function Wrinkle:draw()
stroke(self.colour) strokeWidth(5) noSmooth()
if self.locked and #self.points >= 2 then
local change = (self.points[2] - self.points[1])
local newPoint = self.points[#self.points] + change
newPoint.x, newPoint.y = newPoint.x % WIDTH, newPoint.y % HEIGHT
table.insert(self.points, newPoint)
table.remove(self.points, 1)
end
local long = vec2(WIDTH * 3/4, HEIGHT * 3/4)
for i = 1, #self.points - 1 do
local p1 = self.points[i]
local p2 = self.points[i + 1]
if math.abs(p1.x - p2.x) < long.x and math.abs(p1.y - p2.y) < long.y then
line(p1.x, p1.y, p2.x, p2.y)
end
end
end
function Wrinkle:stopTrail()
for i = #self.trailing, 1, -1 do
tween.stop(self.trailing[i])
end
end
function Wrinkle:resumeTrail()
for i = 1, #self.points do
self.trailing[i].running = 0
self.trailing[i].time = 0.175 + (i/10)
tween.play(self.trailing[i])
end
end
function Wrinkle:lock()
self.locked = true
self:stopTrail()
end
function Wrinkle:unlock()
self.locked = false
self:resumeTrail()
end
function Wrinkle:touched(t)
if t.state == BEGAN then
self:unlock()
end
if (#self.points == 0 or vec2(t.x, t.y):dist(self.points[#self.points]) > 15) then
table.insert(self.points, vec2(t.x, t.y))
table.insert(self.trailing, tween.delay(0.175, function() table.remove(self.points, 1) end))
end
if t.state == ENDED then
self:lock()
end
end
--# Assets
assets = {}
function makeAssets()
assets.object = image(100, 100) setContext(assets.object)
fill(255, 255, 255, 255) noStroke()
ellipse(50, 50, 100)
setContext()
end
--# Functions
function subclasses(c)
local t = {}
for k, v in pairs(_G) do
if type(v) == "table" and v._base and v._base == c then
t[k] = v
end
end
return t
end
function pairsByKeys (t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end
-- Rounds a number
function math.round(num, idp)
return tonumber(string.format("%." .. (idp or 0) .. "f", num))
end
-- Check if line intersects a circle
function intersectsCircle(center, rad, lp1, lp2)
local d = lp1 - lp2
local f = lp2 - center
local r = rad
local a = d:dot( d )
local b = 2*f:dot( d )
local c = f:dot( f ) - r*r
local discriminant = b*b-4*a*c
if( discriminant < 0 ) then
return false
else
discriminant = math.sqrt( discriminant )
local t1 = (-b + discriminant)/(2*a)
local t2 = (-b - discriminant)/(2*a)
if( t1 >= 0 and t1 <= 1 ) or ( t2 >= 0 and t2 <= 1 ) then
return true
else
return false
end
end
end
-- Tween colors
local cmt = getmetatable(color())
cmt.__add = function (c1, c2)
return color(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b, c1.a + c2.a)
end
cmt.__sub = function (c1, c2)
return color(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b, c1.a - c2.a)
end
cmt.__mul = function (c, s)
if type(c) ~= "userdata" then
c, s = s, c
end
return color(c.r * s, c.g * s, c.b * s, c.a * s)
end
cmt.__div = function (c, s)
if type(c) ~= "userdata" then
c, s = s, c
end
return color(c.r / s, c.g / s, c.b / s, c.a / s)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment