/** * @file galaga.h * @brief Shared declarations for the GALAGA embedding example. * * The engine is C on libakgl; the enemies think in BASIC. Everything the two * sides share crosses in exactly one place: the three structures below, which * script.c registers as host types so a script reads and writes them directly. * docs/20-tutorial-galaga.md and docs/21-tutorial-galaga-enemies.md build this * program from an empty file; the split between files follows the split * between chapters. */ #ifndef _GALAGA_H_ #define _GALAGA_H_ #include #include #include #include /* ------------------------------------------------------------- geometry --- */ /* * The view is sized to the artwork rather than the other way round: the Kenney * sprites are ~100 pixels wide, libakgl has no way to draw a sprite smaller * than it is (akgl_Actor.scale is overwritten every frame -- libakgl * docs/12-actors.md), and a ten-column formation of them needs 1120 pixels. */ #define GALAGA_VIEW_WIDTH 1280 #define GALAGA_VIEW_HEIGHT 960 #define GALAGA_FORM_COLUMNS 10 /* formation width, in slots */ #define GALAGA_FORM_LEFT 136 /* x of column 0, map pixels */ #define GALAGA_FORM_TOP 120 /* y of row 0, map pixels */ #define GALAGA_COL_PITCH 112 #define GALAGA_ROW_PITCH 100 #define GALAGA_PLAYER_Y 860.0f #define GALAGA_PLAYER_SPEED 420.0f /* map pixels per second */ #define GALAGA_PLAYER_MARGIN 60.0f /* how close to the edge it may go */ /* ------------------------------------------------------------- entities --- */ #define GALAGA_ENEMY_BEE 0 #define GALAGA_ENEMY_BUTTERFLY 1 #define GALAGA_ENEMY_BOSS 2 #define GALAGA_ENEMY_KINDS 3 #define GALAGA_MAX_ENEMIES 40 #define GALAGA_MAX_PLAYER_SHOTS 2 /* the classic two-on-screen rule */ #define GALAGA_MAX_ENEMY_SHOTS 8 /* * galaga_Enemy.state bits. The script owns these transitions; the engine only * writes the word at spawn and when a script error forces an enemy dumb. * galaga.bas spells the same three values as literals, with a REM naming them. * * 8 0 * 0 0 0 0 0 1 1 1 * | | `-- ENTERING: flying its entry path toward the formation slot * | `---- FORMATION: holding (and breathing around) homex/homey * `------ DIVING: attacking, off the grid until it leaves the screen */ #define GALAGA_ES_ENTERING (1 << 0) #define GALAGA_ES_FORMATION (1 << 1) #define GALAGA_ES_DIVING (1 << 2) /** @brief One enemy, as both sides see it. Hangs off akgl_Actor.actorData. */ typedef struct galaga_Enemy { int32_t kind; /* GALAGA_ENEMY_BEE / BUTTERFLY / BOSS */ int32_t state; /* GALAGA_ES_* bit flags */ float homex; /* formation slot, in map pixels */ float homey; float t; /* parametric clock for the current maneuver */ int32_t hp; int32_t fire; /* outbox: script sets 1, engine consumes */ float rnd; /* inbox: engine writes fresh 0..1 each call */ } galaga_Enemy; /** @brief Frame state every enemy may read. Bound once as GAME@. */ typedef struct galaga_Shared { float playerx; /* the player actor's position, this frame */ float playery; int32_t wave; float rnd; /* fresh 0..1 each frame; the issue #16 route */ } galaga_Shared; /* --------------------------------------------------------------- screens --- */ typedef enum { GALAGA_SCREEN_TITLE = 0, GALAGA_SCREEN_PLAY, GALAGA_SCREEN_GAMEOVER, GALAGA_SCREEN_VICTORY } galaga_Screen; /* ------------------------------------------------------------ game state --- */ typedef struct galaga_Game { galaga_Screen screen; int frame; float dt; /* seconds, clamped; see main.c */ bool autoplay; int score; int lives; int kills[GALAGA_ENEMY_KINDS]; int shots[GALAGA_ENEMY_KINDS]; /* shots each kind fired */ int script_errors; akgl_Actor *player; float fire_cooldown; float respawn_timer; /* > 0 while the player is invulnerable */ bool firing; bool moveleft; bool moveright; int player_shots_live; int enemy_shots_live; } galaga_Game; extern galaga_Game galaga_game; extern galaga_Shared galaga_shared; extern galaga_Enemy galaga_enemies[GALAGA_MAX_ENEMIES]; extern akgl_Actor *galaga_enemy_actors[GALAGA_MAX_ENEMIES]; /* ---------------------------------------------------------------- script --- */ akerr_ErrorContext AKERR_NOIGNORE *galaga_script_boot(char *path); akerr_ErrorContext AKERR_NOIGNORE *galaga_script_update_enemy(galaga_Enemy *enemy, akgl_Actor *actor, float dt); /* --------------------------------------------------------------- enemies --- */ akerr_ErrorContext AKERR_NOIGNORE *galaga_wave_spawn(void); akerr_ErrorContext AKERR_NOIGNORE *galaga_wave_release(void); int galaga_enemies_alive(void); akerr_ErrorContext AKERR_NOIGNORE *galaga_boom_spawn(float x, float y); /* ---------------------------------------------------------------- player --- */ akerr_ErrorContext AKERR_NOIGNORE *galaga_player_spawn(void); akerr_ErrorContext AKERR_NOIGNORE *galaga_player_controls(void); akerr_ErrorContext AKERR_NOIGNORE *galaga_player_autoplay(int frame); /** @brief A 0..1 random draw from the engine's own PRNG (see enemies.c). */ float galaga_random(void); #endif // _GALAGA_H_