252 lines
10 KiB
C
252 lines
10 KiB
C
// Space Taxi (JoeyLib port) -- shared types and decls.
|
|
//
|
|
// Architecture (all four JoeyLib targets):
|
|
//
|
|
// tilemap 40x25 cells, each cell = (tileIndex, paletteSlot).
|
|
// jlTilePaste blits the tile bank into the stage one cell
|
|
// at a time at level boot, then it's static until the
|
|
// scene changes. No per-frame redraw of the tilemap.
|
|
//
|
|
// taxi Single jlSpriteT with multiple cels (thrust frames,
|
|
// facing variants). Save-under + restore-under each frame
|
|
// so we don't repaint the tilemap behind it.
|
|
//
|
|
// passenger Up to 2 simultaneous sprites: one waiting on a pad,
|
|
// one already in the cab (or none). Same save/restore
|
|
// discipline as the taxi.
|
|
//
|
|
// audio One ~3-voice event stream rendered per platform:
|
|
// SB+PSG-synth (DOS), PT 4-voice (Amiga), YM2149 (ST),
|
|
// Ensoniq DOC channels (IIgs). The dispatch lives in
|
|
// stAudio.c with one entry point per voice command.
|
|
//
|
|
// input Joystick port 2 conventionally on the C64; we map
|
|
// thrust to fire and direction to all four cardinals.
|
|
// Keyboard fallback: arrows + space for hosts without
|
|
// joysticks.
|
|
//
|
|
// Level data lives in a small custom .dat per level (see
|
|
// assets/levels/format.md). Tile bitmaps and sprite cels are PNG
|
|
// authored externally and baked to native .tbk / .spr blobs at
|
|
// build time via tools/assetbake/assetbake.py.
|
|
|
|
#ifndef SPACETAXI_H
|
|
#define SPACETAXI_H
|
|
|
|
#include <joey/joey.h>
|
|
|
|
#define ST_TILEMAP_W 40u
|
|
#define ST_TILEMAP_H 25u
|
|
#define ST_TILE_PIXELS 8u
|
|
|
|
// Display field is 320x200 (JoeyLib's SURFACE_WIDTH x SURFACE_HEIGHT).
|
|
// 40 tiles x 8 px = 320, 25 tiles x 8 px = 200. The bottom 3 rows are
|
|
// the HUD band (score / lives / level / current-fare strip). The top
|
|
// 22 rows are the playfield where the taxi moves.
|
|
// Full C64 screen height. The original title uses all 25 rows
|
|
// (frame at 0, 12, 24 + content). For gameplay the bottom 3 rows
|
|
// are HUD territory -- HUD draws over the tilemap there.
|
|
#define ST_PLAYFIELD_ROWS 25u
|
|
#define ST_HUD_ROW 22u
|
|
#define ST_HUD_ROW_COUNT 3u
|
|
|
|
// Maximum stuff. Tuned for fitting the smallest target (IIgs):
|
|
// 10 pads is the highest count in any canonical Space Taxi level
|
|
// (D and M each have 10 pads; everything else <= 9)
|
|
// 2 active passenger sprites covers waiting + carrying
|
|
#define ST_MAX_PADS 10u
|
|
#define ST_MAX_PASSENGERS 2u
|
|
#define ST_MAX_FARES 16u
|
|
|
|
// Fixed-point taxi physics: position and velocity are int16_t in
|
|
// units of 1/16 px, so the taxi can drift fractionally and the
|
|
// thrust/gravity terms are integers without losing precision over
|
|
// the whole field. (22 rows x 8 px x 16 = 2816 < 32767 so int16
|
|
// is fine.)
|
|
// Match the C64 fixed-point scale: 8-bit sub-pixel (256 sub-units per
|
|
// pixel) so the C64's accel=14 and gravity=1 are usable directly
|
|
// (14/256 px/frame initial accel; constant 1/256 px/frame gravity).
|
|
// Position needs int32_t since a 320-wide playfield * 256 sub-units
|
|
// overflows int16_t.
|
|
#define ST_SUBPIXEL_SHIFT 8
|
|
#define ST_SUBPIXEL (1 << ST_SUBPIXEL_SHIFT)
|
|
|
|
// Taxi thrust cel cycling. The sprite asset
|
|
// (assets/genPlaceholderArt.py) lays out 4 taxi cels:
|
|
// cel 0 = idle (no thrust)
|
|
// cel 1..3 = thrust-flame frames (cycled while input is held)
|
|
// thrustFrame on StTaxiT counts 0..(ST_THRUST_CEL_COUNT *
|
|
// ST_THRUST_CEL_TICKS - 1) while thrusting. Renderer divides by
|
|
// ST_THRUST_CEL_TICKS to pick cel 1, 2, or 3.
|
|
#define ST_THRUST_CEL_COUNT 3u
|
|
#define ST_THRUST_CEL_TICKS 2u
|
|
|
|
|
|
typedef enum {
|
|
ST_STATE_TITLE = 0,
|
|
ST_STATE_PLAYING,
|
|
ST_STATE_LEVEL_DONE,
|
|
ST_STATE_GAME_OVER
|
|
} StGameStateE;
|
|
|
|
|
|
typedef enum {
|
|
ST_DIR_RIGHT = 0,
|
|
ST_DIR_LEFT = 1
|
|
} StFacingE;
|
|
|
|
|
|
typedef struct {
|
|
uint8_t letter; // 'A'..'H' identifier (which pad number)
|
|
uint8_t tileX; // landing surface left edge (tile coord)
|
|
uint8_t tileY; // landing surface row (tile coord)
|
|
uint8_t tileW; // pad width in tiles
|
|
} StPadT;
|
|
|
|
|
|
typedef struct {
|
|
uint8_t spawnPad; // pad index where they appear
|
|
uint8_t destPad; // pad index they want to go to
|
|
} StFareT;
|
|
|
|
|
|
typedef struct {
|
|
char name[24]; // level display name ("UP & DOWN", etc.)
|
|
uint8_t tileBankId; // which tile asset (0 = default bank)
|
|
uint8_t musicId; // UNUSED in C64 Space Taxi -- the
|
|
// game has no per-level background
|
|
// music; gameplay is silent except
|
|
// for SFX. Title plays song 8, score
|
|
// screen plays song 6 or 7 (see
|
|
// MECHANICS.md "Sound (SID)"). Kept
|
|
// here as scaffolding for a possible
|
|
// future "level-entry jingle" event.
|
|
uint8_t bgColor; // background palette slot
|
|
uint8_t borderColor; // border palette slot (for HUD if used)
|
|
uint8_t taxiSpawnTileX;
|
|
uint8_t taxiSpawnTileY;
|
|
// Per-level physics templates. Mirror the C64 templates at
|
|
// $7D8F/$7D91 (Y/X accel) and $7D93/$7D95 (Y/X gravity). Accels
|
|
// are unsigned magnitudes; gravities are int8 so a level can pull
|
|
// upward (e.g. canonical level K = -7). Hand-authored levels can
|
|
// leave them zero; loader substitutes per-level defaults below.
|
|
uint8_t xAccel;
|
|
uint8_t yAccel;
|
|
int8_t xGrav;
|
|
int8_t yGrav;
|
|
// VIC color block from C64 $7D00-$7D08, mapped to $D020-$D028 by
|
|
// $62F0 (scene-load). borderColor/bgColor above are $7D00/$7D01.
|
|
// bgColor1/2/3 and spriteMc0/1 only matter in VIC multicolor mode
|
|
// which the JoeyLib port doesn't reproduce -- they're stored so
|
|
// the .dat format stays a faithful capture of $7D00-$7D08 but the
|
|
// runtime ignores them. sprite0Color/sprite1Color drive the cab
|
|
// and flame placeholder colors when no sprite asset is authored.
|
|
uint8_t bgColor1; // $D022 (multicolor only, unused)
|
|
uint8_t bgColor2; // $D023 (multicolor only, unused)
|
|
uint8_t bgColor3; // $D024 (multicolor only, unused)
|
|
uint8_t spriteMc0; // $D025 sprite multicolor 0 (unused)
|
|
uint8_t spriteMc1; // $D026 sprite multicolor 1 (unused)
|
|
uint8_t sprite0Color; // $D027 sprite 0 (taxi)
|
|
uint8_t sprite1Color; // $D028 sprite 1 (flame)
|
|
uint8_t padCount;
|
|
StPadT pads[ST_MAX_PADS];
|
|
uint8_t fareCount;
|
|
StFareT fares[ST_MAX_FARES];
|
|
// Tilemap: tile-index per cell (row-major).
|
|
uint8_t tilemap[ST_TILEMAP_W * ST_PLAYFIELD_ROWS];
|
|
// Palette slot per cell -- which surface palette index a cell uses.
|
|
uint8_t colormap[ST_TILEMAP_W * ST_PLAYFIELD_ROWS];
|
|
} StLevelT;
|
|
|
|
|
|
typedef struct {
|
|
// 16-bit-fixed-point position (8 bits sub-pixel + 8 bits pixel),
|
|
// but stored in int32_t so a 320-wide playfield fits without
|
|
// wrap. `x >> ST_SUBPIXEL_SHIFT` is the pixel column.
|
|
int32_t x;
|
|
int32_t y;
|
|
int16_t vx; // velocity accumulator (sub-pixel/frame)
|
|
int16_t vy;
|
|
StFacingE facing;
|
|
uint8_t thrustFrame; // 0..1 parity bit driving cab-cel flicker
|
|
bool thrusting; // any directional input held this frame
|
|
int8_t thrustDx; // -1 left, 0 neutral, +1 right
|
|
int8_t thrustDy; // -1 up, 0 neutral, +1 down
|
|
bool landed; // sitting on a pad
|
|
uint8_t onPad; // pad index if landed (0xFF if none)
|
|
// Death-animation countdown. >0 means crashed -- engine keeps the
|
|
// cab integrating under gravity (no explosion visual; the C64
|
|
// just lets the cab fall, see VERIFIED.md "Stage 1 $665F death
|
|
// branch"). Reaches 0 -> respawn (or game-over). Mirrors the
|
|
// C64's $6B24/$6B4C phase-2/3 timing.
|
|
uint8_t crashTicks;
|
|
} StTaxiT;
|
|
|
|
|
|
typedef struct {
|
|
bool active;
|
|
bool onboard; // in the cab (true) or waiting at pad (false)
|
|
uint8_t currentPad; // where they are if waiting
|
|
uint8_t destPad;
|
|
int16_t x; // pixel position (for waiting/walking)
|
|
int16_t y;
|
|
uint8_t walkPhase; // animation cel (0..3)
|
|
uint8_t walkDir; // 0 = walking right, 1 = walking left
|
|
} StPassengerT;
|
|
|
|
|
|
typedef struct {
|
|
StGameStateE state;
|
|
StLevelT level;
|
|
StTaxiT taxi;
|
|
StPassengerT passengers[ST_MAX_PASSENGERS];
|
|
uint32_t score;
|
|
uint8_t lives;
|
|
uint8_t levelIndex;
|
|
// Player-selectable fare target (1..4). Mirrors C64 $7213, set on
|
|
// the title screen via LEFT/RIGHT joystick (see $5310-$5328 in
|
|
// MECHANICS.md). Used to cap the per-level fare count: the engine
|
|
// delivers min(level.fareCount, game.fareTarget) before advancing.
|
|
uint8_t fareTarget;
|
|
} StGameT;
|
|
|
|
|
|
// Public entry points (one per source file).
|
|
bool stLevelLoad(StLevelT *out, const char *path);
|
|
|
|
void stRenderInit(jlSurfaceT *stage);
|
|
void stRenderShutdown(void);
|
|
// Blit the level's static tile art into the stage. Called once per
|
|
// scene change; falls back to colored solid tiles per index-range
|
|
// when no tile bank asset is loaded.
|
|
void stRenderLevel(jlSurfaceT *stage, const StLevelT *level);
|
|
void stRenderFrame(jlSurfaceT *stage, const StGameT *game);
|
|
// Tell the renderer the current game.level contents changed. Required
|
|
// after stLevelLoad even when the StLevelT pointer is unchanged --
|
|
// stRenderLevel's dirty-cache compares pointers, not contents.
|
|
void stRenderLevelChanged(void);
|
|
|
|
// Draw an ASCII string into the stage at tile coords (bx, by) using
|
|
// the loaded font asset. No-op if the font asset failed to load.
|
|
void stRenderDrawText(jlSurfaceT *stage, uint8_t bx, uint8_t by, const char *s);
|
|
|
|
void stEngineReset(StGameT *game);
|
|
void stEngineTick(StGameT *game);
|
|
|
|
void stPassengerReset(StGameT *game);
|
|
void stPassengerTick(StGameT *game);
|
|
|
|
void stAudioInit(void);
|
|
void stAudioShutdown(void);
|
|
void stAudioFrameTick(void); // call once per host frame; counts down SFX
|
|
void stAudioPlayMusic(uint8_t musicId);
|
|
void stAudioStopMusic(void);
|
|
void stAudioSfxThrust(bool on);
|
|
void stAudioSfxLand(void);
|
|
void stAudioSfxPickup(void);
|
|
void stAudioSfxDropoff(void);
|
|
void stAudioSfxCrash(void);
|
|
|
|
void stHudDraw(jlSurfaceT *stage, const StGameT *game);
|
|
|
|
#endif
|