]> git.lizzy.rs Git - dungeon_game.git/commitdiff
Add plugins
authorElias Fleckenstein <eliasfleckenstein@web.de>
Wed, 9 Jun 2021 15:10:36 +0000 (17:10 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Wed, 9 Jun 2021 15:10:36 +0000 (17:10 +0200)
plugins/apple/Makefile [new file with mode: 0644]
plugins/apple/apple.c [new file with mode: 0644]
plugins/game/Makefile [new file with mode: 0644]
plugins/game/game.c [new file with mode: 0644]
plugins/game/game.h [new file with mode: 0644]
plugins/monster/Makefile [new file with mode: 0644]
plugins/monster/monster.c [new file with mode: 0644]

diff --git a/plugins/apple/Makefile b/plugins/apple/Makefile
new file mode 100644 (file)
index 0000000..8e3ccca
--- /dev/null
@@ -0,0 +1,2 @@
+apple.so: apple.c ../game/game.h
+       cc -g -shared -fpic -o apple.so apple.c
diff --git a/plugins/apple/apple.c b/plugins/apple/apple.c
new file mode 100644 (file)
index 0000000..7c2de77
--- /dev/null
@@ -0,0 +1,48 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include "dungeon.h"
+
+static struct entity apple;
+
+static void apple_step(struct entity *self, struct entity_step_data stepdata)
+{
+       if (stepdata.dx == 0 && stepdata.dy == 0) {
+               add_score(1);
+               add_health(&player, 1);
+               self->remove = true;
+       }
+}
+
+static void spawn_apple(int x, int y)
+{
+       spawn(apple, x, y);
+}
+
+__attribute__((constructor)) static void init()
+{
+       apple = (struct entity) {
+               .name = "apple",
+               .x = 0,
+               .y = 0,
+               .color = get_color("#FF2A53"),
+               .texture = "🍎",
+               .remove = false,
+               .meta = NULL,
+               .health = 1,
+               .max_health = 1,
+               .collide_with_entities = false,
+
+               .on_step = &apple_step,
+               .on_collide = NULL,
+               .on_collide_with_entity = NULL,
+               .on_spawn = NULL,
+               .on_remove = NULL,
+               .on_death = NULL,
+       };
+
+       register_air_function((struct generator_function) {
+               .chance = 25,
+               .callback = &spawn_apple,
+       });
+}
+
diff --git a/plugins/game/Makefile b/plugins/game/Makefile
new file mode 100644 (file)
index 0000000..83714da
--- /dev/null
@@ -0,0 +1,2 @@
+game.so: game.c game.h
+       cc -g -shared -fpic -o game.so game.c -lm -lpthread
diff --git a/plugins/game/game.c b/plugins/game/game.c
new file mode 100644 (file)
index 0000000..f0496b8
--- /dev/null
@@ -0,0 +1,556 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include <pthread.h>
+#include "dungeon.h"
+
+bool running = true;
+
+int score = 0;
+
+struct color black = {0, 0, 0};
+
+struct material wall;
+struct material air;
+struct material outside;
+
+struct node map[MAP_WIDTH][MAP_HEIGHT];
+
+struct entity player;
+struct list *entities = & (struct list) {
+       .element = &player,
+       .next = NULL,
+};
+
+struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT] = {{NULL}};
+
+struct list *air_functions = NULL;
+
+void quit()
+{
+       running = false;
+}
+
+struct color get_color(const char *str)
+{
+       unsigned int r, g, b;
+       sscanf(str, "#%2x%2x%2x", &r, &g, &b);
+       return (struct color) {r, g, b};
+}
+
+bool is_outside(int x, int y)
+{
+       return x >= MAP_WIDTH || x < 0 || y >= MAP_HEIGHT || y < 0;
+}
+
+struct node get_node(int x, int y)
+{
+       return is_outside(x, y) ? (struct node) {&outside} : map[x][y];
+}
+
+bool is_solid(int x, int y)
+{
+       return get_node(x, y).material->solid;
+}
+
+bool move(struct entity *entity, int xoff, int yoff)
+{
+       int x, y;
+
+       x = entity->x + xoff;
+       y = entity->y + yoff;
+
+       if (is_solid(x, y)) {
+               if (entity->on_collide)
+                       entity->on_collide(entity, x, y);
+               return false;
+       } else if (entity->collide_with_entities && entity_collision_map[x][y]) {
+               if (entity->on_collide_with_entity)
+                       entity->on_collide_with_entity(entity, entity_collision_map[x][y]);
+               return false;
+       } else {
+               entity_collision_map[entity->x][entity->y] = NULL;
+               entity->x = x;
+               entity->y = y;
+               entity_collision_map[entity->x][entity->y] = entity;
+               return true;
+       }
+}
+
+void spawn(struct entity def, int x, int y)
+{
+       if (is_outside(x, y))
+               return;
+
+       if (def.collide_with_entities && entity_collision_map[x][y])
+               return;
+
+       def.x = x;
+       def.y = y;
+
+       struct entity *entity = malloc(sizeof(struct entity));
+       *entity = def;
+
+       add_element(entities, entity);
+
+       if (entity->collide_with_entities)
+               entity_collision_map[x][y] = entity;
+
+       if (entity->on_spawn)
+               entity->on_spawn(entity);
+}
+
+void add_health(struct entity *entity, int health)
+{
+       entity->health += health;
+
+       if (entity->health > entity->max_health)
+               entity->health = entity->max_health;
+       else if (entity->health <= 0 && entity->on_death)
+               entity->on_death(entity);
+}
+
+void add_score(int s)
+{
+       score += s;
+}
+
+struct list *add_element(struct list *list, void *element)
+{
+       struct list **ptr;
+
+       for (ptr = &list; *ptr != NULL; ptr = &(*ptr)->next)
+               ;
+
+       *ptr = malloc(sizeof(struct list));
+       (*ptr)->element = element;
+       (*ptr)->next = NULL;
+
+       return list;
+}
+
+void register_air_function(struct generator_function func)
+{
+       struct generator_function *buf = malloc(sizeof(struct generator_function));
+       *buf = func;
+
+       air_functions = add_element(air_functions, buf);
+}
+
+/* Player */
+
+static void player_death(struct entity *self)
+{
+       (void) self;
+       quit();
+}
+
+/* Mapgen */
+
+static bool check_direction(int x, int y, int dir)
+{
+       if (dir % 2 == 0)
+               return is_solid(x, y + 1) && is_solid(x, y - 1) && (is_solid(x + 1, y) || rand() % 3 > 1) && (is_solid(x - 1, y) || rand() % 3 > 1);
+       else
+               return is_solid(x + 1, y) && is_solid(x - 1, y) && (is_solid(x, y + 1) || rand() % 3 > 1) && (is_solid(x, y - 1) || rand() % 3 > 1);
+}
+
+static void generate_corridor(int lx, int ly, int ldir, bool off)
+{
+       if (is_outside(lx, ly))
+               return;
+
+       /*
+       if (off && rand() % 100 == 0)
+               return;
+       */
+
+       map[lx][ly] = (struct node) {&air};
+
+       for (struct list *ptr = air_functions; ptr != NULL; ptr = ptr->next) {
+               struct generator_function *func = ptr->element;
+
+               if (! func->chance || rand() % func->chance == 0) {
+                       func->callback(lx, ly);
+               }
+       }
+
+       int x, y, dir;
+       int ret = (ldir + 2) % 4;
+       int limit = 50;
+
+       do {
+               x = lx;
+               y = ly;
+
+               if (rand() % 3 > 1)
+                       dir = ldir;
+               else
+                       dir = rand() % 4;
+
+               switch (dir) {
+                       case 0:
+                               x++;
+                               break;
+                       case 1:
+                               y++;
+                               break;
+                       case 2:
+                               x--;
+                               break;
+                       case 3:
+                               y--;
+                               break;
+               }
+
+       } while (dir == ret || (! check_direction(x, y, dir) && --limit));
+
+       if (limit)
+               generate_corridor(x, y, dir, off);
+
+       if (rand() % 20 == 0)
+               generate_corridor(lx, ly, ldir, true);
+}
+
+static void generate_corridor_random(int x, int y)
+{
+       int dir = rand() % 4;
+
+       generate_corridor(x, y, dir, false);
+       generate_corridor(x, y, (dir + 2) % 4, false);
+}
+
+/* Rendering */
+
+void set_color(struct color color, bool bg)
+{
+       printf("\e[%u;2;%u;%u;%um", bg ? 48 : 38, color.r, color.g, color.b);
+}
+
+struct color light_color(struct color color, double light)
+{
+       return (struct color) {
+               color.r * light,
+               color.g * light,
+               color.b * light,
+       };
+}
+
+static void render(render_entity_list entity_list)
+{
+       printf("\e[2J\e[0;0H");
+
+       struct winsize ws;
+       ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
+
+       int cols = ws.ws_col / 2 - LIGHT * 2;
+       int rows = ws.ws_row / 2 - LIGHT;
+
+       int cols_left = ws.ws_col - cols - (LIGHT * 2 + 1) * 2;
+       int rows_left = ws.ws_row - rows - (LIGHT * 2 + 1);
+
+       set_color(black, true);
+
+       for (int i = 0; i < rows; i++)
+               for (int i = 0; i < ws.ws_col; i++)
+                       printf(" ");
+
+       for (int y = -LIGHT; y <= LIGHT; y++) {
+               for (int i = 0; i < cols; i++)
+                       printf(" ");
+
+               for (int x = -LIGHT; x <= LIGHT; x++) {
+                       int map_x, map_y;
+
+                       map_x = x + player.x;
+                       map_y = y + player.y;
+
+                       struct node node = get_node(map_x, map_y);
+
+                       double dist = sqrt(x * x + y * y);
+                       double light = 1.0 - (double) dist / (double) LIGHT;
+
+                       if (light <= 0)
+                               goto empty;
+
+                       set_color(light_color(node.material->color, light), true);
+
+                       struct entity *entity = entity_list[x + LIGHT][y + LIGHT];
+
+                       if (entity) {
+                               set_color(entity->color, false);
+                               printf("%s", entity->texture);
+                       } else {
+                               empty:
+                               printf("  ");
+                       }
+               }
+
+               set_color(black, true);
+
+               for (int i = 0; i < cols_left; i++)
+                       printf(" ");
+       }
+
+       for (int i = 0; i < rows_left + 1; i++)
+               for (int i = 0; i < ws.ws_col; i++)
+                       printf(" ");
+
+       printf("\e[0;0H\e[39m");
+
+       printf("\e[32m\e[3mScore:\e[23m %d\e[39m", score);
+
+       printf("\e[0;0");
+
+       for (int i = 0; i < rows; i++)
+               printf("\n");
+
+       printf("\t\e[1mInventory\e[22m\n\n");
+       printf("\t0x\t\e[3mNothing\e[23m\n");
+
+       printf("\e[0;0H");
+
+       for (int i = 0; i < ws.ws_row - 2; i++)
+               printf("\n");
+
+       int hearts_cols = ws.ws_col / 2 - player.max_health;
+
+       for (int i = 0; i < hearts_cols; i++)
+               printf(" ");
+
+       set_color((struct color) {255, 0, 0}, false);
+
+       for (int i = 0; i < player.max_health; i++) {
+               if (i == player.health)
+                       set_color(get_color("#5A5A5A"), false);
+               printf("\u2665 ");
+       }
+
+       printf("\e[39m\n");
+}
+
+/* Input */
+
+static void handle_input(char c)
+{
+       switch (c) {
+               case 'q':
+                       quit();
+                       break;
+               case 'w':
+                       move(&player, 0, -1);
+                       break;
+               case 'a':
+                       move(&player, -1, 0);
+                       break;
+               case 's':
+                       move(&player, 0, 1);
+                       break;
+               case 'd':
+                       move(&player, 1, 0);
+                       break;
+       }
+}
+
+/* Multithreading */
+
+static void handle_interrupt(int signal)
+{
+       (void) signal;
+
+       running = false;
+}
+
+static void *input_thread(void *unused)
+{
+       (void) unused;
+
+       while (running)
+               handle_input(tolower(fgetc(stdin)));
+
+       return NULL;
+}
+
+/* Main Game */
+
+__attribute__ ((constructor)) static void init()
+{
+       wall = (struct material) {
+               .solid = true,
+               .color = get_color("#5B2F00"),
+       };
+
+       air = (struct material) {
+               .solid = false,
+               .color = get_color("#FFE027"),
+       };
+
+       outside = (struct material) {
+               .solid = true,
+               .color = black,
+       };
+
+       player = (struct entity) {
+               .name = "player",
+               .x = MAP_WIDTH / 2,
+               .y = MAP_HEIGHT / 2,
+               .color = get_color("#00FFFF"),
+               .texture = "🙂",
+               .remove = false,
+               .meta = NULL,
+               .health = 10,
+               .max_health = 10,
+               .collide_with_entities = true,
+
+               .on_step = NULL,
+               .on_collide = NULL,
+               .on_collide_with_entity = NULL,
+               .on_spawn = NULL,
+               .on_remove = NULL,
+               .on_death = &player_death,
+       };
+
+       entity_collision_map[player.x][player.y] = &player;
+
+       for (int x = 0; x < MAP_WIDTH; x++)
+               for (int y = 0; y < MAP_HEIGHT; y++)
+                       map[x][y] = (struct node) {&wall};
+}
+
+void game()
+{
+       srand(time(0));
+
+       struct sigaction sa;
+       sa.sa_handler = &handle_interrupt;
+       sigaction(SIGINT, &sa, NULL);
+
+       generate_corridor_random(player.x, player.y);
+
+       for (int i = 0; i < 50; i++)
+               generate_corridor_random(rand() % MAP_WIDTH, rand() % MAP_HEIGHT);
+
+       struct termios oldtio, newtio;
+       tcgetattr(STDIN_FILENO, &oldtio);
+       newtio = oldtio;
+       newtio.c_lflag &= ~(ICANON | ECHO);
+       tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
+
+       printf("\e[?1049h\e[?25l");
+
+       pthread_t input_thread_id;
+       pthread_create(&input_thread_id, NULL, &input_thread, NULL);
+
+       struct timespec ts, ts_old;
+       clock_gettime(CLOCK_REALTIME, &ts_old);
+
+       while (running) {
+               clock_gettime(CLOCK_REALTIME, &ts);
+               double dtime = (double) (ts.tv_sec - ts_old.tv_sec) + (double) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
+               ts_old = ts;
+
+               render_entity_list render_list = {{NULL}};
+
+               for (struct list **ptr = &entities; *ptr != NULL; ) {
+                       struct entity *entity = (*ptr)->element;
+
+                       if (entity->remove) {
+                               assert(entity != &player);
+                               struct list *next = (*ptr)->next;
+
+                               if (entity->on_remove)
+                                       entity->on_remove(entity);
+
+                               if (entity->meta)
+                                       free(entity->meta);
+
+                               free(entity);
+                               free(*ptr);
+
+                               *ptr = next;
+                               continue;
+                       }
+
+                       int dx, dy;
+
+                       dx = entity->x - player.x;
+                       dy = entity->y - player.y;
+
+                       bool visible = abs(dx) <= LIGHT && abs(dy) <= LIGHT;
+
+                       if (visible)
+                               render_list[dx + LIGHT][dy + LIGHT] = entity;
+
+                       if (entity->on_step)
+                               entity->on_step(entity, (struct entity_step_data) {
+                                       .dtime = dtime,
+                                       .visible = visible,
+                                       .dx = dx,
+                                       .dy = dy,
+                               });
+
+                       ptr = &(*ptr)->next;
+               }
+
+               render(render_list);
+
+               // there is no such thing as glfwSwapBuffers, so we just wait 1 / 60 seconds to prevent artifacts
+               usleep(1000000 / 60);
+       }
+
+       printf("\e[?1049l\e[?25h");
+       tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
+}
+
+/* Use later */
+
+/*
+get_box_char(is_solid(x, y - 1), is_solid(x, y + 1), is_solid(x - 1, y), is_solid(x + 1, y));
+
+const char *get_box_char(bool up, bool down, bool left, bool right)
+{
+       if (left && right && ! up && ! down)
+               return "\u2501\u2501";
+       else if (up && down && ! right && ! left)
+               return "\u2503 ";
+       else if (down && right && ! up && ! left)
+               return "\u250F\u2501";
+       else if (down && left && ! up && ! right)
+               return "\u2513 ";
+       else if (up && right && ! down && ! left)
+               return "\u2517\u2501";
+       else if (up && left && ! down && ! right)
+               return "\u251B ";
+       else if (up && down && right && ! left)
+               return "\u2523\u2501";
+       else if (up && down && left && ! right)
+               return "\u252B ";
+       else if (down && left && right && ! up)
+               return "\u2533\u2501";
+       else if (up && left && right && ! down)
+               return "\u253B\u2501";
+       else if (up && down && left && right)
+               return "\u254b\u2501";
+       else if (left && ! up && ! down && ! right)
+               return "\u2578 ";
+       else if (up && ! down && ! left && ! right)
+               return "\u2579 ";
+       else if (right && ! up && ! down && ! left)
+               return "\u257A\u2501";
+       else if (down && ! up && ! left && ! right)
+               return "\u257B ";
+       else if (! up && ! down && ! left && ! right)
+               return "\u25AA ";
+       else
+               return "??";
+}
+*/
diff --git a/plugins/game/game.h b/plugins/game/game.h
new file mode 100644 (file)
index 0000000..9c6953f
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef _GAME_H_
+#define _GAME_H_
+
+#include <stdbool.h>
+#define MAP_HEIGHT 1000
+#define MAP_WIDTH 1000
+#define LIGHT 10
+
+/* Type definitions */
+
+struct color
+{
+       unsigned char r, g, b;
+};
+
+struct material
+{
+       bool solid;
+       struct color color;
+};
+
+struct node
+{
+       struct material *material;
+};
+
+struct entity_step_data
+{
+       double dtime;
+       int dx;
+       int dy;
+       bool visible;
+};
+
+struct entity
+{
+       const char *name;
+       int x, y;
+       struct color color;
+       char texture[8];
+       bool remove;
+       void *meta;
+       int health;
+       int max_health;
+       bool collide_with_entities;
+
+       void (*on_step)(struct entity *self, struct entity_step_data stepdata);
+       void (*on_collide)(struct entity *self, int x, int y);
+       void (*on_collide_with_entity)(struct entity *self, struct entity *other);
+       void (*on_spawn)(struct entity *self);
+       void (*on_remove)(struct entity *self);
+       void (*on_death)(struct entity *self);
+};
+
+struct list
+{
+       void *element;
+       struct list *next;
+};
+
+typedef struct entity *render_entity_list[LIGHT * 2 + 1][LIGHT * 2 + 1];
+
+struct generator_function
+{
+       int chance;
+       void (*callback)(int x, int y);
+};
+
+extern int score;
+
+extern struct color black;
+
+extern struct material wall;
+extern struct material air;
+extern struct material outside;
+
+extern struct node map[MAP_WIDTH][MAP_HEIGHT];
+
+extern struct entity player;
+extern struct list *entities;
+
+extern struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT];
+
+extern struct list *air_functions;
+
+void quit();
+struct color get_color(const char *str);
+bool is_outside(int x, int y);
+struct node get_node(int x, int y);
+bool is_solid(int x, int y);
+bool move(struct entity *entity, int xoff, int yoff);
+void spawn(struct entity def, int x, int y);
+void add_health(struct entity *entity, int health);
+void add_score(int s);
+void set_color(struct color color, bool bg);
+struct color light_color(struct color color, double light);
+void register_air_function(struct generator_function func);
+struct list *add_element(struct list *list, void *element);
+
+#endif
diff --git a/plugins/monster/Makefile b/plugins/monster/Makefile
new file mode 100644 (file)
index 0000000..1ba16c5
--- /dev/null
@@ -0,0 +1,2 @@
+monster.so: monster.c ../game/game.h
+       cc -g -shared -fpic -o monster.so monster.c
diff --git a/plugins/monster/monster.c b/plugins/monster/monster.c
new file mode 100644 (file)
index 0000000..167de88
--- /dev/null
@@ -0,0 +1,72 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include "dungeon.h"
+
+static struct entity monster;
+
+struct monster_data
+{
+       double timer;
+};
+
+static void monster_spawn(struct entity *self)
+{
+       self->meta = malloc(sizeof(struct monster_data));
+       ((struct monster_data *) self->meta)->timer = 0.5;
+}
+
+static void monster_step(struct entity *self, struct entity_step_data stepdata)
+{
+       struct monster_data *data = self->meta;
+
+       if (stepdata.visible && (data->timer -= stepdata.dtime) <= 0.0) {
+               data->timer = 0.5;
+
+               (stepdata.dx && move(self, stepdata.dx > 0 ? -1 : 1, 0)) || (stepdata.dy && move(self, 0, stepdata.dy > 0 ? -1 : 1));
+       }
+}
+
+static void monster_on_collide_with_entity(struct entity *self, struct entity *other)
+{
+       if (other == &player)
+               add_health(other, -1);
+}
+
+static void monster_death(struct entity *self)
+{
+       add_score(5);
+       self->remove = true;
+}
+
+static void spawn_monster(int x, int y)
+{
+       spawn(monster, x, y);
+}
+
+__attribute__((constructor)) static void init()
+{
+       monster = (struct entity) {
+               .name = "monster",
+               .x = 0,
+               .y = 0,
+               .color = get_color("#FF00F6"),
+               .texture = "👾",
+               .remove = false,
+               .meta = NULL,
+               .health = 5,
+               .max_health = 5,
+               .collide_with_entities = true,
+
+               .on_step = &monster_step,
+               .on_collide = NULL,
+               .on_collide_with_entity = &monster_on_collide_with_entity,
+               .on_spawn = &monster_spawn,
+               .on_remove = NULL,
+               .on_death = &monster_death,
+       };
+
+       register_air_function((struct generator_function) {
+               .chance = 50,
+               .callback = &spawn_monster,
+       });
+}