Skip to content

Instantly share code, notes, and snippets.

@NathanFlurry
Created December 31, 2013 09:08
Show Gist options
  • Save NathanFlurry/8194326 to your computer and use it in GitHub Desktop.
Save NathanFlurry/8194326 to your computer and use it in GitHub Desktop.
Entry for the 2013 Codea Holiday Codea Cook Off Competition.
--# Main
displayMode(FULLSCREEN_NO_BUTTONS)
supportedOrientations(LANDSCAPE_ANY)
function setup()
AssetLoader:init(
{
KAStar = "star.png",
KAPuff = "puff.png",
KAHeart = "heart.png",
KAScore = "plus.png",
KAPlay = "play.png",
KAPause = "pause.png",
KACity = "city.png",
KARocket = "rocket.png"
},
"http://www.interdazzle.com/misc/KickAsteroids/"
)
parameter.number("scaleAmnt",.1,1,1)
parameter.action("Hide Buttons",function() displayMode(FULLSCREEN_NO_BUTTONS) end)
highscore = readLocalData("highscore",0)
transparency = .9
update = true
emitters = {
highscore = Emitter( {
tex = readImage("Documents:KAStar"), -- IMG
startColor = color(255, 200, 0, 149),
endColor = color(68, 59, 30, 0),
minSize = 5,
maxSize = 15,
minSpeed = 100,
maxSpeed = 150,
minLife = 3,
maxLife = 8,
accel = vec2(0,-300),
rAccel = vec2(500,500),
streak = false
} ),
explosion = Emitter( {
startColor = color(255, 69, 0, 255),
endColor = color(255, 142, 0, 0),
minSize = 2,
maxSize = 2,
minSpeed = 200,
maxSpeed = 500,
minLife = .1,
maxLife = 1,
accel = vec2(0,-100),
rAccel = vec2(500,500),
streak = true
} ),
smoke = Emitter( {
tex = readImage("Documents:KAPuff"),
startColor = color(127, 127, 127, 90),
endColor = color(0, 0, 0, 0),
minSize = 20,
maxSize = 40,
minSpeed = .1,
maxSpeed = 1,
minLife = 1,
maxLife = 3,
accel = vec2(0,100),
rAccel = vec2(200,0),
streak = false
} ),
rocketFire = Emitter( {
tex = readImage("Documents:KAPuff"),
startColor = color(255, 123, 0, 90),
endColor = color(255, 197, 0, 0),
minSize = 0,
maxSize = 10,
minSpeed = 0,
maxSpeed = 1,
minLife = 1,
maxLife = 3,
accel = vec2(0,0),
rAccel = vec2(0,0),
streak = false
} ),
spark = Emitter( {
startColor = color(232, 215, 79, 255),
endColor = color(232, 215, 79, 0),
minSize = 1,
maxSize = 1,
minSpeed = 10,
maxSpeed = 100,
minLife = .1,
maxLife = 1,
accel = vec2(0,-100),
rAccel = vec2(500,500),
streak = true
} ),
health = Emitter( {
tex = readImage("Documents:KAHeart"), -- IMG
startColor = color(255, 0, 0, 255),
endColor = color(255, 0, 0, 0),
minSize = 10,
maxSize = 20,
minSpeed = 0,
maxSpeed = 1,
minLife = 1,
maxLife = 3,
accel = vec2(0,0),
rAccel = vec2(100,100),
streak = false
} ),
score = Emitter( {
tex = readImage("Documents:KAScore"),
startColor = color(0, 255, 0, 255),
endColor = color(0, 255, 0, 0),
minSize = 10,
maxSize = 20,
minSpeed = 0,
maxSpeed = 1,
minLife = 1,
maxLife = 3,
accel = vec2(0,0),
rAccel = vec2(100,100),
streak = false
} ),
blowAway = Emitter( {
startColor = color(255, 192, 0, 255),
endColor = color(255, 192, 0, 0),
minSize = 2,
maxSize = 4,
minSpeed = 200,
maxSpeed = 500,
minLife = .1,
maxLife = 1,
accel = vec2(0,-100),
rAccel = vec2(500,500),
streak = true
} )
}
font("HelveticaNeue-UltraLight")
fontSize(35)
fill(253, 253, 253, 255)
BG:init()
StateMachine:init()
StateMachine:registerStates({game = Game,start = Start,help = Help})
StateMachine:setState("start")
end
function draw()
translate(WIDTH/2,HEIGHT/2)
scale(scaleAmnt)
translate(-WIDTH/2,-HEIGHT/2)
--background(0, 0, 0, 255)
physics.gravity(0,0)
if not drawLoadScreen() then
BG:draw()
StateMachine:draw()
end
end
function drawLoadScreen()
local status = AssetLoader:getStatus()
if status.percent < 1 or status.percentFailed ~= 0 then
pushStyle()
fill(0)
rect(0,0,WIDTH,HEIGHT)
popStyle()
if status.percentFailed == 0 then
pushMatrix()
pushStyle()
fill(ColorFunctions:flickerColor(color(255)))
textAlign(LEFT)
local str = "Loading"
local fStr = str
local speed = 4
if math.floor(ElapsedTime*speed) % 4 == 0 then
str = str.."..."
elseif math.floor(ElapsedTime*speed+1) % 4 == 0 then
str = str..".."
elseif math.floor(ElapsedTime*speed+2) % 4 == 0 then
str = str.."."
end
glowText(str,WIDTH/2-textSize(fStr)/2+textSize(str)/2,HEIGHT/3*2)
popMatrix()
popStyle()
else
pushStyle()
if math.random() < .95 then
fill(ColorFunctions:flickerColor(color(255,30,30)))
else
fill(0)
local sparkPos = vec2(math.random(WIDTH/2-400,WIDTH/2+400),
math.random(HEIGHT/3*2-75,HEIGHT/3*2+75))
emitters.spark:emit(sparkPos,10)
end
fontSize(100)
glowText("Download Failed",WIDTH/2,HEIGHT/3*2)
popStyle()
pushStyle()
if status.loadError then
text("Error: "..status.loadError,WIDTH/2,HEIGHT/3)
else
text("Unknown error",WIDTH/2,HEIGHT/3)
end
popStyle()
end
if status.percentFailed == 0 then
pushStyle()
fill(ColorFunctions:flickerColor(color(255)))
rect(0,HEIGHT/2,WIDTH*status.percent,10)
popStyle()
end
drawEmitters()
return true
else
return false
end
end
function drawEmitters()
for i,v in pairs(emitters) do
v:draw()
end
end
function touched(t)
StateMachine:touched(t)
end
function collide(c)
if c.bodyA and c.bodyB and update and not (state.gameOver and (c.bodyA.id == "fragment" or c.bodyB.id == "fragment")) then
StateMachine:collide(c)
end
end
--# Start
Start = class()
function Start:init()
self.startPos = vec2(WIDTH/5*4,HEIGHT/2)
self.startSize = 250
end
function Start:draw()
pushMatrix()
pushStyle()
translate(WIDTH/3,HEIGHT/2+100)
rotate(-10)
local flicker = math.random()
if flicker > .95 then
flicker = true
else
flicker = false
end
fontSize(120)
textAlign(LEFT)
if flicker then
fill(0,0,0,255)
for i = 1,2 do
local sparkPos = vec2(math.random(0,WIDTH/2),math.random(HEIGHT/2,HEIGHT/2+200))
emitters.spark:emit(sparkPos,10)
end
else
fill(ColorFunctions:flickerColor(color(255),30))
end
glowText("KickAsteroids",0,0)
popMatrix()
popStyle()
glowText("Highscore: "..highscore,WIDTH/4,HEIGHT/2)
pushMatrix()
tint(255,transparency*255)
sprite("Documents:KAPlay",self.startPos.x,self.startPos.y)
popMatrix()
glowText("?",WIDTH-fontSize(),fontSize())
drawEmitters()
end
function Start:touched(t)
if tR(vec2(t.x,t.y),self.startPos,vec2(self.startSize,self.startSize)) and t.state == ENDED and touchReady(t) then
touchUsed(t)
StateMachine:setState("game")
end
if tR(vec2(t.x,t.y),vec2(WIDTH-fontSize(),fontSize()),vec2(fontSize()*2,fontSize()*2)) and t.state == ENDED and touchReady(t) then
touchUsed(t)
StateMachine:setState("help")
end
emitters.spark:emit(vec2(t.x,t.y),2)
end
--# Help
-- ADD BACK BUTTON
-- LINK ME ON START
-- ADD CREDITS
Help = class()
function Help:init()
self.slides = {
{
titleTxt = "Welcome to KickAsteroids",
body =
[[
KickAsteroids is a game about defending your planet from incoming asteroids
]]
},
{
titleTxt = "How to Play",
body =
[[
Touch your finger on the screen and move it around to destroy asteroids.
When you hit an asteroid, it will shatter into fragments.
Occasionally you will encounter a care package. The care packages look like rockets coming to your planet. You will read more about them later.
]]
},
{
titleTxt = "Scoring",
body =
[[
Your score in KickAsteroids increases 10 times a second by your score increment. Your score increment is the number of asteroids you have hit without one touching your city. You want to keep a high score increment because the game keeps getting harder, wether or not you have a great score or not.
]]
},
{
titleTxt = "Damage and Loosing",
body =
[[
When an asteroid or fragment hits your city, it will loose health. If an asteroid hits your city, it will loose 25% of its health. If a fragment hits your city, it will loose only 2% of its health.
Once the health of every city on your planet has depleted to nothing, you will loose the game.
]]
},
{
titleTxt = "Care packages",
body =
[[
As mentioned earlier, you will occasionally see a care package, displayed as a rocket, heading towards you city. You do not want to touch it, or it will blow up, but you do not have to worry about destroying asteroids to make a path for it to your city.
There are 3 types of care packages: The first one brings your city health, which is red. The second one will give you a boost in score, which is green. Finally, the third one, which is yellow, will blow away every asteroid on the screen.
]]
},
{
titleTxt = "Credits",
body =
[[
Coding and Graphics:
Nathan Flurry,InterDazzle, LLC
Particle engine:
John Millard
Made with Codea for the 2013 Holiday Codea Cook Off
Theme: Fall
Thank you for playing KickAsteroids!
]]
}
}
for i,v in ipairs(self.slides) do
v.title = image(WIDTH,HEIGHT)
setContext(v.title)
pushStyle()
fontSize(90)
glowText(v.titleTxt,WIDTH/2,HEIGHT/5*4)
popStyle()
setContext()
end
self.slide = -1
self.slideDist = WIDTH + 100
end
function Help:draw()
text("Back",75,HEIGHT-fontSize())
if self.slide > -1 then
self.slide = -1
elseif self.slide < -#self.slides then
self.slide = -#self.slides
end
pushMatrix()
pushStyle()
fill(255, 255, 255, 255)
textWrapWidth(WIDTH-100)
textAlign(CENTER)
translate(self.slide*self.slideDist,0)
for i,v in ipairs(self.slides) do
pushMatrix()
translate(self.slideDist*i,0)
tint(ColorFunctions:flickerColor(color(255)))
sprite(v.title,WIDTH/2,HEIGHT/2)
text(v.body,WIDTH/2,HEIGHT/5*2)
popMatrix()
end
popStyle()
popMatrix()
end
function Help:touched(t)
local speed = .5
local easing = tween.easing.sineInOut
if tR(vec2(t.x,t.y),vec2(WIDTH/4,HEIGHT/2),vec2(WIDTH/2,HEIGHT)) and t.state == ENDED and touchReady(t) then
local dest = math.floor(self.slide + 1)
if dest > -1 then
dest = -#self.slides
end
tween(speed,self,{slide = dest},easing)
elseif tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2),vec2(WIDTH/2,HEIGHT)) and t.state == ENDED and touchReady(t) then
local dest = math.floor(self.slide - 1)
if dest < -#self.slides then
dest = -1
end
tween(speed,self,{slide = dest},easing)
end
if tR(vec2(t.x,t.y),vec2(75,HEIGHT-35),vec2(200,150)) and t.state == ENDED and touchReady(t) then
StateMachine:setState("start")
end
end
--# Game
Game = class()
function Game:init()
self.pauseScreen = false
self.gameOver = false
self.surrendered = false
self.setHighscore = false
self.score = 0
self.sM = 1
self.scoreTimer = Timer(.1,
function()
local s = 1
self.score = self.score + self.sM
end)
self.worldDefender = WorldDefender()
self.world = World()
self.objectManager = ObjectManager()
self.powerUpManager = PUpManager()
self.hud = HUD()
end
function Game:draw()
if update and not self.gameOver then
self.scoreTimer:update()
end
self.world:draw()
self.worldDefender:draw()
self.objectManager:draw()
self.powerUpManager:draw()
self.hud:draw(self.score)
sprite("Documents:KAPause",WIDTH-20,HEIGHT-20) -- IMG
drawEmitters()
if self.pauseScreen then
pushStyle()
fontSize(90)
fill(ColorFunctions:flickerColor(color(255)))
glowText("Paused",WIDTH/4,HEIGHT/2)
popStyle()
pushStyle()
fontSize(50)
glowText("Menu",WIDTH/4*3,HEIGHT/2+fontSize()*2)
glowText("Resume",WIDTH/4*3,HEIGHT/2)
--text("Surrender",WIDTH/4*3,HEIGHT/2-fontSize())
glowText("Restart",WIDTH/4*3,HEIGHT/2-fontSize()*2)
popStyle()
elseif self.gameOver then
pushStyle()
fontSize(90)
fill(ColorFunctions:flickerColor(color(255)))
glowText("Game\nOver",WIDTH/4,HEIGHT/2+fontSize())
popStyle()
pushMatrix()
pushStyle()
fontSize(50)
if self.score >= highscore then
local t = color(255, 190, 0, 255)
fill(t)
end
translate(WIDTH/4,HEIGHT/2-fontSize()*2)
scale(1+math.sin(ElapsedTime*5)*.1)
glowText("Score: "..self.score,0,0)
popMatrix()
popStyle()
pushStyle()
fontSize(50)
glowText("Menu",WIDTH/4*3,HEIGHT/2+fontSize())
glowText("Play Again",WIDTH/4*3,HEIGHT/2-fontSize())
popStyle()
end
end
function Game:setGameOver()
if self.score > highscore then
highscore = self.score
saveLocalData("highscore",highscore)
end
--StateMachine:setState("start")
self.world.worldJoint:destroy()
self.world.body.linearDamping = .5
for i = #self.powerUpManager.powerUps,1,-1 do
v = self.powerUpManager.powerUps[i]
v:destroy()
emitters.explosion:emit(v.position,75)
table.remove(self.powerUpManager.powerUps,i)
end
self.gameOver = true
end
function Game:touched(t)
if tR(vec2(t.x,t.y),vec2(WIDTH-20,HEIGHT-20),vec2(40,40)) and t.state == BEGAN and not self.gameOver and touchReady(t) then
touchUsed(t)
self.pauseScreen = true
update = false
physics.pause()
end
pushStyle()
fontSize(50)
if self.pauseScreen then
if tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2+fontSize()*2),vec2(WIDTH/2,fontSize()*2)) and t.state == BEGAN and touchReady(t) then
touchUsed(t)
self.pauseScreen = false
update = true
physics.resume()
StateMachine:setState("start")
end
if tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2),vec2(WIDTH/2,fontSize()*2)) and t.state == BEGAN and touchReady(t) then
touchUsed(t)
self.pauseScreen = false
update = true
physics.resume()
end
if tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2-fontSize()*2),vec2(WIDTH/2,fontSize()*2)) and t.state == BEGAN and touchReady(t) then
touchUsed(t)
self.pauseScreen = false
update = true
physics.resume()
StateMachine:setState("game")
end
end
if self.gameOver then
if tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2+fontSize()),vec2(WIDTH/2,fontSize()*2)) and t.state == BEGAN and touchReady(t) then
touchUsed(t)
StateMachine:setState("start")
end
if tR(vec2(t.x,t.y),vec2(WIDTH/4*3,HEIGHT/2-fontSize()),vec2(WIDTH/2,fontSize()*2)) and t.state == BEGAN and touchReady(t) then
touchUsed(t)
StateMachine:setState("game")
end
end
popStyle()
self.worldDefender:touched(t)
end
function Game:collide(c)
self.objectManager:collide(c)
self.powerUpManager:collide(c)
self.world:collide(c)
end
function Game:exit()
self.world.body:destroy()
for i,v in ipairs(self.world.cities) do
v:destroy()
end
for i,v in ipairs(self.objectManager.objects) do
v:destroy()
end
for i,v in ipairs(self.objectManager.fragments) do
v:destroy()
end
for i,v in ipairs(self.powerUpManager.powerUps) do
v:destroy()
end
end
--# HUD
HUD = class()
function HUD:init()
pushStyle()
fontSize(35)
self.numImgs = {}
local padding = 25
for i = 0,9 do
local n = image(fontSize()+padding,fontSize()+padding)
setContext(n)
glowText(i,n.width/2,n.height/2)
setContext()
self.numImgs[i] = n
end
local n = image(fontSize()+padding,fontSize()+padding)
setContext(n)
glowText("x",n.width/2,n.height/2)
setContext()
self.numImgs["x"] = n
popStyle()
end
function HUD:draw(score)
if stateIndex == "game" then
-- Draw score
local scoreStr = tostring(score)
local scoreTable = {}
for i = 6,1,-1 do
local num = string.sub(scoreStr,-i,-i)
if type(tonumber(num)) ~= "number" then
num = "0"
end
table.insert(scoreTable,num)
end
for i,v in ipairs(scoreTable) do
--glowText(v,fontSize()*i*.7,HEIGHT-fontSize())
pushStyle()
if state.score >= highscore then
tint(255, 190, 0, 255)
else
tint(255)
end
sprite(self.numImgs[tonumber(v)],fontSize()*i*.7,HEIGHT-fontSize())
popStyle()
end
-- Draw score multiplier
local sMStr = tostring(state.sM)
local sMTable = {}
for i = 1,string.len(state.sM) do
local num = string.sub(sMStr,-i,-i)
if type(tonumber(num)) ~= "number" then
num = "0"
end
table.insert(sMTable,num)
end
table.insert(sMTable,"x")
for i,v in ipairs(sMTable) do
--glowText(v,fontSize()*(#scoreTable-i+1)*.7,HEIGHT-fontSize()*2)
local index = nil
if tonumber(v) then
index = tonumber(v)
else
index = v
end
pushStyle()
if state.score >= highscore then
tint(255, 190, 0, 255)
else
tint(255)
end
sprite(self.numImgs[index],fontSize()*(#scoreTable-i+1)*.7,HEIGHT-fontSize()*2)
popStyle()
end
if state.score >= highscore and not state.setHighscore then
state.setHighscore = true
tween(1,state.world.worldColor,{r = 255, g = 190, b = 0})
emitters.highscore:emit(vec2(100,HEIGHT-fontSize()*2),100)
end
end
end
--# World
World = class()
function World:init()
self.healthBarWidth = 50
self.numCities = 6
self.worldSize = 100
self.worldColor = ColorFunctions:getColor() --color(100,255,0)
local p = {}
for i = 1,self.numCities do
table.insert(p,vec2(
math.cos(math.rad(360/self.numCities*i))*self.worldSize,
math.sin(math.rad(360/self.numCities*i))*self.worldSize
))
end
self.body = physics.body(POLYGON,explodeTable(p))
self.body.id = "world"
self.body.position = vec2(WIDTH/2,HEIGHT/2)
self.body.angularVelocity = -5
--self.body.mass = 100
self.body.fixedRotation = true
self.body.linearDamping = 3
self.revolutePoint = physics.body(CIRCLE,10)
self.revolutePoint.id = "revolute"
self.revolutePoint.type = STATIC
self.revolutePoint.position = vec2(WIDTH/2,HEIGHT/2)
self.revolutePoint.sensor = true
self.worldJoint = physics.joint(DISTANCE,
self.body,
self.revolutePoint,
self.body.position,
self.revolutePoint.position)
self.worldJoint.frequency = .5
self.glowMesh = mesh()
self.glowMesh.shader = shader(radialGradient())
self.glowMesh.shader.col1 = vec4(1,1,1,1)
self.glowMesh.shader.col2 = vec4(1,1,1,0)
self.glowMesh.shader.pos = vec2(.5,.5)
self.glowMesh.shader.size = vec2(.5,.5)
self.glowMesh.shader.angle = 0
local glowAmnt = 2.2
self.glowMesh:addRect(0,0,self.worldSize*glowAmnt,self.worldSize*glowAmnt)
self.worldMesh = mesh()
local p = {}
for i = 1,self.numCities do
table.insert(p,vec2(
math.cos(math.rad(360/self.numCities*i))*self.worldSize,
math.sin(math.rad(360/self.numCities*i))*self.worldSize
))
end
table.insert(p,vec2(
math.cos(math.pi*2/self.numCities-.001)*self.worldSize,
math.sin(math.pi*2/self.numCities-.001)*self.worldSize
))
table.insert(p,vec2(
math.cos(math.pi*2/self.numCities-.001)*self.worldSize*.9,
math.sin(math.pi*2/self.numCities-.001)*self.worldSize*.9
))
for i = self.numCities,1,-1 do
table.insert(p,vec2(
math.cos(math.rad(360/self.numCities*i))*self.worldSize*.9,
math.sin(math.rad(360/self.numCities*i))*self.worldSize*.9
))
end
self.worldMesh.vertices = triangulate(p)
local aP = self.worldMesh.vertices
for i = .8,.1,-.15 do
for j,v in ipairs(self.worldMesh.vertices) do
table.insert(aP,v*i)
end
end
self.worldMesh.vertices = aP
self:setWorldColor(self.worldColor)
self.cities = {}
for i = 1, self.numCities do
local c = physics.body(CIRCLE,30)
c.id = "city"
c.sensor = true
local worldAngle = 360/self.numCities*i+180/self.numCities
local cityOffset = 0
c.x = math.cos(math.rad(worldAngle))*(self.worldSize+cityOffset)+self.body.x
c.y = math.sin(math.rad(worldAngle))*(self.worldSize+cityOffset)+self.body.y
c.angle = math.deg(vec2(0,0):angleBetween(c.position-self.body.position))-90
c.j = physics.joint(WELD,self.body,c,c.position)
c.health = 1
table.insert(self.cities,c)
end
end
function World:draw()
local totalHealth = 0
for i,v in ipairs(self.cities) do
totalHealth = totalHealth + v.health
end
totalHealth = totalHealth/self.numCities
pushStyle()
pushMatrix()
fill(0, 159, 255, 255)
translate(self.body.x,self.body.y)
rotate(self.body.angle)
local darkness = 1-(math.random()*(1-totalHealth))
if state.gameOver then
self:setWorldColor(color(0,0,0,255))
else
self:setWorldColor(color(
darkness*self.worldColor.r,
darkness*self.worldColor.g,
darkness*self.worldColor.b
))
end
self.glowMesh:draw()
self.worldMesh:draw()
popStyle()
popMatrix()
for i,v in ipairs(self.cities) do
if v.health <= .01 or state.gameOver then
v.health = 0
elseif v.health > 1 then
v.health = 1
end
if math.random()*(1+(.8-v.health)) > 1 then
emitters.smoke:emit(v.position,2)
end
pushMatrix()
translate(v.x,v.y)
rotate(v.angle)
if v.health > 0 then
pushStyle()
fill(255, 255, 255, 36)
local b = 2
rect(-self.healthBarWidth/2-b,50-b,self.healthBarWidth+b*2,5+b*2)
fill(255, 0, 0, 255)
rect(-self.healthBarWidth/2,50,self.healthBarWidth*v.health,5)
popStyle()
end
pushStyle()
fill(0, 255, 0, 255)
--ellipse(0,0,v.radius*2)
local grey = v.health*255
tint(grey,grey,grey,255)
sprite("Documents:KACity",0,0) -- IMG
popStyle()
popMatrix()
end
if totalHealth <= 0 and not state.gameOver then
state:setGameOver()
end
end
function World:setWorldColor(col)
self.glowMesh.shader.col1 = vec4(col.r/255,col.g/255,col.b/255,1)
self.glowMesh.shader.col2 = vec4(col.r/255,col.g/255,col.b/255,0)
self.worldMesh:setColors(col)
--self.worldColor = col
end
function World:collide(c)
local hit = false
for i = #self.cities,1,-1 do
v = self.cities[i]
if (c.bodyA == v and c.bodyB.id == "enemy") or
(c.bodyB == v and c.bodyA.id == "enemy") then
local e
if c.bodyA.id == "enemy" then
e = c.bodyA
else --if c.bodyB.id == "enemy" the
e = c.bodyB
end
state.sM = 1
if e.active and v.health > 0 and not e.hit then
emitters.explosion:emit(e.position,50)
v.health = v.health - .25
e.hit = true
e:destroy()
end
end
if (c.bodyA == v and c.bodyB.id == "fragment") or
(c.bodyB == v and c.bodyA.id == "fragment") then
local e
if c.bodyA.id == "fragment" then
e = c.bodyA
else --if c.bodyB.id == "fragment" the
e = c.bodyB
end
--state.sM = 1
if e.active and v.health > 0 and not e.hit then
emitters.explosion:emit(e.position,10)
v.health = v.health - .03
e.hit = true
e:destroy()
end
end
end
if (c.bodyA == self.body and c.bodyB.id == "fragment") or
(c.bodyB == self.body and c.bodyA.id == "fragment") then
local f
if c.bodyA.id == "fragment" then
f = c.bodyA
else
f = c.bodyB
end
f:destroy()
f.active = false
end
end
--# WorldDefender
WorldDefender = class()
function WorldDefender:init()
self.destPoint = vec2(WIDTH/2,HEIGHT/2)
self.moveSpeed = 20
self.maxTouches = 2
self.tM = mesh()
self.tM.shader = shader(radialGradient())
color(255, 105, 0, 255)
self.tM.shader.col1 = vec4(1, .4, 0, .5)
self.tM.shader.col2 = vec4(1, 0, 0, 0)
self.tM.shader.pos = vec2(.5,.5)
self.tM.shader.size = vec2(.5,.5)
self.tM.shader.angle = 0
self.tM:addRect(0,0,60,60)
self.defenders = {}
end
function WorldDefender:draw()
for i,v in pairs(self.defenders) do
v.linearVelocity = (v.destPos-v.position)*self.moveSpeed
pushStyle()
pushMatrix()
--ellipse(v.x,v.y,v.radius*2)
translate(v.x,v.y)
self.tM:draw()
popStyle()
popMatrix()
end
end
function WorldDefender:touched(t)
if update and not state.gameOver then
if t.state == BEGAN and touchReady(t) then
touchUsed(t)
local tCount = 0
for i,v in pairs(self.defenders) do
tCount = tCount + 1
end
if tCount < self.maxTouches then
local b = physics.body(CIRCLE,15)
b.id = "defender"
b.type = KINEMATIC
b.x = t.x
b.y = t.y
b.destPos = vec2(t.x,t.y)
self.defenders[t.id] = b
else
end
elseif t.state == MOVING then
if self.defenders[t.id] then
self.defenders[t.id].destPos = vec2(t.x,t.y)
end
elseif t.state == ENDED or t.state == CANCELLED then
if self.defenders[t.id] then
self.defenders[t.id]:destroy()
self.defenders[t.id] = nil
end
end
else
if self.defenders[t.id] then
self.defenders[t.id]:destroy()
self.defenders[t.id] = nil
end
end
end
--# ObjectManager
ObjectManager = class()
function ObjectManager:init()
self.objects = {}
self.fragments = {}
self.oSize = 20
self.oSizeRange = 8
self.fallSpeed = 20
self.numFragments = 5
self.oDarkness = 100
self.fragmentLimit = 20
self.objectTimer = Timer(1,function() self:addObject() end)
self.objectSpeedUpTimer = Timer(5,
function()
self.objectTimer.interval = self.objectTimer.interval*.95
self.fallSpeed = self.fallSpeed*1.05
end)
end
function ObjectManager:draw()
if update then
self.objectTimer:update()
if not state.gameOver then
self.objectSpeedUpTimer:update()
else
self.objectTimer.interval = 1.2
end
end
for i,o in ipairs(self.objects) do
if o.x then
if o.x > 0 and o.x < WIDTH and o.y > 0 and o.y < HEIGHT then
o.entered = true
end
pushMatrix()
pushStyle()
translate(o.x,o.y)
o:setCol(ColorFunctions:flickerColor(o.oCol))
o.m:draw()
noFill()
stroke(o.col)
strokeWidth(o.radius/6)
ellipse(0,0,o.radius*2)
strokeWidth(o.radius/7)
ellipse(0,0,o.radius*1.5)
strokeWidth(o.radius/8)
ellipse(0,0,o.radius*1)
--[[noStroke()
fill(o.col)
ellipse(0,0,o.radius*.3)]]--
popMatrix()
popStyle()
end
end
for i = #self.objects,1,-1 do
v = self.objects[i]
if (not v.active) or ((v.x < 0 or v.x > WIDTH or v.y < 0 or v.y > HEIGHT) and v.entered) then
v:destroy()
table.remove(self.objects,i)
end
end
local totalDestroyed = 0
for i = #self.fragments,1,-1 do
o = self.fragments[i]
if o.active then
local fAng = vec2(0,0):angleBetween(state.world.body.position-o.position)-math.pi
o.applyForce(
o,
vec2(
math.cos(fAng),
math.sin(fAng)
)*.25)
pushMatrix()
pushStyle()
translate(o.x,o.y)
rotate(o.angle)
o:setCol(ColorFunctions:flickerColor(o.oCol))
o.m:draw()
o.shapeM:draw()
popMatrix()
popStyle()
end
if not o.active or o.x < 0 or o.x > WIDTH or o.y < 0 or o.y > HEIGHT then
o:destroy()
table.remove(self.fragments,i)
totalDestroyed = totalDestroyed + 1
end
end
if totalDestroyed > 10 then
--collectgarbage()
end
end
function ObjectManager:addObject()
local o = physics.body(CIRCLE,
math.random(
self.oSize - self.oSizeRange/2,
self.oSize+self.oSizeRange/2))
o.id = "enemy"
o.bUID = math.random()
o.restitution = 1.5
--local oAngle = math.random()*360
if math.random() > .5 then
o.position = vec2(math.random(0,1)*WIDTH,math.random()*HEIGHT)
else
o.position = vec2(math.random()*WIDTH,math.random(0,1)*HEIGHT)
end
local offset = 100
if o.position.x == 0 then
o.position.x = -offset
elseif o.position.y == WIDTH then
o.position.x = o.position.x + offset
end
if o.position.y == 0 then
o.position.y = -offset
elseif o.position.y == HEIGHT then
o.position.y = o.position.y + offset
end
--o.angularVelocity = (math.random()-.5)*self.rotateSpeed
local angleToPlayer = vec2(0,0):angleBetween(o.position-vec2(WIDTH/2,HEIGHT/2))+135
o.linearVelocity = vec2(math.cos(angleToPlayer),math.sin(angleToPlayer))*self.fallSpeed
o.m = mesh()
o.m.shader = shader(radialGradient())
o.m.shader.col1 = vec4(1,1,1,1)
o.m.shader.col2 = vec4(1,1,1,0)
o.m.shader.pos = vec2(.5,.5)
o.m.shader.size = vec2(.5,.5)
o.m.shader.angle = 0
o.rectID = o.m:addRect(0,0,o.radius*3,o.radius*3)
o.setCol = function(_,col)
o.m.shader.col1 = vec4(col.r/255,col.g/255,col.b/255,1)
o.m.shader.col2 = vec4(col.r/255,col.g/255,col.b/255,0)
o.col = col
end
o.oCol = ColorFunctions:getColor()
o:setCol(o.oCol)
table.insert(self.objects,o)
end
function ObjectManager:collide(c)
for i = #self.objects,1,-1 do
local e = self.objects[i]
if (c.bodyA == e and c.bodyB.id ~= "enemy" and c.bodyB.id ~= "fragment" and c.bodyB.id ~= "city") or
(c.bodyB == e and c.bodyA.id ~= "enemy" and c.bodyA.id ~= "fragment" and c.bodyA.id ~= "city") then
local e
if c.bodyA.id == "enemy" then
e = c.bodyA
else
e = c.bodyB
end
for i = 1,self.numFragments do
local a1,a2,a3 = math.random()*120,math.random()*120+120,math.random()*120+240
local size = 7
local f = physics.body(POLYGON,
vec2(math.cos(math.rad(a1)),math.sin(math.rad(a1)))*size,
vec2(math.cos(math.rad(a2)),math.sin(math.rad(a2)))*size,
vec2(math.cos(math.rad(a3)),math.sin(math.rad(a3)))*size
)
f.id = "fragment"
local dist = e.radius * .5
f.x = math.cos(math.rad(360/self.numFragments*i))*dist+e.x
f.y = math.sin(math.rad(360/self.numFragments*i))*dist+e.y
f.linearVelocity = vec2(
math.cos(math.rad(360/self.numFragments*i)),
math.sin(math.rad(360/self.numFragments*i))
)*30
f.angularDamping = 1
f.m = e.m
f.rectID = e.rectID
f.m:setRect(f.rectID,0,0,size*4,size*4)
f.shapeM = mesh()
f.shapeM.vertices = triangulate(f.points)
f.setCol = function(_,col)
f.m.shader.col1 = vec4(col.r/255,col.g/255,col.b/255,.5)
f.m.shader.col2 = vec4(col.r/255,col.g/255,col.b/255,0)
f.shapeM:setColors(col)
end
f.oCol = ColorFunctions:getColor()
f:setCol(f.oCol)
table.insert(self.fragments,f)
end
if c.bodyA.id == "defender" or c.bodyB.id == "defender" then
state.sM = state.sM + 1
end
e:destroy()
table.remove(self.objects,i)
--collectgarbage()
end
end
end
--# PUpManager
PUpManager = class()
function PUpManager:init()
self.powerUpTypes = {
health = {
chance = 1,
tint = color(255, 0, 0, 255),
emitter = "health",
emitAmount = 5,
hitFunc = function(_,city)
city.health = city.health + .15
end
},
score = {
chance = .8,
tint = color(0, 255, 0, 255),
emitter = "score",
emitAmount = 15,
hitFunc = function(_,city)
state.score = state.score + 1000
end
},
blowAway = {
chance = .2,
tint = color(255, 192, 0, 255),
emitter = "blowAway",
emitAmount = 75,
hitFunc = function(_,city)
for i,v in ipairs(state.objectManager.objects) do
local velAng = vec2(0,0):angleBetween(
city.position-v.position)-math.pi
v.linearVelocity = vec2(math.cos(velAng),math.sin(velAng))*1000
end
for i,v in ipairs(state.objectManager.fragments) do
local velAng = vec2(0,0):angleBetween(
city.position-v.position)-math.pi
v.linearVelocity = vec2(math.cos(velAng),math.sin(velAng))*1000
end
end
},
}
self.fallSpeed = 50
self.powerUps = {}
self.powerUpTimer = Timer(5,function() self:addPowerUp() end)
end
function PUpManager:draw()
if not state.gameOver then
self.powerUpTimer:update()
end
for i = #self.powerUps,1,-1 do
p = self.powerUps[i]
p:calcMovement()
pushStyle()
pushMatrix()
--[[fill(63, 255, 0, 255)
ellipse(p.x,p.y,p.radius*2)]]--
translate(p.x,p.y)
rotate(p.angle)
p.glowMesh:draw()
local flashTime = 2
if p.pUp.flashed ~= math.floor(ElapsedTime+p.randFlashOffset)
and math.floor(ElapsedTime+p.randFlashOffset) % flashTime == 0 then
tint(255)
p.glowMesh.shader.col1 = vec4(1,1,1,.4)
p.glowMesh.shader.col2 = vec4(1,1,1,0)
p.pUp.flashed = math.floor(ElapsedTime+p.randFlashOffset)
else
p.glowMesh.shader.col1 = vec4(p.pUp.tint.r/255,p.pUp.tint.g/255,p.pUp.tint.b/255,.4)
p.glowMesh.shader.col2 = vec4(p.pUp.tint.r/255,p.pUp.tint.g/255,p.pUp.tint.b/255,0)
tint(p.pUp.tint)
end
sprite("Documents:KARocket") -- IMG
emitters.rocketFire:emit(
p.position+vec2(p.radius,p.radius):rotate(math.rad(p.angle-135)),
1)
popStyle()
popMatrix()
end
end
function PUpManager:collide(c)
for i = #self.powerUps,1,-1 do
p = self.powerUps[i]
if c.bodyA == p or c.bodyB == p then
if c.bodyB.id == "city" or c.bodyA.id == "city" then
local cityPos
if c.bodyA.id == "city" then
p.pUp:hitFunc(c.bodyA)
cityPos = c.bodyA.position
else
p.pUp:hitFunc(c.bodyB)
cityPos = c.bodyB.position
end
emitters[p.pUp.emitter]:emit(cityPos,p.pUp.emitAmount)
p:destroy()
table.remove(self.powerUps,i)
elseif c.bodyB.id == "defender" or c.bodyA.id == "defender" then
if c.bodyA == p then
c.bodyA:destroy()
else
c.bodyB:destroy()
end
emitters.explosion:emit(c.position,75)
state.sM = 1
table.remove(self.powerUps,i)
else
p:calcMovement(true)
end
end
end
end
function PUpManager:addPowerUp()
local p = physics.body(CIRCLE,10)
p.id = "powerup"
--p.type = KINEMATIC
if math.random() > .5 then
p.position = vec2(math.random(0,1)*WIDTH,math.random()*HEIGHT)
else
p.position = vec2(math.random()*WIDTH,math.random(0,1)*HEIGHT)
end
local offset = 100
if p.position.x == 0 then
p.position.x = -offset
elseif p.position.y == WIDTH then
p.position.x = p.position.x + offset
end
if p.position.y == 0 then
p.position.y = -offset
elseif p.position.y == HEIGHT then
p.position.y = p.position.y + offset
end
p.chooseCity = function()
local minDist = math.huge
local dest
for i,v in ipairs(state.world.cities) do
if p.position:dist(v.position) < minDist and v.health > 0 then
minDist = p.position:dist(v.position)
dest = i
end
end
p.destCity = dest
end
p:chooseCity()
p.calcMovement = function(collide)
if math.floor(ElapsedTime) % 10 == 0 or collide then
p:chooseCity()
if p.destCity then
local dest = state.world.cities[p.destCity].position
local angleToCity = math.deg(vec2(0,0):angleBetween(p.position-dest))
p.angle = angleToCity+90
p.linearVelocity = vec2(
math.cos(math.rad(angleToCity-180)),
math.sin(math.rad(angleToCity-180)))*self.fallSpeed
end
end
end
local maxPUp = 0
for i,v in pairs(self.powerUpTypes) do
maxPUp = maxPUp + v.chance
end
-- Define selection
local selection = math.random() * maxPUp
-- Find object
local countPUp = 0
for i,v in pairs(self.powerUpTypes) do
countPUp = countPUp + v.chance
if selection <= countPUp and p.pUp == nil then
p.pUp = cleanTable(v)
end
end
p.glowMesh = mesh()
p.glowMesh.shader = shader(radialGradient())
p.glowMesh.shader.col1 = vec4(p.pUp.tint.r/255,p.pUp.tint.g/255,p.pUp.tint.b/255,.4)
p.glowMesh.shader.col2 = vec4(p.pUp.tint.r/255,p.pUp.tint.g/255,p.pUp.tint.b/255,0)
p.glowMesh.shader.pos = vec2(.5,.5)
p.glowMesh.shader.size = vec2(.5,.5)
p.glowMesh.shader.angle = 0
local glowAmnt = 3.5
p.glowMesh:addRect(0,0,p.radius*glowAmnt,p.radius*glowAmnt)
p.randFlashOffset = math.random()
table.insert(self.powerUps,p)
end
--# BG
BG = class()
function BG:init()
self.bgMesh = mesh()
self.bgMesh.shader = shader(radialGradient())
self.bgMesh.shader.col1 = vec4(.15, .15, .15, 1)
self.bgMesh.shader.col2 = vec4(0, 0, 0, 1)
self.bgMesh.shader.pos = vec2(.5,.5)
self.bgMesh.shader.size = vec2(.5,.5)
self.bgMesh.shader.angle = 0
self.bgMesh:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
self.starPos = vec2(WIDTH/2,HEIGHT/2)
self.stars = {}
self.starSpeed = 5000
self.minStarSpeed = 20
for i = 1,200 do
self:addStar(true)
end
end
function BG:draw()
--[[self.starPos = vec2(
WIDTH/2+math.cos(ElapsedTime*3)*100,
HEIGHT/2+math.sin(ElapsedTime*5)*100
)]]--
self.bgMesh:draw()
for i,v in ipairs(self.stars) do
v:draw(i)
end
end
function BG:addStar(init)
local s = {}
s.angle = math.random()*360
s.velocity = vec2(math.cos(math.rad(s.angle)),math.sin(math.rad(s.angle)))
s.speed = self.minStarSpeed+math.random()*self.starSpeed
s.position = vec2(
math.cos(math.rad(s.angle)),
math.sin(math.rad(s.angle))) *
math.random(1,20) + self.starPos
s.draw = function(_,i)
s.position = s.position +
s.velocity *
self.starSpeed *
(s.position:dist(self.starPos)/self.starPos.x) *
DeltaTime
pushStyle()
fill(255, 255, 255,
s.position:dist(self.starPos)/(self.starPos.x)*255)
ellipse(s.position.x,s.position.y,2)
popStyle()
if s.position.x < 0 or s.position.x > WIDTH or
s.position.y < 0 or s.position.y > HEIGHT then
table.remove(self.stars,i)
self:addStar()
end
end
table.insert(self.stars,s)
end
--# ColorFunctions
ColorFunctions = class()
function ColorFunctions:getColor(darkness)
local colDarkness = darkness or 100
return color(
math.random()*colDarkness+(255-colDarkness),
math.random()*colDarkness+(255-colDarkness),
math.random()*colDarkness+(255-colDarkness)
)
end
function ColorFunctions:flickerColor(col,amnt)
local flickerAmnt = amnt or 60
return color(
col.r+(math.random()-.5)*flickerAmnt,
col.g+(math.random()-.5)*flickerAmnt,
col.b+(math.random()-.5)*flickerAmnt
)
end
--# Shaders
function radialGradient()
return [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
uniform vec2 pos;
uniform float angle;
void main()
{
vColor = color;
vTexCoord = texCoord;
vec2 tpos = texCoord - pos;
float ct = cos(angle/360.0*6.28);
float st = sin(angle/360.0*6.28);
vTexCoord = vec2(ct*tpos.x - st*tpos.y,st*tpos.x + ct*tpos.y) + pos;
gl_Position = modelViewProjection * position;
}
]],
[[
precision highp float;
//uniform lowp sampler2D texture;
uniform vec4 col1;
uniform vec4 col2;
uniform vec2 pos;
uniform vec2 size;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
lowp vec4 col = vec4(0,0,0,0);
highp float dX = vTexCoord.x-pos.x;
highp float dY = vTexCoord.y-pos.y;
dX = dX / size.x;
dY = dY / size.y;
col = (col2-col1)*sqrt(dX*dX+dY*dY)+col1;
gl_FragColor = col;
}
]]
end
--# StateMachine
StateMachine = class()
function StateMachine:init()
self.states = {}
self.currentStateIndex = nil
self.currentState = nil
end
function StateMachine:draw()
if self.currentState then
self.currentState:draw()
end
end
function StateMachine:setState(index)
if self.currentState and self.currentState.exit then
self.currentState:exit()
end
if self.states[index] then
self.currentStateIndex = index
self.currentState = self.states[index]()
state = self.currentState
stateIndex = self.currentStateIndex
else
error("Non-existent state")
end
end
function StateMachine:registerStates(states)
for i,v in pairs(states) do
self.states[i] = v
end
end
function StateMachine:touched(t)
if self.currentState and self.currentState.touched then
self.currentState:touched(t)
end
end
function StateMachine:collide(c)
if self.currentState and self.currentState.collide then
self.currentState:collide(c)
end
end
--# AssetLoader
AssetLoader = class()
function AssetLoader:init(imgs,baseURL)
self.totalLoaded = 0
self.totalFailed = 0
self.loadError = nil
self.totalImgs = 0
for i,v in pairs(imgs) do
self.totalImgs = self.totalImgs + 1
end
for i,v in pairs(imgs) do
local img = readImage("Documents:" .. i)
if img == nil then
local url = baseURL..v
if url then
http.request(url, function (data)
local s = function()
local oImg = data
local c = ContentScaleFactor
local img = image(oImg.width/c,oImg.height/c)
pushStyle()
spriteMode(CORNER)
setContext(img)
sprite(oImg,0,0,img.width,img.height)
setContext()
popStyle()
saveImage("Documents:" .. i, img)
end
if pcall(s) then
print("Downloaded and saved "..i)
self.totalLoaded = self.totalLoaded + 1
else
self.totalFailed = self.totalFailed + 1
self.loadError = "Invalid image data. Got this: "..tostring(data)
print(self.loadError)
end
end,
function (e)
self.totalFailed = self.totalFailed + 1
self.loadError = tostring(e)
print(self.loadError)
end)
end
else
self.totalLoaded = self.totalLoaded + 1
end
end
end
function AssetLoader:getStatus()
return {
percent = (self.totalLoaded+self.totalFailed)/self.totalImgs,
percentLoaded = self.totalLoaded/self.totalImgs,
percentFailed = self.totalFailed/self.totalImgs,
loadError = self.loadError
}
end
--# Timer
Timer = class()
function Timer:init(interval,callback)
self.interval = interval
self.callback = callback
self.time = 0
self.occurances = 0
self.paused = false
end
function Timer:update()
if self.paused ~= true then
self.time = self.time + DeltaTime
if self.time >= self.interval then
self.occurances = self.occurances + 1
self.time = 0
if type(self.callback) == "function" then
self.callback(self.occurances)
end
return true
else
return false
end
end
end
function Timer:reset()
self.time = 0
self.occurances = 0
end
function Timer:pause()
self.paused = true
end
function Timer:resume()
self.paused = false
end
function Timer:isPaused()
return self.paused
end
function Timer:getTime()
return self.time
end
function Timer:getCount()
return self.occurances
end
--# Emitter
Emitter = class()
function Emitter:init(args)
self.particleMesh = mesh()
self.particleMesh.texture = args.tex
self.minLife = args.minLife or 1
self.maxLife = args.maxLife or 1
self.spread = args.spread or 360
self.angle = args.angle or 0
self.minSpeed = args.minSpeed or 10
self.maxSpeed = args.maxSpeed or 50
self.minSize = args.minSize or 10
self.maxSize = args.maxSize or 15
self.growth = args.growth or 1
self.startColor = args.startColor or color(255, 255, 255, 255)
self.endColor = args.endColor or color(0, 0, 0, 0)
self.streak = args.streak or false
self.streakMult = args.streakMult or 2
self.accel = args.accel or vec2(0,0)
self.rAccel = args.rAccel or vec2(0,0)
self.particles = {}
self.pCount = 0
for i = 1,1000 do
table.insert(self.particles, Particle())
end
end
function Emitter:emit(pos,count)
for i = 1,#self.particles do
local p = self.particles[i]
if p.dead then
self.pCount = math.max(i, self.pCount)
p.dead = false
p.pos = pos
p.life = math.random(self.minLife, self.maxLife)
p.size = math.random(self.minSize, self.maxSize)
p.maxLife = p.life
p.vel = vec2(0, math.random(self.minSpeed, self.maxSpeed))
p.vel = p.vel:rotate(math.rad(self.angle +
math.random(-self.spread/2,
self.spread/2)))
count = count - 1
if count == 0 then return end
end
end
end
function Emitter:draw()
-- update
self.particleMesh:clear()
for i = 1,self.pCount do
local p = self.particles[i]
if not p.dead then
p.prevPos = p.pos
p.pos = p.pos + p.vel * DeltaTime
p.vel = p.vel + (self.accel +
vec2(math.random(-self.rAccel.x,
self.rAccel.x),
math.random(-self.rAccel.y,
self.rAccel.y)))
* DeltaTime
p.life = math.max(0, p.life - DeltaTime)
p.size = p.size + DeltaTime * self.growth
if p.life == 0 then p.dead = true end
local interp = p.life / p.maxLife
p.col.r = interp * self.startColor.r +
(1-interp) * self.endColor.r
p.col.g = interp * self.startColor.g +
(1-interp) * self.endColor.g
p.col.b = interp * self.startColor.b +
(1-interp) * self.endColor.b
p.col.a = interp * self.startColor.a +
(1-interp) * self.endColor.a
local ind = self.particleMesh:addRect(p.pos.x,
p.pos.y,
p.size, p.size)
self.particleMesh:setRectColor(ind, p.col)
if self.streak then
local dir = (p.pos - p.prevPos)
local len = dir:len()
local pos = (p.pos + p.prevPos) * 0.5
local ang = math.atan2(dir.y, dir.x)
self.particleMesh:setRect(ind, pos.x, pos.y,
p.size * self.streakMult,
p.size, ang)
end
end
end
self.particleMesh:draw()
end
Particle = class()
function Particle:init()
self.pos = vec2(0,0)
self.prevPos = vec2(0,0)
self.vel = vec2(0,0)
self.life = 0
self.maxLife = 0
self.dead = true
self.col = color(0, 0, 0, 255)
self.size = 0
end
--# Functions
-- Functions
-- Fragment mesh
function fragmentMesh(m)
local fragments = {}
for i = 1,#m.vertices,3 do
local p1 = m.vertices[i]
local p1Col = m.colors[i]
local p2 = m.vertices[i+1]
local p2Col = m.colors[i+1]
local p3 = m.vertices[i+2]
local p3Col = m.colors[i+2]
p1Col.r = p1Col.r*255
p1Col.g = p1Col.g*255
p1Col.b = p1Col.b*255
p1Col.a = p1Col.a*255
p2Col.r = p2Col.r*255
p2Col.g = p2Col.g*255
p2Col.b = p2Col.b*255
p2Col.a = p2Col.a*255
p3Col.r = p3Col.r*255
p3Col.g = p3Col.g*255
p3Col.b = p3Col.b*255
p3Col.a = p3Col.a*255
local f = physics.body(POLYGON,
vec2(p1.x,p1.y),
vec2(p2.x,p2.y),
vec2(p3.x,p3.y))
--f.position = vec2(WIDTH/2,HEIGHT/2)
--f.restitution = 1.1
f.m = mesh()
f.m.vertices = {p1,p2,p3}
f.m.colors = {p1Col,p2Col,p3Col}
--f.m:setColors(255,255,255)
table.insert(fragments,f)
end
return fragments
end
-- Explode table
function _values(t)
local i = 0
return function() i = i + 1; return t[i] end
end
function _explodeNext(iter, t)
local v = iter(t)
if v ~= nil then
return v, _explodeNext(iter, t)
else
return
end
end
function explodeTable(t)
local iter = _values(t)
return _explodeNext(iter, t)
end
-- Test Touch Region
function tR(tPos,pos,size)
if tPos.x < pos.x + size.x / 2 and
tPos.x > pos.x - size.x / 2 and
tPos.y < pos.y + size.y / 2 and
tPos.y > pos.y - size.y / 2 then
return true
else
return false
end
end
-- Mangage touches
function touchReady(t)
local ready = true
if tUsed then
for i,v in pairs(tUsed) do
if v.id == t.id then
ready = false
end
end
else
tUsed = {}
end
return ready
end
function touchUsed(t)
tUsed[t.id] = t
end
-- Retro Text
function retroText(t,x,y,offset)
local offset = offset or 0
pushStyle()
textAlign(LEFT)
local str = t
local fStr = str
if math.floor(ElapsedTime*2+offset) % 2 == 0 then
str = str.."_"
end
text(str,x-textSize(fStr)/2+textSize(str)/2,y)
popStyle()
end
-- Glow Text
function glowText(t,x,y,blur,glowCol,angleRes)
local blur = blur or 20
local glowColor = glowColor or color(fill())
if not glowCol then
glowColor.a = 30
end
local angleRes = angleRes or 6
pushMatrix()
pushStyle()
translate(x,y)
pushStyle()
for d = 1,blur do
fill(glowColor.r,glowColor.g,glowColor.b,glowColor.a/blur*(blur-d))
for a = 1,angleRes do
text(t,math.cos(math.rad(360/angleRes*a))*d,math.sin(math.rad(360/angleRes*a))*d)
end
end
popStyle()
text(t,0,0)
popStyle()
popMatrix()
end
-- Unreference table
function cleanTable(t)
local nt = {}
for i,v in pairs(t) do
if type(v) == "table" then
nt[i] = cleanTable(v)
else
nt[i] = v
end
end
return nt
end
--# ccConfig
--[[
###########################################
##Codea Community Project Config Settings##
###########################################
##You can use # to comment out a line
##Use 1 for true and 0 for false
###########################################
# Add project info below #
#==========================================
ProjectName: KickAsteroids
Version: Alpha 1.0 (Competition Edition)
Comments: I didn't get to everything I wanted, but here it is.
Author: Zoyt
##License Info: http://choosealicense.com
##Supported Licneses: MIT, GPL, Apache, NoLicense
License: MIT
#==========================================
###########################################
# Settings #
[Settings]=================================
##Codea Community Configuration settings
##Format: Setting state
Button 0
NotifyCCUpdate 1
ResetUserOption 0
AddHeaderInfo 1
[/Settings]================================
###########################################
# Screenshots #
[Screenshots]==============================
##Screenshots from your project.
##Format: url
##Example: http://www.dropbox.com/screenshot.jpg
[/Screenshots]=============================
###########################################
# Video #
[Video]====================================
##Link to a YouTube.com video.
##Format: url
##Example: http://www.youtube.com/videolink
[/Video]===================================
###########################################
# Dependencies #
[Dependencies]=============================
##Include the names of any dependencies here
##Format: Dependency
##Example: Codea Community
[/Dependencies]============================
############################################
# Tabs #
[Tabs]======================================
##Select which tabs are to be uploaded.
##Keyword 'not' excludes a tab or tabs. Keyword 'add' includes a tab or tabs.
##not * will exclude all tabs to allow for individual selection
##not *tab1 will look for any tab with tab1 on the end.
##not tab1* will look for any tab with tab1 at the beginning.
##Format: (add/not)(*)tab(*)
##Example: not Main --this will remove main.
##Example: not *tab1 --this will remove any tab with "tab1" on the end.
##Example: add Main --This will add Main back in.
[/Tabs]=====================================
#############################################
# Assets #
[Assets]=====================================
##Directory, path and url info for any assets besides the standard Codea assets.
##Format: Folder:sprite URL
##Example: Documents:sprite1 http://www.somewebsite.com/img.jpg
[/Assets]====================================
--]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment