93 Commits

Author SHA1 Message Date
5870c129eb staggered "xp", "hp" labels in UI 2024-09-07 16:33:58 -07:00
68863280f3 stagger the bars 2024-09-07 16:17:38 -07:00
3f7c4f59c0 stretch meter area 2024-09-07 16:15:00 -07:00
804eb62ae7 p is now x in ui
"x" looks pretty bad with a drop shadow! will tweak soon anyway
2024-09-07 16:07:40 -07:00
303148876d Remove power mechanics. Replace with XP stub.
Also a few random comments and cleanups along the way.
2024-09-07 16:04:01 -07:00
b379e47dbf bonus shield powerup
mostly to test whether redistributing the shield and health meters works
2024-09-02 15:22:33 -07:00
4ca3913637 it's still tyrian-like so update last_tyrianlike 2024-09-02 15:09:27 -07:00
f9ba59d992 refactor mknew
saves tokens, harder to forget to use it
2024-09-02 15:08:58 -07:00
dd143060ac squash: refactor bullets to remove enemy flag
commit ead2a7d874
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Sep 2 14:45:56 2024 -0700

    fix remaining `vulcan`-family bug

commit 571412b15e
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Sep 2 14:15:56 2024 -0700

    fix chasey xl offsets

commit 907bd8318c
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Sep 2 14:12:35 2024 -0700

    several more fixes, now runs to the end but shot offset is wrong

commit 7170552448
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Sep 2 13:41:42 2024 -0700

    first three waves of bug fixes

commit 01ab6d3969
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Sep 2 12:59:49 2024 -0700

    maybe the rest of the refactor?

commit 7869192dee
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Tue Aug 20 01:21:13 2024 -0700

    partial refactor continued

commit a4658e3ef4
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Mon Aug 19 16:00:16 2024 -0700

    halfway through bullet refactor
2024-09-02 14:46:51 -07:00
60b685d94b save a copy of vacuum_gambit.p8 as last_tyrianlike
next step is to pick a real direction -- MMBN-like (+STS-like) or
Survivors-like -- and adapt to match. I am likely to completely remove
the energy system and use permanent autofire, freeing both fire buttons
for more interesting tasks. It loses the opportunity to create a dynamic
around baiting an enemy to keep shooting so its shields don't recover,
but I don't think it loses a lot else.

Either energy management needs to become really important and the game
becomes strategic and tactical, or it needs to be a non-issue and it is
an arcade game. Tyrian itself did not make the energy system interesting
and it was just a tax, so making it interesting would be doing something
new. But I think it's a kind of "interesting" that almost nobody would
adopt unless I go _very hard_ into creating a tactical/strategic shmup.
A shmup that is actually a strange kind of RTS sounds... really cool,
actually, but I'm not at all confident I could design it.

Removing energy entirely gives us a button _and_ a meter back, which
can be used for XP (Survivors-like) or rearm time (MMBN-like).
2024-08-18 15:04:17 -07:00
cc1e7ea5b7 surive at 0hp and adjust hp values to match
Instead of doing a special case for 1HP, 0HP is survivable, ships die
at negative HP instead. all ship health is adjusted to match, assuming
the weakest shot is 0.5hp, which is currently true. the "real" game will
totally rebalance all ships and weapons anyway.

we're getting close to when I have to stop dawdling and implement the
real game, the engine is _there._
2024-08-18 14:57:29 -07:00
6b8efe3438 fix HP-only mode
it was showing the bar intended as the warning that there's no HP
under the shield. but I tried it and that bar just makes it look like
there *is* a sliver of health, which there isn't. so it's better off
without that in either mode.
2024-08-18 14:49:44 -07:00
c2668cefea handle 0 shield and 1 max HP cases 2024-08-18 14:40:11 -07:00
eebd84544b one hit comment now shows correct value to use
some guns do less than one damage per shot (vulcan gun does 0.5), so
Instant Death Mode needs to max at 0x0.0001, the smallest nonzero value
in Pico-8's fixed-point numeric type.

One Hit Mode is just a comment for now, but I've been uncommenting it
to test it. Note that replacing the health meter with a "!" is triggered
by max HP + max shield <= 1 because 1 hp shows an empty bar.

this needs some more special cases for low-HP ships with active shields.
2024-08-18 01:57:53 -07:00
965fc0d688 major rebalances
10s generator is too slow -- 10 seconds ago is an eternity in a shmup
and a player who has stopped firing should recover much faster. The
generator's max capacity is much lower and shield cost has been
rebalanced to match.

The Protron is much more expensive to fire, it was previously just
easy mode.

