mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2026-03-19 10:32:37 +01:00
261 lines
5.9 KiB
Lua
261 lines
5.9 KiB
Lua
-- asteroids game
|
|
-- written by maple "mavica" syrup <maple@maple.pet>
|
|
|
|
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
|