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()
end

function _update60()
 mode:update()
end

function call_f(x)
 return x:f()
end

function call_move(x)
 return x:move()
end

function updategame()
 if (primary_ship.xp >= primary_ship.xptarget) and (lframe - primary_ship.last_xp_frame > 0x0.000f) and (not primary_ship.dead) then
  mode = rearm_mode.new()
  return _update60()
 end
 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
	  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

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 += 1
 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 += 1
  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 = lframe + self.shieldpenalty
 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
 self.shield += 1
 self.shield = min(self.shield, self.maxshield)
 self.shield_refresh_ready = lframe + 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
}

-- 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

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 (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

-->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= [[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 = [[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 = [[rapid fire
in a v
shape.

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{
 --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=[[    armor

    +2 hp]],
  action=function()
   self.maxhp += 2
   self.hp += 2
  end,
 },{
  icon=52,
  hdr="shield",
  body=[[  capacity

    +1 hp]],
  action=function()
   self.maxshield += 1
   self.shield += 1
  end,
 },{
  icon=1,
  hdr="thrusters",
  body=[[performance
 
move faster,
steer faster]],
 action=function()
  --maxspd thrust drag
  self.maxspd += 0.5
  self.thrust += 0.0625
  self.drag += 0.03125
 end,
 },{
  icon=20,
  hdr="hull",
  body=[[   magnet
  
 pick up xp
from further
   away]],
  action=function ()
   self.magnet += 2
  end,
 }}

 if cdr > 0 then
  add(ret, {
   icon = 6,
   hdr = "shield",
   body=[[charge rate
 
  ]] .. tostr(ceil(100 * cdr / self.shieldcooldown)) .. "% faster",
   action = function()
    self.shieldcooldown -= cdr
   end
  })
 end

 if pr > 0 then
  add(ret, {
   icon = 6,
   hdr = "shield",
   body=[[disruption
 
 ]] .. tostr(ceil(100 * pr / self.shieldpenalty)) .. "% shorter",
   action = function()
    self.shieldpenalty -= pr
   end
  })
 end

 return ret
end

frownie = mknew(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,
 
 hp = 0.5, -- enemy ships need no max hp
 xp = 0x0.0001,

 -- 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,
})

blocky = mknew(frownie.new{
 sprite = 10,
 hp = 1.5,
 xp = 0x0.0002,
 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
})

spewy = mknew(frownie.new{
 sprite=26,
 xp = 0x0.0003,
 hurt = {
  x_off=0,
  y_off=1,
  width=8,
  height=5
 },
 hp=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, self.y > 10
 end,
 init = function(ship)
  ship.main_gun=ship.main_gun or protron_gun_e.new{}
 end
})

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
})

-- todo: use constraints
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,
})

-->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
-- 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_vulcan_chasey()
 local c = spawn_chasey()
 c.main_gun=vulcan_gun_e.new{enemy=true}
 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)
  end,
 }
 if (altspr) s.spr = altspr
 eships:push_back(s)
 return s
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_vulcan_chasey
61,spawn_blocky
85,spawn_spewy
115,spawn_spewy
130,spawn_frownie
145,spawn_frownie
180,spawn_spewy
230,spawn_chasey
250,spawn_blocking_blocky
310,spawn_blocking_blocky
310,spawn_blocking_blocky
310,spawn_blocking_blocky
311,spawn_frownie
401,spawn_frownie
420,spawn_blocking_frownie
430,spawn_vulcan_chasey
450,spawn_frownie
465,spawn_frownie
480,spawn_chasey
500,multi,20,12,spawn_blocking_blocky
501,spawn_frownie
620,spawn_blocking_blocky
630,spawn_vulcan_chasey
720,spawn_blocking_boss_chasey
721,eol]]

-->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, 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{}

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 lframe up to the the
 -- bit with value 4 as a cheap
 -- way to pick an anim frame
 if (lframe&0x0.003 == 0) qx, qy = (lframe&0x0.0004)<<16, (lframe&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 = lframe
 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
 return pick(primary_ship:small_upgrade_opts(), 2)
end

-->8
-- rearm screen

rearm_mode = mknew{
 sel=1,
 bfm=1,
 crt_frm = 1,
 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[self.crt_frm&0xff])
 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,100,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,101,111,127,self:frame_col(self.sel < 0),1)
 spr(96,15,107,4,2)
 print("full ammo\nfull shield\n+50% health",54, 106, 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: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
04444400044444440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
447777700477777a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
477aaa7a0477aaaa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a0047a047a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a0447a047a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a4477a047a44400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
477777a00477777a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
477770000422aaaa2222000200000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a77700022ee0002eeee002e00022e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a4777002ea2e002e002e02ee022ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a0477a22ea2e002e002e02e2e2e2e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a0047a2e2222e02e222e02e02e02e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
47a0047a2eeeeeea2eeee002e02e02e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0aa000aa2e7aa2ea2e00e002e02e02e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000002e0002e02e002e02e02e02e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
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