Shields now recover faster _once they start recovering_ (every second
if energy is available) but getting hit causes a "penalty cooldown"
that is much longer than the standard recovery interval. This behavior
is taken from Halo and basically every modern FPS that came after it;
it's unlike Tyrian, which had consistent shield recovery behavior.
But I think Halo's rule plays much better.
2024-08-18 01:46:39 -07:00
cc3ed20f76 fix overshield 2024-08-18 01:29:27 -07:00
fa0cff1ffc one hit mode, fix vertmeter
full height meters overflowed p8num range
2024-08-18 01:28:12 -07:00
4f8b861cdb okay I special cased it 2024-08-18 01:14:05 -07:00
5dc259c094 the Secret Hit Point: you have 1hp when the meter is empty
this won't work if your maxhp is 1, will need to special case that
2024-08-18 01:13:25 -07:00
51629376f2 adjust HUD
Health and shields now share one bar. one hit point is (about) the same
size in each. There is an indicator splitting the two categories of HP.
2024-08-18 01:10:20 -07:00
c5e49740c4 reorganize UI 2024-08-17 23:22:42 -07:00
59738d0376 use constraints to make chasey chase; now it is not wiggly 2024-08-16 19:48:44 -07:00
f736f50870 port improvements to autobrake_test too, excluding the weird calc_targets thing 2024-08-16 19:37:43 -07:00
ccb897af24 fix calculation 2024-08-16 19:37:05 -07:00
c130f4cf52 actually invoke calc_targets 2024-08-16 19:25:10 -07:00
cf48497432 ymin and ymax for player 2024-08-16 19:23:20 -07:00
9dc36a95ee port ship constraints logic 2024-08-16 19:20:30 -07:00
d33d7ad6d1 xmax constraint -- imperfect but good enough 2024-07-27 18:49:48 -07:00
00678f97fd Fix brake location calculation; was applying it to the wrong spot. 2024-07-27 17:47:29 -07:00
8fa98e3132 handle undershot and ok cases 2024-01-28 01:33:25 -08:00
5d2dafa64c I am baffled I never noticed this before 2024-01-27 13:25:47 -08:00
bd61ca2639 remove spark stub, not worth it in the test 2024-01-20 11:52:41 -08:00
54426be303 now it actually works! 2024-01-14 20:06:09 -08:00
84a803b828 fixed it! mostly 2024-01-14 20:02:45 -08:00
a4ed50d9e2 okay now i fucked it up differently 2024-01-14 19:59:51 -08:00
a1df463a16 braking distance and visualization. bad calculation
the visualization is really helpful for showing me that I fucked up my math
2024-01-14 19:57:22 -08:00
d0a17488d0 pull out calc_velocity
also prepare constraints to change color based on whether it changed the requested thrust
2024-01-14 19:33:57 -08:00
aa25f87c46 Full debug diagnostics display for autobrake_test 2024-01-14 19:03:34 -08:00
edbde8e689 ship mover, no constraint yet 2024-01-14 18:21:10 -08:00
5b668cf9c9 empty template for autobrake test cart 2024-01-12 11:14:54 -08:00
34af172ca5 Add code-of-conduct 2023-12-30 12:50:18 -08:00
4804402f32 fix search-and-replace damage from a while back
a batch change from ship to self broke the thing that kept the player on
the screen. also spawn_goodie got missed in the migration to `_ENV` for
looking up stuff by name.
2023-12-24 21:39:52 -08:00
97ddfb876b fix weird line break 2023-12-22 00:40:01 -08:00
fbeb313078 ignore vscode config 2023-12-21 23:30:17 -08:00
a359bc5031 duplicating the file -- preparing for major changes.
Vacuum Gambit is about to stop being a Tyrian clone. The hybrid of
Mega Man Battle Network and Slay the Spire mechanics lends itself better
to Galaga than Tyrian. updatedshmup.p8 remains an excellent basis for a
Tyrian-like shmup, especially since it has a (demo of a) level loading
engine that reads strings, and maybe I'll even implement something along
those lines someday -- but I'm about to tear it all down to build it up
again, starting with the entire model for levels and progress, followed
shortly by the "energy" system and its interaction with shields.

(long-term plan: shields will auto-recover after every "flotilla", but
health will be more difficult to recover. Player shots will be limited
entirely by ammo and cooldown, replacing the "burst throughput" vs.
"sustain throughput" system created by the generator, although some
enemy firing patterns may recreate that behavior.)

(plan for the "level" system: create Galaga-style flotillas. I think
ship behaviors can reasonably be declared in the 8 bits available in
sprite flags, meaning I can program simple enemies entirely from the
sprite sheet and draw flotillas on the map.)
2023-12-21 13:09:33 -08:00
cdf517c51c fix missing self plumbing 2023-12-20 18:16:55 -08:00
9aac99ef30 replace grab_butts with act
marshaling through a table is a waste of time, the duplication betweeen
positive and negative thrust vectors is pointless, and pre-multiplying
thrust complicates "stay in a box" goals later on.
2023-12-20 18:14:39 -08:00
5fef5bad00 Squash level_parser into main: parse levels in CSV
commit b91ebeb775
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:33:00 2023 -0800

    fix boss

    it works now except for a square being drawn in the shield. good enough

