0,]'H>S}Wup8d%qy)6o;]AsVl3> """!"CL"!"AD"!"01"!""!"!""        "   ""  """" "   " """     """!""!""!""!""!"!""   ""!"   w-- title: SpaceShooter.tic (WIP) -- author: Louis Vuillermet -- desc: You are a triangle, shoot enemies and grow your colony -- script: lua -- input: gamepad -- version: 0.1 -- Game Manager ------------- t=0 names = { "Khaur'un", "Kraqhed", "Vragzuns", "Gundhi", "Aarqus"} function randomName() local rnd = math.random(1,#names) local name = names[rnd] table.remove(names,rnd) return name end game = { mode = 0,-- 0 Splash 1 Space Center 2 Running 3 Death size = 320, rate = 480, score = 0, kills = 0, funds = 500 } function game:update() if game.mode == 0 then splash() elseif game.mode == 1 then menu:update() elseif game.mode == 2 then -- GAME MANAGER if t%game.rate == 0 or #enemies <= 1 then enemy:spawn({ x = math.random(-game.size,game.size), y = math.random(-1,1)*game.size*0.75 }) end t=t+1 elseif game.mode == 3 then death() end end -- Splash ------------------- function splash() --rect(0,48,240,9,2) local label="SPACESHOOTER.TIC" local size=print(label,0,-6,0,false,2,false) print(label,(240-size)//2+rand(0,2),50+rand(-1,1),9,false,2,false) print(label,(240-size)//2-rand(0,2),50,11,false,2,false) print(label,(240-size)//2,50,10,false,2,false) label="PRESS TO START" size=print(label,0,-6) print(label,(240-size)//2,74,15) print(label,(240-size)//2,73,13) spr(97,108+time()/240%2,72,0) if btnp(5) then game.mode = 2 end end -- Death -------------------- function death() for i=0,136,2 do line(0,i,240,i,0) end local label="YOU DIED!" local size=print(label,0,-6) print(label,(240-size)//2,66,15) print(label,(240-size)//2,65,13) end -- Camera ------------------- cam = { x = 0, y = -68, vx = 0, vy = 0, lerp = 0.1 } function cam:update() if game.mode == 2 then self.x = lerp(self.x, p.tx - 120, self.lerp) self.y = lerp(self.y, p.ty - 68, self.lerp) end if game.mode >= 1 then line(game.size - cam.x, game.size - cam.y, game.size - cam.x, -game.size - cam.y, 15) line(-game.size - cam.x, game.size - cam.y, -game.size - cam.x, -game.size - cam.y, 15) line(game.size - cam.x, game.size - cam.y, -game.size - cam.x, game.size - cam.y, 15) line(game.size - cam.x, -game.size - cam.y, -game.size - cam.x, -game.size - cam.y, 15) local grid = { x = 240 - cam.x % 240, y = 136 - cam.y % 136 } line(grid.x - 16, grid.y, grid.x + 16, grid.y, 15) line(grid.x, grid.y - 16, grid.x, grid.y + 16, 15) rect(grid.x + 3, grid.y - 3, 5, 7, 0) rect(grid.x - 2, grid.y + 3, 5, 7, 0) print(math.floor(cam.x/240) + 1, grid.x + 4, grid.y - 2, 15, false, 1, true) print(math.floor(cam.y/136) + 1, grid.x - 1, grid.y + 4, 15, false, 1, true) end end -- hud ---------------------- buttons = {} hud = { hover = false, hidden = 1, unit = 0, } function hud:hupdate() if self.hover then self.hover = false self.hidden = clamp(self.hidden * 0.9 - 0.01, 0,1) else self.hidden = clamp(self.hidden + 0.1, 0,1) end if units[self.unit] ~= nil then local u = units[self.unit] local width = print(u.name,0,-6,0,false,1,true) local posx = u.x - cam.x - width/2 + 1 -- 120-width/2 local posy = u.y - cam.y - 16 - u.rad -- 4+(-16)*self.hidden clip(posx,posy,width,8) posy = posy + 16*self.hidden print(u.name,posx,posy,p.color,false,1,true) cline(posx, posy+6, width-1, "life", u.health/u.healthMax) clip() end end function hud:update() if game.mode >= 1 then self:hupdate() local offset,a,b = 0 local function add(n) offset = offset + n end -- BUTTONS if buttons.shoot then buttons.shoot = false spr(96, 4, 4 + offset, 0) print("Shoot", 14, 5, 15, false, 1, true) add(9) end if buttons.collect then buttons.collect = false spr(97, 4, 4 + offset, 0) print("Collect", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.unload then buttons.unload = false spr(97, 4, 4 + offset, 0) print("Unload", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.heal then buttons.heal = false spr(97, 4, 4 + offset, 0) print("Collect health", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.recovery then buttons.recovery = false spr(98, 4, 4 + offset, 0) print("Recovery", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.move then buttons.move = false spr(99, 4, 4 + offset, 0) print("Move", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.deploy then buttons.deploy = false spr(97, 4, 4 + offset, 0) print("Deploy", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.open then buttons.open = false spr(99, 4, 4 + offset, 0) print("Menu", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.close then buttons.close = false spr(97, 4, 4 + offset, 0) print("Close", 14, 5 + offset, 15, false, 1, true) add(9) end if buttons.cancel then buttons.cancel = false spr(98, 4, 4 + offset, 0) print("Cancel", 14, 5 + offset, 15, false, 1, true) add(9) end offset = 0 -- GOLD HEALTH spr(240,4 + offset,128,0,1,0,0,1,1) a = print(p.gold .. "/" .. p.goldMax, 10 + offset, 128, 14, false, 1, true) add(a+10) spr(243,4 + offset,128,0,1,0,0,1,1) a = print(p.health .. "/" .. p.healthMax, 11 + offset, 128, 14, false, 1, true) add(a+11) if btn(2) and btnp(3) or btnp(2) and btn(3) then moreinfo = not moreinfo end if moreinfo then spr(246,4 + offset,128,0,1,0,0,1,1) print(t//6/10 .. "s", 11 + offset,128,14,false,1,true) end offset = 0 -- TOTAL CASH spr(242,230,126,0,1,0,0,1,1) a = print(game.funds, 0, -8, 14, false, 1 , true) print(game.funds, 230 - a - 1, 127, 14, false, 1 , true) end end -- Menu --------------------- menu = { x = 0, y = 0, selected = 0, priceOffset = 78, t = 0 } items = { -- 1 { string = "Build TURRET", price = 200, enabled=true, func = function() local a = math.random() * 2 * math.pi unit:spawn({ type = 0, x = station.x, y = station.y, zx = station.x + math.cos(a) * station.rad * 2, zy = station.y + math.sin(a) * station.rad * 2, move = true }) game.mode = 2 end }, -- 2 { string = "Build AUTOMINER", price = 150, enabled=true, func = function() -- function here local a = math.random() * 2 * math.pi unit:spawn({ type = 10, x = station.x, y = station.y, zx = station.x + math.cos(a) * station.rad * 2, zy = station.y + math.sin(a) * station.rad * 2, move = true }) game.mode = 2 end }, -- 3 { string = "Build HEALING MODULE", price = 1000, enabled=false, func = function() -- function here end }, -- 4 { string = "Upgrade STATION", price = 1000, enabled=true, func = function() items[3].enabled=true items[5].enabled=true station.level = station.level + 1 items[4].price = 1200 end }, -- 5 { string = "Upgrade PLAYER", price = 500, enabled=false, func = function() -- function here end }, -- 6 { string = "Repair STATION", price = 800, enabled=true, func = function() -- function here end }, } function menu:update() menu.x = station.x-cam.x menu.y = station.y-cam.y+16 for i=0,136,2 do line(0,i,240,i,0) end if btnp(5) then game.mode = 2 end local count = 0 for k,item in pairs(items) do if item.enabled then local color = 14 local push = 0 if count == menu.selected and game.funds >= item.price then buttons.buy = true color = p.color push = math.sin(menu.t/15)*2 if btnp(6) then game.funds = game.funds - item.price item.func() end elseif count == menu.selected and game.funds < item.price then color = 2 elseif game.funds < item.price then color = 15 end print("> " .. item.string,menu.x+push,menu.y+8*count,color,false,1,true) spr(241,menu.x+menu.priceOffset,menu.y+8*count,0) print(item.price,menu.x+menu.priceOffset+6,menu.y+8*count,15,false,1,true) count = count + 1 end end if buttons.buy then buttons.buy = false spr(98, menu.x, menu.y+8*count+1) print("Buy item",menu.x+10,menu.y+8*count+2,14,false,1,true) else spr(98+4, menu.x, menu.y+8*count+1) print("Buy item",menu.x+10,menu.y+8*count+2,15,false,1,true) end spr(97, menu.x+40, menu.y+8*count+1) print("Close menu",menu.x+10+40,menu.y+8*count+2,14,false,1,true) if btnp(0) then menu.selected=clamp(menu.selected-1,0,count-1) end if btnp(1) then menu.selected=clamp(menu.selected+1,0,count-1) end cam.x = lerp(cam.x,station.x-72,0.1) cam.y = lerp(cam.y,station.y-56+count*4+clamp(menu.selected-6,0,99)*8,0.1) menu.t = menu.t + 1 end -- Space Center ------------- station = { x = 32, y = 32, rad = 8, level = 0, health = 2000, storedUnits = {}, name = randomName() } function station:update() if game.mode == 2 then -- PLAYER ACTIONS if distance(self.x-p.x,self.y-p.y) < self.rad + p.rad + 4 then circb(station.x - cam.x, station.y - cam.y, station.rad + 2, 15) if p.gold > 0 then buttons.unload = true if btn(5) then game.funds = game.funds + p.gold game.score = game.score + p.gold p.gold = 0 end end buttons.open = true if btnp(7) or mb then game.mode = 1 end end -- STORED UNITS UPDATE for u,stored in pairs(self.storedUnits) do if stored.gold > 0 then game.funds = game.funds + stored.gold stored.gold = 0 end if stored.health < stored.healthMax then stored.health = clamp(stored.health + 0.1, 0, stored.healthMax) else station:restore(u) end end end if game.mode >= 1 then -- DRAW circb(station.x - cam.x, station.y - cam.y, station.rad, p.color) rect(station.x - cam.x - 1, station.y - cam.y - 1, station.rad+4, station.rad+4, 0) print(string.upper(station.name), station.x - cam.x, station.y - cam.y, p.color, false, 1, true) print("ZERO", station.x - cam.x, station.y - cam.y + 6, p.color, false, 1, true) end end function station:takedamage(damage) cls(2) station.health = station.health - damage if station.health <= 0 then game.mode = 3 end end function station:store(unit) table.insert(self.storedUnits,unit) end function station:restore(stored) unit:spawn(self.storedUnits[stored]) table.remove(self.storedUnits,stored) end -- Mines -------------------- mines = {} mine = { x = 0, y = 0, rad = 10, t = 0, rate = 120, name = "", } function mine:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function mine:spawn(o) table.insert(mines, self:new(o)) end function mine:die(k) table.remove(mines, k) end function mine:update(k) if game.mode == 2 then -- objectS if self.t <= 0 then local a = math.random() * 2 * math.pi local nvx = math.sin(a) local nvy = math.cos(a) object:spawn({ x = self.x + nvx * self.rad, y = self.y + nvy * self.rad, vx = nvx*0.08, vy = nvy*0.08, type = "gold", value = 1 }) self.t = self.rate + 1 end if t%10 == 0 then local a = math.random()*math.pi*2 particle:spawn({ x = self.x + math.cos(a) * self.rad * 2, y = self.y + math.sin(a) * self.rad * 2, vx = - math.cos(a) * 0.2, vy = - math.sin(a) * 0.2, drag = 1.02, timetolive = 60, color = 14, parallax = 0, postupdate = function(k) if k.timetolive < 30 and k.timetolive % 6 == 0 then k.color = clamp(k.color - 1,8,11) end end, }) end -- TIME self.t = self.t - 1 end if game.mode >= 1 then -- DRAW circb(self.x - cam.x, self.y - cam.y, self.rad, p.color) --circ(self.x - cam.x, self.y - cam.y, 2 + (math.sin(t/self.rate*2)+1)/2 * (self.rad - 3), 15) rect(self.x - cam.x - 1, self.y - cam.y - 1, self.rad+4, 7, 0) print(string.upper(self.name), self.x - cam.x, self.y - cam.y, p.color, false, 1, true) end end -- Player ------------------- p = { x = 0, y = 0, a = 0, rad = 4, vx = 0, vy = 0, va = 0, v = 0, tx = 0, ty = 0, -- CAMERA TARGET force = 0.2, forceA = 0.4, drag = 0.96, color = 10, gold = 0, goldMax = 30, health = 30, healthMax = 30, weapon = { t = 0, rate = 10, force = 4, sight = true, sightlength = 64, }, holding = false, busy = false, } function p:update() if game.mode == 2 then -- INPUT if btn(0) then p.vx = p.vx + math.cos(p.a) * 0.2 * p.force p.vy = p.vy + math.sin(p.a) * 0.2 * p.force end if btn(1) then p.vx = p.vx - math.cos(p.a) * 0.06 * p.force p.vy = p.vy - math.sin(p.a) * 0.06 * p.force end if btn(2) then p.va = p.va - 0.01 * p.forceA end if btn(3) then p.va = p.va + 0.01 * p.forceA end if btn(4) then p:shoot() end -- CAMERA TARGET p.tx = p.x + 24 * math.cos(p.a) p.ty = p.y + 24 * math.sin(p.a) -- DRAG p.vx = p.vx * p.drag p.vy = p.vy * p.drag p.va = p.va * p.drag * p.drag -- MOVEMENT p.x = clamp(p.x + p.vx, -game.size, game.size) p.y = clamp(p.y + p.vy, -game.size, game.size) p.a = p.a + p.va -- HUD buttons.shoot = true -- TIME p.weapon.t = p.weapon.t - 1 p.busy = false end if game.mode >= 1 then -- DRAW p:draw() end end function p:shoot() if p.weapon.t <= 0 then bullet:spawn({ x = p.x, y = p.y, vx = math.cos(p.a) * p.weapon.force, vy = math.sin(p.a) * p.weapon.force, a = p.a }) p.weapon.t = p.weapon.rate + 1 end end function p:takedamage(damage) cls(2) p.health = p.health - damage if p.health <= 0 then game.mode = 3 end end function p:draw() p.s1 = { x = p.x + math.cos(p.a + 120 * (math.pi / 180)) * p.rad * 0.75, y = p.y + math.sin(p.a + 120 * (math.pi / 180)) * p.rad * 0.75 } p.s2 = { x = p.x + math.cos(p.a) * p.rad, y = p.y + math.sin(p.a) * p.rad } p.s3 = { x = p.x + math.cos(p.a - 120 * (math.pi / 180)) * p.rad * 0.75, y = p.y + math.sin(p.a - 120 * (math.pi / 180)) * p.rad * 0.75 } line(p.x - cam.x, p.y - cam.y, p.s1.x - cam.x, p.s1.y - cam.y, p.color) line(p.x - cam.x, p.y - cam.y, p.s3.x - cam.x, p.s3.y - cam.y, p.color) line(p.s2.x - cam.x, p.s2.y - cam.y, p.s1.x - cam.x, p.s1.y - cam.y, p.color) line(p.s2.x - cam.x, p.s2.y - cam.y, p.s3.x - cam.x, p.s3.y - cam.y, p.color) if p.weapon.sight then for i = p.rad + 4, p.rad + 4 + p.weapon.sightlength,8 do local x = p.x + math.cos(p.a) * i - cam.x local y = p.y + math.sin(p.a) * i - cam.y pix(x,y,15) end end end -- Bullets ------------------ bullets = {} bullet = { x = 0, y = 0, vx = 0, vy = 0, drag = 0.99, color = 12, timetolive = 120, rad = 0, damage = 5 } function bullet:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function bullet:spawn(o) table.insert(bullets, self:new(o)) end function bullet:die(k) table.remove(bullets, k) end function bullet:update(k) if game.mode == 2 then -- TIME TO LIVE self.timetolive = self.timetolive - 1 if self.timetolive <= 0 then self:die(k) end -- COLLISION WITH ENEMY for l, enemy in pairs(enemies) do if distance(enemy.x - self.x,enemy.y - self.y) <= enemy.rad then enemy:takedamage(l,self.damage) bullet:die(k) end end -- DRAG self.vx = self.vx * self.drag self.vy = self.vy * self.drag -- MOVEMENT self.x = self.x + self.vx self.y = self.y + self.vy end if game.mode >= 1 then -- DRAW line(self.x - cam.x, self.y - cam.y, self.x - self.vx*2 - cam.x, self.y - self.vy*2 - cam.y, self.color - 2) line(self.x - cam.x, self.y - cam.y, self.x - self.vx - cam.x, self.y - self.vy - cam.y, self.color - 1) pix(self.x - cam.x, self.y - cam.y, self.color) end end -- Enemies ------------------ enemies = {} enemy = { x = 0, y = 0, a = 0, rad = 4, vx = 0, vy = 0, force = 0.08, drag = 0.92, sprite = 2, timetolive = 3600, weapon = { t = 0, rate = 60, force = 2, }, t = 0, rate = 60, health = 5, damage = 3, range = 64, target = { x=0, y=0 } } function enemy:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function enemy:spawn(o) table.insert(enemies, self:new(o)) end function enemy:die(k) table.remove(enemies, k) end function enemy:update(k) if game.mode == 2 then -- TIME TO LIVE self.timetolive = self.timetolive - 1 if self.timetolive <= 0 then self:die(k) end self.t = self.t - 1 -- AI local lastPlayerDist = distance(self.x-p.x,self.y-p.y) local lastStationDist = distance(self.x-station.x,self.y-station.y) local lastUnit, lastObj, lastUnitID, lastObjID local lastUnitDist, lastObjDist = 9^99, 9^99 for u,unit in pairs(units) do local d = distance(self.x-unit.x,self.y-unit.y) if d <= self.range and unit.type >= 10 then if d < lastUnitDist then lastUnitDist = d lastUnit = unit lastUnitID = u end end end for o,obj in pairs(objects) do local d = distance(self.x-obj.x,self.y-obj.y) if d < lastObjDist then lastObjDist = d lastObj = obj lastObjID = o end end if lastPlayerDist <= self.range then self.target = p elseif lastUnitDist <= self.range then self.target = lastUnit elseif lastStationDist <= self.range then self.target = station elseif lastObjDist <= self.range then self.target = lastObj else self.target = p end -- COLLISIONS if lastPlayerDist <= p.rad + self.rad then if self.t <= 0 then p:takedamage(self.damage) self:takedamage(k,self.damage//2) self.t = self.rate + 1 end self.vx = self.vx * -2 self.vy = self.vy * -2 end if lastUnitDist <= unit.rad + self.rad then if self.t <= 0 then units[lastUnitID]:takedamage(lastUnitID,self.damage) self:takedamage(k,self.damage//2) self.t = self.rate + 1 end self.vx = self.vx * -1 self.vy = self.vy * -1 end if lastStationDist <= station.rad + self.rad then if self.t <= 0 then station:takedamage(self.damage) self:takedamage(k,self.damage//2) self.t = self.rate + 1 end self.vx = self.vx * -1 self.vy = self.vy * -1 end if lastObjDist <= lastObj.rad + self.rad then object:die(lastObjID) end -- INPUT self.a = math.atan2(self.target.y - self.y, self.target.x - self.x) self.vx = self.vx + math.cos(self.a) * 0.2 * self.force self.vy = self.vy + math.sin(self.a) * 0.2 * self.force -- DRAG self.vx = self.vx * self.drag self.vy = self.vy * self.drag -- MOVEMENT self.x = self.x + self.vx self.y = self.y + self.vy end if game.mode >= 1 then -- DRAW local anim = 0 if self.timetolive % 60 < 30 then anim = 16 end spr(self.sprite + anim, self.x - cam.x - 4, self.y - cam.y - 4,0) end end function enemy:takedamage(k,damage) self.health = self.health - damage if self.health <= 0 then self:die(k) game.kills = game.kills + 1 if math.random() <= 0.85 then randtype = "gold" else randtype = "health" end local a = math.random() * 2 * math.pi local nvx = math.sin(a) * 0.2 local nvy = math.cos(a) * 0.2 object:spawn({ x = self.x, y = self.y, vx = nvx, vy = nvy, type = randtype, value = 2 }) end particle:create(damage,self.x, self.y) end -- Player Units --------------- units = {} unit = { name = "", type = 00, -- 00 01 02 03 defense unit -- 10 11 12 13 mining unit level = 0, x = 0, y = 0, a = 0, rad = 4, vx = 0, vy = 0, force = 0.08, drag = 0.9, sprite = 0, spritesize = 1, offset = { x = 0, y = 0 }, health = 30, healthMax = 30, gold = 0, goldMax = 15, zx = 0, zy = 0, zd = 0, range = 32, rate = 120, t = 0, weapon = { a = 0, t = 0, rate = 90, force = 2, damage = 2 }, recovery = false, move = false, stored = false, awake = false, hover = false } function unit:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function unit:spawn(o) table.insert(units, self:new(o)) end function unit:die(k) table.remove(units, k) end function unit:init(k) if self.type == 0 then self.name = "TURRET" self.rad = 3 self.range = 72 self.sprite = 38 self.offset.y = 1 elseif self.type == 10 then self.name = "AUTOMINER" self.rad = 2 self.range = 32 self.sprite = 5 self.offset.y = 1 end self.awake = not self.awake end function unit:update(k) if not self.awake then self:init(k) end if game.mode == 2 then -- USER CONTROLS if not p.busy and distance(p.x - self.x, p.y - self.y) <= p.rad + self.rad + 8 then self.hover = true p.busy = true if not p.holding then buttons.move = true circb(self.zx-cam.x, self.zy-cam.y, self.range, 15) if btnp(7) then self.move = true end end if not self.recovery and not p.holding then buttons.recovery = true if btnp(6) then self.recovery = true end end if self.recovery and not p.holding then buttons.cancel = true if btnp(5) then self.recovery = false end end end if self.move then p.holding = true buttons.deploy = true buttons.cancel = true circb(self.zx-cam.x, self.zy-cam.y, self.range, 15) circb(p.x-cam.x, p.y-cam.y, self.range, p.color) if btnp(5) then self.move = false p.holding = false self.zx = p.x self.zy = p.y elseif btnp(6) then self.move = false p.holding = false end end -- DEFENSE AUTO INPUT if self.type < 10 then -- MOVE AROUND ZONE self.a = math.atan2(self.zy - self.y, self.zx - self.x) self.spriterot = (((self.weapon.a/math.pi)+1)*2+0.5)//1+1 self.zd = clamp(distance(self.zx - self.x, self.zy - self.y), 0, 1) -- AUTO SHOOT local lastdist = 9^99 local tx, ty = 0, 0 for k,enemy in pairs(enemies) do local dist = distance(self.x-enemy.x, self.y-enemy.y) if dist < lastdist then lastdist = dist tx = enemy.x ty = enemy.y end end self.weapon.a = math.atan2(ty-self.y, tx-self.x) if self.weapon.t <= 0 and lastdist <= self.range then bullet:spawn({ x = self.x, y = self.y, vx = math.cos(self.weapon.a) * self.weapon.force, vy = math.sin(self.weapon.a) * self.weapon.force, a = self.weapon.a, damage = self.weapon.damage }) self.health = self.health - 1 self.weapon.t = self.weapon.rate + 1 end end -- MINING AUTO INPUT if self.type >= 10 then self.zd = 1 if self.gold < self.goldMax then -- NOT LOADED local lastObj, lastObjID local lastObjDist = 9^99 for l,obj in pairs(objects) do local dist = distance(self.x-obj.x, self.y-obj.y) local zdist = distance(self.zx-obj.x, self.zy-obj.y) if obj.trackedby == k then lastObjDist = dist lastObjZdist = 0 lastObj = obj lastObjID = l break end if dist < lastObjDist and obj.trackedby == 0 then lastObjDist = dist lastObjZdist = zdist lastObj = obj lastObjID = l end end if lastObjDist <= self.range and lastObjZdist <= self.range * 2 and self.gold + lastObj.value <= self.goldMax then -- object IN RANGE objects[lastObjID].trackedby = k self.a = math.atan2(lastObj.y - self.y, lastObj.x - self.x) else -- MOVE AROUND ZONE self.a = math.atan2(self.zy+(math.sin(self.t/240)*self.range*0.5) - self.y, self.zx+(math.cos(self.t/240)*self.range*0.5) - self.x) self.zd = clamp(distance(self.zy+(math.sin(self.t/240)*self.range*0.5) - self.y, self.zx+(math.cos(self.t/240)*self.range*0.5) - self.x), 0, 1) end self.spriterot = (((self.a/math.pi)+1)*2+0.5)//1+1 if lastObjDist < self.rad + lastObj.rad + 4 and self.gold + lastObj.value <= self.goldMax then -- AUTO COLLECT self.gold = self.gold + lastObj.value object:die(lastObjID) end else -- LOADED self.a = math.atan2(station.y - self.y, station.x - self.x) self.spriterot = (((self.a/math.pi)+1)*2+0.5)//1+1 self.zd = 1 if distance(station.x - self.x, station.y - self.y) < self.rad + station.rad + 4 then -- AUTO UNLOAD game.funds = game.funds + self.gold game.score = game.score + self.gold self.gold = 0 end end end -- RECOVERY AUTO INPUT if self.health <= 5 then self.recovery = true end if self.recovery then self.a = math.atan2(station.y - self.y, station.x - self.x) self.spriterot = (((self.a/math.pi)+1)*2+0.5)//1+1 self.zd = 1 if distance(self.x-station.x,self.y-station.x) <= self.rad + station.rad then self.recovery = false station:store(self) self:die(k) end end -- VELOCITY ACCORDING TO ANGLE self.vx = self.vx + math.cos(self.a) * 0.2 * self.force * self.zd self.vy = self.vy + math.sin(self.a) * 0.2 * self.force * self.zd -- DRAG self.vx = self.vx * self.drag self.vy = self.vy * self.drag -- MOVEMENT self.x = self.x + self.vx self.y = self.y + self.vy -- TIME self.t = self.t + 1 if self.type < 10 then self.weapon.t = self.weapon.t - 1 end end if game.mode >= 1 then -- DRAW if self.sprite ~= 0 then local anim = 0 if self.t % 60 < 30 then anim = 16 end spr(self.sprite + anim, self.x - cam.x - self.spritesize * 4 + self.offset.x, self.y - cam.y - self.spritesize * 4 + self.offset.y, 0, 1, 0, self.spriterot) else circb(self.x - cam.x, self.y - cam.y, self.rad, p.color) end if self.type < 10 then for i = self.rad + 4, self.rad + 4 + self.range/4,4 do local x = self.x + math.cos(self.weapon.a) * i - cam.x local y = self.y + math.sin(self.weapon.a) * i - cam.y pix(x,y,15) end end if self.hover then self.hover = false hud.hover = true hud.unit = k if self.t % 30 < 20 then circb(self.x - cam.x, self.y - cam.y, self.rad+6, 15) end --local width = print(self.name, self.x - cam.x + 4, self.y - cam.y - 2, p.color, false, 1, true) --cline(self.x - cam.x + 4, self.y - cam.y + 4, width, 2, self.health/self.healthMax) end if self.recovery and self.t%30 < 5 then pix(self.x-cam.x+self.rad+1, self.y-cam.y-self.rad-1, 2) end end end function unit:takedamage(k,damage) cls(2) particle:create(damage, self.x, self.y) self.health = self.health - damage if self.health <= 0 then self:die(k) end end -- Particles ------------------ particles = {} particle = { x = 0, y = 0, vx = 0, vy = 0, drag = 0.998, color = 11, timetolive = 120, rad = 0, type = "", parallax = 0, } function particle:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function particle:spawn(o) table.insert(particles, self:new(o)) end function particle:die(k) table.remove(particles, k) end function particle:update(k) if game.mode == 2 then -- TIME TO LIVE self.timetolive = self.timetolive - 1 if self.timetolive <= 0 then self:die(k) end -- DRAG self.vx = self.vx * self.drag self.vy = self.vy * self.drag -- MOVEMENT self.x = self.x + self.vx + p.vx*self.parallax self.y = self.y + self.vy + p.vy*self.parallax -- POST UPDATE if self.postupdate then self:postupdate(k) end end if game.mode >= 1 then -- DRAW if self.rad < 1 then pix(self.x - cam.x, self.y - cam.y, self.color) else circ(self.x - cam.x, self.y - cam.y, self.rad, self.color) end end end function particle:create(n,x,y) if not n then n = 5 end for i=1,n do particle:spawn({ x = x, y = y, vx = math.random(-100,100)/400, vy = math.random(-100,100)/400, timetolive = math.random(900,1800), color = 15, parallax = math.random()*0.7 }) end end -- Objects ------------------ objects = {} object = { x = 0, y = 0, vx = 0, vy = 0, drag = 0.99, color = 11, timetolive = 3600, rad = 1, type = "", value = 1, trackedby = 0 } function object:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function object:spawn(o) table.insert(objects, self:new(o)) end function object:die(k) table.remove(objects, k) end function object:update(k) if game.mode == 2 then -- TIME TO LIVE self.timetolive = self.timetolive - 1 if self.timetolive <= 0 then self:die(k) end -- DRAG self.vx = self.vx * self.drag self.vy = self.vy * self.drag -- MOVEMENT self.x = self.x + self.vx self.y = self.y + self.vy -- TYPES if self.type == "gold" then self.color = p.color -- PLAYER ACTIONS if distance(self.x-p.x, self.y-p.y) < self.rad + p.rad + 8 and p.gold + self.value <= p.goldMax then buttons.collect = true circb(self.x - cam.x, self.y - cam.y, self.rad + 2, 15) if btn(5) then p.gold = p.gold + self.value self:die(k) end end elseif self.type == "health" then self.color = 6 -- PLAYER ACTIONS if distance(self.x-p.x, self.y-p.y) < self.rad + p.rad + 8 and p.health < p.healthMax then buttons.heal = true circb(self.x - cam.x, self.y - cam.y, self.rad+2, 15) if btn(5) then p.health = clamp(p.health + self.value, 0, p.healthMax) self:die(k) end end end end if game.mode >= 1 then -- DRAW self.rad = self.value / 2 circb(self.x - cam.x, self.y - cam.y, self.rad, self.color) end end -- Init --------------------- function init() local rx = math.random(-80,-40) local ry = math.random(-48,48) unit:spawn({ type = 00, x = 32, y = -32, zx = 0, zy = 0, health = 4 }) unit:spawn({ type = 10, x = 28, y = 32, zx = rx+8, zy = ry+16 }) unit:spawn({ type = 10, x = rx, y = ry, zx = rx-16, zy = ry-8, health = 4 }) mine:spawn({ x = rx, y = ry, name = randomName() }) local a = math.random() * math.pi for i=1,3 do a = a + (math.random(120-40,120+40)/180) * math.pi mine:spawn({ x = math.cos(a) * game.size * .75, y = math.sin(a) * game.size * .75, rad = 8, rate = 240, name = randomName() }) end end init() -- TIC ---------------------- function TIC() cls() cam:update() for k, particle in pairs(particles) do particle:update(k) end for k, object in pairs(objects) do object:update(k) end for k, bullet in pairs(bullets) do bullet:update(k) end for k, mine in pairs(mines) do mine:update(k) end for k, unit in pairs(units) do unit:update(k) end for k, enemy in pairs(enemies) do enemy:update(k) end station:update() p:update() game:update() hud:update() end -- Functions ---------------- function lerp(a,b,x) return (1-x)*a + x*b end function distance(x,y) return math.sqrt(x^2 + y^2) end function clamp(value,min,max) if value > max then return max elseif value < min then return min else return value end end function rand(a,b) if a and b then return math.random(a,b) elseif a then return math.random(a) else return math.random() end end function cprint(string,x,y,color,value) local width = print(string,x,y,15,false,1,true) clip(x,y,width*(value),6) print(string,x,y,color,false,1,true) clip() return width end function cline(x,y,width,color,value) width = width - 1 line(x,y,x+width,y,15) if color == "life" then if value > 0.5 then color = p.color elseif value > 0.25 then color = 4 elseif value > 0.125 then color = 3 else color = 2 end end if value > 0 then line(x,y,x+width*value,y,color) end end