diff options
Diffstat (limited to 'src/et/et.c')
-rw-r--r-- | src/et/et.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/et/et.c b/src/et/et.c new file mode 100644 index 0000000..3393c0a --- /dev/null +++ b/src/et/et.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2025 Richard Braun. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * Embedded Tetris. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <eetg.h> +#include <macros.h> + +#include "et.h" + +#define ET_SCORE_MISSILE 40 +#define ET_SCORE_ALIENS0 30 +#define ET_SCORE_ALIENS12 20 +#define ET_SCORE_ALIENS34 10 +#define ET_SCORE_UFO_BASE 100 + +#define ET_TITLE_SPRITE \ + +#define ET_HELP_SPRITE \ +" s = left \n" \ +" f = right \n" \ +" space = shoot \n" + +#define ET_START_SPRITE \ +"Press SPACE to start\n" \ +"Press X to leave \n" + +#define ET_PLAYER_SPRITE "/-^-\\\n" + +#define ET_BUNKER_SPRITE \ +" ### \n" \ +" ##### \n" \ +"#######\n" \ +"## ##\n" + +#define ET_ALIENS0_SPRITE_1 ",^,\n" +#define ET_ALIENS0_SPRITE_2 ".-.\n" +#define ET_ALIENS12_SPRITE_1 "-O_\n" +#define ET_ALIENS12_SPRITE_2 "_O-\n" +#define ET_ALIENS34_SPRITE_1 "/^\\\n" +#define ET_ALIENS34_SPRITE_2 "-^-\n" + +#define ET_STATUS_SPRITE_FORMAT "SCORE: %08d Lives: %d\n" + +#define ET_END_TITLE_SPRITE \ +" ________ __ _______ ____ _ _________ \n" \ +" / ___/ _ | / |/ / __/ / __ \\ | / / __/ _ \\\n" \ +"/ (_ / __ |/ /|_/ / _/ / /_/ / |/ / _// , _/\n" \ +"\\___/_/ |_/_/ /_/___/ \\____/|___/___/_/|_| \n" + +#define ET_TYPE_TITLE 0 +#define ET_TYPE_HELP 1 +#define ET_TYPE_START 2 +#define ET_TYPE_PLAYER 3 +#define ET_TYPE_PLAYER_MISSILE 4 +#define ET_TYPE_BUNKER 5 +#define ET_TYPE_ALIEN 6 +#define ET_TYPE_ALIEN_MISSILE 7 +#define ET_TYPE_UFO 8 +#define ET_TYPE_STATUS 9 +#define ET_TYPE_END_TITLE 10 + +static void +et_game_update_status(struct et_game *game) +{ + assert(game); + + snprintf(game->status_sprite, sizeof(game->status_sprite), + ET_STATUS_SPRITE_FORMAT, game->score, 0); +} + +static void +et_game_prepare(struct et_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + game->state = ET_STATE_PREPARED; +} + +static void +et_game_start(struct et_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + eetg_world_add(&game->world, &game->status, 26, 0); + + game->state = ET_STATE_PLAYING; +} + +static void +et_game_terminate(struct et_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + eetg_world_add(&game->world, &game->end_title, 12, 10); + eetg_world_add(&game->world, &game->status, 26, 6); + eetg_world_add(&game->world, &game->start, 30, 20); + + game->state = ET_STATE_GAME_OVER; +} + +static void +et_game_handle_collision(struct eetg_object *object1, + struct eetg_object *object2, + int x, int y, void *arg) +{ +#if 0 + if (et_has_type(object1, object2, ET_TYPE_PLAYER_MISSILE)) { + struct eetg_object *missile, *other; + + missile = et_get_object(object1, object2, ET_TYPE_PLAYER_MISSILE); + other = (object1 == missile) ? object2 : object1; + + et_game_handle_player_missile_collision(arg, missile, other, x, y); + } else if (et_has_type(object1, object2, ET_TYPE_ALIEN)) { + struct eetg_object *alien, *other; + + alien = et_get_object(object1, object2, ET_TYPE_ALIEN); + other = (object1 == alien) ? object2 : object1; + + et_game_handle_alien_collision(arg, other, x, y); + } else if (et_has_type(object1, object2, ET_TYPE_ALIEN_MISSILE)) { + struct eetg_object *missile, *other; + + missile = et_get_object(object1, object2, ET_TYPE_ALIEN_MISSILE); + other = (object1 == missile) ? object2 : object1; + + et_game_handle_alien_missile_collision(arg, missile, other, x, y); + } +#endif +} + +static void +et_game_reset_history(struct et_game *game) +{ + assert(game); + + game->score = 0; + + et_game_update_status(game); +} + +static bool +et_game_process_intro_input(struct et_game *game, char c) +{ + assert(game); + + if (c == 'x') { + return true; + } + + if (c != ' ') { + return false; + } + + et_game_reset_history(game); + et_game_prepare(game); + + return false; +} + +static bool +et_game_process_game_input(struct et_game *game, char c) +{ + assert(game); + + if (c == 'x') { + return true; + } + +#if 0 + if (c == 's') { + struct eetg_object *player_object = &game->player; + int x; + + x = eetg_object_get_x(player_object); + + if (x > 0) { + eetg_object_move(player_object, x - 1, + eetg_object_get_y(player_object)); + } + } else if (c == 'f') { + struct eetg_object *player_object = &game->player; + int x, width; + + x = eetg_object_get_x(player_object); + width = eetg_object_get_width(player_object); + + if ((x + width) < EETG_COLUMNS) { + eetg_object_move(player_object, x + 1, + eetg_object_get_y(player_object)); + } + } else if (c == ' ') { + struct eetg_object *player_missile = &game->player_missile; + + if (eetg_object_get_world(player_missile) == NULL) { + struct eetg_object *player_object = &game->player; + int x, y; + + x = eetg_object_get_x(player_object); + y = eetg_object_get_y(player_object); + + eetg_world_add(&game->world, player_missile, x + 2, y - 1); + + game->player_missile_counter = game->player_missile_counter_reload; + } + } +#endif + + return false; +} + +void +et_game_init(struct et_game *game, eetg_write_fn write_fn, void *arg) +{ + assert(game); + + game->sync_counter_reload = ET_FPS * 2; + game->sync_counter = 1; + + game->state = ET_STATE_INTRO; + + et_game_reset_history(game); + + eetg_world_init(&game->world, write_fn, arg); + eetg_world_register_collision_fn(&game->world, + et_game_handle_collision, + game); + + eetg_object_init(&game->title, ET_TYPE_TITLE, ET_TITLE_SPRITE); + eetg_object_set_color(&game->title, EETG_COLOR_BLUE); + + eetg_object_init(&game->help, ET_TYPE_HELP, ET_HELP_SPRITE); + eetg_object_set_color(&game->help, EETG_COLOR_RED); + + eetg_object_init(&game->start, ET_TYPE_START, ET_START_SPRITE); + eetg_object_set_color(&game->start, EETG_COLOR_RED); + + eetg_object_init(&game->status, ET_TYPE_STATUS, game->status_sprite); + eetg_object_set_color(&game->status, EETG_COLOR_RED); + + eetg_object_init(&game->end_title, ET_TYPE_END_TITLE, ET_END_TITLE_SPRITE); + eetg_object_set_color(&game->end_title, EETG_COLOR_WHITE); + + eetg_world_add(&game->world, &game->title, 8, 1); + eetg_world_add(&game->world, &game->help, 30, 16); + eetg_world_add(&game->world, &game->start, 30, 20); +} + +bool +et_game_process(struct et_game *game, int8_t c) +{ + bool sync = false; + bool leave = false; + + game->sync_counter--; + + if (game->sync_counter == 0) { + sync = true; + game->sync_counter = game->sync_counter_reload; + } + + eetg_world_render(&game->world, sync); + + switch (game->state) { + case ET_STATE_INTRO: + case ET_STATE_GAME_OVER: + if (c >= 0) { + leave = et_game_process_intro_input(game, (char)c); + } + + break; + case ET_STATE_PREPARED: +#if 0 + et_game_start(game); +#endif + break; + case ET_STATE_PLAYING: +#if 0 + et_game_process_player_missile(game); + et_game_process_aliens(game); + et_game_process_ufo(game); + et_game_process_alien_missile(game); +#endif + + if (c >= 0) { + leave = et_game_process_game_input(game, (char)c); + } + + break; + } + + return leave; +} |