summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2025-05-03 16:21:45 +0200
committerRichard Braun <rbraun@sceen.net>2025-05-03 16:24:02 +0200
commita8b1103f5d548641e65b097d93cd53f027ffeeb0 (patch)
tree5631fe696062923d03c24267b3a2718367920a3f
parent6b5a78705a5a85458222fcf1d860d1a19de99ac0 (diff)
tmp
-rw-r--r--Makefile41
-rw-r--r--src/ei/Makefile5
-rw-r--r--src/ei/ei.c (renamed from src/ei.c)5
-rw-r--r--src/ei/ei.h (renamed from src/ei.h)2
-rw-r--r--src/ei/main.c (renamed from src/main.c)3
-rw-r--r--src/et/Makefile5
-rw-r--r--src/et/et.c320
-rw-r--r--src/et/et.h61
-rw-r--r--src/et/main.c99
9 files changed, 523 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index b681393..1cd5024 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,43 @@
MAKEFLAGS += --no-builtin-rules
MAKEFLAGS += --no-builtin-variables
-CC = gcc
+all:
+.PHONY: all
+
+clean:
+.PHONY: clean
-BINARY = embedded_invaders
+CC = gcc
CFLAGS = -std=gnu11
+CFLAGS += -Isrc
CFLAGS += -O0 -g
CFLAGS += -m32
CFLAGS += -Wall -Wextra -Werror=implicit
CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wshadow
-SOURCES = \
- src/main.c \
- src/eetg.c \
- src/ei.c
+%.o: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+SOURCES = src/eetg.c
+.PHONY: $(SOURCES)
-OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES)))
+define gen_target
+.PHONY: $(SOURCES-$(1))
-$(BINARY): $(OBJECTS)
- $(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ $(LIBS)
+OBJECTS-$(1) = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SOURCES) $(SOURCES-$(1))))
-%.o: %.c
- $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+$(1): $$(OBJECTS-$(1))
+ $(CC) -o $$@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $$^ $(LIBS)
-clean:
- rm -f $(BINARY) $(OBJECTS)
+all: $(1)
+
+clean-$(1):
+ rm -f $(1) $$(OBJECTS-$(1))
+.PHONY: clean-$(1)
+
+clean: clean-$(1)
+endef
-.PHONY: clean $(SOURCES)
+include src/ei/Makefile
+include src/et/Makefile
diff --git a/src/ei/Makefile b/src/ei/Makefile
new file mode 100644
index 0000000..641bab6
--- /dev/null
+++ b/src/ei/Makefile
@@ -0,0 +1,5 @@
+SOURCES-ei = \
+ src/ei/main.c \
+ src/ei/ei.c
+
+$(eval $(call gen_target,ei))
diff --git a/src/ei.c b/src/ei/ei.c
index 466b6bf..6cf7dab 100644
--- a/src/ei.c
+++ b/src/ei/ei.c
@@ -23,9 +23,10 @@
#include <stdlib.h>
#include <string.h>
-#include "eetg.h"
+#include <eetg.h>
+#include <macros.h>
+
#include "ei.h"
-#include "macros.h"
#define EI_NR_LIVES 3
diff --git a/src/ei.h b/src/ei/ei.h
index 1d4faf0..e2ee731 100644
--- a/src/ei.h
+++ b/src/ei/ei.h
@@ -22,7 +22,7 @@
#include <stdbool.h>
#include <stdint.h>
-#include "eetg.h"
+#include <eetg.h>
#define EI_FPS 50
diff --git a/src/main.c b/src/ei/main.c
index 0507aae..9216971 100644
--- a/src/main.c
+++ b/src/ei/main.c
@@ -24,7 +24,8 @@
#include <time.h>
#include <unistd.h>
-#include "eetg.h"
+#include <eetg.h>
+
#include "ei.h"
static struct termios orig_ios;
diff --git a/src/et/Makefile b/src/et/Makefile
new file mode 100644
index 0000000..81f05d8
--- /dev/null
+++ b/src/et/Makefile
@@ -0,0 +1,5 @@
+SOURCES-et = \
+ src/et/main.c \
+ src/et/et.c
+
+$(eval $(call gen_target,et))
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;
+}
diff --git a/src/et/et.h b/src/et/et.h
new file mode 100644
index 0000000..86bf989
--- /dev/null
+++ b/src/et/et.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024 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 invaders.
+ */
+
+#ifndef ET_H
+#define ET_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <eetg.h>
+
+#define ET_FPS 50
+
+#define ET_STATE_INTRO 0
+#define ET_STATE_PREPARED 1
+#define ET_STATE_PLAYING 2
+#define ET_STATE_GAME_OVER 3
+
+struct et_bunker {
+ struct eetg_object object;
+ char sprite[33];
+};
+
+struct et_alien {
+ struct eetg_object object;
+};
+
+struct et_game {
+ struct eetg_world world;
+ struct eetg_object title;
+ struct eetg_object help;
+ struct eetg_object start;
+ struct eetg_object status;
+ struct eetg_object end_title;
+ unsigned int score;
+ int8_t state;
+ int8_t level;
+ int8_t sync_counter_reload;
+ int8_t sync_counter;
+ char status_sprite[32];
+};
+
+void et_game_init(struct et_game *game, eetg_write_fn write_fn, void *arg);
+bool et_game_process(struct et_game *game, int8_t c);
+
+#endif /* ET_H */
diff --git a/src/et/main.c b/src/et/main.c
new file mode 100644
index 0000000..afdeada
--- /dev/null
+++ b/src/et/main.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2024-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.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <eetg.h>
+
+#include "et.h"
+
+static struct termios orig_ios;
+
+static struct et_game game;
+
+static void
+restore_termios(void)
+{
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_ios);
+ write(STDOUT_FILENO, "\ec", 2);
+}
+
+static void
+setup_io(void)
+{
+ struct termios ios;
+
+ setvbuf(stdin, NULL, _IONBF, 0);
+
+ fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
+
+ tcgetattr(STDIN_FILENO, &orig_ios);
+ atexit(restore_termios);
+ ios = orig_ios;
+ ios.c_lflag &= ~(ICANON | ECHO);
+ ios.c_cc[VMIN] = 1;
+ ios.c_cc[VTIME] = 0;
+ tcsetattr(STDIN_FILENO, TCSANOW, &ios);
+}
+
+static void
+write_terminal(const void *buffer, size_t size, void *arg)
+{
+ (void)arg;
+
+ write(STDOUT_FILENO, buffer, size);
+}
+
+int
+main(void)
+{
+ bool leave;
+
+ setup_io();
+
+ eetg_init_rand(time(NULL));
+
+ et_game_init(&game, write_terminal, NULL);
+
+ do {
+ ssize_t nr_bytes;
+ int8_t c;
+
+ usleep(1000000 / ET_FPS);
+
+ nr_bytes = read(STDIN_FILENO, &c, 1);
+
+ if (nr_bytes == -1) {
+ if (errno == EAGAIN) {
+ c = -1;
+ } else {
+ break;
+ }
+ }
+
+ leave = et_game_process(&game, c);
+ } while (!leave);
+
+ return EXIT_SUCCESS;
+}