summaryrefslogtreecommitdiff
path: root/src/et/et.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/et/et.c')
-rw-r--r--src/et/et.c320
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;
+}