commit ab687f8f6d
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:23:29 2023 -0800

    adjust spawning

    now it runs for a little tiny bit!

commit bef95df6a1
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:18:20 2023 -0800

    typo

commit 24435a3c15
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:18:01 2023 -0800

    move guns before ships

commit 0c3a36f1fd
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:15:34 2023 -0800

    defer zap_gun creation until it exists

commit a39c419e5f
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:13:51 2023 -0800

    fix mknew

commit 9ef762268f
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 13:04:13 2023 -0800

    many assorted syntax errors

commit e50f516b11
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 12:00:34 2023 -0800

    allow strings when spawning guns

commit f9e28fa0e2
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 11:59:20 2023 -0800

    fix missing paren

commit 38a054dec1
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 11:48:08 2023 -0800

    candidate conversion to csv for level format

commit fd391ff3bc
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 11:40:11 2023 -0800

    use _ENV to get rid of level_events and spawns

commit fbd9f97429
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Wed Dec 20 11:33:59 2023 -0800

    maybe fix the level parser

commit 2a61e8b5d6
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Sun Oct 15 21:09:12 2023 -0700

    partial conversion to CSV-based levels, does not run yet

commit 4ccbe1dc35
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Fri Oct 13 01:02:43 2023 -0700

    okay honestly this all can and should just be CSVs

commit b536d2c987
Author: Kistaro Windrider <kistaro@gmail.com>
Date:   Sun Oct 8 00:41:24 2023 -0700

    base for representing a level as a string
2023-12-20 14:09:07 -08:00
2bbc4e598c clean up macOS garbage 2023-12-19 16:26:11 -08:00
62fe5f51d3 val helper function 2023-10-07 23:57:27 -07:00
fd68ef88ec document The Parser, take an emit function. 2023-10-07 23:53:40 -07:00
fd9866e963 The Parser 2023-10-03 22:41:52 -07:00
a5ce0fd020 update todo list 2023-10-01 14:39:34 -07:00
dae108c231 move spark_particle's mknew call to the right spot 2023-10-01 00:46:32 -07:00
2e46d87a84 refactor spark logic. this loses tokens; may need to revisit 2023-09-30 20:10:42 -07:00
a4590821be pre-initialize palettes for blip. costs tokens, saves time 2023-09-30 19:45:07 -07:00
cf1e1153a3 lframe increment is now 0x0.0001
avoids time rollover! not doing this for `distance` because I don't intend any level script to exceed five minutes, but freeze time could be much longer
2023-09-30 19:30:21 -07:00
f3ac1f492c Dithering-style fade
Higher performance, allows free use of other palettes, frees up compressed space.
2023-09-30 19:15:22 -07:00
fb95085bd9 declare bullet_base before bullets 2023-09-30 15:02:34 -07:00
6f9517cee1 complete conversion to new 2023-09-30 15:01:39 -07:00
8d5f697961 fix incomplete conversions 2023-09-30 14:27:43 -07:00
bad8452f3c migrate ships to mknew style 2023-09-30 14:24:31 -07:00
f49407baca move ship typedefs before ship impls 2023-09-30 14:11:36 -07:00
e8ed97be9e might as well offer a shortcut for that too 2023-09-30 14:07:54 -07:00
f4bcd11bed preserve extra args to puketh
useful for puking to the clipboard instead of the console
2023-09-30 14:07:23 -07:00
4ae0d05b47 actually fix indentation 2023-09-30 14:03:06 -07:00
a4bf3f616a fix indentation when puking up a linked list 2023-09-30 14:00:53 -07:00
8fb54ede26 fix list handling, add puketh 2023-09-30 13:59:07 -07:00
cb65a188a8 lua is not go 2023-09-30 13:50:39 -07:00
7c29c329b7 handle backreferences and linked lists in puke 2023-09-30 13:50:07 -07:00
f67c2da37f remove draw_debug since it doesn't work; add "puke" debug helper
linked lists don't have a measurable length. will use a persistent intangible for debug dumps in the future. `puke` however can be used at the CLI to dump a table. I need to write a `listpuke` variant too
2023-09-30 13:32:52 -07:00
da8a5b9589 update readme section 2023-09-30 13:06:34 -07:00
a58421bd19 once_next_frame helper
token and time inefficient for now, but might help reduce bugs later? dunno
2023-09-30 13:03:38 -07:00
e0b8386849 new events is now always valid to append to
also "vore" now resets the eaten list
2023-09-30 12:55:33 -07:00
2b02d2b94b fix blast projectile for rearranged checks 2023-09-30 12:52:58 -07:00
c90b56b603 fix obvious bugs
not working: ship bounds, "blast" weapon
2023-09-30 02:48:18 -07:00
2e8bba2a0e oops, tilde only means "not" in "not equals" 2023-09-30 02:18:19 -07:00
803062ef43 one-line if doesn't need end 2023-09-30 02:17:19 -07:00
b61fe936e3 lua ain't go 2023-09-30 02:16:24 -07:00
63c97d1bee fix handling pships as an arraylist 2023-09-30 02:15:05 -07:00
814149ceec methodize ship stuff, convert remaining add calls 2023-09-30 02:12:41 -07:00
3b8e86d0e7 drawgame via strip, remove bury_the_dead 2023-09-29 09:55:43 -07:00
1ba869b644 start replacing arrays with intrusive slists
`add` costs ten cycles. `push_back` isn't actually any better, but bury_the_dead can get pretty bad, especially for large arrays (like the bullets collections). also replacing the kill loop structure with the `strip` call removes a massive amount of code repetition that's costing me a lot of tokens. I think the final result is _probably_ actually slower because of function call overhead per iteration except when there are collisions on many frames; hopefully the headroom bought by the bucket collider is enough because I'm definitely going to need the tokens.
2023-09-29 01:10:16 -07:00
bd67006e3c todo -- rectfill 2023-09-23 09:56:55 -07:00
81961ebd6d label sections of The Plan 2023-09-13 00:24:48 -07:00
770420eeef add dividers to make it more legible 2023-09-13 00:22:57 -07:00
362f1f06a6 basic roadmap 2023-09-13 00:21:53 -07:00
2f8703c487 another golf thing 2023-09-13 00:00:09 -07:00
7b0c2e0133 more golfing 2023-09-12 23:52:14 -07:00
5591068f1d split trick on powcols, save more tokens 2023-09-12 23:44:33 -07:00
43e6160dbc csvify some large tables
Steal the csv and usplit routines from Extreme Tetrom. Not yet using usplit.
2023-09-12 23:35:27 -07:00
44bc904ec2 the actual updated version 2023-09-12 22:49:01 -07:00
1785343de0 Starting state: my generic shmup engine from a while back 2023-09-12 22:40:48 -07:00
11 changed files with 7739 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.DS_Store
.vscode/settings.json

