diff options
Diffstat (limited to 'plugins')
32 files changed, 940 insertions, 354 deletions
diff --git a/plugins/apple/Makefile b/plugins/apple/Makefile index 33c3303..f90b872 100644 --- a/plugins/apple/Makefile +++ b/plugins/apple/Makefile @@ -1,4 +1,4 @@ -plugins/apple/apple.so: plugins/apple/apple.c plugins/game/game.h +plugins/apple/apple.so: plugins/apple/apple.c plugins/game/game.h plugins/score/score.h cc -g -shared -fpic -o plugins/apple/apple.so plugins/apple/apple.c PLUGINS := ${PLUGINS} plugins/apple/apple.so diff --git a/plugins/apple/apple.c b/plugins/apple/apple.c index d100e45..795800d 100644 --- a/plugins/apple/apple.c +++ b/plugins/apple/apple.c @@ -1,8 +1,7 @@ #include <stddef.h> #include <stdlib.h> #include "../game/game.h" - -static struct entity apple; +#include "../score/score.h" static void apple_step(struct entity *self, struct entity_step_data stepdata) { @@ -13,34 +12,35 @@ static void apple_step(struct entity *self, struct entity_step_data stepdata) } } +static struct entity apple_entity = { + .name = "apple", + .x = 0, + .y = 0, + .color = {0}, + .use_color = false, + .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, + .on_damage = NULL, +}; + static void spawn_apple(int x, int y) { - spawn(apple, x, y, NULL); + spawn(apple_entity, x, y, NULL); } __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, - .on_damage = NULL, - }; - register_air_function((struct generator_function) { .chance = 25, .callback = &spawn_apple, diff --git a/plugins/apple/dependencies.txt b/plugins/apple/dependencies.txt new file mode 100644 index 0000000..3914551 --- /dev/null +++ b/plugins/apple/dependencies.txt @@ -0,0 +1,2 @@ +game +score diff --git a/plugins/cherry/Makefile b/plugins/cherry/Makefile new file mode 100644 index 0000000..edb1f44 --- /dev/null +++ b/plugins/cherry/Makefile @@ -0,0 +1,4 @@ +plugins/cherry/cherry.so: plugins/cherry/cherry.c plugins/game/game.h plugins/score/score.h plugins/inventory/inventory.h + cc -g -shared -fpic -o plugins/cherry/cherry.so plugins/cherry/cherry.c + +PLUGINS := ${PLUGINS} plugins/cherry/cherry.so diff --git a/plugins/cherry/cherry.c b/plugins/cherry/cherry.c new file mode 100644 index 0000000..f998770 --- /dev/null +++ b/plugins/cherry/cherry.c @@ -0,0 +1,71 @@ +#include <stddef.h> +#include <stdlib.h> +#include "../game/game.h" +#include "../score/score.h" +#include "../inventory/inventory.h" + +static bool use_cherry(struct itemstack *stack) +{ + (void) stack; + + add_health(&player, 2); + return true; +} + +static struct item cherry_item = { + .name = "Cherry", + .stackable = true, + + .on_use = &use_cherry, + .on_destroy = NULL, + .on_create = NULL, +}; + +static void cherry_step(struct entity *self, struct entity_step_data stepdata) +{ + if (stepdata.dx == 0 && stepdata.dy == 0) { + add_score(2); + inventory_add(&player_inventory, (struct itemstack) { + .item = &cherry_item, + .count = 1, + .meta = NULL, + }); + self->remove = true; + } +} + +static struct entity cherry_entity = { + .name = "cherry", + .x = 0, + .y = 0, + .color = {0}, + .use_color = false, + .texture = "🍒", + .remove = false, + .meta = NULL, + .health = 1, + .max_health = 1, + .collide_with_entities = false, + + .on_step = &cherry_step, + .on_collide = NULL, + .on_collide_with_entity = NULL, + .on_spawn = NULL, + .on_remove = NULL, + .on_death = NULL, + .on_damage = NULL, +}; + +static void spawn_cherry(int x, int y) +{ + spawn(cherry_entity, x, y, NULL); +} + +__attribute__((constructor)) static void init() +{ + register_air_function((struct generator_function) { + .chance = 100, + .callback = &spawn_cherry, + }); +} + diff --git a/plugins/cherry/dependencies.txt b/plugins/cherry/dependencies.txt new file mode 100644 index 0000000..4952514 --- /dev/null +++ b/plugins/cherry/dependencies.txt @@ -0,0 +1,3 @@ +game +score +inventory diff --git a/plugins/fireball/Makefile b/plugins/fireball/Makefile index bc0a277..117c092 100644 --- a/plugins/fireball/Makefile +++ b/plugins/fireball/Makefile @@ -1,4 +1,4 @@ -plugins/fireball/fireball.so: plugins/fireball/fireball.c plugins/game/game.h +plugins/fireball/fireball.so: plugins/fireball/fireball.c plugins/game/game.h plugins/movement/movement.h plugins/inventory/inventory.h cc -g -shared -fpic -o plugins/fireball/fireball.so plugins/fireball/fireball.c PLUGINS := ${PLUGINS} plugins/fireball/fireball.so diff --git a/plugins/fireball/dependencies.txt b/plugins/fireball/dependencies.txt new file mode 100644 index 0000000..b19003b --- /dev/null +++ b/plugins/fireball/dependencies.txt @@ -0,0 +1,3 @@ +game +movement +inventory diff --git a/plugins/fireball/fireball.c b/plugins/fireball/fireball.c index 3da29a4..39e93fb 100644 --- a/plugins/fireball/fireball.c +++ b/plugins/fireball/fireball.c @@ -1,8 +1,8 @@ #include <stdlib.h> #include <stddef.h> #include "../game/game.h" - -static struct entity fireball; +#include "../movement/movement.h" +#include "../inventory/inventory.h" struct fireball_data { @@ -40,66 +40,81 @@ static void fireball_collide(struct entity *self, int x, int y) static void fireball_collide_with_entity(struct entity *self, struct entity *other) { - add_health(other, -(1 + rand() % 3)); + add_health(other, -(3 + rand() % 3)); self->remove = true; } -static bool try_shoot(int x, int y, int vx, int vy) +static struct entity fireball_entity = { + .name = "fireball", + .x = 0, + .y = 0, + .color = {0}, + .use_color = true, + .texture = "⬤ ", + .remove = false, + .meta = NULL, + .health = 1, + .max_health = 1, + .collide_with_entities = true, + + .on_step = &fireball_step, + .on_collide = &fireball_collide, + .on_collide_with_entity = &fireball_collide_with_entity, + .on_spawn = &fireball_spawn, + .on_remove = NULL, + .on_death = NULL, + .on_damage = NULL, +}; + +static void shoot_fireball() { - x += vx; - y += vy; + int vx, vy; + vx = vy = 0; - return spawn(fireball, x, y, & (struct fireball_data) { + dir_to_xy(last_player_move, &vx, &vy); + + spawn(fireball_entity, player.x + vx, player.y + vy, & (struct fireball_data) { .timer = 0.1, .vx = vx, .vy = vy, }); } -static void shoot_fireball() +static bool shoot_fireball_item(struct itemstack *stack) { - int x, y; + (void) stack; - x = player.x; - y = player.y; - - for (int tries = 10; tries > 0; tries--) { - int vx, vy; + shoot_fireball(); + return true; +} - vx = vy = 0; +static struct item fireball_item = { + .name = "Fireball", + .stackable = true, - dir_to_xy(rand() % 4, &vx, &vy); + .on_use = &shoot_fireball_item, + .on_destroy = NULL, + .on_create = NULL, +}; - if (try_shoot(x, y, vx, vy)) - return; - } +static void shoot_if_has_fireball() +{ + if (inventory_remove(&player_inventory, &fireball_item)) + shoot_fireball(); } __attribute__((constructor)) static void init() { - fireball = (struct entity) { - .name = "fireball", - .x = 0, - .y = 0, - .color = get_color("#FF6611"), - .texture = "⬤ ", - .remove = false, - .meta = NULL, - .health = 1, - .max_health = 1, - .collide_with_entities = true, - - .on_step = &fireball_step, - .on_collide = &fireball_collide, - .on_collide_with_entity = &fireball_collide_with_entity, - .on_spawn = &fireball_spawn, - .on_remove = NULL, - .on_death = NULL, - .on_damage = NULL, - }; + fireball_entity.color = get_color("#FF6611"); register_input_handler(' ', (struct input_handler) { .run_if_dead = false, - .callback = &shoot_fireball, + .callback = &shoot_if_has_fireball, + }); + + inventory_add(&player_inventory, (struct itemstack) { + .item = &fireball_item, + .count = 7, + .meta = NULL, }); } diff --git a/plugins/game/game.c b/plugins/game/game.c index 6f91ca2..152a5c9 100644 --- a/plugins/game/game.c +++ b/plugins/game/game.c @@ -1,24 +1,18 @@ #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 <string.h> #include "game.h" -bool running = true; -double damage_overlay = 0.0; +/* Shared variables */ -int score = 0; - -struct color black = {0, 0, 0}; +struct color black = {0}; struct material wall; struct material air; @@ -26,22 +20,26 @@ struct material outside; struct node map[MAP_WIDTH][MAP_HEIGHT]; -struct entity player; struct list *entities = & (struct list) { .element = &player, .next = NULL, }; +/* Private variables */ + struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT] = {{NULL}}; -struct list *air_functions = NULL; +static bool running = true; +static double damage_overlay = 0.0; +static struct color damage_overlay_color; -struct input_handler *input_handlers[256] = {NULL}; +static struct list *air_functions = NULL; +static struct input_handler *input_handlers[256] = {NULL}; +static struct entity *render_entities[LIGHT * 2 + 1][LIGHT * 2 + 1]; +static struct list *render_components = NULL; +static struct list *globalsteps = NULL; -void quit() -{ - running = false; -} +/* Helper functions */ struct color get_color(const char *str) { @@ -50,45 +48,118 @@ struct color get_color(const char *str) return (struct color) {r, g, b}; } -bool is_outside(int x, int y) +void set_color(struct color color, bool bg) { - return x >= MAP_WIDTH || x < 0 || y >= MAP_HEIGHT || y < 0; + printf("\e[%u;2;%u;%u;%um", bg ? 48 : 38, color.r, color.g, color.b); } -struct node get_node(int x, int y) +void light_color(struct color *color, double light) { - return is_outside(x, y) ? (struct node) {&outside} : map[x][y]; + color->r *= light; + color->g *= light; + color->b *= light; } -bool is_solid(int x, int y) +void mix_color(struct color *color, struct color other, double ratio) { - return get_node(x, y).material->solid; + double ratio_total = ratio + 1; + + color->r = (color->r + other.r * ratio) / ratio_total; + color->g = (color->g + other.g * ratio) / ratio_total; + color->b = (color->b + other.b * ratio) / ratio_total; } -bool move(struct entity *entity, int xoff, int yoff) +void dir_to_xy(enum direction dir, int *x, int *y) { - int x, y; + switch (dir) { + case UP: + (*y)--; + break; + case LEFT: + (*x)--; + break; + case DOWN: + (*y)++; + break; + case RIGHT: + (*x)++; + break; + } +} - x = entity->x + xoff; - y = entity->y + yoff; +struct list *add_element(struct list *list, void *element) +{ + struct list **ptr; - 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; - } + for (ptr = &list; *ptr != NULL; ptr = &(*ptr)->next) + ; + + *ptr = malloc(sizeof(struct list)); + (*ptr)->element = element; + (*ptr)->next = NULL; + + return list; +} + +int clamp(int v, int min, int max) +{ + return v < min ? min : v > max ? max : v; +} + +int max(int a, int b) +{ + return a > b ? a : b; +} + +int min(int a, int b) +{ + return a < b ? a : b; +} + +void *make_buffer(void *ptr, size_t size) +{ + void *buf = malloc(size); + memcpy(buf, ptr, size); + + return buf; +} + +double calculate_dtime(struct timespec from, struct timespec to) +{ + return (double) (to.tv_sec - from.tv_sec) + (double) (to.tv_nsec - from.tv_nsec) / 1000000000.0; } +/* Game-related utility functions */ + +void quit() +{ + running = false; +} + +bool player_dead() +{ + return player.health <= 0; +} + +/* Map functions */ + +struct node get_node(int x, int y) +{ + return is_outside(x, y) ? (struct node) {&outside} : map[x][y]; +} + +bool is_outside(int x, int y) +{ + return x >= MAP_WIDTH || x < 0 || y >= MAP_HEIGHT || y < 0; +} + +bool is_solid(int x, int y) +{ + return get_node(x, y).material->solid; +} + +/* Entity functions */ + bool spawn(struct entity def, int x, int y, void *data) { if (is_solid(x, y)) @@ -114,6 +185,30 @@ bool spawn(struct entity def, int x, int y, void *data) return true; } +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 add_health(struct entity *entity, int health) { bool was_alive = entity->health > 0; @@ -129,36 +224,11 @@ void add_health(struct entity *entity, int health) entity->on_death(entity); } -void add_score(int s) -{ - score += s; -} - -bool player_dead() -{ - return player.health <= 0; -} - -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; -} +/* Register callback functions */ 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); + air_functions = add_element(air_functions, make_buffer(&func, sizeof(struct generator_function))); } void register_input_handler(unsigned char c, struct input_handler handler) @@ -166,33 +236,17 @@ void register_input_handler(unsigned char c, struct input_handler handler) if (input_handlers[c]) return; - struct input_handler *buf = malloc(sizeof(struct input_handler)); - *buf = handler; - - input_handlers[c] = buf; + input_handlers[c] = make_buffer(&handler, sizeof(struct input_handler)); } -void dir_to_xy(int dir, int *x, int *y) +void register_render_component(void (*callback)(struct winsize ws)) { - switch (dir) { - case 0: - (*x)++; - break; - case 1: - (*y)++; - break; - case 2: - (*x)--; - break; - case 3: - (*y)--; - break; - } -} + render_components = add_element(render_components, callback); +}; -int clamp(int v, int min, int max) +void register_globalstep(struct globalstep step) { - return v < min ? min : v > max ? max : v; + globalsteps = add_element(globalsteps, make_buffer(&step, sizeof(struct globalstep))); } /* Player */ @@ -207,38 +261,95 @@ static void player_damage(struct entity *self, int damage) damage_overlay += (double) damage * 0.5; } +struct entity player = { + .name = "player", + .x = MAP_WIDTH / 2, + .y = MAP_HEIGHT / 2, + .color = {0}, + .use_color = false, + .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, + .on_damage = &player_damage, +}; + /* Mapgen */ -static bool check_direction(int x, int y, int dir) +static void mapgen_set_air(int x, int y) +{ + if (is_outside(x, y)) + return; + + if (map[x][y].material == &air) + return; + + map[x][y] = (struct node) {&air}; + + for (struct list *ptr = air_functions; ptr != NULL; ptr = ptr->next) { + struct generator_function *func = ptr->element; + + if (rand() % func->chance == 0) + func->callback(x, y); + } +} + +static void generate_room(int origin_x, int origin_y) +{ + int left = 5 + rand() % 10; + int right = 5 + rand() % 10; + + int up = 0; + int down = 0; + + for (int x = -left; x <= right; x++) { + if (x < 0) { + up += rand() % 2; + down += rand() % 2; + } else { + up -= rand() % 2; + down -= rand() % 2; + } + + for (int y = -up; y <= down; y++) + mapgen_set_air(origin_x + x, origin_y + y); + } +} + +static bool check_direction(int x, int y, enum direction 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); + else + 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); } -static void generate_corridor(int lx, int ly, int ldir, bool off) +static void generate_corridor(int lx, int ly, enum direction ldir) { if (is_outside(lx, ly)) return; - /* - if (off && rand() % 100 == 0) + if (rand() % 200 == 0) { + generate_room(lx, ly); return; - */ + } - map[lx][ly] = (struct node) {&air}; + mapgen_set_air(lx, ly); - for (struct list *ptr = air_functions; ptr != NULL; ptr = ptr->next) { - struct generator_function *func = ptr->element; + int x, y; - if (! func->chance || rand() % func->chance == 0) { - func->callback(lx, ly); - } - } + enum direction dir; + enum direction ret = (ldir + 2) % 4; - int x, y, dir; - int ret = (ldir + 2) % 4; int limit = 50; do { @@ -254,74 +365,48 @@ static void generate_corridor(int lx, int ly, int ldir, bool off) } while (dir == ret || (! check_direction(x, y, dir) && --limit)); if (limit) - generate_corridor(x, y, dir, off); + generate_corridor(x, y, dir); if (rand() % 20 == 0) - generate_corridor(lx, ly, ldir, true); + generate_corridor(lx, ly, ldir); } static void generate_corridor_random(int x, int y) { - int dir = rand() % 4; + enum direction dir = rand() % 4; - generate_corridor(x, y, dir, false); - generate_corridor(x, y, (dir + 2) % 4, false); + generate_corridor(x, y, dir); + generate_corridor(x, y, (dir + 2) % 4); } /* 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); -} - -void light_color(struct color *color, double light) -{ - color->r *= light; - color->g *= light; - color->b *= light; -} - -void mix_color(struct color *color, struct color other, double ratio) -{ - double ratio_total = ratio + 1; - - color->r = (color->r + other.r * ratio) / ratio_total; - color->g = (color->g + other.g * ratio) / ratio_total; - color->b = (color->b + other.b * ratio) / ratio_total; -} - -static bool render_color(struct color color, double light, bool bg) +static bool render_color(struct color color, double light, bool bg, bool use_color) { if (light <= 0.0) { set_color(black, bg); return false; - } else { + } else if (use_color) { if (damage_overlay > 0.0) - mix_color(&color, get_color("#F20000"), damage_overlay * 2.0); + mix_color(&color, damage_overlay_color, damage_overlay * 2.0); light_color(&color, light); set_color(color, bg); return true; + } else { + return true; } } -static void render(render_entity_list entity_list) +static void render_map(struct winsize ws) { - 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(" "); @@ -341,11 +426,12 @@ static void render(render_entity_list entity_list) double dist = sqrt(x * x + y * y); double light = 1.0 - (double) dist / (double) LIGHT; - render_color(node.material->color, light, true); + render_color(node.material->color, light, true, true); - struct entity *entity = entity_list[x + LIGHT][y + LIGHT]; + struct entity *entity = render_entities[x + LIGHT][y + LIGHT]; + render_entities[x + LIGHT][y + LIGHT] = NULL; - if (entity && render_color(entity->color, light, false)) + if (entity && render_color(entity->color, light, false, entity->use_color)) printf("%s", entity->texture); else printf(" "); @@ -357,60 +443,43 @@ static void render(render_entity_list entity_list) printf(" "); } - for (int i = 0; i < rows_left + 1; i++) + for (int i = 0; i < rows_left; 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; +static void render() +{ + printf("\e[2J"); - for (int i = 0; i < hearts_cols; i++) - printf(" "); + struct winsize ws; + ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); - set_color((struct color) {255, 0, 0}, false); + for (struct list *ptr = render_components; ptr != NULL; ptr = ptr->next) { + printf("\e[0m\e[0;0H"); + set_color(black, true); - for (int i = 0; i < player.max_health; i++) { - if (i >= player.health) - set_color(get_color("#5A5A5A"), false); - printf("\u2665 "); + ((void (*)(struct winsize ws)) ptr->element)(ws); } - printf("\e[39m\n"); + fflush(stdout); } /* Input */ -static void handle_input(unsigned char c) +static void handle_interrupt(int signal) { - struct input_handler *handler = input_handlers[c]; + (void) signal; - if (handler && (handler->run_if_dead || ! player_dead())) - handler->callback(); + quit(); } -/* Multithreading */ - -static void handle_interrupt(int signal) +static void handle_input(unsigned char c) { - (void) signal; + struct input_handler *handler = input_handlers[c]; - running = false; + if (handler && (handler->run_if_dead || ! player_dead())) + handler->callback(); } static void *input_thread(void *unused) @@ -425,60 +494,8 @@ static void *input_thread(void *unused) /* 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, - .on_damage = &player_damage, - }; - - 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}; - - register_input_handler('q', (struct input_handler) { - .run_if_dead = true, - .callback = &quit, - }); -} - void game() { - srand(time(0)); - struct sigaction sa; sa.sa_handler = &handle_interrupt; sigaction(SIGINT, &sa, NULL); @@ -504,11 +521,18 @@ void game() 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; + double dtime = calculate_dtime(ts_old, ts); ts_old = ts; bool dead = player_dead(); + for (struct list *ptr = globalsteps; ptr != NULL; ptr = ptr->next) { + struct globalstep *step = ptr->element; + + if (step->run_if_dead || ! dead) + step->callback(dtime); + } + if (! dead && damage_overlay > 0.0) { damage_overlay -= dtime; @@ -516,8 +540,6 @@ void game() damage_overlay = 0.0; } - render_entity_list render_list = {{NULL}}; - for (struct list **ptr = &entities; *ptr != NULL; ) { struct entity *entity = (*ptr)->element; @@ -549,7 +571,7 @@ void game() bool visible = abs(dx) <= LIGHT && abs(dy) <= LIGHT; if (visible) - render_list[dx + LIGHT][dy + LIGHT] = entity; + render_entities[dx + LIGHT][dy + LIGHT] = entity; if (! dead && entity->on_step) entity->on_step(entity, (struct entity_step_data) { @@ -562,7 +584,7 @@ void game() ptr = &(*ptr)->next; } - render(render_list); + render(); // there is no such thing as glfwSwapBuffers, so we just wait 1 / 60 seconds to prevent artifacts usleep(1000000 / 60); @@ -572,6 +594,43 @@ void game() tcsetattr(STDIN_FILENO, TCSANOW, &oldtio); } +/* Initializer function */ + +__attribute__ ((constructor)) static void init() +{ + srand(time(0)); + + 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, + }; + + 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}; + + register_input_handler('q', (struct input_handler) { + .run_if_dead = true, + .callback = &quit, + }); + + register_render_component(&render_map); + + damage_overlay_color = get_color("#F20000"); +} + /* Use later */ /* diff --git a/plugins/game/game.h b/plugins/game/game.h index 6131402..60c1d61 100644 --- a/plugins/game/game.h +++ b/plugins/game/game.h @@ -2,6 +2,9 @@ #define _GAME_H_ #include <stdbool.h> +#include <sys/ioctl.h> +#include <stddef.h> +#include <time.h> #define MAP_HEIGHT 1000 #define MAP_WIDTH 1000 #define LIGHT 10 @@ -34,10 +37,11 @@ struct entity_step_data struct entity { - const char *name; + char *name; int x, y; struct color color; - const char *texture; + bool use_color; + char *texture; bool remove; void *meta; int health; @@ -59,8 +63,6 @@ struct list struct list *next; }; -typedef struct entity *render_entity_list[LIGHT * 2 + 1][LIGHT * 2 + 1]; - struct generator_function { int chance; @@ -73,7 +75,19 @@ struct input_handler void (*callback)(); }; -extern int score; +struct globalstep +{ + bool run_if_dead; + void (*callback)(double dtime); +}; + +enum direction +{ + UP, + LEFT, + DOWN, + RIGHT, +}; extern struct color black; @@ -88,27 +102,32 @@ extern struct list *entities; extern struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT]; -extern struct list *air_functions; - -extern struct input_handler *input_handlers[256]; +struct color get_color(const char *str); +void set_color(struct color color, bool bg); +void light_color(struct color *color, double light); +void mix_color(struct color *color, struct color other, double ratio); +void dir_to_xy(enum direction dir, int *x, int *y); +struct list *add_element(struct list *list, void *element); +int clamp(int v, int max, int min); +int max(int a, int b); +int min(int a, int b); +void *make_buffer(void *ptr, size_t size); +double calculate_dtime(struct timespec from, struct timespec to); void quit(); -struct color get_color(const char *str); -bool is_outside(int x, int y); +bool player_dead(); + struct node get_node(int x, int y); +bool is_outside(int x, int y); bool is_solid(int x, int y); -bool move(struct entity *entity, int xoff, int yoff); + bool spawn(struct entity def, int x, int y, void *data); +bool move(struct entity *entity, int xoff, int yoff); void add_health(struct entity *entity, int health); -void add_score(int s); -bool player_dead(); -void set_color(struct color color, bool bg); -void light_color(struct color *color, double light); -void mix_color(struct color *color, struct color other, double ratio); + void register_air_function(struct generator_function func); void register_input_handler(unsigned char c, struct input_handler handler); -void dir_to_xy(int dir, int *x, int *y); -int clamp(int v, int max, int min); -struct list *add_element(struct list *list, void *element); +void register_render_component(void (*callback)(struct winsize ws)); +void register_globalstep(struct globalstep step); #endif diff --git a/plugins/healthbar/Makefile b/plugins/healthbar/Makefile new file mode 100644 index 0000000..91c65d4 --- /dev/null +++ b/plugins/healthbar/Makefile @@ -0,0 +1,4 @@ +plugins/healthbar/healthbar.so: plugins/healthbar/healthbar.c plugins/game/game.h + cc -g -shared -fpic -o plugins/healthbar/healthbar.so plugins/healthbar/healthbar.c + +PLUGINS := ${PLUGINS} plugins/healthbar/healthbar.so diff --git a/plugins/healthbar/dependencies.txt b/plugins/healthbar/dependencies.txt new file mode 100644 index 0000000..dc22e61 --- /dev/null +++ b/plugins/healthbar/dependencies.txt @@ -0,0 +1 @@ +game diff --git a/plugins/healthbar/healthbar.c b/plugins/healthbar/healthbar.c new file mode 100644 index 0000000..c66d3f1 --- /dev/null +++ b/plugins/healthbar/healthbar.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include "../game/game.h" + +static struct color red = {255, 0, 0}; +static struct color gray; + +static void render_healthbar(struct winsize ws) +{ + int y = max(ws.ws_row - 2, 0); + int x = max(ws.ws_col / 2 - player.max_health / 2, 0); + + printf("\e[%u;%uH", y, x); + + set_color(red, false); + + int health = max(player.health, 0); + + for (int i = 0; i < player.max_health; i++) { + if (i == health) + set_color(gray, false); + printf("♥ "); + } +} + +__attribute__ ((constructor)) static void init() +{ + gray = get_color("#5A5A5A"); + + register_render_component(&render_healthbar); +} diff --git a/plugins/inventory/Makefile b/plugins/inventory/Makefile new file mode 100644 index 0000000..a665b55 --- /dev/null +++ b/plugins/inventory/Makefile @@ -0,0 +1,4 @@ +plugins/inventory/inventory.so: plugins/inventory/inventory.c plugins/inventory/inventory.h plugins/game/game.h + cc -g -shared -fpic -o plugins/inventory/inventory.so plugins/inventory/inventory.c + +PLUGINS := ${PLUGINS} plugins/inventory/inventory.so diff --git a/plugins/inventory/dependencies.txt b/plugins/inventory/dependencies.txt new file mode 100644 index 0000000..dc22e61 --- /dev/null +++ b/plugins/inventory/dependencies.txt @@ -0,0 +1 @@ +game diff --git a/plugins/inventory/inventory.c b/plugins/inventory/inventory.c new file mode 100644 index 0000000..9923561 --- /dev/null +++ b/plugins/inventory/inventory.c @@ -0,0 +1,210 @@ +#include <stdio.h> +#include <stdlib.h> +#include "../inventory/inventory.h" + +static struct color gray; +static struct color darkgray; + +static bool use_item(struct itemstack *stack) +{ + (void) stack; + return true; +} + +struct inventory player_inventory = {NULL}; + +static int selected_index = 0; + +void inventory_add(struct inventory *self, struct itemstack stack) +{ + struct list **ptr = &self->stacks; + + if (stack.item->stackable) { + for (; *ptr != NULL; ptr = &(*ptr)->next) { + struct itemstack *other = (*ptr)->element; + if (other->item == stack.item) { + other->count += stack.count; + return; + } + } + } + + struct itemstack *buf = make_buffer(&stack, sizeof(struct itemstack)); + *ptr = add_element(*ptr, buf); + + if (buf->item->on_create) + buf->item->on_create(buf); +} + +/* + +bool inventory_remove(struct inventory *self, struct itemstack *stack) +{ + stack.count = -stack.count; + + for (struct list **ptr = &self->stacks; *ptr != NULL; ) { + struct itemstack *other = (*ptr)->element; + + if (other->item == stack.item) { + stack.count += other->count; + + if (stack.count > 0) { + other->count = stack.count; + return true; + } else { + struct list *next = ptr->next; + + other->count = 0; + + if (other->item->on_destroy) + other->item->on_destroy(other); + + free(other); + free(*ptr); + + *ptr = next; + continue; + } + } + + ptr = &(*ptr)->next; + } + + return false; +} + +*/ + +static void decrease_item_count(struct list **ptr, struct itemstack *stack) +{ + stack->count--; + + if (stack->count == 0) { + struct list *next = (*ptr)->next; + + if (stack->item->on_destroy) + stack->item->on_destroy(stack); + + if (stack->meta) + free(stack->meta); + + free(stack); + free(*ptr); + + *ptr = next; + } +} + +bool inventory_remove(struct inventory *self, struct item *item) +{ + for (struct list **ptr = &self->stacks; *ptr != NULL; ptr = &(*ptr)->next) { + struct itemstack *stack = (*ptr)->element; + + if (stack->item == item) { + decrease_item_count(ptr, stack); + + return true; + } + } + + return false; +} + +static void handle_enter() +{ + int i = 0; + + for (struct list **ptr = &player_inventory.stacks; *ptr != NULL; ptr = &(*ptr)->next, i++) { + if (i == selected_index) { + struct itemstack *stack = (*ptr)->element; + + if (stack->item->on_use && stack->item->on_use(stack)) + decrease_item_count(ptr, stack); + + return; + } + } +} + +static void handle_arrow() +{ + char c = fgetc(stdin); + if (c == '[') { + char dir = fgetc(stdin); + + int count = 0; + + for (struct list *ptr = player_inventory.stacks; ptr != NULL; ptr = ptr->next) + count++; + + if (count == 0) + return; + + switch (dir) { + case 'A': + selected_index--; + + if (selected_index < 0) + selected_index = count - 1; + break; + case 'B': + selected_index++; + + if (selected_index >= count) + selected_index = 0; + + break; + } + } else { + ungetc(c, stdin); + } +} + +static void render_inventory(struct winsize ws) +{ + printf("\e[3;0H"); + + printf(" \e[1mInventory\e[21m\n"); + + set_color(gray, false); + + int i = 0; + for (struct list *ptr = player_inventory.stacks; ptr != NULL; ptr = ptr->next, i++) { + struct itemstack *stack = ptr->element; + + if (i == selected_index) { + printf(" \e[39m→ "); + set_color(gray, false); + } else { + printf(" "); + } + + printf("%s", stack->item->name); + + if (stack->count > 1) { + set_color(darkgray, false); + printf(" (x%u)", stack->count); + set_color(gray, false); + } + + printf("\n"); + } +} + +__attribute__ ((constructor)) static void init() +{ + gray = get_color("#9E9E9E"); + darkgray = get_color("#555555"); + + register_render_component(&render_inventory); + + register_input_handler('\033', (struct input_handler) { + .run_if_dead = false, + .callback = &handle_arrow, + }); + + register_input_handler('\n', (struct input_handler) { + .run_if_dead = false, + .callback = &handle_enter, + }); +} diff --git a/plugins/inventory/inventory.h b/plugins/inventory/inventory.h new file mode 100644 index 0000000..437ffb6 --- /dev/null +++ b/plugins/inventory/inventory.h @@ -0,0 +1,31 @@ +#ifndef _INVENTORY_H_ +#define _INVENTORY_H_ +#include "../game/game.h" + +struct itemstack +{ + struct item *item; + int count; + void *meta; +}; + +struct item +{ + char *name; + bool stackable; + + bool (*on_use)(struct itemstack *stack); + void (*on_destroy)(struct itemstack *stack); + void (*on_create)(struct itemstack *stack); +}; + +struct inventory +{ + struct list *stacks; +}; + +void inventory_add(struct inventory *self, struct itemstack stack); +bool inventory_remove(struct inventory *self, struct item *item); + +extern struct inventory player_inventory; +#endif diff --git a/plugins/monster/Makefile b/plugins/monster/Makefile index 28edde8..5cffca3 100644 --- a/plugins/monster/Makefile +++ b/plugins/monster/Makefile @@ -1,4 +1,4 @@ -plugins/monster/monster.so: plugins/monster/monster.c plugins/game/game.h +plugins/monster/monster.so: plugins/monster/monster.c plugins/game/game.h plugins/score/score.h cc -g -shared -fpic -o plugins/monster/monster.so plugins/monster/monster.c PLUGINS := ${PLUGINS} plugins/monster/monster.so diff --git a/plugins/monster/dependencies.txt b/plugins/monster/dependencies.txt new file mode 100644 index 0000000..3914551 --- /dev/null +++ b/plugins/monster/dependencies.txt @@ -0,0 +1,2 @@ +game +score diff --git a/plugins/monster/monster.c b/plugins/monster/monster.c index 59fa380..02957da 100644 --- a/plugins/monster/monster.c +++ b/plugins/monster/monster.c @@ -1,8 +1,7 @@ #include <stdlib.h> #include <stddef.h> #include "../game/game.h" - -static struct entity monster; +#include "../score/score.h" struct monster_data { @@ -40,34 +39,36 @@ static void monster_death(struct entity *self) self->remove = true; } +static struct entity monster_entity = { + .name = "monster", + .x = 0, + .y = 0, + .color = {0}, + .use_color = false, + .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_collide_with_entity, + .on_spawn = &monster_spawn, + .on_remove = NULL, + .on_death = &monster_death, + .on_damage = NULL, +}; + + static void spawn_monster(int x, int y) { - spawn(monster, x, y, NULL); + spawn(monster_entity, x, y, NULL); } __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_collide_with_entity, - .on_spawn = &monster_spawn, - .on_remove = NULL, - .on_death = &monster_death, - .on_damage = NULL, - }; - register_air_function((struct generator_function) { .chance = 50, .callback = &spawn_monster, diff --git a/plugins/movement/Makefile b/plugins/movement/Makefile index 9d23279..c75d541 100644 --- a/plugins/movement/Makefile +++ b/plugins/movement/Makefile @@ -1,4 +1,4 @@ -plugins/movement/movement.so: plugins/movement/movement.c plugins/game/game.h +plugins/movement/movement.so: plugins/movement/movement.c plugins/movement/movement.h plugins/game/game.h cc -g -shared -fpic -o plugins/movement/movement.so plugins/movement/movement.c PLUGINS := ${PLUGINS} plugins/movement/movement.so diff --git a/plugins/movement/dependencies.txt b/plugins/movement/dependencies.txt new file mode 100644 index 0000000..dc22e61 --- /dev/null +++ b/plugins/movement/dependencies.txt @@ -0,0 +1 @@ +game diff --git a/plugins/movement/movement.c b/plugins/movement/movement.c index 494c8b0..25c5f93 100644 --- a/plugins/movement/movement.c +++ b/plugins/movement/movement.c @@ -1,23 +1,36 @@ #include "../game/game.h" +enum direction last_player_move; + +void move_player(enum direction dir) +{ + int x, y; + x = y = 0; + + dir_to_xy(dir, &x, &y); + last_player_move = dir; + + move(&player, x, y); +} + static void move_up() { - move(&player, 0, -1); + move_player(UP); } static void move_left() { - move(&player, -1, 0); + move_player(LEFT); } static void move_down() { - move(&player, 0, 1); + move_player(DOWN); } static void move_right() { - move(&player, 1, 0); + move_player(RIGHT); } __attribute__((constructor)) static void init() diff --git a/plugins/movement/movement.h b/plugins/movement/movement.h new file mode 100644 index 0000000..c3afdac --- /dev/null +++ b/plugins/movement/movement.h @@ -0,0 +1,8 @@ +#ifndef _MOVEMENT_H_ +#define _MOVEMENT_H_ +#include "../game/game.h" + +extern enum direction last_player_move; +void move_player(enum direction dir); + +#endif diff --git a/plugins/score/Makefile b/plugins/score/Makefile new file mode 100644 index 0000000..33400e5 --- /dev/null +++ b/plugins/score/Makefile @@ -0,0 +1,4 @@ +plugins/score/score.so: plugins/score/score.c plugins/score/score.h plugins/game/game.h + cc -g -shared -fpic -o plugins/score/score.so plugins/score/score.c + +PLUGINS := ${PLUGINS} plugins/score/score.so diff --git a/plugins/score/dependencies.txt b/plugins/score/dependencies.txt new file mode 100644 index 0000000..dc22e61 --- /dev/null +++ b/plugins/score/dependencies.txt @@ -0,0 +1 @@ +game diff --git a/plugins/score/score.c b/plugins/score/score.c new file mode 100644 index 0000000..7ad8b66 --- /dev/null +++ b/plugins/score/score.c @@ -0,0 +1,26 @@ +#include <stdio.h> +#include "../game/game.h" + +static int score = 0; + +void add_score(int s) +{ + score += s; +} + +int get_score() +{ + return score; +} + +static void render_score(struct winsize ws) +{ + (void) ws; + + printf("\e[32m\e[3mScore:\e[23m %d", score); +} + +__attribute__ ((constructor)) static void init() +{ + register_render_component(&render_score); +} diff --git a/plugins/score/score.h b/plugins/score/score.h new file mode 100644 index 0000000..8ee531a --- /dev/null +++ b/plugins/score/score.h @@ -0,0 +1,7 @@ +#ifndef _SCORE_H_ +#define _SCORE_H_ + +void add_score(int s); +int get_score(); + +#endif diff --git a/plugins/sword/Makefile b/plugins/sword/Makefile new file mode 100644 index 0000000..ee28952 --- /dev/null +++ b/plugins/sword/Makefile @@ -0,0 +1,4 @@ +plugins/sword/sword.so: plugins/sword/sword.c plugins/game/game.h plugins/movement/movement.h plugins/inventory/inventory.h + cc -g -shared -fpic -o plugins/sword/sword.so plugins/sword/sword.c + +PLUGINS := ${PLUGINS} plugins/sword/sword.so diff --git a/plugins/sword/dependencies.txt b/plugins/sword/dependencies.txt new file mode 100644 index 0000000..b19003b --- /dev/null +++ b/plugins/sword/dependencies.txt @@ -0,0 +1,3 @@ +game +movement +inventory diff --git a/plugins/sword/sword.c b/plugins/sword/sword.c new file mode 100644 index 0000000..f399150 --- /dev/null +++ b/plugins/sword/sword.c @@ -0,0 +1,59 @@ +#include <stdlib.h> +#include <stddef.h> +#include "../game/game.h" +#include "../movement/movement.h" +#include "../inventory/inventory.h" + +static bool use_broken_sword(struct itemstack *stack) +{ + (void) stack; + + return true; +} + +static struct item broken_sword = { + .name = "Broken Sword", + .stackable = false, + + .on_use = &use_broken_sword, + .on_destroy = NULL, + .on_create = NULL, +}; + +static bool use_sword(struct itemstack *stack) +{ + int x, y; + x = player.x; + y = player.y; + + dir_to_xy(last_player_move, &x, &y); + + struct entity *entity = entity_collision_map[x][y]; + + if (entity) { + add_health(entity, -1); + + if (rand() % 100 == 0) + stack->item = &broken_sword; + } + + return false; +} + +static struct item sword = { + .name = "Sword", + .stackable = false, + + .on_use = &use_sword, + .on_destroy = NULL, + .on_create = NULL, +}; + +__attribute__((constructor)) static void init() +{ + inventory_add(&player_inventory, (struct itemstack) { + .item = &sword, + .count = 1, + .meta = NULL, + }); +} |