Compare commits

..

7 Commits

2 changed files with 400 additions and 383 deletions

View File

@ -1,5 +1,5 @@
pico-8 cartridge // http://www.pico-8.com
version 42
version 41
__lua__
-- vacuum gambit
-- by kistaro windrider
@ -18,17 +18,12 @@ function csv(s)
end
return ret
end
function const_fxn(x)
return function()
return x
end
end
-- generate standard "overlay"
-- constructor for type tt.
-- if tt.init is defined, generated
-- new calls tt.init(ret) after
-- ret is definitely not nil,
-- if more is defined, generated
-- new calls more(ret) after
-- ret is definitely not nil
-- before calling setmetatable.
-- use to initialize mutables.
--
@ -37,8 +32,8 @@ end
-- object *after* more, because
-- this works better with the
-- `more` impls i use.
function mknew(tt)
local mt,oldnew,more = {__index=tt},tt.new,rawget(tt, "init")
function mknew(tt, more)
local mt,oldnew = {__index=tt},tt.new
tt.new=function(ret)
if(not ret) ret = {}
if(more) more(ret)
@ -46,18 +41,15 @@ function mknew(tt)
setmetatable(ret, mt)
return ret
end
return tt
end
-- intrusive singly-linked list.
-- cannot be nested!
linked_list = mknew{
is_linked_list=true,
init = function(x)
linked_list = {is_linked_list=true}
mknew(linked_list, function(x)
x.next=nil
x.tail=x
end,
}
end)
function linked_list:push_back(x)
self.tail.next = x
@ -126,7 +118,7 @@ end
function _init()
init_blip_pals()
wipe_level()
primary_ship.main_gun = zap_gun_p.new() -- redundant?
primary_ship.main_gun = zap_gun.new()
load_level(example_level_csv)
state = game
pal(2,129)
@ -420,7 +412,7 @@ end
scrollrate = 0.25 --in px/frame
ship_m = mknew{
ship_m = {
-- ships have no shield by default
shield = 0,
@ -445,6 +437,7 @@ ship_m = mknew{
-- ymin, ymax default to nil
-- pship needs more constraint
}
mknew(ship_m)
function ship_m:die()
self.dead = true
@ -587,20 +580,16 @@ end
-->8
-- bullet and gun behaviors
function player_blt_cat()
return pbullets
end
bullet_base = {
enemyspd = 0.5
}
mknew(bullet_base)
function enemy_blt_cat()
return ebullets
end
bullet_base = mknew{ }
gun_base = mknew{
gun_base = {
shoot_ready = -32768,
icon = 20
}
mknew(gun_base)
function bullet_base:hitship(_)
self:die()
@ -612,11 +601,19 @@ end
function bullet_base:move()
self.x += self.dx
if self.enemy then
self.y += self.dy
if (self.y > 128) or (self.y < -8 * self.height) then
if self.y > 128 then
self:die()
return true
end
else
self.y -= self.dy
if self.y < -8*self.height then
self:die()
return true
end
end
return false
end
@ -624,18 +621,17 @@ function bullet_base:draw()
spr(self.sprite, self.x, self.y, self.width, self.height)
end
-- An `actually_shoot` factory
-- for trivial guns
function spawn_one(t)
return function(gun, x, y)
t.new{}:spawn_at(x, y)
end
end
function bullet_base:spawn_at(x, y)
self.x = x - self.x_off
self.y = y - self.y_off
self.category():push_back(self)
self.x = x - self.center_x_off
if self.enemy then
self.dx *= self.enemyspd
self.dy *= self.enemyspd
self.y = y + self.top_y_off
ebullets:push_back(self)
else
self.y = y - (8 * self.height) + self.bottom_y_off
pbullets:push_back(self)
end
end
function gun_base:shoot(x, y)
@ -649,12 +645,23 @@ function gun_base:shoot(x, y)
return true
end
function gun_base:actually_shoot(x, y)
local typ = self.t
local b = typ.new{
enemy = self.enemy,
sprite = self.enemy and typ.esprite or typ.psprite,
}
b:spawn_at(x, y)
return true
end
-->8
-- bullets and guns
zap_e = mknew(bullet_base.new{
zap = bullet_base.new{
--shape
sprite = 9, --index of enemy ammo sprite
psprite = 8, --index of player ammo sprite
esprite = 9, -- index of enemy ammo sprite
width = 1, --in 8x8 blocks
height = 1,
hurt = { -- hurtbox - where this ship can be hit
@ -663,39 +670,33 @@ zap_e = mknew(bullet_base.new{
width = 2,
height = 8
},
x_off = 1, -- how to position by ship
y_off = 8,
center_x_off = 1, -- how to position by ship
bottom_y_off = 0,
top_y_off = 0,
damage = 1,
dx = 0, -- px/frame
dy = 4,
dy = 8,
hitship = const_fxn(true),
hitship = function(_, _)
return true
end
}
mknew(zap)
category = enemy_blt_cat,
})
zap_p = mknew(zap_e.new{
sprite = 8,
dy = -8,
y_off = 0,
category = player_blt_cat,
})
zap_gun_e = mknew(gun_base.new{
zap_gun = gun_base.new{
enemy = false,
power = 20, -- power consumed per shot
cooldown = 0x0.000a, -- frames between shots
ammo = nil, -- unlimited ammo - main gun
actually_shoot = spawn_one(zap_e),
})
t = zap -- metatable of bullet to fire
}
mknew(zap_gun)
zap_gun_p = mknew(zap_gun_e.new{
actually_shoot = spawn_one(zap_p),
})
blast = mknew(bullet_base.new{
blast = bullet_base.new{
--shape
sprite = 12, --index of player ammo sprite
psprite = 12, --index of player ammo sprite
esprite = 3, -- index of enemy ammo sprite
width = 1, --in 8x8 blocks
height = 1,
hurt = { -- hurtbox - where this ship can be hit
@ -704,12 +705,13 @@ blast = mknew(bullet_base.new{
width = 6,
height = 6
},
x_off = 4, -- how to position by ship
y_off = 0,
center_x_off = 4, -- how to position by ship
bottom_y_off = 0,
top_y_off = 0,
damage = 4,
dx = 0, -- px/frame
dy = -2,
dy = 2,
awaitcancel = false,
-- disable damage for 2 frames
@ -734,22 +736,25 @@ blast = mknew(bullet_base.new{
self.awaitcancel = false
end)
end
end,
category=player_blt_cat
})
end
}
mknew(blast)
blast_gun = mknew(gun_base.new{
blast_gun = gun_base.new{
icon = 13,
power = 0, -- only cost is ammo
enemy = false,
power = 0, -- ammo, not power
cooldown = 0x0.0020, -- frames between shots
ammo = 5,
maxammo = 5,
actually_shoot = spawn_one(blast),
})
t = blast -- type of bullet to fire
}
mknew(blast_gun)
protron_e = mknew(bullet_base.new{
protron = bullet_base.new{
--shape
sprite = 24,
psprite = 23, --index of player ammo sprite
esprite = 24, -- index of enemy ammo sprite
width = 1, --in 8x8 blocks
height = 1,
hurt = { -- hurtbox - where this ship can be hit
@ -758,59 +763,56 @@ protron_e = mknew(bullet_base.new{
width = 2,
height = 2
},
x_off = 1, -- how to position by ship
y_off = 4,
center_x_off = 1, -- how to position by ship
bottom_y_off = 4,
top_y_off = 0,
damage = 1,
dym = 0.5, -- gun sets dy;
-- this is mult
category = enemy_blt_cat,
})
dx = 0, -- px/frame
dy = 3,
}
mknew(protron)
protron_p = mknew(protron_e.new{
sprite=23,
dym = -1,
y_off = 0,
category=player_blt_cat,
})
protron_gun_e = mknew(gun_base.new{
protron_gun = gun_base.new{
icon = 25,
enemy = false,
power = 60,
cooldown = 0x0.000f, -- frames between shots
ammo = nil,
maxammo = nil,
munition = protron_e
})
function protron_gun_e:actually_shoot(x, y)
local m = self.munition.dym
actually_shoot = function(self, x, y)
local sprite = protron.psprite
if (self.enemy) sprite=protron.esprite
for i=1,3 do
local b = self.munition.new{
dx = i*m,
dy = (4-i)*m,
local b = protron.new{
enemy=self.enemy,
sprite=sprite,
dx = i,
dy = 4-i
}
b:spawn_at(x,y)
local b2 = self.munition.new{
dx = -i*m,
dy = (4-i)*m,
local b2 = protron.new{
enemy=self.enemy,
sprite=sprite,
dx = -i,
dy = 4-i
}
b2:spawn_at(x,y)
end
local bup = self.munition.new{
dx=0,
dy=4*m,
local bup = protron.new{
enemy=self.enemy,
sprite=sprite,
dy=4
}
bup:spawn_at(x,y)
end
end
}
mknew(protron_gun)
protron_gun_p = mknew(protron_gun_e.new{
munition = protron_p,
})
vulcan_e = mknew(bullet_base.new{
vulcan = bullet_base.new{
--shape
sprite = 21,
psprite = 22, --index of player ammo sprite
esprite = 21, -- index of enemy ammo sprite
width = 1, --in 8x8 blocks
height = 1,
hurt = { -- hurtbox - where this ship can be hit
@ -819,46 +821,39 @@ vulcan_e = mknew(bullet_base.new{
width = 1,
height = 4
},
x_off = 0.5, -- how to position by ship
y_off = 0,
center_x_off = 0.5, -- how to position by ship
bottom_y_off = 4,
top_y_off = 0,
damage = 0.5,
-- dx from gun
dy = 2,
category=enemy_blt_cat
})
dx = 0, -- px/frame
dy = 4,
}
mknew(vulcan)
vulcan_p = mknew(vulcan_e.new{
sprite=22,
y_off = 4,
dy = -4,
category=player_blt_cat
})
vulcan_gun_e = mknew(gun_base.new{
vulcan_gun = gun_base.new{
icon = 37,
enemy = false,
power = 8,
cooldown = 0x0.0002, -- frames between shots
ammo = nil,
maxammo = nil,
munition=vulcan_e,
dxs = {0.35, -0.35, -0.7, 0.7, 0.35, -0.35},
xoffs = {1, 0, -1, 1, 0, -1},
dxidx = 1,
actually_shoot = function(self, x, y)
local b = self.munition.new{
local sprite = self.enemy and vulcan.esprite or vulcan.psprite
local b = vulcan.new{
enemy=self.enemy,
sprite=sprite,
dx = self.dxs[self.dxidx],
}
b:spawn_at(self.xoffs[self.dxidx]+x,y)
self.dxidx += 1
if (self.dxidx > #self.dxs) self.dxidx = 1
end
})
vulcan_gun_p = mknew(vulcan_gun_e.new{
munition=vulcan_p,
})
}
mknew(vulcan_gun)
-->8
--ships, including player
@ -866,7 +861,7 @@ vulcan_gun_p = mknew(vulcan_gun_e.new{
firespark = split"9, 8, 2, 5, 1"
smokespark = split"13, 13, 5, 5"
player = mknew(ship_m.new{
player = ship_m.new{
--shape
sprite = 1, --index of ship sprite
size = 1, --all ships are square; how many 8x8 sprites?
@ -916,20 +911,21 @@ player = mknew(ship_m.new{
end
--dx, dy, shoot_spec, shoot_main
return (((b&0x2)>>1) - (b&0x1)) * th, (((b&0x8)>>3) - ((b&0x4)>>2)) * th, (b&0x10) > 0, (b&0x20) > 0
end,
init = function(p)
p.main_gun = zap_gun_p.new()
end
}
mknew(player,
function(p)
p.main_gun = zap_gun.new()
-- ONE HIT MODE
--
-- p.hp = 0
-- p.maxhp = 0
-- p.shield = 0
-- p.maxshield = 0
end,
})
end
)
frownie = mknew(ship_m.new{
frownie = ship_m.new{
--shape
sprite = 3, --index of ship sprite
size = 1, --all ships are square; how many 8x8 sprites?
@ -960,9 +956,10 @@ frownie = mknew(ship_m.new{
if (tstate>=4) dx=self.thrust
return dx,0,false,false
end,
})
}
mknew(frownie)
blocky = mknew(frownie.new{
blocky = frownie.new{
sprite = 10,
hp = 1.5,
hurt = {
@ -980,9 +977,10 @@ blocky = mknew(frownie.new{
end
ship_m.ow(self)
end
})
}
mknew(blocky)
spewy = mknew(frownie.new{
spewy = frownie.new{
sprite=26,
power=-20,
hurt = {
@ -999,13 +997,13 @@ spewy = mknew(frownie.new{
act=function(self)
local dx,dy,shoot_spec=frownie.act(self)
return dx, dy, shoot_spec, true
end,
init = function(ship)
ship.main_gun=ship.main_gun or protron_gun_e.new{}
end
})
}
mknew(spewy, function(ship)
ship.main_gun=ship.main_gun or protron_gun.new{enemy=true}
end)
chasey = mknew(ship_m.new{
chasey = ship_m.new{
sprite = 5,
size = 1,
hurt = {
@ -1028,11 +1026,10 @@ chasey = mknew(ship_m.new{
thrust = 0.2,
drag = 0.075,
slip = true,
init = function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
end
})
}
mknew(chasey, function(ship)
ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
end)
function chasey:act()
self.xmin = max(primary_ship.x-8, 0)
@ -1040,7 +1037,7 @@ function chasey:act()
return 0, 0, false, self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x
end
xl_chasey=mknew(chasey.new{
xl_chasey=chasey.new{
size=2,
maxspd=1.25,
hurt = {
@ -1049,8 +1046,6 @@ xl_chasey=mknew(chasey.new{
width = 12,
height = 10
},
fire_off_x = 8,
fire_off_y = 15,
hp = 19.5,
shield = 5,
boss = true,
@ -1065,11 +1060,10 @@ xl_chasey=mknew(chasey.new{
sspr(40, 0, 8, 8, self.x, self.y, 16, 16)
pal()
end,
init = function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
end,
})
}
mknew(xl_chasey, function(ship)
ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
end)
-->8
-- collisions
@ -1083,11 +1077,12 @@ function collides(box1, box2)
or box1.y+box1.height<box2.y)
end
collider = mknew{
init = function(x)
collider = { }
mknew(collider,
function(x)
x.suppress = {}
end,
}
end
)
function collider_indexes(box)
local ret = {}
@ -1276,9 +1271,9 @@ end
function spawn_bonus_vulcan_chasey()
local c = spawn_chasey()
c.main_gun=vulcan_gun_e.new{enemy=true}
c.main_gun=vulcan_gun.new{enemy=true}
c.die = function(self)
spawn_main_gun_at(self.x-1, self.y-1, vulcan_gun_p)
spawn_main_gun_at(self.x-1, self.y-1, vulcan_gun)
chasey.die(self)
end
c.sprite=4
@ -1381,7 +1376,7 @@ example_level_csv=[[1,spawn_frownie
310,spawn_blocking_blocky
310,spawn_blocking_blocky
311,spawn_frownie
350,spawn_main_gun_at,70,-11,protron_gun_p
350,spawn_main_gun_at,70,-11,protron_gun
401,spawn_frownie
420,spawn_blocking_frownie
430,spawn_bonus_vulcan_chasey
@ -1491,9 +1486,8 @@ bullets
shots much easier to dodge
* damage - damage per hit;
used by ships
* sprite - sprite index.
* x_off, y_off - renamed for
the next two vars. may revert
* psprite, esprite - index of
player or enemy sprite.
* center_off_x - the horizontal
centerpoint of the bullet,
for positioning when firing.
@ -1782,7 +1776,7 @@ true, they are dropped.
-->8
-- standard events
blip_fx = mknew{
blip_fx = {
cancel=false
}
@ -1800,6 +1794,8 @@ function blip_fx:abort()
self.cancel=true
end
mknew(blip_fx)
blip_pals = {}
function init_blip_pals()
for i=0,15 do
@ -1834,7 +1830,8 @@ function boom(x,y,boominess,is_boss)
return
end
spark_particle=mknew{}
spark_particle={}
mknew(spark_particle)
function spark_particle:move()
if (rnd(4) < 1) self.sidx += 1
@ -1863,7 +1860,7 @@ end
-->8
-- powerups
powerup = mknew(bullet_base.new{
powerup = bullet_base.new{
-- animated sprite array: "sprites"
-- to draw under or over anim,
-- override draw, draw the
@ -1879,12 +1876,13 @@ powerup = mknew(bullet_base.new{
-- easy to pick up
dx = 0,
dy = 1.5, -- 0.75 after enemyspd
category = enemy_blt_cat, -- collides with player ship
enemy = true, -- collides with player ship
damage = 0,
anim_speed = 2,
loop_pause = 30 -- affected by animspeed
})
}
mknew(powerup)
-- sprite indexes for "sheen" animation
sheen8x8 = split"2,54,55,56,57,58,59,60,61"
@ -1904,15 +1902,16 @@ function powerup:draw()
self.width, self.height)
end
repair = mknew(powerup.new{
repair = powerup.new{
hurt = {
x_off = -2,
y_off = -2,
width = 12,
height = 12
},
x_off = 4,
y_off = 0,
center_x_off = 4,
top_y_off = 0,
bottom_y_off = 0,
sprites = sheen8x8,
hitship = function(self, ship)
if (ship ~= primary_ship) return false
@ -1923,13 +1922,14 @@ repair = mknew(powerup.new{
spr(53, self.x, self.y, self.width, self.height)
powerup.draw(self)
end
})
}
mknew(repair)
function spawn_repair_at(x, y)
repair.new():spawn_at(x, y)
end
gun_swap = mknew(powerup.new{
gun_swap = powerup.new{
hurt = {
x_off = -2,
y_off = -2,
@ -1937,8 +1937,9 @@ gun_swap = mknew(powerup.new{
height = 16
},
-- gun = gun_type.new{}
x_off = 6,
y_off = 0,
center_x_off = 6,
top_y_off = 0,
bottom_y_off = 4,
width = 2,
height = 2,
sprites = {64, 66, 68, 70, 72, 74, 76, 78},
@ -1951,7 +1952,8 @@ gun_swap = mknew(powerup.new{
powerup.draw(self)
spr(self.gun.icon, self.x+2, self.y+2, 1, 1)
end
})
}
mknew(gun_swap)
function spawn_main_gun_at(x, y, gunt)
if (type(gunt)=="string") gunt=_ENV[gunt]

View File

@ -26,9 +26,9 @@ end
-- generate standard "overlay"
-- constructor for type tt.
-- if tt.init is defined, generated
-- new calls tt.init(ret) after
-- ret is definitely not nil,
-- if more is defined, generated
-- new calls more(ret) after
-- ret is definitely not nil
-- before calling setmetatable.
-- use to initialize mutables.
--
@ -37,8 +37,8 @@ end
-- object *after* more, because
-- this works better with the
-- `more` impls i use.
function mknew(tt)
local mt,oldnew,more = {__index=tt},tt.new,rawget(tt, "init")
function mknew(tt, more)
local mt,oldnew = {__index=tt},tt.new
tt.new=function(ret)
if(not ret) ret = {}
if(more) more(ret)
@ -46,18 +46,15 @@ function mknew(tt)
setmetatable(ret, mt)
return ret
end
return tt
end
-- intrusive singly-linked list.
-- cannot be nested!
linked_list = mknew{
is_linked_list=true,
init = function(x)
linked_list = {is_linked_list=true}
mknew(linked_list, function(x)
x.next=nil
x.tail=x
end,
}
end)
function linked_list:push_back(x)
self.tail.next = x
@ -337,30 +334,28 @@ function drawhud()
line(113,127)
draw_gun_info("❎",1,116,3,primary_ship.main_gun)
draw_gun_info("🅾️",1,116,29,primary_ship.special_gun)
draw_gun_info("🅾️",1,116,31,primary_ship.special_gun)
inset(114,57,119,118)
rectfill(119,57,124,58,13)
inset(120,64,125,125)
rectfill(114,124,120,125,7)
print("XP",119,55,1)
print("HP",114,122,1)
dropshadow("p h",114,59,1)
inset(114,66,119,125)
fillp(0x5a5a)
vertmeter(115,58,118,117,primary_ship.xp, primary_ship.xptarget, powcols)
-- 59 px vertically
vertmeter(115,67,118,124,primary_ship.power, primary_ship.max_power, powcols)
inset(120,66,125,125)
-- 57 px vertically
local mxs, cs, mxh, ch = primary_ship.maxshield, primary_ship.shield, primary_ship.maxhp, primary_ship.hp
if (mxs > 0) and (mxh > 0) then
local split = 59 * (mxs / (mxs + mxh)) \ 1 + 64
local split = 57 * (mxs / (mxs + mxh)) \ 1 + 66
line(121, split, 124, split, 0xba)
vertmeter(121,65,124,split-1,cs, mxs,shlcols)
vertmeter(121,67,124,split-1,cs, mxs,shlcols)
vertmeter(121,split+1,124,124,ch, mxh, hpcols)
elseif mxs > 0 then
vertmeter(121,65,124,124,cs,mxs,shlcols)
vertmeter(121,67,124,124,cs,mxs,shlcols)
elseif mxh > 0 then
vertmeter(121,65,124,124,ch,mxh,hpcols)
vertmeter(121,67,124,124,ch,mxh,hpcols)
else
print("!", 122, 93, 9)
print("!", 121, 92, 8)
print("!", 122, 94, 9)
print("!", 121, 93, 8)
end
fillp(0)
end
@ -409,7 +404,7 @@ function inset(x0,y0,x1,y1)
-- fillp
rect(x0,y0,x1,y1,119)
line(x1,y0,x0,y0,85)
line(x0,y1-1,85)
line(x0,y1,85)
end
function dropshadow(str, x, y, col)
@ -422,14 +417,19 @@ end
scrollrate = 0.25 --in px/frame
ship_m = mknew{
ship_m = {
-- ships have no shield by default
shield = 0,
maxshield = 0,
shieldcost = 32767.9,
shieldcooldown = 0x0.003c,--1s
shieldpenalty = 0x0.012c, --5s
max_power = 120,
power = 120,
generator = 2, -- power gen per frame
slip = true, -- most enemies slide
xmomentum = 0,
@ -442,6 +442,7 @@ ship_m = mknew{
-- ymin, ymax default to nil
-- pship needs more constraint
}
mknew(ship_m)
function ship_m:die()
self.dead = true
@ -489,6 +490,7 @@ end
function ship_m:move()
self:refresh_shield()
self.power = min(self.max_power, self.power + self.generator)
local dx, dy, shoot_spec, shoot_main = self:act()
dx = self:constrain(self.x, self.xmomentum, self.xmin, self.xmax, dx)
dy = self:constrain(self.y, self.ymomentum, self.ymin, self.ymax, dy)
@ -530,7 +532,9 @@ end
function ship_m:maybe_shoot(gun)
if (not gun) return
return gun:shoot(self.x + self.fire_off_x, self.y + self.fire_off_y)
if (self.power < gun.power) return
if (not gun:shoot(self.x + self.fire_off_x, self.y + self.fire_off_y)) return
self.power -= gun.power
end
function ship_m:hitship(other)
@ -571,8 +575,10 @@ end
function ship_m:refresh_shield()
if (self.shield >= self.maxshield) return
if (lframe < self.shield_refresh_ready) return
if (self.power < self.shieldcost) return
self.shield += 1
self.shield = min(self.shield, self.maxshield)
self.power -= self.shieldcost
self.shield_refresh_ready = lframe + self.shieldcooldown
end
@ -587,34 +593,14 @@ function enemy_blt_cat()
return ebullets
end
-- x, y: position
-- dx, dy: movement (linear)
-- f: frames remaining; nil for no limit
-- sprite: what sprite to draw
-- hurt -- hurtbox
-- width, height -- in sprites
-- x_off, y_off -- how to
-- initially position relative
-- to firing point. weird
-- details, check impl
-- damage -- damage to do to
-- a ship that gets hit
-- category -- function that
-- returns which bullet list
-- to spawn onto
-- hitship -- event handler,
-- takes ship as argument.
-- default: die, return true.
-- returns whether to delete
-- the bullet
-- die -- on-removal event,
-- default no-op
bullet_base = mknew{ }
bullet_base = { }
mknew(bullet_base)
gun_base = mknew{
gun_base = {
shoot_ready = -32768,
icon = 20
}
mknew(gun_base)
function bullet_base:hitship(_)
self:die()
@ -627,8 +613,7 @@ end
function bullet_base:move()
self.x += self.dx
self.y += self.dy
if (self.f) self.f -= 1
if (self.y > 128) or (self.y < -8 * self.height) or (self.f and self.f < 0) then
if (self.y > 128) or (self.y < -8 * self.height) then
self:die()
return true
end
@ -667,9 +652,9 @@ end
-->8
-- bullets and guns
zap_e = mknew(bullet_base.new{
zap_e = bullet_base.new{
--shape
sprite = 9, --index of ammo sprite
sprite = 9, --index of enemy ammo sprite
width = 1, --in 8x8 blocks
height = 1,
hurt = { -- hurtbox - where this ship can be hit
@ -688,26 +673,31 @@ zap_e = mknew(bullet_base.new{
hitship = const_fxn(true),
category = enemy_blt_cat,
})
}
mknew(zap_e)
zap_p = mknew(zap_e.new{
zap_p = zap_e.new{
sprite = 8,
dy = -8,
y_off = 0,
category = player_blt_cat,
})
}
mknew(zap_p)
zap_gun_e = mknew(gun_base.new{
zap_gun_e = gun_base.new{
power = 20, -- power consumed per shot
cooldown = 0x0.000a, -- frames between shots
ammo = nil, -- unlimited ammo - main gun
actually_shoot = spawn_one(zap_e),
})
}
mknew(zap_gun_e)
zap_gun_p = mknew(zap_gun_e.new{
zap_gun_p = zap_gun_e.new{
actually_shoot = spawn_one(zap_p),
})
}
mknew(zap_gun_p)
blast = mknew(bullet_base.new{
blast = bullet_base.new{
--shape
sprite = 12, --index of player ammo sprite
width = 1, --in 8x8 blocks
@ -750,17 +740,20 @@ blast = mknew(bullet_base.new{
end
end,
category=player_blt_cat
})
}
mknew(blast)
blast_gun = mknew(gun_base.new{
blast_gun = gun_base.new{
icon = 13,
power = 0, -- only cost is ammo
cooldown = 0x0.0020, -- frames between shots
ammo = 5,
maxammo = 5,
actually_shoot = spawn_one(blast),
})
}
mknew(blast_gun)
protron_e = mknew(bullet_base.new{
protron_e = bullet_base.new{
--shape
sprite = 24,
width = 1, --in 8x8 blocks
@ -778,22 +771,26 @@ protron_e = mknew(bullet_base.new{
dym = 0.5, -- gun sets dy;
-- this is mult
category = enemy_blt_cat,
})
}
mknew(protron_e)
protron_p = mknew(protron_e.new{
protron_p = protron_e.new{
sprite=23,
dym = -1,
y_off = 0,
category=player_blt_cat,
})
}
mknew(protron_p)
protron_gun_e = mknew(gun_base.new{
protron_gun_e = gun_base.new{
icon = 25,
power = 60,
cooldown = 0x0.000f, -- frames between shots
ammo = nil,
maxammo = nil,
munition = protron_e
})
}
mknew(protron_gun_e)
function protron_gun_e:actually_shoot(x, y)
local m = self.munition.dym
@ -816,11 +813,12 @@ function protron_gun_e:actually_shoot(x, y)
bup:spawn_at(x,y)
end
protron_gun_p = mknew(protron_gun_e.new{
protron_gun_p = protron_gun_e.new{
munition = protron_p,
})
}
mknew(protron_gun_p)
vulcan_e = mknew(bullet_base.new{
vulcan_e = bullet_base.new{
--shape
sprite = 21,
width = 1, --in 8x8 blocks
@ -838,18 +836,21 @@ vulcan_e = mknew(bullet_base.new{
-- dx from gun
dy = 2,
category=enemy_blt_cat
})
}
mknew(vulcan_e)
vulcan_p = mknew(vulcan_e.new{
vulcan_p = vulcan_e.new{
sprite=22,
y_off = 4,
dy = -4,
category=player_blt_cat
})
}
mknew(vulcan_p)
vulcan_gun_e = mknew(gun_base.new{
vulcan_gun_e = gun_base.new{
icon = 37,
enemy = false,
power = 8,
cooldown = 0x0.0002, -- frames between shots
ammo = nil,
maxammo = nil,
@ -865,11 +866,13 @@ vulcan_gun_e = mknew(gun_base.new{
self.dxidx += 1
if (self.dxidx > #self.dxs) self.dxidx = 1
end
})
}
mknew(vulcan_gun_e)
vulcan_gun_p = mknew(vulcan_gun_e.new{
vulcan_gun_p = vulcan_gun_e.new{
munition=vulcan_p,
})
}
mknew(vulcan_gun_p)
-->8
--ships, including player
@ -877,7 +880,7 @@ vulcan_gun_p = mknew(vulcan_gun_e.new{
firespark = split"9, 8, 2, 5, 1"
smokespark = split"13, 13, 5, 5"
player = mknew(ship_m.new{
player = ship_m.new{
--shape
sprite = 1, --index of ship sprite
size = 1, --all ships are square; how many 8x8 sprites?
@ -891,16 +894,13 @@ player = mknew(ship_m.new{
sparkodds = 2,
boss = true, -- dramatic special effects
-- health
-- health and power
hp = 3, -- current health, non-regenerating
maxhp = 3, -- player only; other ships never heal
shield = 2, -- regenerates
shield = 2, -- regenerates, using power
maxshield = 2,
-- xp, increments of 0x0.01
xp = 0,
xptarget = 0x0.05,
level = 1,
shieldcost = 60, -- power cost to refill shield
generator = 2,
-- gun
main_gun = nil, -- assign at spawn time
@ -930,9 +930,10 @@ player = mknew(ship_m.new{
end
--dx, dy, shoot_spec, shoot_main
return (((b&0x2)>>1) - (b&0x1)) * th, (((b&0x8)>>3) - ((b&0x4)>>2)) * th, (b&0x10) > 0, (b&0x20) > 0
end,
init = function(p)
end
}
mknew(player,
function(p)
p.main_gun = zap_gun_p.new()
-- ONE HIT MODE
--
@ -940,10 +941,10 @@ player = mknew(ship_m.new{
-- p.maxhp = 0
-- p.shield = 0
-- p.maxshield = 0
end,
})
end
)
frownie = mknew(ship_m.new{
frownie = ship_m.new{
--shape
sprite = 3, --index of ship sprite
size = 1, --all ships are square; how many 8x8 sprites?
@ -956,7 +957,7 @@ frownie = mknew(ship_m.new{
sparks = smokespark,
sparkodds = 8,
-- health
-- health and power
hp = 0.5, -- enemy ships need no max hp
-- position
@ -974,9 +975,10 @@ frownie = mknew(ship_m.new{
if (tstate>=4) dx=self.thrust
return dx,0,false,false
end,
})
}
mknew(frownie)
blocky = mknew(frownie.new{
blocky = frownie.new{
sprite = 10,
hp = 1.5,
hurt = {
@ -994,10 +996,12 @@ blocky = mknew(frownie.new{
end
ship_m.ow(self)
end
})
}
mknew(blocky)
spewy = mknew(frownie.new{
spewy = frownie.new{
sprite=26,
power=-20,
hurt = {
x_off=0,
y_off=1,
@ -1005,18 +1009,20 @@ spewy = mknew(frownie.new{
height=5
},
hp=0.5,
maxpower=70,
generator=0.5,
fire_off_x=4,
fire_off_y=7,
fire_off_y = 7,
act=function(self)
local dx,dy,shoot_spec=frownie.act(self)
return dx, dy, shoot_spec, self.y > 10
end,
init = function(ship)
ship.main_gun=ship.main_gun or protron_gun_e.new{}
return dx, dy, shoot_spec, true
end
})
}
mknew(spewy, function(ship)
ship.main_gun=ship.main_gun or protron_gun_e.new{enemy=true}
end)
chasey = mknew(ship_m.new{
chasey = ship_m.new{
sprite = 5,
size = 1,
hurt = {
@ -1030,6 +1036,7 @@ chasey = mknew(ship_m.new{
hp = 1.5,
shield = 1,
maxshield = 1,
shieldcost = 180,
fire_off_x = 4,
fire_off_y = 7,
@ -1038,19 +1045,18 @@ chasey = mknew(ship_m.new{
thrust = 0.2,
drag = 0.075,
slip = true,
init = function(ship)
}
mknew(chasey, function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
end
})
end)
function chasey:act()
self.xmin = max(primary_ship.x-8, 0)
self.xmax = min(primary_ship.x + 8, 112 - 8*self.size)
return 0, 0, false, self.y > 10 and self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x
return 0, 0, false, self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x
end
xl_chasey=mknew(chasey.new{
xl_chasey=chasey.new{
size=2,
maxspd=1.25,
hurt = {
@ -1075,11 +1081,10 @@ xl_chasey=mknew(chasey.new{
sspr(40, 0, 8, 8, self.x, self.y, 16, 16)
pal()
end,
init = function(ship)
}
mknew(xl_chasey, function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
end,
})
end)
-->8
-- collisions
@ -1093,11 +1098,12 @@ function collides(box1, box2)
or box1.y+box1.height<box2.y)
end
collider = mknew{
init = function(x)
collider = { }
mknew(collider,
function(x)
x.suppress = {}
end,
}
end
)
function collider_indexes(box)
local ret = {}
@ -1295,16 +1301,6 @@ function spawn_bonus_vulcan_chasey()
return c
end
function spawn_bonus_shield_chasey()
local c = spawn_chasey()
c.die = function(self)
spawn_shield_upgrade_at(self.x-1, self.y-1)
chasey.die(self)
end
c.sprite=4
return c
end
helpers = {
spawn_frownie,
spawn_frownie,
@ -1394,7 +1390,7 @@ example_level_csv=[[1,spawn_frownie
115,spawn_spewy
130,spawn_bonus_frownie
145,spawn_spewy
200,spawn_bonus_shield_chasey
200,spawn_chasey
250,spawn_blocking_blocky
285,spawn_spec_gun_at,35,-11,blast_gun
310,spawn_blocking_blocky
@ -1411,9 +1407,8 @@ example_level_csv=[[1,spawn_frownie
500,multi,20,12,spawn_blocking_blocky
501,spawn_bonus_frownie
620,spawn_blocking_blocky
630,spawn_bonus_shield_chasey
720,spawn_blocking_boss_chasey
721,eol]]
700,spawn_blocking_boss_chasey
701,eol]]
-->8
-- readme.md
@ -1465,6 +1460,10 @@ algorithm expressed.
guns
----
* power - cost in generator
power to fire. may be 0.
field directly read by ships;
required in all guns.
* t - metatable for bullet type.
fired once in the bullet's
default direction per shot.
@ -1492,6 +1491,11 @@ actually_shoot to change
projectile logic while keeping
cooldown and ammo logic.
ships manage generator power
before asking the gun to shoot.
this behavior is in
ship_m:maybe_shoot.
bullets
-------
* dx, dy - movement per frame.
@ -1578,19 +1582,39 @@ or less hp calls self:die() and
tells the main game loop to
remove it.
shieldcooldown is the interval
between restoring shield points.
shieldpenalty is the delay
before restoring points after
any damage, reset to this value
on every damaging hit (whether
it is absorbed by the shield or
not) -- shield behaves like
halo and other shooters in its
heritage, where it recovers if
you avoid damage for a while.
not that there is any safe cover
in this kind of game.
ships have power, from 0 to
ship.maxpower, increasing by
ship.generator per frame.
in maybe_shoot, ships check that
they have power to fire before
trying to fire (the gun itself
checks ammo and cooldown), and
spend that power if they fire.
power is also used to restore
shields - ship.shieldcost per
point of shields. shieldcooldown
is the interval between
restoring shield points, which
is reset to shieldpenalty when a
ship takes damage (regardless of
whether that damage is stopped
by the shield or not).
shieldpenalty is much worse than
shieldcooldown (hALO shield).
therefore:
* damaged ships spend power
repairing shields, which may
affect ability to fire guns.
this looks like a slow firing
rate because the ship will
eventually recover enough
energy to fire.
* a ship firing nonstop will
typically be unable to recover
any shields because it will
not have energy to do so.
ships do not repair hp on their
own. negative-damage bullets
@ -1774,7 +1798,7 @@ true, they are dropped.
-->8
-- standard events
blip_fx = mknew{
blip_fx = {
cancel=false
}
@ -1792,6 +1816,8 @@ function blip_fx:abort()
self.cancel=true
end
mknew(blip_fx)
blip_pals = {}
function init_blip_pals()
for i=0,15 do
@ -1826,7 +1852,8 @@ function boom(x,y,boominess,is_boss)
return
end
spark_particle=mknew{}
spark_particle={}
mknew(spark_particle)
function spark_particle:move()
if (rnd(4) < 1) self.sidx += 1
@ -1855,7 +1882,7 @@ end
-->8
-- powerups
powerup = mknew(bullet_base.new{
powerup = bullet_base.new{
-- animated sprite array: "sprites"
-- to draw under or over anim,
-- override draw, draw the
@ -1876,7 +1903,8 @@ powerup = mknew(bullet_base.new{
anim_speed = 2,
loop_pause = 30 -- affected by animspeed
})
}
mknew(powerup)
-- sprite indexes for "sheen" animation
sheen8x8 = split"2,54,55,56,57,58,59,60,61"
@ -1896,7 +1924,7 @@ function powerup:draw()
self.width, self.height)
end
repair = mknew(powerup.new{
repair = powerup.new{
hurt = {
x_off = -2,
y_off = -2,
@ -1906,37 +1934,23 @@ repair = mknew(powerup.new{
x_off = 4,
y_off = 0,
sprites = sheen8x8,
icon = 53,
hitship = function(self, ship)
if (ship ~= primary_ship) return false
primary_ship.hp = min(primary_ship.maxhp, primary_ship.hp + 1)
return true
end,
draw = function(self)
spr(self.icon, self.x, self.y, self.width, self.height)
spr(53, self.x, self.y, self.width, self.height)
powerup.draw(self)
end
})
}
mknew(repair)
function spawn_repair_at(x, y)
repair.new():spawn_at(x, y)
end
shield_upgrade = mknew(repair.new{
icon=52
})
function shield_upgrade:hitship(ship)
if (ship ~= primary_ship) return false
primary_ship.maxshield += 1
return true
end
function spawn_shield_upgrade_at(x, y)
shield_upgrade.new():spawn_at(x,y)
end
gun_swap = mknew(powerup.new{
gun_swap = powerup.new{
hurt = {
x_off = -2,
y_off = -2,
@ -1958,7 +1972,8 @@ gun_swap = mknew(powerup.new{
powerup.draw(self)
spr(self.gun.icon, self.x+2, self.y+2, 1, 1)
end
})
}
mknew(gun_swap)
function spawn_main_gun_at(x, y, gunt)
if (type(gunt)=="string") gunt=_ENV[gunt]
@ -2017,14 +2032,14 @@ __gfx__
00000000000000000000000000000000000000000009080000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000cccccccccccccccc77000000007700000000770000000077000000000000000000000000000000000000000000000000
00000000000000000000000000000000c116611dc11ee11d70000000077000000007700000000770000000070000000000000000000000000000000000000000
00000000000000000000000000000000c1611c1dc11ee11d00000000770000000077000000007700000000770000000700000000000000000000000000000000
00000000000000000000000000000000c61111cdceeeeeed00000000700000000770000000077000000007700000007700000000000000000000000000000000
00000000000000000000000000000000c6111bcdceeeeeed00000000000000007700000000770000000077000000077000000007000000000000000000000000
00000000000000000000000000000000c161bbbdc11ee11d00000000000000007000000007700000000770000000770000000077000000000000000000000000
00000000000000000000000000000000c11ccb1dc11ee11d00000000000000000000000077000000007700000007700000000770000000070000000000000000
00000000000000000000000000000000cdddddddcddddddd00000000000000000000000070000000077000000077000000007700000000770000000000000000
0000000000000000000000000000000000000000cccccccc77000000007700000000770000000077000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000c11ee11d70000000077000000007700000000770000000070000000000000000000000000000000000000000
0000000000000000000000000000000000000000c11ee11d00000000770000000077000000007700000000770000000700000000000000000000000000000000
0000000000000000000000000000000000000000ceeeeeed00000000700000000770000000077000000007700000007700000000000000000000000000000000
0000000000000000000000000000000000000000ceeeeeed00000000000000007700000000770000000077000000077000000007000000000000000000000000
0000000000000000000000000000000000000000c11ee11d00000000000000007000000007700000000770000000770000000077000000000000000000000000
0000000000000000000000000000000000000000c11ee11d00000000000000000000000077000000007700000007700000000770000000070000000000000000
0000000000000000000000000000000000000000cddddddd00000000000000000000000070000000077000000077000000007700000000770000000000000000
cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000