From 44bc904ec225bd1ed1b726eaf0b7ede4c1cae712 Mon Sep 17 00:00:00 2001 From: Kistaro Windrider Date: Tue, 12 Sep 2023 22:49:01 -0700 Subject: [PATCH] the actual updated version --- updatedshmup.p8 | 2175 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2175 insertions(+) create mode 100644 updatedshmup.p8 diff --git a/updatedshmup.p8 b/updatedshmup.p8 new file mode 100644 index 0000000..8b3e093 --- /dev/null +++ b/updatedshmup.p8 @@ -0,0 +1,2175 @@ +pico-8 cartridge // http://www.pico-8.com +version 41 +__lua__ +-- generic shmup +-- by kistaro windrider +-- dev docs in tab 8 + +game = 1 +win = 2 +lose = 3 + +debug = {} + +function _init() + init_bullet_mt() + init_powerup_mt() + init_ship_mt() + wipe_level() + primary_ship.main_gun = new_gun_of(zap_gun_t) + load_level(example_level) + state = game + pal(2,129) + pal() +end + +-- health gradients for 1..5 hp +-- exactly, then all "more". +hpcols_lut = { + {36}, + {34, 136}, + {34, 130, 136}, + {34, 34, 130, 136}, + {34, 34, 34, 130, 136}, + {34, 34, 34, 130, 130, 136} +} + +-- call after any change to maxhp +-- configures health gradient +function init_hpcols() + hpcols = hpcols_lut[min(primary_ship.maxhp,6)] +end + +function wipe_level() + primary_ship = new_p1() + init_hpcols() + pships = {primary_ship} + eships = {} + pbullets = {} + ebullets = {} + intangibles_fg = {} + intangibles_bg = {} + events = {} +end + +function _update60() + updategame() +end + +function updategame() + leveldone = level_frame() + new_events = {} + local deaths = {} + for i, e in ipairs(events) do + if (e()) add(deaths, i) + end + bury_the_dead(events, deaths) + foreach(new_events, function(e) + add(events, e) + end) + deaths = {} + for i, x in ipairs(intangibles_bg) do + if (x:move()) add(deaths, i) + end + bury_the_dead(intangibles_bg, deaths) + for _, tbl in ipairs({pships, eships, pbullets, ebullets, intangibles}) do + local deaths = {} + for i, x in ipairs(tbl) do + if (x:move()) add(deaths, i) + end + bury_the_dead(tbl, deaths) + end + --then, calculate collisions + local pdeaths = {} + local edeaths = {} + local eskips = {} + -- todo: always use a collider, + -- it saves if pships is as low as 2s + -- pships usually contians 1 thing, + -- so don't bother with a bucket collider + for ip, ps in ipairs(pships) do + for ie, es in ipairs(eships) do + if not eskips[ie] then + if collides(hurtbox(ps), hurtbox(es)) then + if (es:hitship(ps)) then + add(edeaths, ie) + eskips[ie] = true + end + if ps:hitship(es) then + add(pdeaths, ip) + break + end + end + end + end + end + bury_the_dead(pships, pdeaths) + bury_the_dead(eships, edeaths) + pdeaths = {} + edeaths = {} + eskips = {} + for ip, ps in ipairs(pships) do + for ie, eb in ipairs(ebullets) do + if not eskips[ie] then + if collides(hurtbox(ps), hurtbox(eb)) then + local dead = false + if ps:hitbullet(eb) then + add(pdeaths, ip) + dead = true + end + if (eb:hitship(ps)) then + add(edeaths, ie) + eskips[ie] =true + end + if (dead) break + end + end + end + end + bury_the_dead(pships, pdeaths) + bury_the_dead(ebullets, edeaths) + pdeaths = {} + edeaths = {} + -- many bullets and many enemy ships; + -- use bucket collider for efficiency + local pbullet_collider = new_collider() + for idx, pb in ipairs(pbullets) do + pb.___pbullets_idx = idx + pbullet_collider:insert(pb) + end + + for es_idx, es in ipairs(eships) do + for pb in all(pbullet_collider:get_collisions(es)) do + local dead = false + if es:hitbullet(pb) then + add(edeaths, es_idx) + dead=true + end + if pb:hitship(es) then + add(pdeaths, pb.___pbullets_idx) + pbullet_collider:hide(pb) + end + if (dead) break + end + end + bury_the_dead(eships, edeaths) + bury_the_dead(pbullets, pdeaths) + for i, x in ipairs(intangibles_fg) do + if (x:move()) add(deaths, i) + end + bury_the_dead(intangibles_fg, deaths) + + if leveldone and ((#eships + #ebullets + #events) == 0) then + state = win + end + if #pships == 0 then + state = lose + end +end + +function _draw() + drawgame() + draw_debug() + if (state == game) fadelvl = -45 + if (state == win) dropshadow("win",50,61,11) + if (state == lose) dropshadow("fail",48,61,8) + fadescreen() +end + +local fadetable={ + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,129,129,129,129,129,129,129,129,0,0,0,0,0}, + {2,2,2,130,130,130,130,130,128,128,128,128,128,0,0}, + {3,3,3,131,131,131,131,129,129,129,129,129,0,0,0}, + {4,4,132,132,132,132,132,132,130,128,128,128,128,0,0}, + {5,5,133,133,133,133,130,130,128,128,128,128,128,0,0}, + {6,6,134,13,13,13,141,5,5,5,133,130,128,128,0}, + {7,6,6,6,134,134,134,134,5,5,5,133,130,128,0}, + {8,8,136,136,136,136,132,132,132,130,128,128,128,128,0}, + {9,9,9,4,4,4,4,132,132,132,128,128,128,128,0}, + {10,10,138,138,138,4,4,4,132,132,133,128,128,128,0}, + {11,139,139,139,139,3,3,3,3,129,129,129,0,0,0}, + {12,12,12,140,140,140,140,131,131,131,1,129,129,129,0}, + {13,13,141,141,5,5,5,133,133,130,129,129,128,128,0}, + {14,14,14,134,134,141,141,2,2,133,130,130,128,128,0}, + {15,143,143,134,134,134,134,5,5,5,133,133,128,128,0} +} + +function fadescreen() + fadelvl += 0.25 + if fadelvl < 0 then + pal() + return + end + local i = flr(fadelvl) + for c=0,15 do + if i+1>=16 then + pal(c,0,1) + else + pal(c,fadetable[c+1][i+1],1) + end + end +end + +function draw_debug() + cursor(0,0,7) + -- uncomment the followingh + -- to display object counts +--[[ + print("es "..tostr(#eships)) + print("eb "..tostr(#ebullets)) + print("pb "..tostr(#pbullets)) + print("ib "..tostr(#intangibles_bg)) + print("if "..tostr(#intangibles_fg)) + print("v "..tostr(#events)) +]] + if (#debug==0) return + foreach(debug,print) + debug={} +end + +function drawgame() + cls() + clip(0,0,112,128) + for tbl in all{intangibles_bg, pbullets, pships, eships, ebullets, intangibles_fg} do + for x in all(tbl) do + x:draw() + end + end + clip(0,0,128,128) + drawhud() +end + +function drawhud() + -- 112-and-right is hud zone + rectfill(112, 0, 127, 127,0x56) + rect(112,0,127,127,7) + line(127,1,127,127,5) + line(113,127) + + draw_gun_info("❎",1,116,3,primary_ship.main_gun) + draw_gun_info("🅾️",2,116,31,primary_ship.special_gun) + + dropshadow("pwr",114,59,1) + inset(114,66,125,92) + fillp(0x5a5a) + vertmeter(115,67,124,91,primary_ship.power, primary_ship.max_power, {170,154,153,148,68}) + + + dropshadow("h s",114,97,1) + inset(114,104,125,125) + line(119,105,119,124,119) + line(120,105,120,125,85) + vertmeter(115,105,118,124,primary_ship.hp, primary_ship.maxhp, hpcols) + vertmeter(121,105,124,124,primary_ship.shield, primary_ship.maxshield,{204,220,221}) + fillp(0) +end + +function draw_gun_info(lbl,fgc,x,y,gun) + dropshadow(lbl,x,y,fgc) + inset(114,y+7,125,y+18) + inset(114,y+20,125,y+24) + if(gun) then + spr(gun.icon,116,y+9,1,1) + --115 to 124 - ammo bar. round up + if gun.ammo == nil then + fillp(0xa5a5) + rectfill(115,y+21,124,y+23,0xea) + fillp(0) + elseif gun.ammo > 0 then + rectfill( + 115,y+21, + 115+flr(9*gun.ammo/gun.maxammo), + y+23,10) + else + line(118, y+22, 121, y+22, 2) + end + end +end + +function vertmeter(x0,y0,x1,y1,val,maxval,cols) + if (val <= 0) return + local h = y1-y0 + local px = -flr(-(h*val)\maxval) + local ncols = #cols + local firstcol = ((h-px)*ncols\h)+1 + local lastbottom = y0+(h*firstcol\ncols) + rectfill(x0, y1-px, x1, lastbottom, cols[firstcol]) + for i=firstcol+1,ncols do + local bottom = y0+h*i\ncols + rectfill(x0,lastbottom,x1,bottom,cols[i]) + lastbottom = bottom + end +end + +function inset(x0,y0,x1,y1) + rectfill(x0,y0,x1,y1,0) + -- use "wide colors" to draw + -- monochrome regardless of + -- fillp + rect(x0,y0,x1,y1,119) + line(x1,y0,x0,y0,85) + line(x0,y1,85) +end + +function dropshadow(str, x, y, col) + print(str, x+1, y+1, 5) + print(str, x, y, col) +end + +function grab_p1_butts() + if state ~= game then + local r = {0,0,0,0,0} + r[0] = 0 + return r + end + local b = btn() + return { + [0]=b&0x1, + [1]=(b&0x2)>>1, + [2]=(b&0x4)>>2, + [3]=(b&0x8)>>3, + [4]=(b&0x10)>>4, + [5]=(b&0x20)>>5 + } +end + +function bury_the_dead(tbl, dead) + if (#dead == 0) return + local tail = dead[1] + local head = tail + 1 + local deaddex = 2 + + while head <= #tbl do + while deaddex <= #dead and head == dead[deaddex] do + deaddex += 1 + head += 1 + end + if (head > #tbl) break + tbl[tail] = tbl[head] + head += 1 + tail += 1 + end + + for i=1,(head-tail) do + deli(tbl) + end +end +-->8 +--ships, including player + +function init_ship_mt() + setmetatable(player, ship_t) + setmetatable(frownie, ship_t) + setmetatable(blocky, frownie_t) + setmetatable(chasey, ship_t) +end + +firespark = {9, 8, 2, 5, 1} -- bright spark colors +smokespark = {13, 13, 5, 5} + +player = { + --shape + sprite = 1, --index of ship sprite + size = 1, --all ships are square; how many 8x8 sprites? + hurt = { -- hurtbox - where this ship can be hit + x_off = 3, -- upper left corner + y_off = 2, -- relative to ship ulc + width = 1, + height = 3 + }, + sparks = firespark, -- see tab 9 + sparkodds = 2, + boss = true, -- dramatic special effects + + -- health and power + hp = 3, -- current health, non-regenerating + maxhp = 3, -- player only; other ships never heal + shield = 2, -- regenerates, using power + maxshield = 2, + shieldcost = 300, -- power cost to refill shield + generator = 1.5, -- 1 feels too slow + + -- gun + main_gun = nil, -- assign at spawn time + special_gun = nil, + fire_off_x = 4, -- offset where bullets come from + fire_off_y = 0, + + -- position + x=52, -- x and y are for upper left corner + y=96, + xmomentum = 0, + ymomentum = 0, + maxspd = 2.5, -- momentum cap + thrust = 0.25, -- momentum added from button + drag = 0.125, -- momentum lost per frame + slip = false, -- does not slide down screen + grab_butts = function(self) -- fetch buttons + local butts = grab_p1_butts() + if butts[0] == butts[1] then + self.sprite = 1 + elseif butts[0] > 0 then + self.sprite = 17 + else + self.sprite = 18 + end + return butts + end +} + +player_t = { + __index = player +} + +function new_p1() + p = { + main_gun = new_gun_of(zap_gun_t, false) + } + setmetatable(p, player_t) + return p +end + +frownie = { + --shape + sprite = 3, --index of ship sprite + size = 1, --all ships are square; how many 8x8 sprites? + hurt = { -- hurtbox - where this ship can be hit + x_off = 0, -- upper left corner + y_off = 1, -- relative to ship ulc + width = 8, + height = 6 + }, + sparks = smokespark, + sparkodds = 8, + + -- health and power + hp = 1, -- enemy ships need no max hp + + -- position + x=60, -- x and y are for upper left corner + y=8, + xmomentum = 0, + ymomentum = 0, + maxspd = 2, -- momentum cap + thrust = 0.12, -- momentum added from button + drag = 0.07, -- momentum lost per frame + slip = true, + grab_butts = function(discard_self) + -- buttons are effectively analog + -- and negative buttons work just fine! + local butts = {} + local tstate = (1 + flr(4*t() + 0.5)) % 6 + butts[0] = ((tstate==1 or tstate==2) and 1) or 0 + butts[1] = ((tstate==4 or tstate==5) and 1) or 0 + for b=2, 5 do + butts[b]=0 + end + return butts + end, -- button fetch algorithm +} +frownie_t = { + __index = frownie +} + +blocky = { + sprite = 10, + hp = 2, + hurt = { + x_off = 0, + y_off = 0, + width = 8, + height = 7 + }, + + ow = function(self) + if self.hp <= 1 then + self.sprite = 11 + else + self.sprite = 10 + end + ship_t.__index.ow(self) + end +} + +blocky_t = { + __index = blocky +} + +function spawn_spewy_at(x, y) + local spewy = { + x = x, + y = y, + sprite = 26, + power = -20, + hurt = { + x_off = 0, + y_off = 1, + width = 8, + height = 5 + }, + + hp = 1, + maxpower = 70, + generator = 0.5, + main_gun = new_gun_of(protron_gun_t, true), + fire_off_x = 4, + fire_off_y = 7, + grab_butts = function() + local butts = frownie.grab_butts() + butts[5] = 1 + return butts + end, + } + setmetatable(spewy, frownie_t) + add(eships, spewy) + return spewy +end + +chasey = { + sprite = 5, + size = 1, + hurt = { + x_off = 1, + y_off = 2, + width = 6, + height = 5, + }, + sparks = smokespark, + sparkodds = 8, + hp = 2, + shield = 1, + maxshield = 1, + shieldcost = 180, + + fire_off_x = 4, + fire_off_y = 7, + + maxspd = 2, + thrust = 0.2, + drag = 0.075, + slip = true, + + grab_butts = function(self) + local butts = {0,0,0,0,0} + butts[0] = 0 + if (self.x < primary_ship.x) butts[1] = 1 + if (self.x > primary_ship.x) butts[0] = 1 + if (self.x - 16 < primary_ship.x and self.x + 16 > primary_ship.x) butts[5] = 1 + return butts + end, +} +chasey_t = { + __index = chasey +} +function spawn_chasey_at(x, y) + local c = { + x = x, + y = y, + main_gun = new_gun_of(zap_gun_t, true) + } + setmetatable(c, chasey_t) + add(eships, c) + return c +end + +function spawn_xl_chasey_at(x, y) + local c = { + x = x, + y = y, + size = 2, + maxspd = 1.25, + hurt = { + x_off = 2, + y_off = 4, + width = 12, + height = 10 + }, + hp = 20, + shield = 5, + boss = true, + slip = false, + main_gun = new_gun_of(zap_gun_t, true), + grab_butts = function(self) + local butts = chasey.grab_butts(self) + if (self.y < 4) butts[3] = 1 + return butts + 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, + } + setmetatable(c, chasey_t) + add(eships, c) + return c +end +-->8 +--ship behavior + +scrollrate = 0.25 --in px/frame + +ship_m = { + + -- ships have no shield by default + shield = 0, + maxshield = 0, + shieldcost = 32767.9, + shieldcooldown = 180, + + -- default generator behavior: + -- 10 seconds for a full charge + max_power = 600, + power = 600, + generator = 1, -- power gen per frame + + invincible_until = -32768, + + slip = true, -- most enemies slide + + xmomentum = 0, + ymomentum = 0, +} + +ship_t = { + __index = ship_m, +} + +function ship_m.die(s) + s.dead = true + if (s.hp <= 0) boom(s.x+s.size*4, s.y+s.size*4,12*s.size, s.boss) +end + +function ship_m.move(ship) + ship:refresh_shield() + ship.power = min(ship.max_power, ship.power + ship.generator) + butt = ship:grab_butts() + if (butt[5] > 0) ship:maybe_shoot(ship.main_gun) + if (butt[4] > 0) ship:maybe_shoot(ship.special_gun) + if (butt[0]-butt[1] ~= 0 or butt[2]-butt[3] ~= 0) spark(ship.sparks, ship.x + 4*ship.size, ship.y + 4*ship.size, butt, ship.thrust, ship.sparkodds) + ship.xmomentum += (ship.thrust * butt[1]) - (ship.thrust * butt[0]) + ship.ymomentum += (ship.thrust * butt[3]) - (ship.thrust * butt[2]) + ship.xmomentum = mid(-ship.maxspd, ship.maxspd, ship.xmomentum) + ship.ymomentum = mid(-ship.maxspd, ship.maxspd, ship.ymomentum) + + ship.x += ship.xmomentum + ship.y += ship.ymomentum + + if ship == primary_ship then + ship.x = mid(0, 112 - 8 * ship.size, ship.x) + ship.y = mid(0, 128 - 8 * ship.size, ship.y) + end + + --friction + local d = ship.drag + ship.xmomentum -= mid(d, -d, ship.xmomentum) + ship.ymomentum -= mid(d, -d, ship.ymomentum) + + -- "scrolling" behavior + if ship.slip then + ship.y += scrollrate + if ship.y >= 128 then + ship:die() + return true + end + end + return false +end + +function ship_m.draw(ship) + if(ship.fx_pal) pal(ship.fx_pal) + spr(ship.sprite, ship.x, ship.y, ship.size, ship.size) + pal() +end + +function hurtbox(ship) + local h = ship.hurt + return { + x=ship.x + h.x_off, + y=ship.y + h.y_off, + width=h.width, + height=h.height + } +end + +function ship_m:maybe_shoot(gun) + if (not gun) return + if (self.power < gun.power) return + if (not gun:shoot(self.x + self.fire_off_x, self.y + self.fire_off_y)) return + self.power -= gun.power +end + +function ship_m:hitship(other) + return self:hitsomething(1) +end + +function ship_m:hitbullet(b) + return self:hitsomething(b.damage) +end + +function ship_m:hitsomething(dmg) + if (dmg <= 0) return false + if (lframe < self.invincible_until) return false + self.shield_refresh_ready = lframe + self.shieldcooldown + if self.shield >= dmg then + self.shield -= dmg + self:ow(true) + return false + end + dmg -= self.shield + self.shield = 0 + self.hp -= dmg + if self.hp <= 0 then + self:die() + return true + end + self:ow(false) + return false +end + +function ship_m:ow(shielded) + if (shielded) then + blip(self,12,3) + return + end + blip(self, 7, 3) +end + +function ship_m:refresh_shield() + if (self.shield >= self.maxshield) return + if (lframe < self.shield_refresh_ready) return + if (self.power < self.shieldcost) return + self.shield += 1 + self.power -= self.shieldcost + self.shield_refresh_ready = lframe + self.shieldcooldown +end +-->8 +-- 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.width8 +-- level and event system + +-- a level is a map from +-- effective frame number to +-- callback for that frame. + +-- 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 +lframe = 0 + +-- do not advance distance when +-- nonzero +freeze = 0 + +eol = {} + +function load_level(lvltbl) + distance = 0 + lframe = 0 + freeze = 0 + current_level = {} + -- copy the level so we can + -- modify it dynamically + -- without losing the original + for frame, cb in pairs(lvltbl) do + current_level[frame] = cb + end + leveldone = false +end + +function level_frame() + lframe += 1 + if (current_level == nil) return true + if freeze == 0 then + distance += 1 + local cb = current_level[distance] + if cb ~= nil then + if cb == eol then + current_level = nil + return true + else + cb() + end + end + end + return false +end +-->8 +-- example level + +function spawn_rnd_x(mt) + s = { + x = rnd(104), + y = -(mt.__index.size * 8 - 1) + } + setmetatable(s, mt) + add(eships, s) + return s +end + +function spawn_blocking_rnd_x(mt) + freeze += 1 + s = { + x = rnd(104), + y = -7, + ice = 1, + die = function(self) + freeze -= self.ice + self.ice = 0 + mt.__index.die(self) + end + } + setmetatable(s, mt) + add(eships, s) + return s +end + +function spawn_frownie() + return spawn_rnd_x(frownie_t) +end + +function spawn_blocking_frownie() + spawn_blocking_rnd_x(frownie_t) +end + +function spawn_blocky() + spawn_rnd_x(blocky_t) +end + +function spawn_blocking_blocky() + spawn_blocking_rnd_x(blocky_t) +end + +function spawn_spewy() + return spawn_spewy_at(rnd(104), -7) +end + +function spawn_chasey() + return spawn_chasey_at(rnd(104), -7) +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_t.__index.die(self) + end +end + +function spawn_bonus_frownie() + local f = spawn_frownie() + f.sprite = 7 + f.die = function(self) + spawn_repair_at(self.x+4, self.y+4) + frownie_t.__index.die(self) + end +end + +function spawn_bonus_vulcan_chasey() + local c = spawn_chasey() + c.main_gun=new_gun_of(vulcan_gun_t, true) + c.die = function(self) + spawn_main_gun_at(self.x-1, self.y-1, vulcan_gun_t) + chasey.die(self) + end + c.sprite=4 + return c +end + +helpers = { + spawn_frownie, + spawn_frownie, + spawn_frownie, + spawn_blocky, + spawn_blocky, + spawn_chasey, + spawn_spewy, +} + +function spawn_blocking_boss_chasey() + freeze += 1 + local c = spawn_xl_chasey_at(44, -15) + c.ice = 1 + c.die = function(self) + freeze -= self.ice + self.ice = 0 + chasey_t.__index.die(self) + end + + local nextspawn = lframe + 120 + add(events, function() + if lframe >= nextspawn then + helpers[flr(rnd(#helpers))+1]() + nextspawn += 60 + end + return c.dead + end) + + return c +end + +example_level = { + [1]=spawn_frownie, + [60]=spawn_bonus_vulcan_chasey, + [61]=spawn_blocky, + [85]=spawn_spewy, + [100]=spawn_spewy, + [115]=spawn_spewy, + [130]=spawn_bonus_frownie, + [145]=spawn_spewy, + [200]=spawn_chasey, + [250]=spawn_blocking_blocky, + [285]=function() + spawn_spec_gun_at(35, -11, blast_gun_t) + end, + [310]=function() + spawn_blocking_blocky() + spawn_blocking_blocky() + spawn_blocking_blocky() + end, + [311]=spawn_frownie, + [350]=function() + spawn_main_gun_at(70, -11, protron_gun_t) + end, + [401]=spawn_frownie, + [420]=spawn_blocking_frownie, + [430]=spawn_bonus_vulcan_chasey, + [450]=spawn_frownie, + [465]=spawn_bonus_frownie, + [480]=spawn_chasey, + [500]=function() + local tnext = lframe + local remain = 20 + add(events, function() + if (lframe < tnext) return false + spawn_blocking_blocky() + tnext = lframe + 12 + remain -= 1 + return (remain <= 0) + end) + end, + [501]=spawn_bonus_frownie, + [620]=spawn_blocking_blocky, + [700]=spawn_blocking_boss_chasey, + [701]=eol +} +-->8 +-- bullets and guns + +function init_bullet_mt() + setmetatable(zap, bullet_t) + setmetatable(zap_gun, gun_t) + setmetatable(blast, bullet_t) + setmetatable(blast_gun, gun_t) + setmetatable(protron, bullet_t) + setmetatable(protron_gun, gun_t) + setmetatable(vulcan, bullet_t) + setmetatable(vulcan_gun, gun_t) +end + +function new_gun_of(mt, is_enemy) + local g = { + enemy = is_enemy + } + setmetatable(g, mt) + return g +end + +zap = { + --shape + psprite = 8, --index of player ammo sprite + esprite = 9, -- index of enemy ammo sprite + width = 1, --in 8x8 blocks + height = 1, + hurt = { -- hurtbox - where this ship can be hit + x_off = 0, -- upper left corner + y_off = 0, -- relative to sprite + width = 2, + height = 8 + }, + center_x_off = 1, -- how to position by ship + bottom_y_off = 0, + top_y_off = 0, + + damage = 1, + dx = 0, -- px/frame + dy = 8, + + hitship = function(_, _) + return true + end +} +zap_t = { + __index = zap +} + +zap_gun = { + enemy = false, + power = 20, -- power consumed per shot + cooldown = 10, -- frames between shots + ammo = nil, -- unlimited ammo - main gun + t = zap_t -- metatable of bullet to fire +} + +zap_gun_t = { + __index = zap_gun +} + +blast = { + --shape + psprite = 12, --index of player ammo sprite + esprite = 3, -- index of enemy ammo sprite + width = 1, --in 8x8 blocks + height = 1, + hurt = { -- hurtbox - where this ship can be hit + x_off = 1, -- upper left corner + y_off = 1, -- relative to sprite + width = 6, + height = 6 + }, + center_x_off = 4, -- how to position by ship + bottom_y_off = 0, + top_y_off = 0, + + damage = 4, + dx = 0, -- px/frame + dy = 2, + + -- disable damage for 2 frames + -- when hitting something + hitship = function(self, _) + self.damage = 0 + local wait = 2 + e = function() + wait -= 1 + if wait <= 0 then + self.damage = 4 + return true + end + return false + end + add(events, e) + end +} +blast_t = { + __index = blast +} + +blast_gun = { + icon = 13, + enemy = false, + power = 0, -- ammo, not power + cooldown = 30, -- frames between shots + ammo = 5, + maxammo = 5, + t = blast_t -- metatable of bullet to fire +} + +blast_gun_t = { + __index = blast_gun +} + +protron = { + --shape + psprite = 23, --index of player ammo sprite + esprite = 24, -- index of enemy ammo sprite + width = 1, --in 8x8 blocks + height = 1, + hurt = { -- hurtbox - where this ship can be hit + x_off = 1, -- upper left corner + y_off = 1, -- relative to sprite + width = 2, + height = 2 + }, + center_x_off = 1, -- how to position by ship + bottom_y_off = 4, + top_y_off = 0, + + damage = 1, + dx = 0, -- px/frame + dy = 3, +} +protron_t = { + __index = protron +} + +protron_gun = { + icon = 25, + enemy = false, + power = 35, + cooldown = 15, -- frames between shots + ammo = nil, + maxammo = nil, + actually_shoot = function(self, x, y) + local sprite = protron.psprite + if (self.enemy) sprite=protron.esprite + for i=1,3 do + local b = { + enemy=self.enemy, + sprite=sprite, + dx = i, + dy = 4-i + } + setmetatable(b, protron_t) + b:spawn_at(x,y) + local b2 = { + enemy=self.enemy, + sprite=sprite, + dx = -i, + dy = 4-i + } + setmetatable(b2, protron_t) + b2:spawn_at(x,y) + end + local bup = { + enemy=self.enemy, + sprite=sprite, + dy=4 + } + setmetatable(bup, protron_t) + bup:spawn_at(x,y) + end +} + +protron_gun_t = { + __index = protron_gun +} + +vulcan = { + --shape + psprite = 22, --index of player ammo sprite + esprite = 21, -- index of enemy ammo sprite + width = 1, --in 8x8 blocks + height = 1, + hurt = { -- hurtbox - where this ship can be hit + x_off = 0, -- upper left corner + y_off = 0, -- relative to sprite + width = 1, + height = 4 + }, + center_x_off = 0.5, -- how to position by ship + bottom_y_off = 4, + top_y_off = 0, + + damage = 0.5, + dx = 0, -- px/frame + dy = 4, +} +vulcan_t = { + __index = vulcan +} +vulcan_gun = { + icon = 37, + enemy = false, + power = 8, + cooldown = 2, -- frames between shots + ammo = nil, + maxammo = nil, + dxs = {0.35, -0.35, -0.7, 0.7, 0.35, -0.35}, + xoffs = {1, 0, -1, 1, 0, -1}, + dxidx = 1, + actually_shoot = function(self, x, y) + local sprite = self.enemy and vulcan.esprite or vulcan.psprite + local b = { + enemy=self.enemy, + sprite=sprite, + dx = self.dxs[self.dxidx], + } + setmetatable(b, vulcan_t) + b:spawn_at(self.xoffs[self.dxidx]+x,y) + self.dxidx += 1 + if (self.dxidx > #self.dxs) self.dxidx = 1 + end +} +vulcan_gun_t = { + __index = vulcan_gun +} + +-->8 +-- bullet and gun behaviors + +bullet_base = { + enemyspd = 0.5 +} + +bullet_t = { + __index = bullet_base +} + +gun_base = { + shoot_ready = -32768, + + icon = 20 +} + +gun_t = { + __index = gun_base +} + +function bullet_base:hitship(_) + self:die() + return true +end + +function bullet_base:die() +end + +function bullet_base:move() + self.x += self.dx + if self.enemy then + self.y += self.dy + if self.y > 128 then + self:die() + return true + end + else + self.y -= self.dy + if self.y < -8*self.height then + self:die() + return true + end + end + return false +end + +function bullet_base:draw() + spr(self.sprite, self.x, self.y, self.width, self.height) +end + +function bullet_base:spawn_at(x, y) + self.x = x - self.center_x_off + if self.enemy then + self.dx *= self.enemyspd + self.dy *= self.enemyspd + self.y = y + self.top_y_off + add(ebullets, self) + else + self.y = y - (8 * self.height) + self.bottom_y_off + add(pbullets, self) + end +end + +function gun_base:shoot(x, y) + if (lframe < self.shoot_ready) return false + if self.ammo then + if (self.ammo <= 0) return false + self.ammo -= 1 + end + self.shoot_ready = lframe + self.cooldown + self:actually_shoot(x, y) + return true +end + +function gun_base:actually_shoot(x, y) + b = { } + setmetatable(b, self.t) + if self.enemy then + b.enemy = true + b.sprite = b.esprite + else + b.enemy = false + b.sprite = b.psprite + end + b:spawn_at(x, y) + return true +end +-->8 +-- readme.md + +--[[ + +main loop sequence +================== +1. level_frame +2. events +3. merge new_events into events +4. update bg intangibles +5. move ships (player first) +6. move bullets (player first) +7. calculate collisions + 1. pship on eship + 2. ebullet on pship + 3. pbullet on eship +8. update fg intangibles +9. check for end of level + +draw order +---------- +bottom to top: +1. intangibles_bg +2. player bullets +3. player ships +4. enemy ships +5. enemy bullets +6. intangibles_fg + +notes +----- +intangibles_fg move()s after +all collisions and other moves +are processed. if an intangible +is added to the list as a result +of a collision or move, it will +itself be move()d before it is +drawn. + +don't modify a list while it's +being iterated. ships that want +to create ships and bullets +that want to create bullets +need to create an event to +do this (which will run before +the ship and bullet phases of +the next frame). events that +want to create events add to +new_events, rather than events. + +data-driven items +================= +guns and bullets both allow the most +common behaviors to be +expressed with data alone. +ships only need a movement +algorithm expressed. + +guns +---- +* power - cost in generator + power to fire. may be 0. + field directly read by ships; + required in all guns. +* t - metatable for bullet type. + fired once in the bullet's + default direction per shot. +* enemy - if true, fired bullets + are flagged as enemy bullets. +* icon - sprite index of an + 8x8 sprite to display in the + hud when the player has this + gun. default is 20, a generic + crosshair bullseye thing. +* cooldown - min frames between + shots. +* ammo, maxammo - permitted + number of shots. 0 is empty + and unfireable. maxammo = 0 + will cause a divide by zero + so don't do that. if nil, + ammo is infinite. + +default guns manage ammo and +cooldown in shoot, then call +actually_shoot to create the +projectile. override only +actually_shoot to change +projectile logic while keeping +cooldown and ammo logic. + +ships manage generator power +before asking the gun to shoot. +this behavior is in +ship_m:maybe_shoot. + +bullets +------- +* dx, dy - movement per frame. + player bullets use -dy + instead. +* enemyspd - multiplier for dx + and dy on enemy bullets. + default is 0.5, making enemy + shots much easier to dodge +* damage - damage per hit; + used by ships +* psprite, esprite - index of + player or enemy sprite. +* center_off_x - the horizontal + centerpoint of the bullet, + for positioning when firing. + assume a pixel's coordinates + refer to the upper left corner + of the pixel; the center of + a 2-width bullet with an + upper left corner at 0 is 1, + not 0.5. +* top_off_y, bottom_off_y - + also for positioning when + firing. positive distance from + top or bottom edge to image. + top_off_y will usually be 0, + bottom_off_y will not be when + bullets are smaller than + the sprite box. +* width, height - measured in + full sprites (8x8 boxes), not + pixels. used for drawing. + +bullets despawn when above or +below the screen (player or +enemy bullets, respectively). + +by default, bullets despawn +when they hit something. +override hitship to change this. + +ships +____ + +ships move by calculating +momentum, then offsetting their +position by that momentum, then +clamping their position to the +screen (horizontally only for +ships that autoscroll). ships +that autoscroll (slip==true) +then slide down by scrollspeed. +fractional coordinates are ok. +after movement, ships lose +momentum (ship.drag along each +axis). abs(momentum) can't +exceed ship.maxspeed. + +ships gain momentum by acting +like a player pushing buttons. +the player ship actually reads +buttons for this. + +grab_butts - ship thrust control +based on btn() api. returns a +table indexed from 0..5 with +0 to not push this button and 1 +to push it. ships can use +fractional or out-of-range +numbers to get varying amounts +of thrust, including using +negative numbers for thrust in +the opposite direction. 4 and 5 +just check for nonzeroness to +attempt to fire. + +ships hitting another ship take +1 damage per frame of overlap. +ships hitting a bullet check +bullet.damage to find out how +much damage they take. damage +is applied to shields, then hp. +damaged ships flash briefly - +blue (12) if all damage was +shielded, white (7) if hp was +damaged. a ship that then has 0 +or less hp calls self:die() and +tells the main game loop to +remove it. + +ships have power, from 0 to +ship.maxpower, increasing by +ship.generator per frame. +in maybe_shoot, ships check that +they have power to fire before +trying to fire (the gun itself +checks ammo and cooldown), and +spend that power if they fire. + +power is also used to restore +shields - ship.shieldcost per +point of shields. shieldcooldown +is the interval between +restoring shield points, which +is reset when a ship takes +damage (regardless of whether +that damage is stopped by the +shield or not). + +therefore: +* damaged ships spend power + repairing shields, which may + affect ability to fire guns. + this looks like a slow firing + rate because the ship will + eventually recover enough + energy to fire. +* a ship firing nonstop will + typically be unable to recover + any shields because it will + not have energy to do so. + +ships do not repair hp on their +own. negative-damage bullets +are treated as 0, but a bullet +can choose to repair the ship +it hits in its own hitship +method, or otherwise edit it +(changing weapons, refilling +weapon ammo). powerups are +therefore a kind of bullet. + +levels +====== + +a level is a table mapping +effective frame number to +functions. when a level starts, +it sets lframe ("level frame") +and distance to 0. + +every frame, level_frame +increments lframe. then if the +level is not frozen (more on +that later), it increments +distance and runs the function +in the level table for exactly +that frame number (if any). +distance is therefore "nonfrozen +frames", and is used to trigger +level progress. lframe always +increments. ships are encouraged +to use lframe to control +animation and movement, and may +use distance to react to level +progress separately from overall +time. + +a special sentinel value, eol, +marks the end of the level. +(the level engine doesn't know +when it's out of events, so +without eol, the level will +simply have no events forever.) +when it finds eol, level_frame +throws away the current level +and tells the main loop that it +might be done. the main loop +agrees the level is over and the +player has won when the level +has reached eol and there are +no more enemy ships, enemy +bullets, or background events +remaining. player ships, player +bullets, and intangibles are +not counted. + +level freezing +-------------- +the level is frozen when the +global value freeze > 0. +generally, something intending +to block level progress (a +miniboss, a minigame, etc.) +increments freeze and prepares +some means of decrementing it +when it no longer wants to block +level progress. + +most commonly, we want to block +until some specific ship or +group of ships has died. for +these ships, override ship:die +to decrement freeze. make sure +to set ship.dead in any new +ship:die method so anything else +looking at it can recognize +the ship as dead. + +for anything else, you probably +want an event to figure out when +to unfreeze. + +levels start at 1 +----------------- + +distance is initialized to 0 +but gets incremented before the +first time the engine looks for +events. therefore, the first +frame of the level executes +level[1]. since levelframe +executes before anything else, +level[1] sets up the first frame +drawn in the level. the player +does not see a blank world +before level[1] runs. +level[1] can therefore be used +to reconfigure the player ship, +set up backgrounds, start music, +kick off some kind of fade-in +animation, etc. + + +events +====== +the global list "events" stores +0-argument functions which are +called every frame. if they +return true, they are removed +from the list and not run again; +if they return false, they stay +and will be called in later +frames. the level does not end +while the events table is +nonempty. + +events are most commonly used +to set up something for later +(for example, blip uses an event +to remove the fx_pallete from +the flashing ship when the blip +expires), but can also be used +to implement a "level within a +level" that does something +complicated until it's done. if +you froze the level when +creating the event, remember +to thaw it (freeze -= 1) on all +paths that return true. + +to do complex stuff in events, +use a closure or a metatable +that specifies __call. + +to avoid editing the events +list while it is being iterated, +events that create new events +must add those events to +new_events rather than events. +new_events is only valid during +the "event execution" stage, so +events created at any other time +must go directly on events +without using new_events. + +intangibles +=========== + +the intangibles_fg and +intangibles_bg lists contain +items with :move and :draw. +like ships and bullets, they +move during _update60 and +draw during _draw. they are +not checked for collisions. + +intangibles_bg moves/draws +before anything else moves or +draws. intangibles_fg +moves/draws last. this controls +whether your intangible object +draws in front of or behind +other stuff. you probably want +intangibles_bg for decorative +elements and intangibles_fg +for explosions, score popups, +etc. + +there's no scrolling background +engine but intangibles_bg could +be used to create one, including +using the map (otherwise unused +in this engine) for the purpose. + +intangibles do not prevent the +level from ending. like bullets +and ships, if :move returns +true, they are dropped. + +weird coding conventions +======================== + + * i am inconsistent between + including underscores or not + (sorry) + * a triple underscore prefix + is used for fields created + by invasive algorithms + (those algorithms that + modify the items they're + given to perform some kind + of bookkeeping) + +]] +-->8 +-- standard events + +blip_fx = { + abort = function(self) + self.cancel = true + end +} + +blip_fx_t = { + __index = blink_fx, + __call = function(self) + 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(obj, col, frames) + obj.fx_pal = { + [0] = 0, + [1] = col, + [2] = col, + [3] = col, + [4] = col, + [5] = col, + [6] = col, + [7] = col, + [8] = col, + [9] = col, + [10] = col, + [11] = col, + [12] = col, + [13] = col, + [14] = col, + [15] = col + } + if (obj.___fx_pal_event) obj.___fx_pal_event:abort() + local e = { + frames = frames, + obj = obj + } + setmetatable(e, blip_fx_t) + add(events, e) +end + +bossspark = {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) + local butts = {0, sin(angle), 0} + butts[0] = cos(angle) + spark(sp,x+4,y+4,butts,boombase+rnd(boombonus),1, true) + end + return +end + +function spark(sprs, x, y, butts, thrust, odds, fg) + if (sprs==nil or flr(rnd(odds)) ~= 0) return + thrust *= 2.5 + add(fg and intangibles_fg or intangibles_bg, { + x = x + rnd(4) - 2, + y = y + rnd(4) - 2, + sprs = sprs, + sidx = 1, + dx = (butts[0] - butts[1]) * thrust + rnd(2) - 1, + dy = (butts[2] - butts[3]) * thrust + rnd(2) - 1, + draw = function(self) + pset(self.x, self.y, self.sprs[self.sidx]) + end, + move = function(self) + 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 + }) +end +-->8 +-- powerups +function init_powerup_mt() + setmetatable(powerup, bullet_t) + setmetatable(gun_swap, powerup_t) +end + +powerup = { + -- animated sprite array: "sprites" + -- to draw under or over anim, + -- override draw, draw the + -- under-part, call into + -- powerup.draw(self), then + -- draw the over-part + width = 1, + height = 1, + -- note: make hurtboxes larger + -- than sprite by 2px per side + -- since ship hitbox is tiny + -- but powerups should feel + -- easy to pick up + dx = 0, + dy = 1.5, -- 0.75 after enemyspd + enemy = true, -- collides with player ship + damage = 0, + + anim_speed = 2, + loop_pause = 30 -- affected by animspeed +} + +-- sprite indexes for "sheen" animation +sheen8x8 = {2, 54, 55, 56, 57, 58, 59, 60, 61} + +powerup_t = { + __index = powerup +} + +-- todo: draw two sprites +-- on top of each other here +-- so all powerups can share +-- the "sheen" animation? + +function powerup:draw() + spr(self.sprites[max(1, + (lframe\self.anim_speed) + %(#self.sprites+self.loop_pause) + -self.loop_pause + +1)], + self.x, self.y, + self.width, self.height) +end + +function spawn_repair_at(x, y) + local repair = { + hurt = { + x_off = -2, + y_off = -2, + width = 12, + height = 12 + }, + center_x_off = 4, + top_y_off = 0, + bottom_y_off = 0, + sprites = sheen8x8, + hitship = function(self, ship) + if (ship ~= primary_ship) return false + primary_ship.hp = min(primary_ship.maxhp, primary_ship.hp + 1) + return true + end, + draw = function(self) + spr(53, self.x, self.y, self.width, self.height) + powerup.draw(self) + end + } + setmetatable(repair, powerup_t) + repair:spawn_at(x, y) +end + +gun_swap = { + hurt = { + x_off = -2, + y_off = -2, + width = 16, + height = 16 + }, + -- gun = new_gun_of(t, false) + center_x_off = 6, + top_y_off = 0, + bottom_y_off = 4, + width = 2, + height = 2, + sprites = {64, 66, 68, 70, 72, 74, 76, 78}, + hitship = function(self, ship) + if (ship ~= primary_ship) return false + ship.main_gun = self.gun + return true + end, + draw = function(self) + powerup.draw(self) + spr(self.gun.icon, self.x+2, self.y+2, 1, 1) + end +} +gun_swap_t = { + __index=gun_swap +} + +function spawn_main_gun_at(x, y, mt) + local gun_p = { + gun = new_gun_of(mt, false) + } + setmetatable(gun_p, gun_swap_t) + gun_p:spawn_at(x, y) +end + +spec_gun_pl = { + [1] = 2, + [14] = 6, + [2] = 14 +} + +function spawn_spec_gun_at(x, y, mt) + local gun_p = { + gun = new_gun_of(mt, false), + hitship = function(self, ship) + if (ship ~= primary_ship) return false + ship.special_gun = self.gun + return true + end, + draw = function(self) + pal(spec_gun_pl) + powerup.draw(self) + pal() + spr(self.gun.icon, self.x+2, self.y+2, 1, 1) + end + } + setmetatable(gun_p, gun_swap_t) + gun_p:spawn_at(x, y) +end +__gfx__ +00000000000650000000000000000000bb0b50b59909209200cc0c00000000003b00000082000000e00e8002e00e800200333300002222000000000000000000 +00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb0037000000a2000000e0e8880240e8480403bbbb30028888200000000000000000 +00700700006d6500000000000cddddd00b33335009444420c00c000c0b333330b7000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000 +00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b35b7000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000 +00077000067d665000000000cd10cdd100b3350000944200c0000000b350b335b7000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000 +0070070065666765000000000ddddd100b33355009444220c000000c03333350b7000000a800000008888820048488203bbaabb3288aa8820000000000000000 +000000006506506500000000001111000b0b5050090920200c0000c00055550037000000a2000000008882000048420003bbbb30028888200000000000000000 +00000000650000650000000000000000000b50000009200000c0cc00000000003b00000082000000000820000008200000333300002222000000000000000000 +00000000000650000006500000000000b000000b80000000700000000bb0000008800000000000000009200000000000cccccccd000650000000000000000000 +0000000000675000000765000000000000bbbb0080000000b0000000b76300008a920000000000009009200200000000c111111d006765000000000000000000 +00000000006d6500006d6500000000000b0000b09000000030000000b663000089920000000550009994444200000000c111111d006d65000000000000000000 +00000000067c6650067c6650000000000b0bb0b0a000000030000000033000000220000000576d009446544200000000c111111d067c66500000000000000000 +00000000067d6650067d6650000000000b0bb0b00000000000000000000000000000000000566d009244442200000000c111111d067d66500000000000000000 +000000005666657576667650000000000b0000b000000000000000000000000000000000000dd0009092220200000000c111111d656667650000000000000000 +0000000056565066665656500000000000bbbb0000000000000000000000000000000000000000000090020000000000c111111d650650650000000000000000 +00000000565000566500065000000000b000000b000000000000000000000000000000000000000000a00a0000000000cddddddd650000650000000000000000 +000000000000000000000000000000000000000000a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000090008000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000a080000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000009080000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000cccccccc77000000007700000000770000000077000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000c11ee11d70000000077000000007700000000770000000070000000000000000000000000000000000000000 +0000000000000000000000000000000000000000c11ee11d00000000770000000077000000007700000000770000000700000000000000000000000000000000 +0000000000000000000000000000000000000000ceeeeeed00000000700000000770000000077000000007700000007700000000000000000000000000000000 +0000000000000000000000000000000000000000ceeeeeed00000000000000007700000000770000000077000000077000000007000000000000000000000000 +0000000000000000000000000000000000000000c11ee11d00000000000000007000000007700000000770000000770000000077000000000000000000000000 +0000000000000000000000000000000000000000c11ee11d00000000000000000000000077000000007700000007700000000770000000070000000000000000 +0000000000000000000000000000000000000000cddddddd00000000000000000000000070000000077000000077000000007700000000770000000000000000 +cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000cccccccccccc0000 +c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000 +c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000 +c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000 +c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000 +c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000 +c1111111111d0000c1111ee1111d0000c111e22e111d0000c11e2112e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000 +c1111111111d0000c1111111111d0000c1111ee1111d0000c11ee22ee11d0000c1e221122e1d0000ce21111112ed0000c2111111112d0000c1111111111d0000 +c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c11e2222e11d0000c1e211112e1d0000ce21111112ed0000c2111111112d0000 +c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000c1ee2222ee1d0000ce22111122ed0000c2111111112d0000 +c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c1111111111d0000c111eeee111d0000ceee2222eeed0000c2221111222d0000 +cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000cddddddddddd0000 +__label__ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777777777777777 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115151166665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666111611156665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115161156665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111556665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650b000000b0765 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765000bbbb000765 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0000b00765 +00000000000000000000000000000000000000000000e00e800200000000000000000000000000000000000000000000000000000000000076500b0bb0b00765 +00000000000000000000000050000000000000000000e0e8880200000000000000000000000000000000000000000000000000000000000076500b0bb0b00765 +00000000000000000000000000000000000000000000e88e288200000000000000000000000000000000000000000000000000000000000076500b0000b00765 +00000000000000000000000000000000000000000d00e88e2882000000000000000000000000000000000000000000000000000000000000765000bbbb000765 +00000000000000000000000000000000000000000000e88e28820000000000000000000000000000000000000000000000000000000000007650b000000b0765 +00000000000000000000000000000000000000000000088888200000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000000000000000000000000008882000000000000000000000000000000000000000000000000000000000000007657777777777765 +00000000000000000000000000000000000000000000000820000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565 +0000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000765aeaeaeaeae765 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765eaeaeaeaea765 +0000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000765aeaeaeaeae765 +00000000000000000000000000000000000000000000000000000009909209200000000000000000000000000000000000000000000000007657777777777765 +00000000000000000000000000000000000000000000000000000009209402200000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000944442000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000094dd42000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000094420000000000000000000000000000000000000000000000000007666622222666665 +00000000000000000000000000000000000000000000000000500000944422000000000000000000000000000000000000000000000000007666225552266665 +00000000000000000000000000000000000000000000000000000000909202000000000000000000000000000000000000000000000000007666225262256665 +00000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000007666225652256665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666622222556665 +00000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000005000000009200000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000050000000000000009009200200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +000000000000000000d0000009994444200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000009446544200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000009244442200000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +0000000000000000000000000909222020000000000000000bbbb000000000000000000000000000000000000000000000000000000000007650000000000765 +000000000000000000000000000900200000000000000000b3333300000000000000000000000000000000000000000000000000000000007650000000000765 +000000000000000000000000000a00a0000000000000000b3350b350000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000000000000000000000000000b350b3350000000000000000000000000000000000000000000000000000000007657777777777765 +00000000000000000000000000000000000000000000000033333500000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000088000000000000005555000000000000000000000000000000000000000000000000000000000007655555555555565 +000000000000000000000000000000058a9200000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765 +00000000000000000000000000000000899200000000000000000000008200000000000000000000000000000000000000000000000000007650000000000765 +0000000000000000000000000000000002200000000000000000000000a200000000000000000000000000000000000000000000000000007650000000000765 +0000000000000000000500000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007657777777777765 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000088000000000050000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +20000000000000000000000008a9200000000000000000000000000000a200000000000000000000000000000000000000000000000000007611161616111665 +20000000000000000000000008992000000000000000000000000000008200000000000000000000000000000000000000000000000000007615151515151565 +000000000000000000000000002200000d0000000000000000000000000000000000000000000000000000000000000000000000000000007611151515116565 +0000000000000000000000000000000000000e00e800200000000000e00e80020000000000000000000000000000000000000000000000007615551115151665 +0000000000000000000000000000000d00000e0e8880200000000000e0e888020000000000000000000000000000000000000000000000007615661115151565 +0000000000000000000000000000000000d00e88e2882000000d0000e88e28820000000000000000000000000000000000000000000000007665666555656565 +0000088000000000000880000000000000000e88e288200000000000e88e28820000000000000000000000000000000000000000000000007666666666666665 +00008a9200000000008a92000000000000000e88e288200000000000e88e28820000000000000000000000000000000000000000000000007655555555555565 +0000899200000000008992000000000000000088888200000000000008888820000000000000000000000000000000000000000000000000765aaaaaaaaaa765 +0000022000000000000220000000000000000008882000000000000000888200000000000000000000000000000000000000000000000000765aaaaaaaaaa765 +0000000000000000000000000000000000000000820000000000000000082000000000000000000000000000000000000000000000000000765aaaaaaaaaa765 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aaaaaaaaaa765 +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765 +00000000000088000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659a9a9a9a9a765 +000000000008a920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765 +00000000000899200000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000007659a9a9a9a9a765 +0000000000002200000000000000000000008a92000000000000000000000000000000000000000000000000000000000000000000000000765a9a9a9a9a9765 +00000000000000000000000000000000000089920000000000000000000000000000000000000000000000000000000000000000000000007659999999999765 +00000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000000000007659999999999765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659494949494765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659494949494765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654949494949765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765 +00000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000007654444444444765 +0000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000007654444444444765 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007657777777777765 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000000a800000000000000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000000a200000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000007616166666611665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666165565 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007611156666111665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666651565 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615156666116565 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007665656666655665 +00000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000007666666666666665 +0000000000000000000000000000000000000000000000000000000000000000008a920000000000000000000000000000000000000000007655555555555565 +00000000000000000000000000000000000000000000000000000000000000000089920000000000000000000000000000000000000000007652222750000765 +00000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000007652222750000765 +00000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000007652222750000765 +00000000000000000000000000000000000000000000000000000000067650000000000000000000000000000000000000000000000000007652222750000765 +0000000000000000000000000000000000000000000000000000000006d650000000000000000000000000000000000000000000000000007652222750000765 +0000000000000000000000000000000000000000000000000000000067c665000000000000000000000000000000000000000000000000007652222750000765 +0000000000000000000000000000000000000000000000000000000067d665000000000000000000000000000000000000000000000000007652828750000765 +00000000000000000000000000000000000000000000000000000006566676500000000000000000000000000000000000000000000000007658282750000765 +00000000000000000000000000000000000000000000000000000006506506500000000000000000000000000000000000000000000000007652828750000765 +00000000000000000000000000000000000000000000000000000006500006500000000000000000000000000000000000000000000000007658282750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007652828750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658282750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777757777765 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007555555555555555 +