Created
May 21, 2012 12:22
-
-
Save shawndumas/2762088 to your computer and use it in GitHub Desktop.
Codea Game: StarFighter
This file contains 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
--# HeroAttack | |
HeroAttack = class() | |
function HeroAttack:init(hero) | |
self.duration = 0.5 | |
self.currentTime = 0 | |
self.endTime = self.currentTime + self.duration | |
self.hero = hero | |
self.size = hero.size | |
self.blastSize = self.size * ((self.hero.energy / 25) + 1) | |
self.currentSize = self.size | |
end | |
function HeroAttack:draw() | |
self.currentTime = self.currentTime + 1 / 30 | |
local attackTime = self.currentTime / self.duration | |
pushStyle() | |
noFill() | |
stroke(255, 0, 0, 255 * (1 - attackTime)) | |
strokeWidth(10 * (1 - attackTime)) | |
self.currentSize = self.blastSize * attackTime + (self.size * (1 - attackTime)) | |
ellipse(self.hero.pos.x, self.hero.pos.y, self.currentSize) | |
popStyle() | |
end | |
function HeroAttack:isDone() | |
return self.currentTime > self.endTime * 2 | |
end | |
--# PowerUp | |
PowerUp = class() | |
function PowerUp:init(type) | |
self.type = type or "shield" | |
self.size = 50 | |
self.pos = vec2(math.random(35, WIDTH - 35), HEIGHT) | |
self.pos.y = self.pos.y + self.size | |
self.cull = false | |
end | |
function PowerUp:update(hero) | |
local y = nil | |
local x = nil | |
local size = nil | |
if self.type == "energy" then | |
y = math.random(0, 4) | |
x = math.random(-35, 35) | |
size = math.random(5, 50) | |
elseif self.type == "shield" then | |
y = math.random(-5, 10) | |
x = math.random(-4, 4) | |
size = math.random(10, 100) | |
elseif self.type == "fire" then | |
y = math.random(-15, 30) | |
x = math.random(-35, 35) | |
size = math.random(50, 150) | |
end | |
self:shouldCull() | |
self.pos.y = self.pos.y - y | |
self.pos.x = self.pos.x + x | |
self.size = size | |
if hero and self.pos:dist(hero.pos) < hero.size then | |
if self.type == "energy" then | |
hero.energy = hero.energy + 25 | |
sound(SOUND_POWERUP, 23401) | |
elseif self.type == "shield" then | |
hero.shields = hero.shields + 25 | |
sound(SOUND_POWERUP, 23414) | |
elseif self.type == "fire" then | |
hero.fire = hero.fire + 2 | |
sound(SOUND_POWERUP, 23418) | |
end | |
self.cull = true | |
end | |
end | |
function PowerUp:draw() | |
pushMatrix() | |
pushStyle() | |
if self.type == "energy" then | |
tint(255, 0, 0, 255) | |
elseif self.type == "fire" then | |
tint(255, 110, 0, 255) | |
elseif self.type == "shield" then | |
noTint() | |
end | |
sprite("Tyrian Remastered:Energy Orb 1", self.pos.x, self.pos.y, self.size) | |
popStyle() | |
popMatrix() | |
end | |
function PowerUp:shouldCull() | |
if (self.pos.y + self.size) < 0 then self.cull = true end | |
self.cull = false | |
end | |
--# Rocks | |
Rock = class() | |
function Rock:init() | |
self.size = 60 | |
self.pos = vec2(math.random(WIDTH), HEIGHT) | |
self.pos.y = self.pos.y + self.size | |
self.spriteName = "Tyrian Remastered:Rock " .. math.random(3, 5) | |
self.onFire = false | |
self.cull = false | |
self.hit = false | |
end | |
function Rock:update(spd) | |
self.pos.y = self.pos.y - spd | |
self:shouldCull() | |
end | |
function Rock:draw() | |
local spriteNames = { | |
"Tyrian Remastered:Fire Rock", | |
"Tyrian Remastered:Firestroid" | |
} | |
if self.onFire then self.spriteName = spriteNames[math.random(2)] end | |
pushMatrix() | |
sprite(self.spriteName, self.pos.x, self.pos.y, self.size) | |
popMatrix() | |
end | |
function Rock:shouldCull() | |
if (self.pos.y + self.size) < 0 then self.cull = true end | |
self.cull = false | |
end | |
Rocks = class() | |
function Rocks:init() | |
self.frame = 0 | |
self.rocks = {} | |
self.explosions = {} | |
end | |
function Rocks:draw(speed, hero) | |
self.frame = (self.frame + 1) % 128 | |
if self.frame % 40 == 0 then table.insert(self.rocks, Rock()) end | |
local function explodeRock (rk) | |
rk.cull = true | |
table.insert(self.explosions, Explosion(rk.pos)) | |
end | |
if hero then | |
for i, v in ipairs(self.rocks) do | |
if v.pos:dist(hero.pos) < hero.size and not v.cull then | |
explodeRock(v) | |
end | |
end | |
for i, v in ipairs(self.rocks) do | |
for j, fbPos in ipairs(hero.fireballs) do | |
if v.pos:dist(fbPos) < 25 and not v.cull then | |
table.remove(hero.fireballs, j) | |
sound(SOUND_EXPLODE, 45815) | |
if v.onFire then | |
explodeRock(v) | |
else | |
v.onFire = true | |
end | |
end | |
end | |
end | |
end | |
for i, v in ipairs(self.rocks) do | |
if not v.cull then | |
v:update(speed) | |
v:draw() | |
end | |
end | |
for i, e in ipairs(self.explosions) do | |
e:draw() | |
if e:isDone() then table.remove(self.explosions, i) end | |
end | |
end | |
--# Explosion | |
Explosion = class() | |
function Explosion:init(pos) | |
self.pos = pos | |
self.opacity = 255 | |
self.time = 0 | |
self.lines = {} | |
sound(SOUND_EXPLODE, 969) | |
for i = 1, 20 do | |
dir = vec2(0,1) | |
dir = dir:rotate(math.rad(math.random(360))) | |
table.insert(self.lines, (dir * math.random(2, 7))) | |
end | |
end | |
function Explosion:isDone() | |
return (self.opacity <= 0) | |
end | |
function Explosion:draw() | |
self.time = (self.time + 3) | |
pushStyle() | |
lineCapMode(ROUND) | |
strokeWidth(6) | |
smooth() | |
stroke(255, 255, 255, math.max(self.opacity, 0)) | |
p = self.pos | |
for i, v in ipairs(self.lines) do | |
vt = (p + v * self.time) | |
line(p.x, p.y, vt.x, vt.y) | |
end | |
self.opacity = (255 * (1 - (self.time / 50))) | |
popStyle() | |
end | |
--# Main | |
cfg = { | |
maxSpeed = 75, | |
delayMin = 10, | |
newShield = 500, | |
newEnergy = 1000, | |
newFire = 1500, | |
heroSize = 75 | |
} | |
gbl = {} | |
function setup() | |
displayMode(FULLSCREEN) | |
supportedOrientations(LANDSCAPE_RIGHT) | |
gbl = { | |
stars = StarField(), | |
hero = Hero(180, cfg.heroSize), | |
rks = Rocks(), | |
frame = 0, | |
speed = 15, | |
playing = true, | |
score = 0, | |
level = 1 | |
} | |
end | |
function levelUp() | |
gbl.level = gbl.level + 1 | |
gbl.speed = gbl.speed + 5 | |
if gbl.playing then gbl.score = gbl.score + 789 end | |
if gbl.speed > cfg.maxSpeed then gbl.speed = cfg.maxSpeed end | |
end | |
function draw() | |
background(0, 0, 0, 255) | |
gbl.frame = gbl.frame + 1 | |
if (gbl.frame % cfg.newShield) == 0 then | |
if (not gbl.hero or gbl.hero.shields < 100) then | |
gbl.powerUp = PowerUp("shield") | |
end | |
end | |
if (gbl.frame % cfg.newEnergy) == 0 then | |
if gbl.playing then levelUp() end | |
if (not gbl.hero or gbl.hero.energy < 100) then | |
gbl.powerUp = PowerUp("energy") | |
end | |
end | |
if (gbl.frame % cfg.newFire) == 0 then | |
if (not gbl.hero or gbl.hero.fire == 0) then | |
gbl.powerUp = PowerUp("fire") | |
end | |
end | |
if gbl.powerUp then | |
gbl.powerUp:update(gbl.hero) | |
if not gbl.powerUp.cull then | |
gbl.powerUp:draw() | |
else | |
gbl.powerUp = nil | |
end | |
end | |
gbl.stars:update(gbl.speed) | |
gbl.stars:draw() | |
fontSize(30) | |
textMode(CORNER) | |
local shields = 0 | |
local energy = 0 | |
local fire = 0 | |
if gbl.playing then | |
shields = gbl.hero.shields | |
energy = gbl.hero.energy | |
fire = gbl.hero.fire | |
gbl.rks:draw(gbl.speed, gbl.hero) | |
else | |
gbl.rks:draw(gbl.speed, false) | |
end | |
text("Level: " .. gbl.level, 10, HEIGHT - 50) | |
text("Score: " .. gbl.score, 10, HEIGHT - 100) | |
local at = "Shields: " .. shields .. "%" | |
local atWidth = textSize(at) | |
text(at, WIDTH - atWidth - 10, HEIGHT - 50) | |
local at = "Energy: " .. energy .. "%" | |
local atWidth = textSize(at) | |
text(at, WIDTH - atWidth - 10, HEIGHT - 100) | |
if energy > 0 then | |
pushStyle() | |
strokeWidth(1) | |
fill(255, 0, 0, 100) | |
ellipse(80, 100, 90, 90) | |
popStyle() | |
text(gbl.hero.energy / 25, 72, 82) | |
end | |
if fire > 0 then | |
pushStyle() | |
strokeWidth(1) | |
fill(255, 110, 0, 100) | |
ellipse(WIDTH - 80, 100, 90, 90) | |
popStyle() | |
text(gbl.hero.fire, WIDTH - 88, 82) | |
end | |
if gbl.playing then | |
gbl.score = gbl.score + math.floor(gbl.speed * .1) | |
gbl.hero:update() | |
gbl.hero:draw() | |
for i, v in ipairs(gbl.rks.rocks) do | |
if not v.hit and v.pos:dist(gbl.hero.pos) < gbl.hero.size then | |
if gbl.hero.attack and not gbl.hero.attack:isDone() then | |
else | |
local function boom () | |
gbl.hero.boom = Explosion(gbl.hero.pos) | |
sound(SOUND_EXPLODE, 969) | |
gbl.playing = false | |
end | |
if gbl.hero.shields ~= 0 then | |
local dmg = 25 | |
if v.onFire then dmg = 50 end | |
local shields = gbl.hero.shields - dmg | |
if shields > 0 then | |
gbl.hero.shields = shields | |
else | |
gbl.hero.shields = 0 | |
boom() | |
end | |
v.hit = true | |
else | |
boom() | |
end | |
end | |
if v.cull then table.remove(gbl.rks.rocks, i) end | |
end | |
end | |
else | |
fontSize(60) | |
textMode(CENTER) | |
text("GAME OVER", WIDTH/2, HEIGHT/2) | |
fontSize(40) | |
text("Tap screen to restart.", WIDTH/2, (HEIGHT/2 - 80)) | |
if gbl.speed > 5 then gbl.speed = gbl.speed - 1 end | |
gbl.hero = nil | |
end | |
end | |
function touched(touch) | |
if CurrentTouch.state == BEGAN then | |
if not gbl.playing then | |
setup() | |
else | |
gbl.hero:touched(touch) | |
end | |
end | |
end | |
--# Hero | |
Hero = class() | |
function Hero:init(rotDeg, size) | |
self.fromTop = 700 | |
self.rotDeg = rotDeg | |
self.shields = 100 | |
self.energy = 100 | |
self.fire = 9 | |
self.size = size | |
self.attack = nil | |
self.boom = nil | |
self.pos = nil | |
self.fireballs = {} | |
end | |
function Hero:spawnFireball() | |
if self.fire > 0 and #self.fireballs < 2 then | |
sound(SOUND_EXPLODE, 18) | |
table.insert(self.fireballs, vec2(self.pos.x, self.pos.y + 30)) | |
self.fire = self.fire - 1 | |
end | |
end | |
function Hero:drawFireballs() | |
pushStyle() | |
for i, v in ipairs(self.fireballs) do | |
v.y = v.y + 10 | |
tint(255, 255, 255, 175) | |
sprite("Tyrian Remastered:Bullet Fire C", v.x, v.y, 25) | |
if v.y > (HEIGHT + 10) then | |
table.remove(self.fireballs, i) | |
end | |
end | |
popStyle() | |
end | |
function Hero:update() | |
local x = ((Gravity.x * self.fromTop) + (WIDTH / 2)) | |
local y = ((Gravity.y * self.fromTop) + HEIGHT) | |
self.pos = vec2(x, y) | |
end | |
function Hero:draw() | |
self:drawFireballs() | |
pushMatrix() | |
pushStyle() | |
translate(self.pos.x, self.pos.y) | |
rotate(self.rotDeg) | |
if self.shields > 0 then | |
sprite("Tyrian Remastered:Plane Boss") | |
pushMatrix() | |
pushStyle() | |
fill(50, 50, 100, self.shields) | |
stroke(50, 50, 200, 150 + self.shields) | |
strokeWidth(0.1) | |
translate(-self.pos.x, -self.pos.y) | |
ellipse(self.pos.x, self.pos.y - 10, 111) | |
popStyle() | |
popMatrix() | |
else | |
tint(255, 150, 150, 255) | |
sprite("Tyrian Remastered:Plane Boss Destroyed") | |
end | |
popStyle() | |
popMatrix() | |
pushMatrix() | |
pushStyle() | |
translate(self.pos.x, self.pos.y - 117) | |
scale(.19, .59) | |
rotate(180) | |
tint(255, 150, 150, math.random(100, 255)) | |
sprite("Small World:Beam") | |
popStyle() | |
popMatrix() | |
if self.attack then | |
self.attack:draw() | |
if self.attack:isDone() then self.attack = nil end | |
end | |
if self.boom then | |
self.boom:draw() | |
if self.boom:isDone() then self.boom = nil end | |
end | |
end | |
function Hero:energyAttack() | |
if self.attack == nil then | |
sound(SOUND_RANDOM, 156) | |
self.attack = HeroAttack(self) | |
self.energy = self.energy - 25 | |
end | |
end | |
function Hero:touched(touch) | |
local isFire = touch.x > WIDTH / 2 | |
if isFire then | |
self:spawnFireball() | |
else | |
if self.energy ~= 0 then self:energyAttack() end | |
end | |
end | |
--# StarField | |
StreamLine = class() | |
function StreamLine:init(pos, vel) | |
local function r () return math.random(255) end | |
local clrs = { | |
color(255, 0, 0, r()), | |
color(0, 0, 255, r()) | |
} | |
for i = 3, 150 do | |
table.insert(clrs, i, color(179, 153, 180, r())) | |
end | |
self.position = pos | |
self.velocity = vel | |
self.color = clrs[math.random(1, #clrs)] | |
end | |
function StreamLine:update() | |
self.position.y = self.position.y - self.velocity | |
end | |
function StreamLine:draw() | |
p = self.position | |
line(p.x, p.y, p.x, p.y + self.velocity - 4) | |
end | |
function StreamLine:shouldCull() | |
if (self.position.y + self.velocity) < 0 then | |
return true | |
end | |
return false | |
end | |
StarField = class() | |
function StarField:init() | |
self.minSpeed = 5 | |
self.spawnRate = 1 | |
self.speed = 15 | |
self.lines = {} | |
end | |
function StarField:updateAndCull() | |
toCull = {} | |
for i, v in ipairs(self.lines) do | |
if v:shouldCull() then | |
table.remove(self.lines, i) | |
else | |
v:update() | |
end | |
end | |
end | |
function StarField:update(speed) | |
self.speed = speed | |
for i = 1, self.spawnRate do | |
vel = math.random(self.minSpeed, self.speed) | |
spawn = vec2(math.random(WIDTH), HEIGHT + vel) | |
table.insert(self.lines, StreamLine(spawn, vel)) | |
end | |
self:updateAndCull() | |
end | |
function StarField:draw() | |
pushStyle() | |
noSmooth() | |
strokeWidth(1) | |
lineCapMode(ROUND) | |
for i, v in ipairs(self.lines) do | |
stroke(v.color) | |
v:draw() | |
end | |
popStyle() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment