156 lines
5.2 KiB
C
156 lines
5.2 KiB
C
|
|
/**
|
||
|
|
* @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 <stdbool.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
|
||
|
|
#include <akerror.h>
|
||
|
|
|
||
|
|
#include <akgl/actor.h>
|
||
|
|
|
||
|
|
/* ------------------------------------------------------------- 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_
|