mmbshmup/vacuum_gambit.p8

2199 lines
65 KiB
Lua

pico-8 cartridge // http://www.pico-8.com
version 42
__lua__
-- vacuum gambit
-- by kistaro windrider
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
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,
-- 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)
local mt,oldnew,more = {__index=tt},tt.new,rawget(tt, "init")
tt.new=function(ret)
if(not ret) ret = {}
if(more) more(ret)
if(oldnew) oldnew(ret)
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)
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()
mode = game_mode
init_blip_pals()
wipe_game() -- redundant?
load_level(example_level_csv)
game_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_game()
xpwhoosh = nil
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()
primary_ship.main_gun = zap_gun_p.new()
primary_ship.main_gun:peel()
gframe = 0
interlude = 0
waves_complete = 0
current_wave = nil
end
function _update60()
mode:update()
end
function call_f(x)
return x:f()
end
function call_move(x)
return x:move()
end
function ones(n)
local ret = 0
while n != 0 do
ret +=
end
end
function updategame()
if (primary_ship.xp >= primary_ship.xptarget) and (gframe - primary_ship.last_xp_frame > 0x0.000f) and (not primary_ship.dead) then
mode = rearm_mode.new()
return _update60()
end
gframe += 1
if current_wave then
if current_wave:update() then
-- end of stage
waves_complete += 1
current_wave = nil
if waves_complete < 32767 then
interlude = 59 -- plus one dead frame with load_level but no update
else
-- you maxed out the level
-- counter. what the fuck
-- is wrong with you?
-- go outside.
--
-- do not spawn more levels.
interlude = 32767
end
end
elseif interlude > 0 then
interlude -= 1
else
current_wave = flotilla.new()
current_wave:load_level(0, 0, min(ones(waves_complete)\2, 4))
end
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 waves_complete == 32767 and not eships.next and not ebullets.next and not events.next then
game_state = win
end
if (not pships.next) game_state = lose
if primary_ship.xp >= primary_ship.xptarget then
if not xpwhoosh then
xpwhoosh = 0
else
xpwhoosh += 1
if (xpwhoosh > 60) xpwhoosh = 0
end
else
xpwhoosh = nil
end
end
function _draw()
mode:draw()
end
function drawgame_top()
camera()
fillp(0)
drawgame()
if (game_state == game) fadelvl = -45
if (game_state == win) dropshadow("win",50,61,11)
if (game_state == lose) dropshadow("fail",48,61,8)
fadescreen()
end
game_mode = {
update = updategame,
draw = drawgame_top,
}
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"
shlcols = split"204,220,221"
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,1)
draw_gun_info("🅾️",1,116,29,2)
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)
fillp(0x5a5a)
if xpwhoosh then
clip(115,58,4,60)
rectfill(115,58,118,117,0xaa)
local voff = 5*xpwhoosh+6
rectfill(115,118-voff,118,117-voff+10,0xbb)
rectfill(115,118-voff+11,118,117-voff+20,0xba)
clip()
else
vertmeter(115,58,118,117,primary_ship.xp, primary_ship.xptarget, powcols)
end
-- 60 px vertically. note that
-- there was at one point an
-- off-by-one and I'm not sure
-- it's actually fixed
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
line(121, split, 124, split, 0xba)
vertmeter(121,65,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)
elseif mxh > 0 then
vertmeter(121,65,124,124,ch,mxh,hpcols)
else
print("!", 122, 93, 9)
print("!", 121, 92, 8)
end
fillp(0)
end
function draw_gun_info(lbl,fgc,x,y,gn)
dropshadow(lbl,x,y,fgc)
inset(114,y+7,125,y+18)
inset(114,y+20,125,y+24)
if (not primary_ship.special_guns) return
local gun = primary_ship.special_guns[gn]
if (not gun) return
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
function vertmeter(x0,y0,x1,y1,val,maxval,cols)
if ((val <= 0) or (maxval <= 0)) return
if val < 0x0.001 or maxval < 0x0.001 then
val *= 16
maxval *= 16
end
val=min(val, maxval)
local h = y1-y0
local px = val*h/maxval \ 1
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-1,85)
end
function dropshadow(str, x, y, col)
print(str, x+1, y+1, 5)
print(str, x, y, col)
end
-->8
--ship behavior
-- generic full sprite hurtbox
box8 = {
x_off = 0,
y_off = 1,
width = 8,
height = 8
}
scrollrate = 0.25 --in px/frame
ship_m = mknew{
-- ships have no shield by default
shield = 0,
maxshield = 0,
shieldcooldown = 0x0.003c,--1s
shieldpenalty = 0x0.012c, --5s
shield_refresh_ready = 0,
slip = true, -- most enemies slide
xmomentum = 0,
ymomentum = 0,
-- xmin, xmax, ymin, ymax:
-- movement constraints
-- enforced by `constrain`.
xmin = 0, xmax = 104,
-- ymin, ymax default to nil
-- pship needs more constraint
}
function ship_m:die()
self.dead = true
if (self.hp >= 0) return
-- blow up and drop xp
local sz4 = self.size * 4
local cx, cy, xp, z = self.x + sz4, self.y + sz4, self.xp or 0, 0
boom(cx, cy, 3*sz4, self.boss)
if xp > 0x0.01f3 then -- dec 499
-- spawn a huge gem with all
-- overage XP, min 100
spawn_xp_at(cx, cy, 0, xp-0x0.018f)
xp = 0x0.018f -- dec 399
z += 2
end
-- 100, 25, 5, 1
for gsz in all{0x0.0064, 0x0.0019, 0x0.0005, 0x0.0001} do
while xp >= gsz do
spawn_xp_at(cx, cy, z, gsz)
xp -= gsz
z += 2
end
end
end
function ship_m:calc_velocity(v0, t)
v0 = mid(v0 + t, self.maxspd, -self.maxspd)
return v0 - mid(self.drag, -self.drag, v0)
end
function ship_m:brake_dist(v0)
local brake_max = self.thrust + self.drag
local tri_frames = abs(v0\brake_max)
local chunks = tri_frames * (tri_frames - 1) >> 1
local chunk_zone = chunks * brake_max
local overage = abs(v0) - tri_frames * brake_max
return (chunk_zone + overage * (tri_frames + 1)) * sgn(v0), (overage > 0) and tri_frames + 1 or tri_frames
end
function ship_m:constrain(p, dp, pmin, pmax, want)
if (not pmin) return want
local v1, bd, bf, bp
function calc_targets()
-- velocity after move
v1 = self:calc_velocity(dp, want)
-- brake distance and frames
bd, bf = self:brake_dist(v1)
-- brake point
bp = p + bd + v1
end
calc_targets()
if bp < pmin then
-- undershoot. max thrust,
-- then treat as overshoot
-- targeting minimum bound
want, pmax = self.thrust, pmin
calc_targets()
end
if (bp <= pmax) return want
-- spread overshoot across frames
want -= (bp - pmax)/max(bf,1)
return max(want, -self.thrust)
end
function ship_m:move()
self:refresh_shield()
local dx, dy, shoot_spec1, shoot_spec2 = 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)
self:maybe_shoot(self.main_gun)
if (shoot_spec1 and self.special_guns) self:maybe_shoot(self.special_guns[1])
if (shoot_spec2 and self.special_guns) self:maybe_shoot(self.special_guns[2])
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 = self:calc_velocity(self.xmomentum, dx)
self.ymomentum = self:calc_velocity(self.ymomentum, dy)
self.x += self.xmomentum
self.y += 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
return gun:shoot(self.x + self.fire_off_x, self.y + self.fire_off_y)
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 = gframe + self.shieldpenalty
if self.shield > 0 then
self.shield -= dmg
if self.shield > 0 then
blip(self,12)
else
self.shield = 0
blip(self,7)
end
return false
end
self.hp -= dmg
if self.hp < 0 then
self:die()
return true
end
blip(self, self.friendly and 8 or 7)
return false
end
function ship_m:refresh_shield()
if (self.shield >= self.maxshield) return
if (gframe < self.shield_refresh_ready) return
self.shield += 1
self.shield = min(self.shield, self.maxshield)
self.shield_refresh_ready = gframe + self.shieldcooldown
end
-->8
-- bullet and gun behaviors
function player_blt_cat()
return pbullets
end
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{ }
gun_base = mknew{
shoot_ready = -32768,
icon = 20,
ammobonus = 1,
-- fractional frames of
-- cooldown reduction from
-- upgrades, not yet applied
cd_remainder = 0,
}
-- gun_base subtypes are
-- level-up options that,
-- as an action, assign
-- themselves to the player
function gun_base:action()
local item = self.new()
item:peel()
item.ammo = item.maxammo
if not primary_ship.special_guns then
primary_ship.special_guns = {item}
else
add(primary_ship.special_guns, item)
end
end
-- make shot type unique so
-- stat modifications do not
-- damage base data
function gun_base:peel()
self.munition = mknew(self.munition.new())
end
-- default firing behavior:
-- single shot
function gun_base:actually_shoot(x, y)
self.munition.new{}:spawn_at(x, y)
end
-- upgrade
function gun_base:small_upgrade_opts()
local ret = {
self:ammo_upgrade_opt(),
self:rate_upgrade_opt(),
}
local s = self.special_upgrade_opt
if (s) add(ret, s(self))
return ret
end
function gun_base:ammo_upgrade_opt()
local a=self.maxammo
local x=a\10+self.ammobonus
return {
icon=self.icon,
hdr=self.hdr,
body=[[--------AMMO
more shots
before you
run out.
is: ]]..tostr(a)..[[
add: ]]..tostr(x)..[[
----------
total: ]]..tostr(a+x),
action=function()
self.maxammo+=x
self.ammo+=x
end,
}
end
function gun_base:rate_upgrade_opt()
local c=self.cooldown<<16
local rawnewc=0.85*(c-self.cd_remainder)
local newc=ceil(rawnewc)
return {
icon=self.icon,
hdr=self.hdr,
body=[[--------RATE
reduce delay
between each
shot when
firing.
is: ]]..tostr(c)..[[f
minus: ]]..tostr(c-newc)..[[f
----------
total: ]]..tostr(newc)..[[f
remainder:
]]..sub(tostr(newc-rawnewc),0,5),
action=function()
self.cooldown=newc>>16
self.cd_remainder=newc-rawnewc
end,
}
end
function bullet_base:hitship(_)
self:die()
return true
end
function bullet_base:die()
end
function bullet_base:move()
self.x += self.dx
self.y += self.dy
if (self.f) self.f -= 1
if (self.y > 145) or (self.y < -8 * self.height) or (self.f and self.f < 0) then
self:die()
return true
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.x_off
self.y = y - self.y_off
self.category():push_back(self)
end
function gun_base:shoot(x, y)
if (gframe < self.shoot_ready) return false
if self.ammo then
if (self.ammo <= 0) return false
self.ammo -= 1
end
self.shoot_ready = gframe + self.cooldown
self:actually_shoot(x, y)
return true
end
-->8
-- bullets and guns
zap_e = mknew(bullet_base.new{
--shape
sprite = 9, --index of 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
},
x_off = 1, -- how to position by ship
y_off = 8,
damage = 1,
dx = 0, -- px/frame
dy = 4,
hitship = const_fxn(true),
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{
cooldown = 0x0.0020, -- frames between shots
munition = zap_e,
})
zap_gun_p = mknew(zap_gun_e.new{
icon = 19,
munition = zap_p,
hdr = "mAIN gUN",
})
blast = mknew(bullet_base.new{
--shape
sprite = 12, --index of player 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
},
x_off = 4, -- how to position by ship
y_off = 0,
damage = 4,
dx = 0, -- px/frame
dy = -1,
awaitcancel = false,
-- disable damage for 4 frames
-- when hitting something
-- todo: rewrite all ship hit
-- logic so i can avoid
-- repeating hits to the
-- same ship instead of
-- using a cooldown
hitship = function(self, _)
if self.damage > 0 and not self.awaitcancel then
self.awaitcancel = true
once_next_frame(function()
new_events:push_back{
wait = 4,
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,
category=player_blt_cat
})
blast_gun = mknew(gun_base.new{
icon = 13,
cooldown = 0x0.0078, -- 120 frames between shots
ammo = 5,
maxammo = 5,
munition = blast,
hdr = "bLASTER",
body= [[---------GUN
plasma orb
cuts through
enemies.
slow.
ammo: 5
rate: 1/2sec
dmg: 4
]],
})
protron_e = mknew(bullet_base.new{
--shape
sprite = 24,
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
},
x_off = 1, -- how to position by ship
y_off = 4,
damage = 1,
dym = 0.5, -- gun sets dy;
-- this is mult
category = enemy_blt_cat,
})
protron_p = mknew(protron_e.new{
sprite=23,
dym = -1,
y_off = 0,
category=player_blt_cat,
})
protron_gun_e = mknew(gun_base.new{
icon = 25,
cooldown = 0x0.0040, -- frames between shots
ammo = nil,
maxammo = nil,
munition = protron_e
})
function protron_gun_e:actually_shoot(x, y)
local m = self.munition.dym
for i=1,3 do
local b = self.munition.new{
dx = i*m,
dy = (4-i)*m,
}
b:spawn_at(x,y)
local b2 = self.munition.new{
dx = -i*m,
dy = (4-i)*m,
}
b2:spawn_at(x,y)
end
local bup = self.munition.new{
dx=0,
dy=4*m,
}
bup:spawn_at(x,y)
end
protron_gun_p = mknew(protron_gun_e.new{
munition = protron_p,
maxammo = 20,
cooldown = 0x0.0018,
hdr = "pROTRON",
body = [[---------GUN
spray shots
in a dense
arc.
ammo: 20
rate: 2/sec
dmg: 1
]],
})
vulcan_e = mknew(bullet_base.new{
--shape
sprite = 21,
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
},
x_off = 0.5, -- how to position by ship
y_off = 0,
damage = 0.5,
-- dx from gun
dy = 2,
category=enemy_blt_cat
})
vulcan_p = mknew(vulcan_e.new{
sprite=22,
y_off = 4,
dy = -4,
category=player_blt_cat
})
vulcan_gun_e = mknew(gun_base.new{
icon = 37,
enemy = false,
cooldown = 0x0.0003, -- 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{
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,
maxammo = 100,
hdr = "vULCAN",
body = [[---------GUN
rapidly fire
in a v.
ammo: 100
rate: 20/sec
dmg: 0.5
]],
})
-->8
--ships, including player
firespark = split"9, 8, 2, 5, 1"
smokespark = split"13, 13, 5, 5"
player = mknew(ship_m.new{
friendly=true,
--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
hp = 3, -- current health, non-regenerating
maxhp = 3, -- player only; other ships never heal
shield = 2, -- regenerates
maxshield = 2,
-- xp in increments of 0x0.0001
xp = 0,
xptarget = 0x0.0004,
last_xp_frame = 0,
level = 1,
magnet = 10,
-- gun
main_gun = nil, -- assign at spawn time
special_guns = 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 = 1.5, -- momentum cap
thrust = 0.1875, -- momentum added from button
ymin = 0, ymax = 120, -- stay on screen
drag = 0.0625, -- 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,
init = function(p)
p.main_gun = zap_gun_p.new()
-- ONE HIT MODE
--
-- p.hp = 0
-- p.maxhp = 0
-- p.shield = 0
-- p.maxshield = 0
end,
})
function player:small_upgrade_opts()
local cdr, pr = (self.shieldcooldown - 0x0.000f) / 8, (self.shieldpenalty - 0x0.003c) / 9
if (cdr == 0 and self.shieldcooldown > 0x0.000f) cdr = 0x0.0001
if (pr == 0 and self.shieldpenalty > 0x0.003c) pr = 0x0.0001
local ret = {{
icon=53,
hdr="hull",
body=[[--------SHIP
survive more
unshielded
hits.
+2 hp]],
action=function()
self.maxhp += 2
self.hp += 2
end,
},{
icon=52,
hdr="capacity",
body=[[------SHIELD
shield can
absorb more
hits before
recharging.
+1 hp]],
action=function()
self.maxshield += 1
self.shield += 1
end,
},{
icon=1,
hdr="thrusters",
body=[[--------SHIP
move faster,
steer more
sharply.]],
action=function()
--maxspd thrust drag
self.maxspd += 0.5
self.thrust += 0.0625
self.drag += 0.03125
end,
},{
icon=20,
hdr="magnet",
body=[[--------SHIP
pick up xp
from further
away.]],
action=function ()
self.magnet += 2
end,
},
self.main_gun:rate_upgrade_opt(),
}
if cdr > 0 then
add(ret, {
icon = 6,
hdr = "recharge",
body=[[------SHIELD
shield will
recharge at
a faster
pace.
]] .. tostr(ceil(100 * cdr / self.shieldcooldown)) .. "% faster",
action = function()
self.shieldcooldown -= cdr
end
})
end
if pr > 0 then
add(ret, {
icon = 6,
hdr = "recovery",
body=[[------SHIELD
reduce the
delay after
a hit before
shield will
start to
recharge.
]] .. tostr(ceil(100 * pr / self.shieldpenalty)) .. "% shorter",
action = function()
self.shieldpenalty -= pr
end
})
end
return ret
end
-- original prototype leftover;
-- likely to be the basis of a
-- standard raider type, so
-- i am keeping it around
chasey = mknew(ship_m.new{
sprite = 5,
xp = 0x0.0004,
size = 1,
hurt = {
x_off = 1,
y_off = 2,
width = 6,
height = 5,
},
sparks = smokespark,
sparkodds = 8,
hp = 1.5,
shield = 1,
maxshield = 1,
fire_off_x = 4,
fire_off_y = 7,
maxspd = 2,
thrust = 0.2,
drag = 0.075,
slip = true,
init = function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
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
end
xl_chasey=mknew(chasey.new{
size=2,
xp = 0x0.000a,
maxspd=1.25,
hurt = {
x_off = 2,
y_off = 4,
width = 12,
height = 10
},
fire_off_x = 8,
fire_off_y = 15,
hp = 19.5,
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,
init = function(ship)
ship.main_gun=ship.main_gun or zap_gun_e.new{}
end,
})
-- flotilla ships
ship_f = mknew(ship_m.new{
-- sprite required
size = 1,
hurt = box8,
-- no sparks
hp = 0.5,
xp = 0x0.0001,
maxspd = 3,
thrust = 0.1,
drag = 0.05,
slip = false,
act = function(self)
local wx,wy=self.want_x,self.want_y
self.xmin,self.xmax,self.ymin,self.ymax = wx,wx,wy,wy
return 0,0,false,false
end,
})
ship_mook = mknew(ship_f.new{
sprite=103
})
ship_defender = mknew(ship_f.new{
sprite=105,
hp = 2.5,
xp = 0x0.0003,
})
ship_turret = mknew(ship_f.new{
sprite=106,
xp = 0x0.0002,
})
ship_skirmisher = mknew(ship_f.new{
sprite=107,
xp = 0x0.0004,
spark = smokespark,
sparkodds = 4,
})
function rnd_spawn_loc()
local x,y = flr(rnd(304)), flr(rnd(32))
if (x<184) return x-40,-y-8
if (x<244) return -y-8,x-184
return 112+y, x-244
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{
init = 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
-- flotillas
-- a template for a wave, read
-- from the map. each ship can
-- alternate between "attack"
-- and "formation" modes, like
-- galaxian or galaga. ships
-- with different roles have
-- different rules for becoming
-- aggressive, but aggression
-- ramps up during the wave.
-- flotilla placeholders are
-- defined by sprite flags.
-- see obsidian vault for
-- full docs.
flotilla = mknew{
use_var = 0x0001,
opt_odds = split"0.5,0.5,0.5,0.5",
init=function(this)
this.ship_bases={
[0]=mknew(ship_mook.new{ship_t=0}),
[1]=mknew(ship_mook.new{ship_t=1}),
[4]=mknew(ship_defender.new{ship_t=4}),
[5]=mknew(ship_defender.new{ship_t=5}),
[8]=mknew(ship_turret.new{ship_t=8}),
[9]=mknew(ship_turret.new{ship_t=9}),
[12]=mknew(ship_skirmisher.new{ship_t=12}),
[13]=mknew(ship_skirmisher.new{ship_t=13}),
}
end,
}
function flotilla:load(ulc_cx, ulc_cy, lvl)
local rows,cy,uv,counts={},ulc_cy,self.use_var+0xc840,{
[0]=0,
[1]=0,
[4]=0,
[5]=0,
[8]=0,
[9]=0,
[12]=0,
[13]=0,
}
repeat
local row,cx,opt,f = {},ulc_cx,{},0
for i,v in ipairs(self.opt_odds) do
opt[i*4-4]=rnd()<v
end
repeat
f=fget(mget(cx, cy))
-- bits 0x03: control mark or ship?
local mode = f&0x03
if mode==2 then
-- bits 0x0c: ship class
local ship_t = f&0x0c
-- bit 0x20: optional?
if f&0x20 == 0 or opt[ship_t] then
-- bit 0x10: alternate ship?
-- increment ship id if
-- alternate is requested
-- and we allow alternates
-- for this type of ship
ship_t+=(uv>>ship_t&0x1)&(f&0x10>>4)
add(row, self.ship_bases[ship_t]:new{col=cx-ulc_cx})
end
end
until mode==1
-- mode 1: end of line control mark
-- bits 0x18: what size flotilla is this row used for?
if (f&0x18)>>3 <= lvl then
-- keep the row; count it
for s in all(row) do
counts[s.ship_t] += 1
eships:push_back(s)
end
add(rows, row)
end
-- control mark bit 0x04: end of flotilla
until f&0x04 == 1
self.rows=rows
self:statisfy(counts)
end
function flotilla:statisfy(counts)
-- TODO: now that we know how
-- many ships of each kind
-- exist, build ships to match
-- difficulty target
--
-- no difficulty model is yet
-- implemented, though, so
-- just use base ships only
-- for this prototype
end
function flotilla:update()
-- algorithm: redistribute
-- TODO: alternate flotilla movement algorithms
-- find effective width and height
local min_col,max_col,live_rows=999,0,0
for row in all(self.rows) do
local row_alive=false
for ship in all(row) do
if not ship.dead then
row_alive=true
local col=ship.col
if (col < min_col) min_col = col
if (col > max_col) max_col = col
end
end -- scanned row
if (row_alive) live_rows += 1
end -- extent search
if (live_rows == 0) return true -- done
-- distribute across box:
-- x = [4, 100)
-- y = [4, 80)
local x_interval,x_offset = 0,52
if min_col < max_col then
x_interval=96/(max_col-min_col)
x_offset = 4-min_col*x_interval
end
local y_interval,y_offset=0,40
if live_rows > 1 then
y_interval=76/(live_rows-1)
x_offset=4-y_interval
end
-- now assign target locations
local real_row=0
for row in all(self.rows) do
local row_alive = false
for ship in all(row) do
if not ship.dead then
if not row_alive then
real_row += 1
row_alive = true
end
ship.want_x = ship.col * x_interval - x_offset
ship.want_y = real_row * y_interval - y_offset
end -- ship updated
end -- row updated
end -- table updated
return false -- some ships remain
end
-->8
-- standard events
blip_fx = mknew{
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
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)
obj.fx_pal = blip_pals[col]
if (obj.___fx_pal_event) obj.___fx_pal_event:abort()
events:push_back(blip_fx.new{frames=3, 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{}
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
xp_gem = mknew(bullet_base.new{
dx = 0,
dy = 0.75,
width=1, -- not used for spr but
height=1,-- bullet_base uses it
category = enemy_blt_cat,
damage = 0,
hurt = {
x_off = -2,
y_off = -2,
width = 8,
height = 8,
},
x_off = 2,
y_off = 2,
})
function xp_gem:draw()
local s,qx,qy = self.qsprite,0,0
-- sprite map position:
-- sprite id to x and y,
-- offset shifts specific low
-- bits of gframe up to the the
-- bit with value 4 as a cheap
-- way to pick an anim frame
if (gframe&0x0.003 == 0) qx, qy = (gframe&0x0.0004)<<16, (gframe&0x0.0008)<<15
sspr(
(s%16<<3)+qx,
(s\16<<3)+qy,
4, 4,
self.x, self.y
)
end
function xp_gem:move()
if not primary_ship.dead and abs(self.x + 1 - primary_ship.x - primary_ship.hurt.x_off) <= primary_ship.magnet and abs(self.y + 1 - primary_ship.y - primary_ship.hurt.y_off) <= primary_ship.magnet then
if (self.x < primary_ship.x + 3) self.x += 1
if (self.x > primary_ship.x + 5) self.x -= 1
if (self.y < primary_ship.y + 3) self.y += 1
if (self.y > primary_ship.y + 5) self.y -= 1
end
return bullet_base.move(self)
end
-- todo: "magnetic" behavior
-- when near player ship
function xp_gem:hitship(ship)
if (ship ~= primary_ship or primary_ship.dead) return false
primary_ship.xp += self.val
primary_ship.last_xp_frame = gframe
return true
end
-- small gems for 1, 5, 25
-- exactly; else huge
function spawn_xp_at(x, y, off, amt)
x += rnd(off+off)-off
y += rnd(off+off)-off
xp_gem.new{
qsprite=amt == 0x0.0001 and 32 or amt == 0x0.0005 and 33 or amt == 0x0.0019 and 34 or 35,
val = amt,
}:spawn_at(mid(x, 0, 124),mid(y,-4,125))
end
-->8
-- upgrade options
-- all these return
-- a [2] of rearm_t:
--
-- icon: sprite id
-- hdr: title text
-- body: text
-- action: callback
-- (method)
spec_gunt = {
protron_gun_p,
vulcan_gun_p,
blast_gun,
}
-- picks n random items from
-- tbl; permutes tbl, selected
-- items at end
function pick(tbl, n)
local ret, top={}, #tbl
for x=top,top-n,-1 do
local idx = 1+rnd(x)\1
add(ret, tbl[idx])
tbl[idx]=tbl[x]
tbl[x]=ret[#ret]
end
return ret
end
-- add a new gun
function spec_gun_opts()
return pick(spec_gunt, 2)
end
-- major upgrades
function big_opts()
return {{
icon=1,
hdr="placeholder",
body="placeholder",
action = function() end,
},
{
icon=1,
hdr="placeholder",
body="placeholder",
action = function() end,
}}
end
-- ordinary upgrades
function small_opts()
-- todo: include gun opts
if(not primary_ship.special_guns) return pick(primary_ship:small_upgrade_opts(), 2)
local opts = {rnd(primary_ship:small_upgrade_opts())}
for g in all(primary_ship.special_guns) do
add(opts, rnd(g:small_upgrade_opts()))
end
return pick(opts, 2)
end
-->8
-- rearm screen
rearm_mode = mknew{
sel=1,
bfm=1,
crt_frm = 0,
pos=-1,
init=function(this)
poke(0x5f5c, 255) --no btnp repeat
rearm_mode.shuffle(this)
end,
}
crt={-91,-166,-2641,-1441,-23041,23295,-20491,24570}
function rearm_mode:glow_box(x0, y0, x1, y1, c, cf)
for i,v in ipairs{c[1],c[2],c[1],0} do
i -= 1
rect(x0+i,y0+i,x1-i,y1-i,v)
end
fillp(crt[1+(self.crt_frm&7)])
rectfill(x0+4, y0+4, x1-4, y1-4, cf)
fillp()
end
function easeoutbounce(t)
local n1=7.5625
local d1=2.75
if (t<1/d1) then
return n1*t*t;
elseif(t<2/d1) then
t-=1.5/d1
return n1*t*t+.75;
elseif(t<2.5/d1) then
t-=2.25/d1
return n1*t*t+.9375;
else
t-=2.625/d1
return n1*t*t+.984375;
end
end
function rearm_mode:frame_col(hot)
if (not hot) return {4,10}
if (self.bfm<=16) return {14,7}
return {2,8}
end
function rearm_mode:draw_option(id)
local rec = self.options[id]
self:glow_box(0,0,55,101,self:frame_col(self.sel == id),1)
spr(rec.icon,5, 5)
print(rec.hdr, 13, 8, 7)
print(rec.body, 5, 15, 6)
end
function rearm_mode:pos_frac()
local pos = self.pos
if (not pos) return
if (pos < 0) return 1-easeoutbounce(1+pos)
if (pos > 0) return (1-pos)*(1-pos)
return 0
end
function rearm_mode:shuffle()
-- these will be placeholders
-- until the upgrade deck
-- is a thing that exists
local lev = primary_ship.level + 1
if lev == 4 or lev == 12 then
self.options = spec_gun_opts()
elseif lev % 4 == 0 then
self.options = big_opts()
else
self.options = small_opts()
end
end
function rearm_mode:draw()
drawgame_top()
local frac = self:pos_frac()
camera(frac * 55, 0)
self:draw_option(1)
camera(frac * -128 + (1-frac) * -56, 0)
self:draw_option(2)
camera(0, -28 * frac)
self:glow_box(0,102,111,127,self:frame_col(self.sel < 0),1)
spr(96,15,107,4,2)
print("full ammo\nfull shield\n+50% health",54, 107, 6)
end
function rearm_mode:update_pos()
local pos = self.pos
if (not pos) return
if (pos == 0) then
if (primary_ship.xp < primary_ship.xptarget) self.pos = 1
xpwhoosh = nil
return
end
if (pos < 0) pos = min(pos + 0x0.05, 0)
if pos > 0 then
pos -= 0x0.1
if (pos <= 0) pos = 999
end
self.pos = pos
end
function rearm_mode:update()
self.crt_frm+=0.25
self:update_pos()
if self.pos > 1 then
mode = game_mode
return -- do not advance frame
end
local sel, bfm = self.sel, self.bfm
if (btn(3) and sel > 0 or btn(2) and sel < 0) sel=-sel
if (btn(0)) sel = 1
if (btn(1)) sel = 2
if (btn()&0xF ~= 0) and bfm >= 10 or bfm >= 30 then
bfm = 1
else
bfm += 1
end
self.bfm = bfm
if primary_ship.xp < primary_ship.xptarget then
sel = 0
elseif btnp(4) or btnp(5) and self.pos == 0 then
if sel < 0 then
-- todo: sound: rearm
primary_ship.shield = primary_ship.maxshield
-- todo: rewrite for three guns
local specs = primary_ship.special_guns
if specs then
specs[1].ammo = specs[1].maxammo
if (specs[2]) specs[2].ammo = specs[2].maxammo
end
primary_ship.hp = min(primary_ship.maxhp, primary_ship.hp + primary_ship.maxhp/2)
primary_ship.xp -= primary_ship.xptarget / 2
else
local c = self.options[sel]
if c then
-- todo: sound: upgrade
c:action()
primary_ship.xp -= primary_ship.xptarget
primary_ship.xptarget += primary_ship.level * 0x0.0002
primary_ship.level += 1
if (primary_ship.xp >= primary_ship.xptarget) self:shuffle()
end
end
end
self.sel = sel
end
__gfx__
00000000000650000000000000000000bb0b50b59909209200cc0c00000000003b00000082000000e00e8002e00e800200333300002222000000000000000000
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb0037000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
00700700006d6500000000000cddddd00b33335009444420c00c000c0b333330b7000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b35b7000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b335b7000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
0070070065666765000000000ddddd100b33355009444220c000000c03333350b7000000a800000008888820048488203bbaabb3288aa8820000000000000000
000000006506506500000000001111000b0b5050090920200c0000c00055550037000000a2000000008882000048420003bbbb30028888200000000000000000
00000000650000650000000000000000000b50000009200000c0cc00000000003b00000082000000000820000008200000333300002222000000000000000000
0000000000065000000650000003b0000070070080000000700000000bb0000008800000000000000009200000000000cccccccd000650000000000000000000
000000000067500000076500000370000005500080000000b0000000b76300008a920000000000009009200200000000c111111d006765000000000000000000
00000000006d6500006d6500000b7000700660079000000030000000b663000089920000000550009994444200000000c111111d006d65000000000000000000
00000000067c6650067c6650000b7000056ccd50a000000030000000033000000220000000576d009446544200000000c111111d067c66500000000000000000
00000000067d6650067d6650000b7000056ccd500000000000000000000000000000000000566d009244442200000000c111111d067d66500000000000000000
000000005666657576667650000b7000700dd00700000000000000000000000000000000000dd0009092220200000000c111111d656667650000000000000000
000000005656506666565650000370000005500000000000000000000000000000000000000000000090020000000000c111111d650650650000000000000000
0000000056500056650006500003b00000700700000000000000000000000000000000000000000000a00a0000000000cddddddd650000650000000000000000
060007000600070006600770766c777c0000000000a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000
6cd07cd06cd07cd06ccd7ccd6ccd7ccd000000000090008000000000000000000000000000000000000000000000000000000000000000000000000000000000
0d000d006cd07cd06ccd7ccd6ccd7ccd0000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000d000d000dd00dd0cdd1cdd0000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000
0600060006000600066006607667766c00000000000a080000000000000000000000000000000000000000000000000000000000000000000000000000000000
67d06c7067d06c70677d6cc7677d6cc7000000000009080000000000000000000000000000000000000000000000000000000000000000000000000000000000
0d00070067d06c7067cd6cc767cd6cc7000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000d0007000dd007707dd1c771000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000cccccccccccccccc77000000007700000000770000000077000000000000000000000000000000000000000000000000
00000000000000000000000000000000c116611dc11ee11d70000000077000000007700000000770000000070000000000000000000000000000000000000000
00000000000000000000000000000000c1611c1dc11ee11d00000000770000000077000000007700000000770000000700000000000000000000000000000000
00000000000000000000000000000000c61111cdceeeeeed00000000700000000770000000077000000007700000007700000000000000000000000000000000
00000000000000000000000000000000c6111bcdceeeeeed00000000000000007700000000770000000077000000077000000007000000000000000000000000
00000000000000000000000000000000c161bbbdc11ee11d00000000000000007000000007700000000770000000770000000077000000000000000000000000
00000000000000000000000000000000c11ccb1dc11ee11d00000000000000000000000077000000007700000007700000000770000000070000000000000000
00000000000000000000000000000000cdddddddcddddddd00000000000000000000000070000000077000000077000000007700000000770000000000000000
cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000
c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000
c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000
c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000
c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000
cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
044444000444444400000000000000000000000000000000000000000000000000000000000000000000000000000000000bb000000aa0000009900000088000
447777700477777a0000000000000000000000000000000000000000000666000077700008888800000ab00006000060000bb000000aa0000009900000088000
477aaa7a0477aaaa0000000000000000000000000000000000000000006ddd5007fff70008eeee20000ab00006c006d0000bb000000aa0000009900000088000
47a0047a047a0000000000000000000000000000000000000000000006dd7d5007ffff4008eeee200aaabbb006ccccd0000bb000000aa0000009900000088000
47a0447a047a0000000000000000000000000000000000000000000006d7dd5007ffff4008eeee200bbb333006ccccd0000bb000000aa0000009900000088000
47a4477a047a4440000000000000000000000000000000000000000006ddd500004fff4008eeee20000b300006d00cd0000bb000000aa0000009900000088000
477777a00477777a0000000000000000000000000000000000000000005550000004440002222200000b30000d0000d0000bb000000aa0000009900000088000
477770000422aaaa22220002000002000000000000000000000000000000000000000000000000000000000000000000000bb000000aa0000009900000088000
47a77700022ee0002eeee002e00022e00000000000000000000000000000000000000000000000000000000000000000000bb000000aa0000009900000088000
47a4777002ea2e002e002e02ee022ee00000000000000000000000000005600000474000028282000004b000060000000bbbbbb00aaaaaa00999999008888880
47a0477a22ea2e002e002e02e2e2e2e0000000000000000000000000005d0d0004f0f400080e0e00000a000000c000d00b0000b00a0000a00900009008000080
47a0047a2e2222e02e222e02e02e02e000000000000000000000000005d07050070f0f2002e0e02004a0b0b0060c0c000b0bb0b00a0aa0a00909909008088080
47a0047a2eeeeeea2eeee002e02e02e000000000000000000000000006070d1004f0f040080e0e000b0b035000c0c0d00b0bb0b00a0aa0a00909909008088080
0aa000aa2e7aa2ea2e00e002e02e02e000000000000000000000000000d0d100002f0f2002e0e0200000300006000c000b0000b00a0000a00900009008000080
000000002e0002e02e002e02e02e02e0000000000000000000000000000510000002420002020200000b5000000000d00bbbbbb00aaaaaa00999999008888880
000000000e0000e00e000e00e00e00e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__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
__gff__
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000212060a0e01091119000000000000002232363a3e050d151d
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
00006b6b00006f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7b6a00006a7b6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7778686878776c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
6767777767676c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7877676777787d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000