244
autobrake_test.p8 Normal file
View File

@ -0,0 +1,244 @@
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- vacuum gambit automatic brake test
-- by kistaro windrider
function usplit(str)
return unpack(split(str))
end
function csv(s)
local ret = split(s, "\n")
for i, v in ipairs(ret) do
ret[i] = type(v) == "string" and split(v) or { v }
end
return ret
end
-- generate standard "overlay"
-- constructor for type tt.
-- if more is defined, generated
-- new calls more(ret) after
-- ret is definitely not nil
-- before calling setmetatable.
-- use to initialize mutables.
--
-- if there was a previous new,
-- it is invoked on the new
-- object *after* more, because
-- this works better with the
-- `more` impls i use.
function mknew(tt, more)
local mt, oldnew = { __index = tt }, tt.new
tt.new = function(ret)
if (not ret) ret = {}
if (more) more(ret)
if (oldnew) oldnew(ret)
setmetatable(ret, mt)
return ret
end
end
function _init()
pal(1,129,1)
the_ship = ship.new()
constraints:setup()
slomo = 1
sloc = 0
reroll()
end
function reroll()
frames=0
sloc=0
the_ship:reroll()
end
function _update60()
if (btnp(4)) reroll()
if (btnp(5)) constraints:cycle()
if (btnp(3)) slomo <<= 1
if (btnp(2)) slomo >>= 1
slomo = (slomo < 1) and 1 or (slomo > 8192) and 8192 or slomo
sloc += 1
if sloc >= slomo then
frames += 1
the_ship:update()
sloc=0
end
end
function _draw()
cls(1)
constraints:draw()
the_ship:draw()
print("frames: " .. frames, 4, 64, 7)
print("speed: 1/" .. slomo, 8, 70, 7)
print("thrust: ".. actual_t, 4, 80, 7)
meter(80, 80, 128, 84, actual_t/the_ship.thrust/2)
print("dx: ".. the_ship.dx, 20, 86, 7)
meter(80, 86, 128, 90, the_ship.dx/the_ship.maxspd/2)
print("x: "..the_ship.x, 24, 92, 7)
print("bx: "..gbx, 20, 98, 7)
print("xmin:"..tostr(constraints.xmin), 12, 108, 7)
print("xmax:"..tostr(constraints.xmax), 12, 114, 7)
end
function meter(x0, y0, x1, y1, frac)
local c = 11
if frac < 0 then
frac = -frac
c = 8
end
local range = x1-x0
local midpoint = x0 + (range/2)
rectfill(x0, y0-1, x0, y1+1, 13)
rectfill(midpoint, y0-1, midpoint, y1 + 1, 13)
local width = range * frac
if (width ~= 0) rectfill(x0, y0, x0 + width, y1, c)
end
-->8
-- ship
ship = {
maxspd=4,
thrust=0.25,
drag=0.0625,
y=32,
}
mknew(ship)
function ship:reroll()
self.x=rnd(128)
self.dx=rnd(2*self.maxspd)-self.maxspd
end
function ship:draw()
if self.x < -7 then
spr(2, 0, self.y-7)
spr(2, 0, self.y+8)
elseif self.x > 127 then
spr(2, 120, self.y-7, 1, 1, true)
spr(2, 120, self.y+8, 1, 1, true)
else
spr(1,self.x,self.y)
end
--if (self.dx == 0) return
local bd, f = brake_dist(self.dx, self.thrust + self.drag)
gbx = self.x+bd
spr(3, gbx-2,self.y-2)
print(tostr(f), gbx-2, self.y - 8, 14)
end
function calc_velocity(v0, t, vmax, drag)
v0 = mid(v0 + t, vmax, -vmax)
return v0 - mid(drag, -drag, v0)
end
function ship:update()
local t = btn(0) and -1 or btn(1) and 1 or 0
t *= self.thrust
t = constraints:constrain(self, t)
-- t = constraints:constrain(self, t)
-- t = constraints:constrain(self, t)
local s = calc_velocity(self.dx, t, self.maxspd, self.drag)
self.x += s
self.dx = s
actual_t = t
end
-->8
-- constraints
constraints = {
ymin=20,
ymax=52,
color=10
}
function constraints:constrain(s, want)
self.color=10
if (not self.xmin) return want
-- bmx: brake max
local v1, bmx = calc_velocity(s.dx, want, s.maxspd, s.drag), s.thrust + s.drag
local bd, bf = brake_dist(v1, bmx)
local bx, txm = s.x + bd + v1, self.xmax
if bx < self.xmin then
-- predicted brake point left
-- of xmin; apply max reverse
-- thrust, treat xmin as our
-- max target, and handle
-- overbraking by coalescing
-- with past +xmax case
self.color = 9
want = s.thrust
txm = self.xmin
v1 = calc_velocity(s.dx, want, s.maxspd, s.drag)
bd, bf = brake_dist(v1, bmx)
bx = bd + s.x + v1
end
if (bx <= txm) return want
self.color = 8
local overage = bx - txm
want -= overage/max(bf,1)
if (want < -s.thrust) want = -s.thrust
return want
end
function brake_dist(v0, brake_max)
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 constraints:cycle()
if self.ctype=="bounds" then
self.ctype="point"
elseif self.ctype=="point" then
self.ctype="off"
else
self.ctype="bounds"
end
self:setup()
end
function constraints:setup()
if self.ctype=="point" then
self.xmin = 64
self.xmax = 64
elseif self.ctype=="bounds" then
self.xmin = 32
self.xmax = 96
else
self.xmin = nil
self.xmax = nil
end
end
function constraints:draw()
if (not self.xmin) return
rect(self.xmin, self.ymin, self.xmax, self.ymax, self.color)
end
-->8
-- fx
-- todo: spark ring buffer
__gfx__
000000008000000000080000a000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000006666000080000009090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000067777600800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000675555758008888009090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000006750007508000000a000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000067777500080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000005555000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

