mmbshmup/old_readme.md

357 lines
8.5 KiB
Markdown

This file contains text that used to be in the cartridge itself, but
I'm getting increasingly anxious about cartridge space so I'm moving
it out ot a separate file.
---
main loop sequence
==================
1. level_frame
2. events
3. merge new_events into events
4. update bg intangibles
5. move ships (player first)
6. move bullets (player first)
7. calculate collisions
1. pship on eship
2. ebullet on pship
3. pbullet on eship
8. update fg intangibles
9. check for end of level
draw order
----------
bottom to top:
1. intangibles_bg
2. player bullets
3. player ships
4. enemy ships
5. enemy bullets
6. intangibles_fg
notes
-----
intangibles_fg move()s after
all collisions and other moves
are processed. if an intangible
is added to the list as a result
of a collision or move, it will
itself be move()d before it is
drawn.
data-driven items
=================
guns and bullets both allow the
most common behaviors to be
expressed with data alone.
ships only need a movement
algorithm expressed.
guns
----
* 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.
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
* sprite - sprite index.
* x_off, y_off - renamed for
the next two vars. may revert
* center_off_x - the horizontal
centerpoint of the bullet,
for positioning when firing.
assume a pixel's coordinates
refer to the upper left corner
of the pixel; the center of
a 2-width bullet with an
upper left corner at 0 is 1,
not 0.5.
* top_off_y, bottom_off_y -
also for positioning when
firing. positive distance from
top or bottom edge to image.
top_off_y will usually be 0,
bottom_off_y will not be when
bullets are smaller than
the sprite box.
* width, height - measured in
full sprites (8x8 boxes), not
pixels. used for drawing.
bullets despawn when above or
below the screen (player or
enemy bullets, respectively).
by default, bullets despawn
when they hit something.
override hitship to change this.
ships
____
ships move by calculating
momentum, then offsetting their
position by that momentum, then
clamping their position to the
screen (horizontally only for
ships that autoscroll). ships
that autoscroll (slip==true)
then slide down by scrollspeed.
fractional coordinates are ok.
after movement, ships lose
momentum (ship.drag along each
axis). abs(momentum) can't
exceed ship.maxspeed.
ships gain momentum by acting
like a player pushing buttons.
the player ship actually reads
buttons for this.
act -- returns new acceleration:
dx, dy, shoot_spec, shoot_main.
dx and dy are change in momentum
in px/frame. this is controls
only -- friction is handled in
ship:move (`drag` value).
ships hitting another ship take
1 damage per frame of overlap.
ships hitting a bullet check
bullet.damage to find out how
much damage they take. damage
is applied to shields, then hp.
damaged ships flash briefly -
blue (12) if all damage was
shielded, white (7) if hp was
damaged. a ship that then has 0
or less hp calls self:die() and
tells the main game loop to
remove it.
shieldcooldown is the interval
between restoring shield points.
shieldpenalty is the delay
before restoring points after
any damage, reset to this value
on every damaging hit (whether
it is absorbed by the shield or
not) -- shield behaves like
halo and other shooters in its
heritage, where it recovers if
you avoid damage for a while.
not that there is any safe cover
in this kind of game.
ships do not repair hp on their
own. negative-damage bullets
are treated as 0, but a bullet
can choose to repair the ship
it hits in its own hitship
method, or otherwise edit it
(changing weapons, refilling
weapon ammo). powerups are
therefore a kind of bullet.
levels
======
a level is a table mapping
effective frame number to
functions. when a level starts,
it sets lframe ("level frame")
and distance to 0.
every frame, level_frame
increments lframe by 0x0.0001.
then if the level is not frozen,
it increments distance by 1.0
and runs the function in the
level table for exactly that
frame number (if any). distance
is therefore "nonfrozen frames",
and is used to trigger level
progress. lframe always
increments. ships are encouraged
to use lframe to control
animation and movement, and may
use distance to react to level
progress separately from overall
time. remember to multiply
lframe-related stuff by 0x0001.
a special sentinel value, eol,
marks the end of the level.
(the level engine doesn't know
when it's out of events, so
without eol, the level will
simply have no events forever.)
when it finds eol, level_frame
throws away the current level
and tells the main loop that it
might be done. the main loop
agrees the level is over and the
player has won when the level
has reached eol and there are
no more enemy ships, enemy
bullets, or background events
remaining. player ships, player
bullets, and intangibles are
not counted.
level freezing
--------------
the level is frozen when the
global value freeze > 0.
generally, something intending
to block level progress (a
miniboss, a minigame, etc.)
increments freeze and prepares
some means of decrementing it
when it no longer wants to block
level progress.
most commonly, we want to block
until some specific ship or
group of ships has died. for
these ships, override ship:die
to decrement freeze. make sure
to set ship.dead in any new
ship:die method so anything else
looking at it can recognize
the ship as dead.
for anything else, you probably
want an event to figure out when
to unfreeze.
levels start at 1
-----------------
distance is initialized to 0
but gets incremented before the
first time the engine looks for
events. therefore, the first
frame of the level executes
level[1]. since levelframe
executes before anything else,
level[1] sets up the first frame
drawn in the level. the player
does not see a blank world
before level[1] runs.
level[1] can therefore be used
to reconfigure the player ship,
set up backgrounds, start music,
kick off some kind of fade-in
animation, etc.
events
======
the global list "events" stores
0-argument functions which are
called every frame. if they
return true, they are removed
from the list and not run again;
if they return false, they stay
and will be called in later
frames. the level does not end
while the events table is
nonempty.
events are most commonly used
to set up something for later
(for example, blip uses an event
to remove the fx_pallete from
the flashing ship when the blip
expires), but can also be used
to implement a "level within a
level" that does something
complicated until it's done. if
you froze the level when
creating the event, remember
to thaw it (freeze -= 1) on all
paths that return true.
to do complex stuff in events,
use a closure or a metatable
that specifies __call.
to avoid editing the events
list while it is being iterated,
events that create new events
must add those events to
new_events rather than events.
new_events is only valid during
the "event execution" stage, so
events created at any other time
must go directly on events
without using new_events.
intangibles
===========
the intangibles_fg and
intangibles_bg lists contain
items with :move and :draw.
like ships and bullets, they
move during _update60 and
draw during _draw. they are
not checked for collisions.
intangibles_bg moves/draws
before anything else moves or
draws. intangibles_fg
moves/draws last. this controls
whether your intangible object
draws in front of or behind
other stuff. you probably want
intangibles_bg for decorative
elements and intangibles_fg
for explosions, score popups,
etc.
there's no scrolling background
engine but intangibles_bg could
be used to create one, including
using the map (otherwise unused
in this engine) for the purpose.
intangibles do not prevent the
level from ending. like bullets
and ships, if :move returns
true, they are dropped.