Created
December 31, 2013 09:08
-
-
Save NathanFlurry/8194326 to your computer and use it in GitHub Desktop.
Entry for the 2013 Codea Holiday Codea Cook Off Competition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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