-- asteroids game -- written by maple "mavica" syrup local poly_ship = {{0, -10}, {5, 5}, {0, 0}, {-5, 5}, {0, -10}} local poly_thrust = {{-2.5, 2,5}, {0, 4}, {2.5, 2.5}} local poly_shot = {{0, 0}, {0, -5}} local poly_rock = {{-1,0},{-0.8,-0.6},{-0.4,-0.5}, {0,-1},{0.5,-0.9},{0.3,-0.4},{0.8,-0.5}, {1,0},{0.7,0.4},{0.7,0.6},{0.3,0.8},{0,0.7}, {-0.3,0.9},{-0.7,0.8},{-0.7,0.5},{-1,0}} local bg = colors.fromRGB(0,0,0) local fg = colors.fromRGB(0,255,0) local width = 320 local height = 320 local max_shots = 5 local shot_speed = 7 local rock_speed = 0.6 local rock_size = 30 local rock_step = 10 local player = { x=width/2, y=height/2, accel = 0.06, vx=0, vy=0, ang=0, cdown=0, inv=0 } local shots = {} local rocks = {} local score = 0 local lives = 3 local last_frame = 0 local game_over = false local function sign(number) return (number > 0 and 1) or (number == 0 and 0) or -1 end local function distance(x1, y1, x2, y2) return math.sqrt((x2-x1)^2+(y2-y1)^2) end local function draw_rotated(shape, x, y, ang, scale, color) processed = {} local sin = math.sin(ang) * scale local cos = math.cos(ang) * scale for _,v in pairs(shape) do local px = x + v[1] * cos - v[2] * sin local py = y + v[1] * sin + v[2] * cos table.insert(processed,math.floor(px+0.5)) table.insert(processed,math.floor(py+0.5)) end draw.polygon(processed, color) end local shot_meta = { erase = function(self) draw_rotated(poly_shot, self.x, self.y, self.ang, 1, bg) end, update = function(self, k) self.x = self.x + self.vx self.y = self.y + self.vy if self.x > width or self.x < 0 or self.y > height or self.y < 0 then self:erase() table.remove(shots,k) else for rockid,rock in pairs(rocks) do if distance(self.x, self.y, rock.x, rock.y) < rock.scale then rock:hit(rockid,self,k) end end end end, draw = function(self) draw_rotated(poly_shot, self.x, self.y, self.ang, 1, fg) end } local rock_meta = {} --hoist local rock_create = function(x, y, ang, size) rock = { x = x, y = y, ang = ang, vx = math.sin(ang) * rock_speed, vy = -math.cos(ang) * rock_speed, scale = size } setmetatable(rock, { __index = rock_meta }) table.insert(rocks, rock) end local function level_init() for i=1,4 do rock_create(math.random(30,width-30), math.random(30,height-30), math.random()*6.28, 30) end player.inv = 50 end rock_meta = { erase = function(self) draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, bg) end, update = function(self, k) self.x = (self.x + self.vx) % width self.y = (self.y + self.vy) % height if player.inv == 0 and distance(self.x, self.y, player.x, player.y) < self.scale then player:hit() end end, hit = function(self, k, shot, sk) sound.playPitch(1, 0.5, sound.drums.snare2) self:erase() score = score + ((rock_size + rock_step) - self.scale) rock_speed = rock_speed + 0.06 if self.scale > rock_step then rock_create(self.x, self.y, shot.ang - math.random()*3.14, self.scale-rock_step) rock_create(self.x, self.y, shot.ang + math.random()*3.14, self.scale-rock_step) end table.remove(rocks, k) table.remove(shots, sk) if #rocks == 0 then rock_speed = rock_speed - 0.6 score = score + 500 level_init() end end, draw = function(self) draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, fg) end } function player:fire() if self.cdown == 0 and #shots < max_shots then sound.playPitch(1, 2, sound.drums.tom) shot = { x = self.x, y = self.y, ang = self.ang, vx = math.sin(self.ang) * shot_speed, vy = -math.cos(self.ang) * shot_speed, } setmetatable(shot, { __index = shot_meta }) table.insert(shots, shot) self.cdown = 8 end end function player:erase() draw_rotated(poly_ship, self.x, self.y, self.ang, 1, bg) draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, bg) end function player:update() local angle_d = (keys.getState(keys.right) and 0.15 or 0) - (keys.getState(keys.left) and 0.15 or 0) self.ang = self.ang + angle_d if keys.getState(keys.up) then self.vx = (self.vx + math.sin(self.ang) * self.accel) self.vy = (self.vy - math.cos(self.ang) * self.accel) elseif keys.getState(keys.down) then self.vx = (self.vx - sign(self.vx) * self.accel) self.vy = (self.vy - sign(self.vy) * self.accel) end self.x = (self.x + self.vx) % width self.y = (self.y + self.vy) % height if self.cdown > 0 then self.cdown = self.cdown - 1 else if keys.getState(keys.enter) then self:fire() end end if self.inv > 0 then self.inv = self.inv - 1 end end function player:hit() sound.playPitch(1, 0.4, sound.drums.snare1) if lives == 0 then game_over = true return end lives = lives - 1 self.x = width/2 self.y = height/2 self.vx = 0 self.vy = 0 self.ang = 0 self.inv = 50 end function player:draw() if math.floor(self.inv / 3) % 2 == 0 then draw_rotated(poly_ship, self.x, self.y, self.ang, 1, fg) end if keys.getState(keys.up) then draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, fg) end end local function hud_draw() draw.text(15,15," ",fg,bg) draw.text(15,15,score,fg,bg) for i = 0, 3 do local color = i < lives and fg or bg draw_rotated(poly_ship, width - 20 - 20*i, 20, 0, 1, color) end end term.clear() --draw.enableBuffer(true) draw.rectFill(0, 0, width, height, bg) level_init() while true do if keys.getState(keys.esc) or game_over then break end local now = os.clock() if now >= last_frame + 0.05 then last_frame = now -- erase player:erase() for _,v in pairs(shots) do v:erase() end for _,v in pairs(rocks) do v:erase() end -- move player player:update() for k,v in pairs(shots) do v:update(k) end for k,v in pairs(rocks) do v:update(k) end --draw.clear() hud_draw() player:draw() for _,v in pairs(shots) do v:draw() end for _,v in pairs(rocks) do v:draw() end --draw.blitBuffer() collectgarbage() end end draw.enableBuffer(false) if game_over then term.clear() print("game over!") print("final score: "..score) end