1
code-of-conduct.md Normal file
View File

@ -0,0 +1 @@
Trans rights.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

945
genericshmup.p8 Normal file
View File

@ -0,0 +1,945 @@
pico-8 cartridge // http://www.pico-8.com
version 34
__lua__
-- generic shmup
-- by kistaro windrider
game = 1
win = 2
lose = 3
debug = {}
function _init()
-- create the pwr gradient
-- 15 is the gradient color
poke(0x5f5f,0x3f)
poke(0x5f68,10)
poke(0x5f69,10)
poke(0x5f6a,9)
poke(0x5f6b,4)
memset(0x5f78,0xa8,3)
init_bullet_mt()
init_ship_mt()
wipe_level()
primary_ship.special_gun = new_gun_of(blast_gun_t)
load_level(example_level)
state = game
end
function wipe_level()
primary_ship = new_p1()
pships = {primary_ship}
eships = {}
pbullets = {}
ebullets = {}
intangibles = {}
events = {}
end
function _update60()
if (state == game) updategame()
end
function updategame()
local leveldone = level_frame()
local deaths = {}
for i, e in ipairs(events) do
if (e()) add(deaths, i)
end
bury_the_dead(events, 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 = {}
for ip, ps in ipairs(pships) do
for ie, es in ipairs(eships) do
if collides(hurtbox(ps), hurtbox(es)) then
if (es:hitship(ps)) add(edeaths, ie)
if ps:hitship(es) then
add(pdeaths, ip)
break
end
end
end
end
bury_the_dead(pships, pdeaths)
bury_the_dead(eships, edeaths)
pdeaths = {}
edeaths = {}
for ip, ps in ipairs(pships) do
for ie, eb in ipairs(ebullets) do
if collides(hurtbox(ps), hurtbox(eb)) then
if (eb:hitship(ps)) add(edeaths, ie)
if ps:hitbullet(eb) then
add(pdeaths, ip)
break
end
end
end
end
bury_the_dead(pships, pdeaths)
bury_the_dead(ebullets, edeaths)
pdeaths = {}
edeaths = {}
for ip, pb in ipairs(pbullets) do
for ie, es in ipairs(eships) do
if collides(hurtbox(pb), hurtbox(es)) then
if (es:hitbullet(pb)) add(edeaths, ie)
if pb:hitship(es) then
add(pdeaths, ip)
break
end
end
end
end
bury_the_dead(eships, edeaths)
bury_the_dead(pbullets, pdeaths)
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 == win) dropshadow("win",50,61,11)
if (state == lose) dropshadow("fail",48,61,8)
end
function draw_debug()
if (#debug==0) return
cursor(0,0,7)
foreach(debug,print)
debug={}
end
function drawgame()
cls()
clip(0,0,112,128)
for tbl in all{pbullets, pships, eships, intangibles, ebullets} 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("❎",116,3,primary_ship.main_gun)
draw_gun_info("🅾️",116,31,primary_ship.special_gun)
dropshadow("pwr",114,59,1)
inset(114,66,125,92)
vertmeter(115,67,124,91,primary_ship.power, primary_ship.max_power, 15)
dropshadow("h s",114,97,1)
inset(114,104,125,125)
line(119,105,119,124,7)
line(120,105,120,125,5)
vertmeter(115,105,118,124,primary_ship.hp, primary_ship.maxhp, 8)
vertmeter(121,105,124,124,primary_ship.shield, primary_ship.maxshield,12)
end
function draw_gun_info(lbl,x,y,gun)
dropshadow(lbl,x,y,1)
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,col)
if (val <= 0) return
local h = y1-y0
local pxm1 = flr(h*val/maxval)
rectfill(x0,y1-pxm1,x1,y1,col)
end
function inset(x0,y0,x1,y1)
rectfill(x0,y0,x1,y1,0)
rect(x0,y0,x1,y1,7)
line(x1,y0,x0,y0,5)
line(x0,y1,5)
end
function dropshadow(str, x, y, col)
print(str, x+1, y+1, 5)
print(str, x, y, col)
end
function grab_p1_butts()
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)
end
player = {
--shape
sprite = 1, --index of ship sprite
size = 2, --all ships are square; how many 8x8 sprites?
hurt = { -- hurtbox - where this ship can be hit
x_off = 6, -- upper left corner
y_off = 6, -- relative to ship ulc
width = 4,
height = 4
},
-- 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 = 8, -- 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 = grab_p1_butts, -- button fetch algorithm
}
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 = 1, -- upper left corner
y_off = 1, -- relative to ship ulc
width = 6,
height = 6
},
-- 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,
ow = function(self)
if self.hp <= 1 then
self.sprite = 11
else
self.sprite = 10
end
end
}
blocky_t = {
__index = blocky
}
-->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
}
ship_t = {
__index = ship_m,
}
function ship_m.die(_)
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)
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 = mid(0, 112 - 8 * ship.size, ship.x + ship.xmomentum)
if not ship.slip then
ship.y = mid(0, 128 - 8 * ship.size, ship.y + ship.ymomentum)
end
--friction
local xfric = 0
local yfric = 0
if (ship.xmomentum > 0) xfric = min(ship.xmomentum, ship.drag)
if (ship.xmomentum < 0) xfric = max(ship.xmomentum, -ship.drag)
if (ship.ymomentum > 0) yfric = min(ship.ymomentum, ship.drag)
if (ship.ymomentum < 0) yfric = max(ship.ymomentum, -ship.drag)
ship.xmomentum -= xfric
ship.ymomentum -= yfric
-- "scrolling" behavior
if ship.slip then
ship.y += scrollrate
if ship.y >= 128 then
ship:die()
return true
end
end
return false
end -- moveship(ship)
function ship_m.draw(ship)
spr(ship.sprite, ship.x, ship.y, ship.size, ship.size)
end
function hurtbox(ship)
return {
x=ship.x + ship.hurt.x_off,
y=ship.y + ship.hurt.y_off,
width=ship.hurt.width,
height=ship.hurt.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()
return false
end
dmg -= self.shield
self.shield = 0
self.hp -= dmg
if self.hp <= 0 then
self:die()
return true
end
self:ow()
return false
end
function ship_m:ow()
-- todo: implement damage blink
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)
local dx = box2.x - box1.x
local cx = dx >= 0 and dx <= (box1.width or 0)
dx = box1.x - box2.x
cx = cx or (dx >= 0 and dx <= (box2.width or 0))
if (not cx) return false
local dy = box2.y - box1.y
local cy = dy >= 0 and dy <= (box1.height or 0)
dy = box1.y - box2.y
cy = cy or (dy >= 0 and dy <= (box2.height or 0))
return cy
end
-->8
-- 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 = {}
for frame, cb in pairs(lvltbl) do
current_level[frame] = cb
end
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)
end
function spawn_blocking_rnd_x(mt)
freeze += 1
s = {
x = rnd(104),
y = -7,
die = function(self)
freeze -= 1
mt.__index.die(self)
end
}
setmetatable(s, mt)
add(eships, s)
end
function spawn_frownie()
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
example_level = {
[1]=spawn_frownie,
[60]=spawn_blocky,
[61]=spawn_blocky,
[250]=spawn_blocking_blocky,
[310]=function()
spawn_blocking_blocky()
spawn_blocking_blocky()
spawn_blocking_blocky()
end,
[311]=spawn_frownie,
[401]=spawn_frownie,
[420]=spawn_blocking_frownie,
[450]=spawn_frownie,
[451]=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)
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 = 30, -- 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
}
function new_gun_of(mt, is_enemy)
g = {
enemy = e
}
setmetatable(g, mt)
return g
end
-->8
-- bullet and gun behaviors
bullet_base = { }
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.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
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
__gfx__
000000000000000aa00000000088880090000009e00000000000000e00dddd003b00000082000000066666600555555000333300002222000000000000000000
000000000000000aa00000000822228099000009ee0000000000000e0d1111d037000000a2000000655555565555555503bbbb30028888200000000000000000
00700700000000aaaa00000082a22a280990009000ee0000000000eed171171db7000000a800000065755756557557553bbaabb3288aa8820000000000000000
00077000000000aaaa0000008222222800990900000ee00000000ee0d111111db7000000a800000065555556555555553ba77ab328a77a820000000000000000
0007700000000aaaaaa00000822aa228000990000000ee000000ee00d117711db7000000a800000065555556555775553ba77ab328a77a820000000000000000
0070070000000aaaaaa0000082a22a2800999900000000eee00ee000d171171db7000000a800000065777756557557553bbaabb3288aa8820000000000000000
000000000000aaaaaaaa0000082222800990099000000000eeee00000d1111d037000000a2000000655555565557755503bbbb30028888200000000000000000
000000000000aa8888aa0000008888009900009900000000eeee000000dddd003b00000082000000066666600555555000333300002222000000000000000000
00000000000aaa8888aaa00000000000b000000b0000000ee00ee000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000aaa8888aaa0000000000000bbbb00000000ee000eeee0000000000000000000000000000000000000000000000000000000000000000000000000
0000000000aaaa8888aaaa00000000000b0000b000000ee0000eeee0000000000000000000000000000000000000000000000000000000000000000000000000
0000000000aaaaaaaaaaaa00000000000b0bb0b00000ee000000eee0000000000000000000000000000000000000000000000000000000000000000000000000
000000000aaaaaaaaaaaaaa0000000000b0bb0b0000ee00000000eee000000000000000000000000000000000000000000000000000000000000000000000000
000000000aaaaaaaaaaaaaa0000000000b0000b000ee000000000eee000000000000000000000000000000000000000000000000000000000000000000000000
00000000aaaaaaaaaaaaaaaa0000000000bbbb00ee000000000000ee000000000000000000000000000000000000000000000000000000000000000000000000
00000000aaaaaaaaaaaaaaaa00000000b000000be0000000000000ee000000000000000000000000000000000000000000000000000000000000000000000000
__label__
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777777777777777
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115151166665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666111611156665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115161156665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111556665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650b000000b0765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765000bbbb000765
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0000b00765
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0bb0b00765
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0bb0b00765
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076500b0000b00765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765000bbbb000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650b000000b0765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777777777765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aeaeaeaeae765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765eaeaeaeaea765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aeaeaeaeae765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777777777765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115551166665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115161156665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666115651156665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666611111556665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666665555566665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650002222000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650028888200765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650288aa8820765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765028a77a820765
0000000000000000000000000000000000006666660000000000000000000000000000000000000005555550006666660000000000000000765028a77a820765
00000000000000000000000000000000000655555560000000000000000000000000000000000000555555550655555560000000000000007650288aa8820765
00000000000000000000000000000000000657557560000000000000000000000000000000000000557557550657557560000000000000007650028888200765
00000000000000000000000000000000000655555560000000000000000000000000000000000000555555550655555560000000000000007650002222000765
00000000000000000000000000000000000655555560000000000000000000000000000000000000555775550655555560000000000000007650000000000765
00000000000000000000000000000000000657777560000000000000000000000000000000000000557557550657777560000000000000007657777777777765
00000000000000000000000000000000000655555560000000000000000000000000000000000000555775550655555560000000000000007666666666666665
00000000000000000000000000000000000066666600000000000000000000000000000000000000055555500066666600000000000000007655555555555565
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aaaaaaaaaa765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aaaaaaaaaa765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765aaaaaaaaaa765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777777777765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007611161616111665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615151515151565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007611151515116565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615551115151665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007615661115151565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007665666555656565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007655555555555565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007650000000000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007659999999999765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007654444444444765
000000000000000000000000000000000000000000000000aa000000000000000000000000000000000000000000000000000000000000007654444444444765
000000000000000000000000000000000000000000000000aa000000000000000000000000000000000000000000000000000000000000007654444444444765
00000000000000000000000000000000000000000000000aaaa00000000000000000000000000000000000000000000000000000000000007654444444444765
00000000000000000000000000000000000000000000000aaaa00000000000000000000000000000000000000000000000000000000000007657777777777765
0000000000000000000000000000000000000000000000aaaaaa0000000000000000000000000000000000000000000000000000000000007666666666666665
0000000000000000000000000000000000000000000000aaaaaa0000000000000000000000000000000000000000000000000000000000007666666666666665
000000000000000000000000000000000000000000000aaaaaaaa000000000000000000000000000000000000000000000000000000000007666666666666665
000000000000000000000000000000000000000000000aa8888aa000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000aaa8888aaa00000000000000000000000000000000000000000000000000000000007616166666611665
00000000000000000000000000000000000000000000aaa8888aaa00000000000000000000000000000000000000000000000000000000007615156666165565
0000000000000000000000000000000000000000000aaaa8888aaaa0000000000000000000000000000000000000000000000000000000007611156666111665
0000000000000000000000000000000000000000000aaaaaaaaaaaa0000000000000000000000000000000000000000000000000000000007615156666651565
000000000000000000000000000000000000000000aaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000007615156666116565
000000000000000000000000000000000000000000aaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000007665656666655665
00000000000000000000000000000000000000000aaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000aaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000007655555555555565
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007658888750000765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000765888875cccc765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007657777757777765
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007666666666666665
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007555555555555555

