From: Elias Fleckenstein Date: Wed, 9 Jun 2021 15:10:36 +0000 (+0200) Subject: Add plugins X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=5d0c8c6e70115feb7f46b7aab11112cdc0f9f696;p=dungeon_game.git Add plugins --- diff --git a/plugins/apple/Makefile b/plugins/apple/Makefile new file mode 100644 index 0000000..8e3ccca --- /dev/null +++ b/plugins/apple/Makefile @@ -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 index 0000000..7c2de77 --- /dev/null +++ b/plugins/apple/apple.c @@ -0,0 +1,48 @@ +#include +#include +#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 index 0000000..83714da --- /dev/null +++ b/plugins/game/Makefile @@ -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 index 0000000..f0496b8 --- /dev/null +++ b/plugins/game/game.c @@ -0,0 +1,556 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..9c6953f --- /dev/null +++ b/plugins/game/game.h @@ -0,0 +1,100 @@ +#ifndef _GAME_H_ +#define _GAME_H_ + +#include +#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 index 0000000..1ba16c5 --- /dev/null +++ b/plugins/monster/Makefile @@ -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 index 0000000..167de88 --- /dev/null +++ b/plugins/monster/monster.c @@ -0,0 +1,72 @@ +#include +#include +#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, + }); +}