Kistaro Windrider
4804402f32
a batch change from ship to self broke the thing that kept the player on the screen. also spawn_goodie got missed in the migration to `_ENV` for looking up stuff by name.
2111 lines
61 KiB
Lua
2111 lines
61 KiB
Lua
pico-8 cartridge // http://www.pico-8.com
|
|
version 41
|
|
__lua__
|
|
-- generic shmup
|
|
-- by kistaro windrider
|
|
-- dev docs in tab 8
|
|
|
|
game = 1
|
|
win = 2
|
|
lose = 3
|
|
|
|
function usplit(str)
|
|
return unpack(split(str))
|
|
end
|
|
function csv(s)
|
|
local ret=split(s,"\n")
|
|
for i,v in ipairs(ret) do
|
|
ret[i] = type(v) == "string" and split(v) or {v} end
|
|
return ret
|
|
end
|
|
|
|
-- generate standard "overlay"
|
|
-- constructor for type tt.
|
|
-- if more is defined, generated
|
|
-- new calls more(ret) after
|
|
-- ret is definitely not nil
|
|
-- before calling setmetatable.
|
|
-- use to initialize mutables.
|
|
--
|
|
-- if there was a previous new,
|
|
-- it is invoked on the new
|
|
-- object *after* more, because
|
|
-- this works better with the
|
|
-- `more` impls i use.
|
|
function mknew(tt, more)
|
|
local mt,oldnew = {__index=tt},tt.new
|
|
tt.new=function(ret)
|
|
if(not ret) ret = {}
|
|
if(more) more(ret)
|
|
if(oldnew) oldnew(ret)
|
|
setmetatable(ret, mt)
|
|
return ret
|
|
end
|
|
end
|
|
|
|
-- intrusive singly-linked list.
|
|
-- cannot be nested!
|
|
linked_list = {is_linked_list=true}
|
|
mknew(linked_list, function(x)
|
|
x.next=nil
|
|
x.tail=x
|
|
end)
|
|
|
|
function linked_list:push_back(x)
|
|
self.tail.next = x
|
|
self.tail = x
|
|
end
|
|
|
|
function linked_list:push_front(x)
|
|
if (not self.next) self.tail = x
|
|
x.next = self.next
|
|
self.next = x
|
|
end
|
|
|
|
-- vore eats another linked list
|
|
-- by appending its contents.
|
|
-- the ingested linked is empty.
|
|
function linked_list:vore(x)
|
|
if (not x.next) return
|
|
self.tail.next = x.next
|
|
self.tail = x.tail
|
|
x.next = nil
|
|
x.tail = x
|
|
end
|
|
|
|
-- strip calls f(x) for each
|
|
-- node, removing each node for
|
|
-- which f(x) returns true. it
|
|
-- returns the new tail; nil
|
|
-- if the list is now empty.
|
|
function linked_list:strip(f)
|
|
local p, n = self, self.next
|
|
while n do
|
|
if f(n) then
|
|
p.next = n.next
|
|
else
|
|
p = n
|
|
end
|
|
n = n.next
|
|
end
|
|
self.tail = p
|
|
return p
|
|
end
|
|
|
|
-- optimized special case -
|
|
-- could be done with strip but
|
|
-- this avoids extra function
|
|
-- calls and comparisions since
|
|
-- draw isn't allowed to kill
|
|
-- the item
|
|
function linked_list:draw()
|
|
local n = self.next
|
|
while n do
|
|
n:draw()
|
|
n = n.next
|
|
end
|
|
end
|
|
|
|
|
|
function linked_list:pop_front()
|
|
local ret = self.next
|
|
if (not ret) return
|
|
self.next = ret.next
|
|
if (not ret.next) ret.tail = nil
|
|
return ret
|
|
end
|
|
|
|
function _init()
|
|
init_blip_pals()
|
|
wipe_level()
|
|
primary_ship.main_gun = zap_gun.new()
|
|
load_level(example_level_csv)
|
|
state = game
|
|
pal(2,129)
|
|
pal()
|
|
end
|
|
|
|
function once_next_frame(f)
|
|
new_events:push_back{
|
|
move = function()
|
|
f()
|
|
return true
|
|
end,
|
|
}
|
|
end
|
|
|
|
-- health gradients for 1..5 hp
|
|
-- exactly, then all "more".
|
|
hpcols_lut = csv[[36
|
|
34, 136
|
|
34, 130, 136
|
|
34, 34, 130, 136
|
|
34, 34, 130, 130, 136]]
|
|
|
|
-- call after any change to maxhp
|
|
-- configures health gradient
|
|
function init_hpcols()
|
|
hpcols = hpcols_lut[min(primary_ship.maxhp,6)]
|
|
end
|
|
|
|
function wipe_level()
|
|
primary_ship = player.new()
|
|
init_hpcols()
|
|
pships = linked_list.new()
|
|
pships:push_back(primary_ship)
|
|
eships = linked_list.new()
|
|
pbullets = linked_list.new()
|
|
ebullets = linked_list.new()
|
|
intangibles_fg = linked_list.new()
|
|
intangibles_bg = linked_list.new()
|
|
events = linked_list.new()
|
|
new_events = linked_list.new()
|
|
end
|
|
|
|
function _update60()
|
|
updategame()
|
|
end
|
|
|
|
function call_f(x)
|
|
return x:f()
|
|
end
|
|
|
|
function call_move(x)
|
|
return x:move()
|
|
end
|
|
|
|
function updategame()
|
|
leveldone = level_frame()
|
|
events:vore(new_events)
|
|
events:strip(call_move)
|
|
for _, lst in ipairs{intangibles_bg, pships, eships, pbullets, ebullets} do
|
|
lst:strip(call_move)
|
|
end
|
|
|
|
pships:strip(
|
|
function(ps)
|
|
local pbox, pded = hurtbox(ps), false
|
|
eships:strip(
|
|
function(es)
|
|
if (not collides(pbox, hurtbox(es))) return
|
|
pded = pded or ps:hitship(es)
|
|
return es:hitship(ps)
|
|
end
|
|
)
|
|
return pded
|
|
end
|
|
)
|
|
pships:strip(
|
|
function(ps)
|
|
local pbox, pded = hurtbox(ps), false
|
|
ebullets:strip(
|
|
function(eb)
|
|
if (not collides(pbox, hurtbox(eb))) return
|
|
pded = pded or ps:hitbullet(eb)
|
|
return eb:hitship(ps)
|
|
end
|
|
)
|
|
return pded
|
|
end
|
|
)
|
|
|
|
-- many bullets and many enemy ships;
|
|
-- use bucket collider for efficiency
|
|
local pbullet_collider = collider.new()
|
|
local p, n = pbullets, pbullets.next
|
|
while n do
|
|
n.prev = p
|
|
pbullet_collider:insert(n)
|
|
p = n
|
|
n = p.next
|
|
end
|
|
|
|
eships:strip(
|
|
function(es)
|
|
for pb in all(pbullet_collider:get_collisions(es)) do
|
|
if pb:hitship(es) then
|
|
pbullet_collider:hide(pb)
|
|
pb.prev.next = pb.next
|
|
if pb.next then
|
|
pb.next.prev = pb.prev
|
|
else
|
|
pbullets.tail = pb.prev
|
|
end
|
|
end
|
|
if (es:hitbullet(pb)) return true
|
|
end
|
|
end
|
|
)
|
|
|
|
intangibles_fg:strip(call_move)
|
|
|
|
if leveldone and not eships.next and not ebullets.next and not events.next then
|
|
state = win
|
|
end
|
|
if (not pships.next) state = lose
|
|
end
|
|
|
|
function _draw()
|
|
fillp(0)
|
|
drawgame()
|
|
if (state == game) fadelvl = -45
|
|
if (state == win) dropshadow("win",50,61,11)
|
|
if (state == lose) dropshadow("fail",48,61,8)
|
|
fadescreen()
|
|
end
|
|
|
|
fadetable = split"0,1.5,1025.5,1029.5,1285.5,1413.5,9605.5,9637.5,-23130.5,-23066.5,-18970.5,-18954.5,-2570.5,-2568.5,-520.5,-8.5,-0.5"
|
|
|
|
function fadescreen()
|
|
fadelvl += 0.25
|
|
if (fadelvl < 1) return
|
|
local i = min(flr(fadelvl), #fadetable)
|
|
fillp(fadetable[#fadetable+1-i])
|
|
rectfill(0,0,128,128,0)
|
|
end
|
|
|
|
-- puke emits a verbose string
|
|
-- describing item, indented to
|
|
-- the specified depth (0 by
|
|
-- default). used for table
|
|
-- debugging. table-type keys
|
|
-- are not legible here
|
|
function puke(item, indent, seen, hidekey)
|
|
if (type(item) ~= "table") return tostr(item)
|
|
|
|
seen = seen or {}
|
|
if (seen[item]) return "<<...>>"
|
|
seen[item] = true
|
|
|
|
indent = indent or 0
|
|
local pfx = "\n"
|
|
for _=1,indent do
|
|
pfx ..= " "
|
|
end
|
|
local xpfx = pfx.." "
|
|
|
|
if item.is_linked_list then
|
|
local ret,n = "linked_list <",0
|
|
item:strip(function(x)
|
|
n += 1
|
|
ret ..= xpfx..tostr(n)..": "..puke(x, indent+2, seen, "next")
|
|
end)
|
|
return ret..pfx..">"
|
|
end
|
|
|
|
local ret = "{"
|
|
for k, v in pairs(item) do
|
|
if (k ~= hidekey) ret ..= xpfx..tostr(k)..": "..puke(v, indent+2, seen)
|
|
end
|
|
return ret..pfx.."}"
|
|
end
|
|
|
|
-- convenience for debugging
|
|
function puketh(item, ...)
|
|
printh(puke(item), ...)
|
|
end
|
|
|
|
function pukeboard(item)
|
|
puketh(item, "@clip")
|
|
end
|
|
|
|
function drawgame()
|
|
clip(0,0,112,128)
|
|
rectfill(0,0,112,128,0)
|
|
for slist in all{intangibles_bg, pbullets, pships, eships, ebullets, intangibles_fg} do
|
|
slist:draw()
|
|
end
|
|
clip(0,0,128,128)
|
|
drawhud()
|
|
end
|
|
|
|
powcols=split"170,154,153,148,68"
|
|
function drawhud()
|
|
-- 112-and-right is hud zone
|
|
rectfill(112, 0, 127, 127,0x56)
|
|
rect(112,0,127,127,7)
|
|
line(127,1,127,127,5)
|
|
line(113,127)
|
|
|
|
draw_gun_info("❎",1,116,3,primary_ship.main_gun)
|
|
draw_gun_info("🅾️",2,116,31,primary_ship.special_gun)
|
|
|
|
dropshadow("pwr",114,59,1)
|
|
inset(114,66,125,92)
|
|
fillp(0x5a5a)
|
|
vertmeter(115,67,124,91,primary_ship.power, primary_ship.max_power, powcols)
|
|
|
|
|
|
dropshadow("h s",114,97,1)
|
|
inset(114,104,125,125)
|
|
line(119,105,119,124,119)
|
|
line(120,105,120,125,85)
|
|
vertmeter(115,105,118,124,primary_ship.hp, primary_ship.maxhp, hpcols)
|
|
vertmeter(121,105,124,124,primary_ship.shield, primary_ship.maxshield,{204,220,221})
|
|
fillp(0)
|
|
end
|
|
|
|
function draw_gun_info(lbl,fgc,x,y,gun)
|
|
dropshadow(lbl,x,y,fgc)
|
|
inset(114,y+7,125,y+18)
|
|
inset(114,y+20,125,y+24)
|
|
if(gun) then
|
|
spr(gun.icon,116,y+9,1,1)
|
|
--115 to 124 - ammo bar. round up
|
|
if gun.ammo == nil then
|
|
fillp(0xa5a5)
|
|
rectfill(115,y+21,124,y+23,0xea)
|
|
fillp(0)
|
|
elseif gun.ammo > 0 then
|
|
rectfill(
|
|
115,y+21,
|
|
115+flr(9*gun.ammo/gun.maxammo),
|
|
y+23,10)
|
|
else
|
|
line(118, y+22, 121, y+22, 2)
|
|
end
|
|
end
|
|
end
|
|
|
|
function vertmeter(x0,y0,x1,y1,val,maxval,cols)
|
|
if (val <= 0) return
|
|
local h = y1-y0
|
|
local px = -flr(-(h*val)\maxval)
|
|
local ncols = #cols
|
|
local firstcol = ((h-px)*ncols\h)+1
|
|
local lastbottom = y0+(h*firstcol\ncols)
|
|
rectfill(x0, y1-px, x1, lastbottom, cols[firstcol])
|
|
for i=firstcol+1,ncols do
|
|
local bottom = y0+h*i\ncols
|
|
rectfill(x0,lastbottom,x1,bottom,cols[i])
|
|
lastbottom = bottom
|
|
end
|
|
end
|
|
|
|
function inset(x0,y0,x1,y1)
|
|
rectfill(x0,y0,x1,y1,0)
|
|
-- use "wide colors" to draw
|
|
-- monochrome regardless of
|
|
-- fillp
|
|
rect(x0,y0,x1,y1,119)
|
|
line(x1,y0,x0,y0,85)
|
|
line(x0,y1,85)
|
|
end
|
|
|
|
function dropshadow(str, x, y, col)
|
|
print(str, x+1, y+1, 5)
|
|
print(str, x, y, col)
|
|
end
|
|
|
|
-->8
|
|
--ship behavior
|
|
|
|
scrollrate = 0.25 --in px/frame
|
|
|
|
ship_m = {
|
|
|
|
-- ships have no shield by default
|
|
shield = 0,
|
|
maxshield = 0,
|
|
shieldcost = 32767.9,
|
|
shieldcooldown = 0x0.00a0,
|
|
|
|
-- default generator behavior:
|
|
-- 10 seconds for a full charge
|
|
max_power = 600,
|
|
power = 600,
|
|
generator = 1, -- power gen per frame
|
|
|
|
slip = true, -- most enemies slide
|
|
|
|
xmomentum = 0,
|
|
ymomentum = 0,
|
|
}
|
|
mknew(ship_m)
|
|
|
|
function ship_m:die()
|
|
self.dead = true
|
|
if (self.hp <= 0) boom(self.x+self.size*4, self.y+self.size*4,12*self.size, self.boss)
|
|
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()
|
|
if (shoot_main) self:maybe_shoot(self.main_gun)
|
|
if (shoot_spec) self:maybe_shoot(self.special_gun)
|
|
if (dx ~= 0 or dy ~= 0) spark(self.sparks, self.x + 4*self.size, self.y + 4*self.size, dx*2.5, dy*2.5, self.sparkodds)
|
|
self.xmomentum += dx
|
|
self.ymomentum += dy
|
|
self.xmomentum = mid(-self.maxspd, self.maxspd, self.xmomentum)
|
|
self.ymomentum = mid(-self.maxspd, self.maxspd, self.ymomentum)
|
|
|
|
self.x += self.xmomentum
|
|
self.y += self.ymomentum
|
|
|
|
if self == primary_ship then
|
|
self.x = mid(0, 112 - 8 * self.size, self.x)
|
|
self.y = mid(0, 128 - 8 * self.size, self.y)
|
|
end
|
|
|
|
--friction
|
|
local d = self.drag
|
|
self.xmomentum -= mid(d, -d, self.xmomentum)
|
|
self.ymomentum -= mid(d, -d, self.ymomentum)
|
|
|
|
-- "scrolling" behavior
|
|
if self.slip then
|
|
self.y += scrollrate
|
|
if self.y >= 128 then
|
|
self:die()
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ship_m:draw()
|
|
if(self.fx_pal) pal(self.fx_pal)
|
|
spr(self.sprite, self.x, self.y, self.size, self.size)
|
|
pal()
|
|
end
|
|
|
|
function hurtbox(ship)
|
|
local h = ship.hurt
|
|
return {
|
|
x=ship.x + h.x_off,
|
|
y=ship.y + h.y_off,
|
|
width=h.width,
|
|
height=h.height
|
|
}
|
|
end
|
|
|
|
function ship_m:maybe_shoot(gun)
|
|
if (not gun) return
|
|
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)
|
|
return self:hitsomething(1)
|
|
end
|
|
|
|
function ship_m:hitbullet(b)
|
|
return self:hitsomething(b.damage)
|
|
end
|
|
|
|
function ship_m:hitsomething(dmg)
|
|
if (dmg <= 0) return false
|
|
self.shield_refresh_ready = lframe + self.shieldcooldown
|
|
if self.shield >= dmg then
|
|
self.shield -= dmg
|
|
self:ow(true)
|
|
return false
|
|
end
|
|
dmg -= self.shield
|
|
self.shield = 0
|
|
self.hp -= dmg
|
|
if self.hp <= 0 then
|
|
self:die()
|
|
return true
|
|
end
|
|
self:ow(false)
|
|
return false
|
|
end
|
|
|
|
function ship_m:ow(shielded)
|
|
if (shielded) then
|
|
blip(self,12,3)
|
|
return
|
|
end
|
|
blip(self, 7, 3)
|
|
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.power -= self.shieldcost
|
|
self.shield_refresh_ready = lframe + self.shieldcooldown
|
|
end
|
|
|
|
-->8
|
|
-- bullet and gun behaviors
|
|
|
|
bullet_base = {
|
|
enemyspd = 0.5
|
|
}
|
|
mknew(bullet_base)
|
|
|
|
gun_base = {
|
|
shoot_ready = -32768,
|
|
icon = 20
|
|
}
|
|
mknew(gun_base)
|
|
|
|
function bullet_base:hitship(_)
|
|
self:die()
|
|
return true
|
|
end
|
|
|
|
function bullet_base:die()
|
|
end
|
|
|
|
function bullet_base:move()
|
|
self.x += self.dx
|
|
if self.enemy then
|
|
self.y += self.dy
|
|
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
|
|
|
|
function bullet_base:draw()
|
|
spr(self.sprite, self.x, self.y, self.width, self.height)
|
|
end
|
|
|
|
function bullet_base:spawn_at(x, y)
|
|
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)
|
|
if (lframe < self.shoot_ready) return false
|
|
if self.ammo then
|
|
if (self.ammo <= 0) return false
|
|
self.ammo -= 1
|
|
end
|
|
self.shoot_ready = lframe + self.cooldown
|
|
self:actually_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 = bullet_base.new{
|
|
--shape
|
|
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
|
|
x_off = 0, -- upper left corner
|
|
y_off = 0, -- relative to sprite
|
|
width = 2,
|
|
height = 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 = 8,
|
|
|
|
hitship = function(_, _)
|
|
return true
|
|
end
|
|
}
|
|
mknew(zap)
|
|
|
|
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
|
|
t = zap -- metatable of bullet to fire
|
|
}
|
|
mknew(zap_gun)
|
|
|
|
blast = bullet_base.new{
|
|
--shape
|
|
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
|
|
x_off = 1, -- upper left corner
|
|
y_off = 1, -- relative to sprite
|
|
width = 6,
|
|
height = 6
|
|
},
|
|
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,
|
|
awaitcancel = false,
|
|
|
|
-- disable damage for 2 frames
|
|
-- when hitting something
|
|
hitship = function(self, _)
|
|
if self.damage > 0 and not self.awaitcancel then
|
|
self.awaitcancel = true
|
|
once_next_frame(function()
|
|
new_events:push_back{
|
|
wait = 2,
|
|
obj = self,
|
|
saved_dmg = self.damage,
|
|
move = function(self)
|
|
self.wait -= 1
|
|
if self.wait <= 0 then
|
|
self.obj.damage = self.saved_dmg
|
|
return true
|
|
end
|
|
end,
|
|
}
|
|
self.damage = 0
|
|
self.awaitcancel = false
|
|
end)
|
|
end
|
|
end
|
|
}
|
|
mknew(blast)
|
|
|
|
blast_gun = gun_base.new{
|
|
icon = 13,
|
|
enemy = false,
|
|
power = 0, -- ammo, not power
|
|
cooldown = 0x0.0020, -- frames between shots
|
|
ammo = 5,
|
|
maxammo = 5,
|
|
t = blast -- type of bullet to fire
|
|
}
|
|
mknew(blast_gun)
|
|
|
|
protron = bullet_base.new{
|
|
--shape
|
|
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
|
|
x_off = 1, -- upper left corner
|
|
y_off = 1, -- relative to sprite
|
|
width = 2,
|
|
height = 2
|
|
},
|
|
center_x_off = 1, -- how to position by ship
|
|
bottom_y_off = 4,
|
|
top_y_off = 0,
|
|
|
|
damage = 1,
|
|
dx = 0, -- px/frame
|
|
dy = 3,
|
|
}
|
|
mknew(protron)
|
|
|
|
protron_gun = gun_base.new{
|
|
icon = 25,
|
|
enemy = false,
|
|
power = 35,
|
|
cooldown = 0x0.000f, -- frames between shots
|
|
ammo = nil,
|
|
maxammo = nil,
|
|
actually_shoot = function(self, x, y)
|
|
local sprite = protron.psprite
|
|
if (self.enemy) sprite=protron.esprite
|
|
for i=1,3 do
|
|
local b = protron.new{
|
|
enemy=self.enemy,
|
|
sprite=sprite,
|
|
dx = i,
|
|
dy = 4-i
|
|
}
|
|
b:spawn_at(x,y)
|
|
local b2 = protron.new{
|
|
enemy=self.enemy,
|
|
sprite=sprite,
|
|
dx = -i,
|
|
dy = 4-i
|
|
}
|
|
b2:spawn_at(x,y)
|
|
end
|
|
local bup = protron.new{
|
|
enemy=self.enemy,
|
|
sprite=sprite,
|
|
dy=4
|
|
}
|
|
bup:spawn_at(x,y)
|
|
end
|
|
}
|
|
mknew(protron_gun)
|
|
|
|
vulcan = bullet_base.new{
|
|
--shape
|
|
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
|
|
x_off = 0, -- upper left corner
|
|
y_off = 0, -- relative to sprite
|
|
width = 1,
|
|
height = 4
|
|
},
|
|
center_x_off = 0.5, -- how to position by ship
|
|
bottom_y_off = 4,
|
|
top_y_off = 0,
|
|
|
|
damage = 0.5,
|
|
dx = 0, -- px/frame
|
|
dy = 4,
|
|
}
|
|
mknew(vulcan)
|
|
|
|
vulcan_gun = gun_base.new{
|
|
icon = 37,
|
|
enemy = false,
|
|
power = 8,
|
|
cooldown = 0x0.0002, -- frames between shots
|
|
ammo = nil,
|
|
maxammo = nil,
|
|
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 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
|
|
}
|
|
mknew(vulcan_gun)
|
|
|
|
-->8
|
|
--ships, including player
|
|
|
|
firespark = split"9, 8, 2, 5, 1"
|
|
smokespark = split"13, 13, 5, 5"
|
|
|
|
player = ship_m.new{
|
|
--shape
|
|
sprite = 1, --index of ship sprite
|
|
size = 1, --all ships are square; how many 8x8 sprites?
|
|
hurt = { -- hurtbox - where this ship can be hit
|
|
x_off = 3, -- upper left corner
|
|
y_off = 2, -- relative to ship ulc
|
|
width = 1,
|
|
height = 3
|
|
},
|
|
sparks = firespark, -- see tab 9
|
|
sparkodds = 2,
|
|
boss = true, -- dramatic special effects
|
|
|
|
-- health and power
|
|
hp = 3, -- current health, non-regenerating
|
|
maxhp = 3, -- player only; other ships never heal
|
|
shield = 2, -- regenerates, using power
|
|
maxshield = 2,
|
|
shieldcost = 300, -- power cost to refill shield
|
|
generator = 1.5, -- 1 feels too slow
|
|
|
|
-- gun
|
|
main_gun = nil, -- assign at spawn time
|
|
special_gun = nil,
|
|
fire_off_x = 4, -- offset where bullets come from
|
|
fire_off_y = 0,
|
|
|
|
-- position
|
|
x=52, -- x and y are for upper left corner
|
|
y=96,
|
|
xmomentum = 0,
|
|
ymomentum = 0,
|
|
maxspd = 2.5, -- momentum cap
|
|
thrust = 0.25, -- momentum added from button
|
|
drag = 0.125, -- momentum lost per frame
|
|
slip = false, -- does not slide down screen
|
|
act = function(self) -- fetch buttons
|
|
local b,th = btn(),self.thrust
|
|
local blr = b&0x3
|
|
if blr == 1 then
|
|
self.sprite=17
|
|
elseif blr==2 then
|
|
self.sprite=18
|
|
else
|
|
self.sprite=1
|
|
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
|
|
}
|
|
mknew(player,
|
|
function(p)
|
|
p.main_gun = zap_gun.new()
|
|
end
|
|
)
|
|
|
|
frownie = ship_m.new{
|
|
--shape
|
|
sprite = 3, --index of ship sprite
|
|
size = 1, --all ships are square; how many 8x8 sprites?
|
|
hurt = { -- hurtbox - where this ship can be hit
|
|
x_off = 0, -- upper left corner
|
|
y_off = 1, -- relative to ship ulc
|
|
width = 8,
|
|
height = 6
|
|
},
|
|
sparks = smokespark,
|
|
sparkodds = 8,
|
|
|
|
-- health and power
|
|
hp = 1, -- enemy ships need no max hp
|
|
|
|
-- position
|
|
x=60, -- x and y are for upper left corner
|
|
y=8,
|
|
xmomentum = 0,
|
|
ymomentum = 0,
|
|
maxspd = 2, -- momentum cap
|
|
thrust = 0.12, -- momentum added from button
|
|
drag = 0.07, -- momentum lost per frame
|
|
slip = true,
|
|
act = function(self)
|
|
local tstate,dx = (1 + flr(4*t() + 0.5)) % 6,0
|
|
if (tstate==1 or tstate==2) dx=-self.thrust
|
|
if (tstate>=4) dx=self.thrust
|
|
return dx,0,false,false
|
|
end,
|
|
}
|
|
mknew(frownie)
|
|
|
|
blocky = frownie.new{
|
|
sprite = 10,
|
|
hp = 2,
|
|
hurt = {
|
|
x_off = 0,
|
|
y_off = 0,
|
|
width = 8,
|
|
height = 7
|
|
},
|
|
|
|
ow = function(self)
|
|
if self.hp <= 1 then
|
|
self.sprite = 11
|
|
else
|
|
self.sprite = 10
|
|
end
|
|
ship_m.ow(self)
|
|
end
|
|
}
|
|
mknew(blocky)
|
|
|
|
spewy = frownie.new{
|
|
sprite=26,
|
|
power=-20,
|
|
hurt = {
|
|
x_off=0,
|
|
y_off=1,
|
|
width=8,
|
|
height=5
|
|
},
|
|
hp=1,
|
|
maxpower=70,
|
|
generator=0.5,
|
|
fire_off_x=4,
|
|
fire_off_y = 7,
|
|
act=function(self)
|
|
local dx,dy,shoot_spec=frownie.act(self)
|
|
return dx, dy, shoot_spec, true
|
|
end
|
|
}
|
|
mknew(spewy, function(ship)
|
|
ship.main_gun=ship.main_gun or protron_gun.new{enemy=true}
|
|
end)
|
|
|
|
chasey = ship_m.new{
|
|
sprite = 5,
|
|
size = 1,
|
|
hurt = {
|
|
x_off = 1,
|
|
y_off = 2,
|
|
width = 6,
|
|
height = 5,
|
|
},
|
|
sparks = smokespark,
|
|
sparkodds = 8,
|
|
hp = 2,
|
|
shield = 1,
|
|
maxshield = 1,
|
|
shieldcost = 180,
|
|
|
|
fire_off_x = 4,
|
|
fire_off_y = 7,
|
|
|
|
maxspd = 2,
|
|
thrust = 0.2,
|
|
drag = 0.075,
|
|
slip = true,
|
|
}
|
|
mknew(chasey, function(ship)
|
|
ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
|
|
end)
|
|
|
|
function chasey:act()
|
|
local dx = 0
|
|
if (self.x < primary_ship.x) dx=self.thrust
|
|
if (self.x > primary_ship.x) dx=-self.thrust
|
|
return dx, 0, false, self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x
|
|
end
|
|
|
|
xl_chasey=chasey.new{
|
|
size=2,
|
|
maxspd=1.25,
|
|
hurt = {
|
|
x_off = 2,
|
|
y_off = 4,
|
|
width = 12,
|
|
height = 10
|
|
},
|
|
hp = 20,
|
|
shield = 5,
|
|
boss = true,
|
|
slip = false,
|
|
act = function(self)
|
|
local dx,dy,shoot_spec,shoot_main = chasey.act(self)
|
|
if (self.y < 4) dy=self.thrust
|
|
return dx,dy,shoot_spec,shoot_main
|
|
end,
|
|
draw = function(self)
|
|
if(self.fx_pal) pal(self.fx_pal)
|
|
sspr(40, 0, 8, 8, self.x, self.y, 16, 16)
|
|
pal()
|
|
end,
|
|
}
|
|
mknew(xl_chasey, function(ship)
|
|
ship.main_gun=ship.main_gun or zap_gun.new{enemy=true}
|
|
end)
|
|
-->8
|
|
-- collisions
|
|
|
|
-- box: x, y, width, height
|
|
|
|
function collides(box1, box2)
|
|
return not (
|
|
box1.x>box2.x+box2.width
|
|
or box1.y>box2.y+box2.height
|
|
or box1.x+box1.width<box2.x
|
|
or box1.y+box1.height<box2.y)
|
|
end
|
|
|
|
collider = { }
|
|
mknew(collider,
|
|
function(x)
|
|
x.suppress = {}
|
|
end
|
|
)
|
|
|
|
function collider_indexes(box)
|
|
local ret = {}
|
|
for x = box.x\8, (box.x+box.width)\8 do
|
|
for y = box.y\8, (box.y+box.height)\8 do
|
|
add(ret, x+256*y)
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function collider:insert(item)
|
|
-- todo: separate "big items" list?
|
|
local bdx = collider_indexes(hurtbox(item))
|
|
for i in all(bdx) do
|
|
local x = self[i]
|
|
if not x then
|
|
x = {}
|
|
self[i] = x
|
|
end
|
|
add(x, item)
|
|
end
|
|
end
|
|
|
|
function collider:hide(item)
|
|
self.suppress[item]=true
|
|
end
|
|
|
|
function collider:get_collisions(item)
|
|
local found = { }
|
|
local seen = { }
|
|
local box = hurtbox(item)
|
|
local bucket_ids = collider_indexes(box)
|
|
for b_idx in all(bucket_ids) do
|
|
local bucket = self[b_idx]
|
|
if bucket then
|
|
for candidate in all(bucket) do
|
|
if not (seen[candidate] or self.suppress[candidate]) then
|
|
seen[candidate] = true
|
|
if (collides(box, hurtbox(candidate))) add(found, candidate)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return found
|
|
end
|
|
-->8
|
|
-- level and event system
|
|
|
|
-- a level is a map from
|
|
-- effective frame number to
|
|
-- a list of actions for that
|
|
-- frame. an action is a
|
|
-- method name and its args.
|
|
|
|
-- effective frame number stops
|
|
-- when freeze count is nonzero
|
|
|
|
-- a level is won when it hits
|
|
-- the end-of-level sentinel
|
|
-- and there are no more
|
|
-- tracked enemies.
|
|
-- lost when there are no
|
|
-- player ships left.
|
|
|
|
-- effective frame
|
|
distance = 0
|
|
-- actual frame count since
|
|
-- start of level times 0x0.0001
|
|
lframe = 0
|
|
|
|
-- do not advance distance when
|
|
-- nonzero
|
|
freeze = 0
|
|
|
|
eol = {}
|
|
|
|
function load_level(levelfile)
|
|
distance = 0
|
|
lframe = 0
|
|
freeze = 0
|
|
leveldone = false
|
|
current_level = {}
|
|
local found_eol = false
|
|
if (type(levelfile)=="string") levelfile = csv(levelfile)
|
|
for row in all(levelfile) do
|
|
local x = current_level[row[1]]
|
|
if row[2] == "eol" then
|
|
found_eol = true
|
|
assert(x==nil, "events on eol frame")
|
|
current_level[row[1]] = eol
|
|
else
|
|
row.next = x
|
|
current_level[row[1]]=row
|
|
end
|
|
end
|
|
assert(found_eol)
|
|
end
|
|
|
|
function level_frame()
|
|
lframe += 0x0.0001
|
|
if (current_level == nil) return true
|
|
if freeze == 0 then
|
|
distance += 1
|
|
local cbs = current_level[distance]
|
|
if cbs ~= nil then
|
|
if cbs == eol then
|
|
current_level = nil
|
|
return true
|
|
else
|
|
while cbs do
|
|
assert(cbs[1] == distance)
|
|
local f = _ENV[cbs[2]]
|
|
assert(type(f) == "function", cbs[2].." at "..distance.." is not a function")
|
|
f(unpack(cbs, 3))
|
|
cbs=cbs.next
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
-->8
|
|
-- example level
|
|
|
|
function spawn_blocking_rnd_x(typ)
|
|
freeze += 1
|
|
s = typ.new{
|
|
x = rnd(104),
|
|
y = -7,
|
|
ice = 1,
|
|
orig_die = typ.die,
|
|
die = function(self)
|
|
freeze -= self.ice
|
|
self.ice = 0
|
|
self:orig_die()
|
|
end,
|
|
}
|
|
eships:push_back(s)
|
|
return s
|
|
end
|
|
|
|
function spawn_frownie()
|
|
return spawn_rnd(frownie)
|
|
end
|
|
|
|
function spawn_blocking_frownie()
|
|
spawn_blocking_rnd_x(frownie)
|
|
end
|
|
|
|
function spawn_blocky()
|
|
spawn_rnd(blocky)
|
|
end
|
|
|
|
function spawn_blocking_blocky()
|
|
spawn_rnd(blocky, 1)
|
|
end
|
|
|
|
function spawn_spewy()
|
|
return spawn_rnd(spewy)
|
|
end
|
|
|
|
function spawn_chasey()
|
|
return spawn_rnd(chasey)
|
|
end
|
|
|
|
function spawn_blocking_spewy()
|
|
freeze += 1
|
|
local s = spawn_spewy()
|
|
s.ice = 1
|
|
s.die = function(self)
|
|
freeze -= self.ice
|
|
self.ice = 0
|
|
frownie.die(self)
|
|
end
|
|
end
|
|
|
|
function spawn_bonus_frownie()
|
|
local f = spawn_frownie()
|
|
f.sprite = 7
|
|
f.die = function(self)
|
|
spawn_repair_at(self.x+4, self.y+4)
|
|
frownie.die(self)
|
|
end
|
|
end
|
|
|
|
function spawn_bonus_vulcan_chasey()
|
|
local c = spawn_chasey()
|
|
c.main_gun=vulcan_gun.new{enemy=true}
|
|
c.die = function(self)
|
|
spawn_main_gun_at(self.x-1, self.y-1, vulcan_gun)
|
|
chasey.die(self)
|
|
end
|
|
c.sprite=4
|
|
return c
|
|
end
|
|
|
|
helpers = {
|
|
spawn_frownie,
|
|
spawn_frownie,
|
|
spawn_frownie,
|
|
spawn_blocky,
|
|
spawn_blocky,
|
|
spawn_chasey,
|
|
spawn_spewy,
|
|
}
|
|
|
|
function spawn_blocking_boss_chasey()
|
|
local c = spawn_rnd(xl_chasey, 1)
|
|
local nextspawn = lframe + 0x0.0080
|
|
events:push_back{move=function()
|
|
if lframe >= nextspawn then
|
|
helpers[flr(rnd(#helpers))+1]()
|
|
nextspawn += 0x0.0040
|
|
end
|
|
return c.dead
|
|
end}
|
|
|
|
return c
|
|
end
|
|
|
|
function std_spawn(tnm, n, blocking, goodie,altspr)
|
|
local typ = _ENV[tnm]
|
|
assert(typ and typ.new, tostr(tnm).." not a class")
|
|
for i=1,(n or 1) do
|
|
spawn_rnd(typ, blocking, goodie,altspr)
|
|
end
|
|
end
|
|
|
|
-- blocking: 1 or 0
|
|
function spawn_rnd(typ, blocking, goodie,altspr)
|
|
blocking = blocking or 0
|
|
freeze += blocking
|
|
s = typ.new{
|
|
x = rnd(104),
|
|
y = -(typ.size * 8 - 1),
|
|
ice=blocking,
|
|
die=function(self)
|
|
freeze -= self.ice
|
|
self.ice=0
|
|
typ.die(self)
|
|
spawn_goodie(goodie, self.x, self.y, self.size)
|
|
end,
|
|
}
|
|
if (altspr) s.spr = altspr
|
|
eships:push_back(s)
|
|
return s
|
|
end
|
|
|
|
-- TODO: spawn_goodie compatible versions of gun drops
|
|
-- TODO: goodie table
|
|
function spawn_goodie(goodie_name, x, y, sz)
|
|
if (not goodie_name or #goodie_name == 0) return
|
|
local sh = sz and sz/2 or 0
|
|
_ENV[goodie_name].new{}:spawn_at(x+sh,y+sh)
|
|
end
|
|
|
|
function multi(times, interval, fnm, ...)
|
|
local f,irm,vargs = _ENV[fnm],interval,pack(...)
|
|
assert(type(f) == "function", fnm.." not a function")
|
|
f(...)
|
|
events:push_back{move=function()
|
|
irm-=1
|
|
if irm <= 0 then
|
|
irm=interval
|
|
times-=1
|
|
f(unpack(vargs))
|
|
return times <= 1
|
|
end
|
|
end}
|
|
end
|
|
|
|
-- then convert sample_level to csv.
|
|
-- spawn_spec_gun_at and spawn_main_gun_at will need parsed forms.
|
|
-- the boss also needs to be reachable, but one-off is fine.
|
|
-- each row of level csv is offset,event,event-args...
|
|
-- where offset,eol is a special case.
|
|
|
|
example_level_csv=[[1,spawn_frownie
|
|
60,spawn_bonus_vulcan_chasey
|
|
61,spawn_blocky
|
|
85,spawn_spewy
|
|
100,spawn_spewy
|
|
115,spawn_spewy
|
|
130,spawn_bonus_frownie
|
|
145,spawn_spewy
|
|
200,spawn_chasey
|
|
250,spawn_blocking_blocky
|
|
285,spawn_spec_gun_at,35,-11,blast_gun
|
|
310,spawn_blocking_blocky
|
|
310,spawn_blocking_blocky
|
|
310,spawn_blocking_blocky
|
|
311,spawn_frownie
|
|
350,spawn_main_gun_at,70,-11,protron_gun
|
|
401,spawn_frownie
|
|
420,spawn_blocking_frownie
|
|
430,spawn_bonus_vulcan_chasey
|
|
450,spawn_frownie
|
|
465,spawn_bonus_frownie
|
|
480,spawn_chasey
|
|
500,multi,20,12,spawn_blocking_blocky
|
|
501,spawn_bonus_frownie
|
|
620,spawn_blocking_blocky
|
|
700,spawn_blocking_boss_chasey
|
|
701,eol]]
|
|
|
|
-->8
|
|
-- readme.md
|
|
|
|
--[[
|
|
|
|
main loop sequence
|
|
==================
|
|
1. level_frame
|
|
2. events
|
|
3. merge new_events into events
|
|
4. update bg intangibles
|
|
5. move ships (player first)
|
|
6. move bullets (player first)
|
|
7. calculate collisions
|
|
1. pship on eship
|
|
2. ebullet on pship
|
|
3. pbullet on eship
|
|
8. update fg intangibles
|
|
9. check for end of level
|
|
|
|
draw order
|
|
----------
|
|
bottom to top:
|
|
1. intangibles_bg
|
|
2. player bullets
|
|
3. player ships
|
|
4. enemy ships
|
|
5. enemy bullets
|
|
6. intangibles_fg
|
|
|
|
notes
|
|
-----
|
|
intangibles_fg move()s after
|
|
all collisions and other moves
|
|
are processed. if an intangible
|
|
is added to the list as a result
|
|
of a collision or move, it will
|
|
itself be move()d before it is
|
|
drawn.
|
|
|
|
data-driven items
|
|
=================
|
|
guns and bullets both allow the
|
|
most common behaviors to be
|
|
expressed with data alone.
|
|
ships only need a movement
|
|
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.
|
|
* enemy - if true, fired bullets
|
|
are flagged as enemy bullets.
|
|
* icon - sprite index of an
|
|
8x8 sprite to display in the
|
|
hud when the player has this
|
|
gun. default is 20, a generic
|
|
crosshair bullseye thing.
|
|
* cooldown - min frames between
|
|
shots.
|
|
* ammo, maxammo - permitted
|
|
number of shots. 0 is empty
|
|
and unfireable. maxammo = 0
|
|
will cause a divide by zero
|
|
so don't do that. if nil,
|
|
ammo is infinite.
|
|
|
|
default guns manage ammo and
|
|
cooldown in shoot, then call
|
|
actually_shoot to create the
|
|
projectile. override only
|
|
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.
|
|
player bullets use -dy
|
|
instead.
|
|
* enemyspd - multiplier for dx
|
|
and dy on enemy bullets.
|
|
default is 0.5, making enemy
|
|
shots much easier to dodge
|
|
* damage - damage per hit;
|
|
used by ships
|
|
* psprite, esprite - index of
|
|
player or enemy sprite.
|
|
* center_off_x - the horizontal
|
|
centerpoint of the bullet,
|
|
for positioning when firing.
|
|
assume a pixel's coordinates
|
|
refer to the upper left corner
|
|
of the pixel; the center of
|
|
a 2-width bullet with an
|
|
upper left corner at 0 is 1,
|
|
not 0.5.
|
|
* top_off_y, bottom_off_y -
|
|
also for positioning when
|
|
firing. positive distance from
|
|
top or bottom edge to image.
|
|
top_off_y will usually be 0,
|
|
bottom_off_y will not be when
|
|
bullets are smaller than
|
|
the sprite box.
|
|
* width, height - measured in
|
|
full sprites (8x8 boxes), not
|
|
pixels. used for drawing.
|
|
|
|
bullets despawn when above or
|
|
below the screen (player or
|
|
enemy bullets, respectively).
|
|
|
|
by default, bullets despawn
|
|
when they hit something.
|
|
override hitship to change this.
|
|
|
|
ships
|
|
____
|
|
|
|
ships move by calculating
|
|
momentum, then offsetting their
|
|
position by that momentum, then
|
|
clamping their position to the
|
|
screen (horizontally only for
|
|
ships that autoscroll). ships
|
|
that autoscroll (slip==true)
|
|
then slide down by scrollspeed.
|
|
fractional coordinates are ok.
|
|
after movement, ships lose
|
|
momentum (ship.drag along each
|
|
axis). abs(momentum) can't
|
|
exceed ship.maxspeed.
|
|
|
|
ships gain momentum by acting
|
|
like a player pushing buttons.
|
|
the player ship actually reads
|
|
buttons for this.
|
|
|
|
act -- returns new acceleration:
|
|
dx, dy, shoot_spec, shoot_main.
|
|
dx and dy are change in momentum
|
|
in px/frame. this is controls
|
|
only -- friction is handled in
|
|
ship:move (`drag` value).
|
|
|
|
ships hitting another ship take
|
|
1 damage per frame of overlap.
|
|
ships hitting a bullet check
|
|
bullet.damage to find out how
|
|
much damage they take. damage
|
|
is applied to shields, then hp.
|
|
damaged ships flash briefly -
|
|
blue (12) if all damage was
|
|
shielded, white (7) if hp was
|
|
damaged. a ship that then has 0
|
|
or less hp calls self:die() and
|
|
tells the main game loop to
|
|
remove it.
|
|
|
|
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 when a ship takes
|
|
damage (regardless of whether
|
|
that damage is stopped by the
|
|
shield or not).
|
|
|
|
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
|
|
are treated as 0, but a bullet
|
|
can choose to repair the ship
|
|
it hits in its own hitship
|
|
method, or otherwise edit it
|
|
(changing weapons, refilling
|
|
weapon ammo). powerups are
|
|
therefore a kind of bullet.
|
|
|
|
levels
|
|
======
|
|
|
|
a level is a table mapping
|
|
effective frame number to
|
|
functions. when a level starts,
|
|
it sets lframe ("level frame")
|
|
and distance to 0.
|
|
|
|
every frame, level_frame
|
|
increments lframe by 0x0.0001.
|
|
then if the level is not frozen,
|
|
it increments distance by 1.0
|
|
and runs the function in the
|
|
level table for exactly that
|
|
frame number (if any). distance
|
|
is therefore "nonfrozen frames",
|
|
and is used to trigger level
|
|
progress. lframe always
|
|
increments. ships are encouraged
|
|
to use lframe to control
|
|
animation and movement, and may
|
|
use distance to react to level
|
|
progress separately from overall
|
|
time. remember to multiply
|
|
lframe-related stuff by 0x0001.
|
|
|
|
a special sentinel value, eol,
|
|
marks the end of the level.
|
|
(the level engine doesn't know
|
|
when it's out of events, so
|
|
without eol, the level will
|
|
simply have no events forever.)
|
|
when it finds eol, level_frame
|
|
throws away the current level
|
|
and tells the main loop that it
|
|
might be done. the main loop
|
|
agrees the level is over and the
|
|
player has won when the level
|
|
has reached eol and there are
|
|
no more enemy ships, enemy
|
|
bullets, or background events
|
|
remaining. player ships, player
|
|
bullets, and intangibles are
|
|
not counted.
|
|
|
|
level freezing
|
|
--------------
|
|
the level is frozen when the
|
|
global value freeze > 0.
|
|
generally, something intending
|
|
to block level progress (a
|
|
miniboss, a minigame, etc.)
|
|
increments freeze and prepares
|
|
some means of decrementing it
|
|
when it no longer wants to block
|
|
level progress.
|
|
|
|
most commonly, we want to block
|
|
until some specific ship or
|
|
group of ships has died. for
|
|
these ships, override ship:die
|
|
to decrement freeze. make sure
|
|
to set ship.dead in any new
|
|
ship:die method so anything else
|
|
looking at it can recognize
|
|
the ship as dead.
|
|
|
|
for anything else, you probably
|
|
want an event to figure out when
|
|
to unfreeze.
|
|
|
|
levels start at 1
|
|
-----------------
|
|
|
|
distance is initialized to 0
|
|
but gets incremented before the
|
|
first time the engine looks for
|
|
events. therefore, the first
|
|
frame of the level executes
|
|
level[1]. since levelframe
|
|
executes before anything else,
|
|
level[1] sets up the first frame
|
|
drawn in the level. the player
|
|
does not see a blank world
|
|
before level[1] runs.
|
|
level[1] can therefore be used
|
|
to reconfigure the player ship,
|
|
set up backgrounds, start music,
|
|
kick off some kind of fade-in
|
|
animation, etc.
|
|
|
|
|
|
events
|
|
======
|
|
the global list "events" stores
|
|
0-argument functions which are
|
|
called every frame. if they
|
|
return true, they are removed
|
|
from the list and not run again;
|
|
if they return false, they stay
|
|
and will be called in later
|
|
frames. the level does not end
|
|
while the events table is
|
|
nonempty.
|
|
|
|
events are most commonly used
|
|
to set up something for later
|
|
(for example, blip uses an event
|
|
to remove the fx_pallete from
|
|
the flashing ship when the blip
|
|
expires), but can also be used
|
|
to implement a "level within a
|
|
level" that does something
|
|
complicated until it's done. if
|
|
you froze the level when
|
|
creating the event, remember
|
|
to thaw it (freeze -= 1) on all
|
|
paths that return true.
|
|
|
|
to do complex stuff in events,
|
|
use a closure or a metatable
|
|
that specifies __call.
|
|
|
|
to avoid editing the events
|
|
list while it is being iterated,
|
|
events that create new events
|
|
must add those events to
|
|
new_events rather than events.
|
|
new_events is only valid during
|
|
the "event execution" stage, so
|
|
events created at any other time
|
|
must go directly on events
|
|
without using new_events.
|
|
|
|
intangibles
|
|
===========
|
|
|
|
the intangibles_fg and
|
|
intangibles_bg lists contain
|
|
items with :move and :draw.
|
|
like ships and bullets, they
|
|
move during _update60 and
|
|
draw during _draw. they are
|
|
not checked for collisions.
|
|
|
|
intangibles_bg moves/draws
|
|
before anything else moves or
|
|
draws. intangibles_fg
|
|
moves/draws last. this controls
|
|
whether your intangible object
|
|
draws in front of or behind
|
|
other stuff. you probably want
|
|
intangibles_bg for decorative
|
|
elements and intangibles_fg
|
|
for explosions, score popups,
|
|
etc.
|
|
|
|
there's no scrolling background
|
|
engine but intangibles_bg could
|
|
be used to create one, including
|
|
using the map (otherwise unused
|
|
in this engine) for the purpose.
|
|
|
|
intangibles do not prevent the
|
|
level from ending. like bullets
|
|
and ships, if :move returns
|
|
true, they are dropped.
|
|
]]
|
|
-->8
|
|
-- standard events
|
|
|
|
blip_fx = {
|
|
cancel=false
|
|
}
|
|
|
|
function blip_fx:move()
|
|
if (self.cancel) return true
|
|
self.frames -= 1
|
|
if self.frames < 0 then
|
|
self.obj.fx_pal = nil
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function blip_fx:abort()
|
|
self.cancel=true
|
|
end
|
|
|
|
mknew(blip_fx)
|
|
|
|
blip_pals = {}
|
|
function init_blip_pals()
|
|
for i=0,15 do
|
|
local pp = {[0]=0}
|
|
for j=1,15 do
|
|
pp[j] = i
|
|
end
|
|
blip_pals[i]=pp
|
|
end
|
|
end
|
|
|
|
function blip(obj, col, frames)
|
|
obj.fx_pal = blip_pals[col]
|
|
if (obj.___fx_pal_event) obj.___fx_pal_event:abort()
|
|
events:push_back(blip_fx.new{frames=frames, obj=obj})
|
|
end
|
|
|
|
bossspark = split"7,7,10,10,9,9,9,8,8,8,2,2,5,5"
|
|
|
|
function boom(x,y,boominess,is_boss)
|
|
local sp = firespark
|
|
if is_boss then
|
|
boominess *= 10
|
|
sp = bossspark
|
|
end
|
|
local boombase = min(0.023 * boominess, 0.25)
|
|
local boombonus = min(0.05 * boominess, 1.25)
|
|
for _=1,boominess do
|
|
local angle = rnd(1)
|
|
spark(sp,x+4,y+4,cos(angle), sin(angle),boombase+rnd(boombonus),1, true)
|
|
end
|
|
return
|
|
end
|
|
|
|
spark_particle={}
|
|
mknew(spark_particle)
|
|
|
|
function spark_particle:move()
|
|
if (rnd(4) < 1) self.sidx += 1
|
|
if (self.sidx > #self.sprs) return true
|
|
self.x += self.dx
|
|
self.y += self.dy
|
|
self.dx -= mid(0.05,-0.05, self.dx)
|
|
self.dy -= mid(0.05,-0.05, self.dy)
|
|
end
|
|
function spark_particle:draw()
|
|
pset(self.x,self.y,self.sprs[self.sidx])
|
|
end
|
|
|
|
function spark(sprs, x, y, dx, dy, odds, fg)
|
|
if (sprs==nil or flr(rnd(odds)) ~= 0) return
|
|
local target = fg and intangibles_fg or intangibles_bg
|
|
target:push_back(spark_particle.new{
|
|
x = x + rnd(4) - 2,
|
|
y = y + rnd(4) - 2,
|
|
sprs = sprs,
|
|
sidx = 1,
|
|
dx = dx + rnd(2) - 1,
|
|
dy = dy + rnd(2) - 1,
|
|
})
|
|
end
|
|
-->8
|
|
-- powerups
|
|
|
|
powerup = bullet_base.new{
|
|
-- animated sprite array: "sprites"
|
|
-- to draw under or over anim,
|
|
-- override draw, draw the
|
|
-- under-part, call into
|
|
-- powerup.draw(self), then
|
|
-- draw the over-part
|
|
width = 1,
|
|
height = 1,
|
|
-- note: make hurtboxes larger
|
|
-- than sprite by 2px per side
|
|
-- since ship hitbox is tiny
|
|
-- but powerups should feel
|
|
-- easy to pick up
|
|
dx = 0,
|
|
dy = 1.5, -- 0.75 after enemyspd
|
|
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"
|
|
|
|
-- todo: draw two sprites
|
|
-- on top of each other here
|
|
-- so all powerups can share
|
|
-- the "sheen" animation?
|
|
|
|
function powerup:draw()
|
|
spr(self.sprites[max(1,
|
|
((lframe<<16)\self.anim_speed)
|
|
%(#self.sprites+self.loop_pause)
|
|
-self.loop_pause
|
|
+1)],
|
|
self.x, self.y,
|
|
self.width, self.height)
|
|
end
|
|
|
|
repair = powerup.new{
|
|
hurt = {
|
|
x_off = -2,
|
|
y_off = -2,
|
|
width = 12,
|
|
height = 12
|
|
},
|
|
center_x_off = 4,
|
|
top_y_off = 0,
|
|
bottom_y_off = 0,
|
|
sprites = sheen8x8,
|
|
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(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 = powerup.new{
|
|
hurt = {
|
|
x_off = -2,
|
|
y_off = -2,
|
|
width = 16,
|
|
height = 16
|
|
},
|
|
-- gun = gun_type.new{}
|
|
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},
|
|
hitship = function(self, ship)
|
|
if (ship ~= primary_ship) return false
|
|
ship.main_gun = self.gun
|
|
return true
|
|
end,
|
|
draw = function(self)
|
|
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]
|
|
local gun_p = gun_swap.new{
|
|
gun = gunt.new()
|
|
}
|
|
gun_p:spawn_at(x, y)
|
|
end
|
|
|
|
spec_gun_pl = {
|
|
[1] = 2,
|
|
[14] = 6,
|
|
[2] = 14
|
|
}
|
|
|
|
function spawn_spec_gun_at(x, y, gunt)
|
|
if (type(gunt)=="string") gunt=_ENV[gunt]
|
|
local gun_p = gun_swap.new{
|
|
gun = gunt.new(),
|
|
hitship = function(self, ship)
|
|
if (ship ~= primary_ship) return false
|
|
ship.special_gun = self.gun
|
|
return true
|
|
end,
|
|
draw = function(self)
|
|
pal(spec_gun_pl)
|
|
powerup.draw(self)
|
|
pal()
|
|
spr(self.gun.icon, self.x+2, self.y+2, 1, 1)
|
|
end
|
|
}
|
|
gun_p:spawn_at(x, y)
|
|
end
|
|
__gfx__
|
|
00000000000650000000000000000000bb0b50b59909209200cc0c00000000003b00000082000000e00e8002e00e800200333300002222000000000000000000
|
|
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb0037000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
|
|
00700700006d6500000000000cddddd00b33335009444420c00c000c0b333330b7000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
|
|
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b35b7000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
|
|
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b335b7000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
|
|
0070070065666765000000000ddddd100b33355009444220c000000c03333350b7000000a800000008888820048488203bbaabb3288aa8820000000000000000
|
|
000000006506506500000000001111000b0b5050090920200c0000c00055550037000000a2000000008882000048420003bbbb30028888200000000000000000
|
|
00000000650000650000000000000000000b50000009200000c0cc00000000003b00000082000000000820000008200000333300002222000000000000000000
|
|
00000000000650000006500000000000b000000b80000000700000000bb0000008800000000000000009200000000000cccccccd000650000000000000000000
|
|
0000000000675000000765000000000000bbbb0080000000b0000000b76300008a920000000000009009200200000000c111111d006765000000000000000000
|
|
00000000006d6500006d6500000000000b0000b09000000030000000b663000089920000000550009994444200000000c111111d006d65000000000000000000
|
|
00000000067c6650067c6650000000000b0bb0b0a000000030000000033000000220000000576d009446544200000000c111111d067c66500000000000000000
|
|
00000000067d6650067d6650000000000b0bb0b00000000000000000000000000000000000566d009244442200000000c111111d067d66500000000000000000
|
|
000000005666657576667650000000000b0000b000000000000000000000000000000000000dd0009092220200000000c111111d656667650000000000000000
|
|
0000000056565066665656500000000000bbbb0000000000000000000000000000000000000000000090020000000000c111111d650650650000000000000000
|
|
00000000565000566500065000000000b000000b000000000000000000000000000000000000000000a00a0000000000cddddddd650000650000000000000000
|
|
000000000000000000000000000000000000000000a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
00000000000000000000000000000000000000000090008000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
00000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
0000000000000000000000000000000000000000000a080000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
00000000000000000000000000000000000000000009080000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
0000000000000000000000000000000000000000cccccccc77000000007700000000770000000077000000000000000000000000000000000000000000000000
|
|
0000000000000000000000000000000000000000c11ee11d70000000077000000007700000000770000000070000000000000000000000000000000000000000
|
|
0000000000000000000000000000000000000000c11ee11d00000000770000000077000000007700000000770000000700000000000000000000000000000000
|
|
0000000000000000000000000000000000000000ceeeeeed00000000700000000770000000077000000007700000007700000000000000000000000000000000
|
|
0000000000000000000000000000000000000000ceeeeeed00000000000000007700000000770000000077000000077000000007000000000000000000000000
|
|
0000000000000000000000000000000000000000c11ee11d00000000000000007000000007700000000770000000770000000077000000000000000000000000
|
|
0000000000000000000000000000000000000000c11ee11d00000000000000000000000077000000007700000007700000000770000000070000000000000000
|
|
0000000000000000000000000000000000000000cddddddd00000000000000000000000070000000077000000077000000007700000000770000000000000000
|
|
cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000
|
|
c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
|
|
c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
|
|
c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
|
|
c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000
|
|
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000
|
|
cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000
|
|
__label__
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777777777777777
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115151166665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666111611156665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115161156665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111556665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650b000000b0765
|
|
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765000bbbb000765
|
|
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0000b00765
|
|
00000000000000000000000000000000000000000000e00e800200000000000000000000000000000000000000000000000000000000000076500b0bb0b00765
|
|
00000000000000000000000050000000000000000000e0e8880200000000000000000000000000000000000000000000000000000000000076500b0bb0b00765
|
|
00000000000000000000000000000000000000000000e88e288200000000000000000000000000000000000000000000000000000000000076500b0000b00765
|
|
00000000000000000000000000000000000000000d00e88e2882000000000000000000000000000000000000000000000000000000000000765000bbbb000765
|
|
00000000000000000000000000000000000000000000e88e28820000000000000000000000000000000000000000000000000000000000007650b000000b0765
|
|
00000000000000000000000000000000000000000000088888200000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000000000000000000000000008882000000000000000000000000000000000000000000000000000000000000007657777777777765
|
|
00000000000000000000000000000000000000000000000820000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
|
|
0000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000765aeaeaeaeae765
|
|
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765eaeaeaeaea765
|
|
0000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000765aeaeaeaeae765
|
|
00000000000000000000000000000000000000000000000000000009909209200000000000000000000000000000000000000000000000007657777777777765
|
|
00000000000000000000000000000000000000000000000000000009209402200000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000944442000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000094dd42000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000094420000000000000000000000000000000000000000000000000007666622222666665
|
|
00000000000000000000000000000000000000000000000000500000944422000000000000000000000000000000000000000000000000007666225552266665
|
|
00000000000000000000000000000000000000000000000000000000909202000000000000000000000000000000000000000000000000007666225262256665
|
|
00000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000007666225652256665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666622222556665
|
|
00000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000005000000009200000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000050000000000000009009200200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
000000000000000000d0000009994444200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000009446544200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000009244442200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
0000000000000000000000000909222020000000000000000bbbb000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
000000000000000000000000000900200000000000000000b3333300000000000000000000000000000000000000000000000000000000007650000000000765
|
|
000000000000000000000000000a00a0000000000000000b3350b350000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000000000000000000000000000b350b3350000000000000000000000000000000000000000000000000000000007657777777777765
|
|
00000000000000000000000000000000000000000000000033333500000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000088000000000000005555000000000000000000000000000000000000000000000000000000000007655555555555565
|
|
000000000000000000000000000000058a9200000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
|
|
00000000000000000000000000000000899200000000000000000000008200000000000000000000000000000000000000000000000000007650000000000765
|
|
0000000000000000000000000000000002200000000000000000000000a200000000000000000000000000000000000000000000000000007650000000000765
|
|
0000000000000000000500000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007657777777777765
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000088000000000050000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
20000000000000000000000008a9200000000000000000000000000000a200000000000000000000000000000000000000000000000000007611161616111665
|
|
20000000000000000000000008992000000000000000000000000000008200000000000000000000000000000000000000000000000000007615151515151565
|
|
000000000000000000000000002200000d0000000000000000000000000000000000000000000000000000000000000000000000000000007611151515116565
|
|
0000000000000000000000000000000000000e00e800200000000000e00e80020000000000000000000000000000000000000000000000007615551115151665
|
|
0000000000000000000000000000000d00000e0e8880200000000000e0e888020000000000000000000000000000000000000000000000007615661115151565
|
|
0000000000000000000000000000000000d00e88e2882000000d0000e88e28820000000000000000000000000000000000000000000000007665666555656565
|
|
0000088000000000000880000000000000000e88e288200000000000e88e28820000000000000000000000000000000000000000000000007666666666666665
|
|
00008a9200000000008a92000000000000000e88e288200000000000e88e28820000000000000000000000000000000000000000000000007655555555555565
|
|
0000899200000000008992000000000000000088888200000000000008888820000000000000000000000000000000000000000000000000765aaaaaaaaaa765
|
|
0000022000000000000220000000000000000008882000000000000000888200000000000000000000000000000000000000000000000000765aaaaaaaaaa765
|
|
0000000000000000000000000000000000000000820000000000000000082000000000000000000000000000000000000000000000000000765aaaaaaaaaa765
|
|
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aaaaaaaaaa765
|
|
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765
|
|
00000000000088000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659a9a9a9a9a765
|
|
000000000008a920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765
|
|
00000000000899200000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000007659a9a9a9a9a765
|
|
0000000000002200000000000000000000008a92000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765
|
|
00000000000000000000000000000000000089920000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
|
|
00000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659494949494765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659494949494765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
|
|
00000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000007654444444444765
|
|
0000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000007654444444444765
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007657777777777765
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000007616166666611665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666165565
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007611156666111665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666651565
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666116565
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007665656666655665
|
|
00000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000007666666666666665
|
|
0000000000000000000000000000000000000000000000000000000000000000008a920000000000000000000000000000000000000000007655555555555565
|
|
00000000000000000000000000000000000000000000000000000000000000000089920000000000000000000000000000000000000000007652222750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000007652222750000765
|
|
00000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000007652222750000765
|
|
00000000000000000000000000000000000000000000000000000000067650000000000000000000000000000000000000000000000000007652222750000765
|
|
0000000000000000000000000000000000000000000000000000000006d650000000000000000000000000000000000000000000000000007652222750000765
|
|
0000000000000000000000000000000000000000000000000000000067c665000000000000000000000000000000000000000000000000007652222750000765
|
|
0000000000000000000000000000000000000000000000000000000067d665000000000000000000000000000000000000000000000000007652828750000765
|
|
00000000000000000000000000000000000000000000000000000006566676500000000000000000000000000000000000000000000000007658282750000765
|
|
00000000000000000000000000000000000000000000000000000006506506500000000000000000000000000000000000000000000000007652828750000765
|
|
00000000000000000000000000000000000000000000000000000006500006500000000000000000000000000000000000000000000000007658282750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007652828750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658282750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777757777765
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
|
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007555555555555555
|
|
|