BIN
genericshmup.p8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

2162
last_tyrianlike.p8 Normal file

File diff suppressed because it is too large Load Diff

61
the_parser.p8 Normal file
View File

@ -0,0 +1,61 @@
pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- the parser
parser = {}
mknew(parser)
-- calls parse_into with a nop
-- emit function.
function parser:parse(str)
self:parse_into(str, function() end)
end
-- read a file of commands and
-- execute them, emitting the
-- results from each call into
-- `emit` as a table per row.
--
-- a "command" is a method on
-- self. a row alternates
-- commands with args. when
-- calling a command, it also
-- gets a table of previous
-- results as the first arg.
-- args are split on ','.
function parser:parse_into(str, emit)
for row in all(split(str, "\n")) do
local prev = {}
local sectors = split(row, ":")
for i=1,#sectors,2 do
local x = self[sectors[i]](self, prev, usplit(sectors[i+1]))
if (x) add(prev, x)
end
emit(prev)
end
end
-- saves prev[sel] as self.name.
-- if sel is unspecified, saves
-- all of prev (as a table).
function parser:saveas(prev, name, sel)
self[name] = sel and prev[sel] or prev
end
-- returns its args, ignoring
-- prev. Used to stuff things
-- into prev. args are packed
-- if there's multiple.
function parser:val(_, ...)
local ret := pack(...)
if (#ret == 1) return ret[1]
return ret
end
function parser:bind(_, fn, ...)
local f = self[fn]
return function()
f(...)
end
end

45
todo.md Normal file
View File

@ -0,0 +1,45 @@
## 1. refine existing engine (knowing what I know now)
- [x] rewrite event queue as a linked list
- [x] rewrite animator stacks as linked lists
- [x] rewrite ship/bullet collections as linked lists
- [ ] update/draw mode switching system (high-efficiency version)
- [ ] render ship shields (even for large ships)
- [ ] duplicate file -- about to split away from Tyrian features
## 2. convert from Tyrian clone to MMBN clone
- [ ] remove PWR meter, replace with weapon queue
- [ ] remove power mechanics from _player_ ship (only!)
- [ ] all player weapons are now ammo limited except pea shooter
- [ ] remove weapon drops
- [ ] implement fallback pea shooter
- [ ] implement turn timer (screen-height bar)
- [ ] replace per-frame CLR with rectfill (saves time)
- [ ] implement extremely crude prototype for weapon select intermezzo
- [ ] implement "deck"
- [ ] implement basic weapon cards
- [ ] implement starter deck
- [ ] stabilize this with just starter deck and sample level
## 3. add deckbuilder mechanics
- [ ] implement post-encounter "get a card"
- [ ] implement deck lister
- [ ] implement more cards, mini-encounters for deck buildup
- [ ] implement card levels?
- [ ] implement card removal (shop?)
## 4. make it a real game
- [ ] actually design a branching encounter sequence
- [ ] map
- [ ] "minimap"/scanner on weapon picker
- [ ] high score board (difficulty x time)
- [ ] difficulty increase system
- [ ] as much more crap as I can fit in under the token limit, which
is probably not much at this point
## 5. this is just futile isn't it
- [ ] give up and move to TIC-80 because the token limits

2110
updatedshmup.p8 Normal file

File diff suppressed because it is too large Load Diff

2169
vacuum_gambit.p8 Normal file

File diff suppressed because it is too large Load Diff