]> git.lizzy.rs Git - dungeon_game.git/commitdiff
Merge branch 'master' of https://github.com/EliasFleckenstein03/dungeon_game
authorElias Fleckenstein <eliasfleckenstein@web.de>
Mon, 14 Jun 2021 08:20:58 +0000 (10:20 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Mon, 14 Jun 2021 08:20:58 +0000 (10:20 +0200)
34 files changed:
README.md
dungeon.c
plugins/apple/Makefile
plugins/apple/apple.c
plugins/apple/dependencies.txt [new file with mode: 0644]
plugins/cherry/Makefile [new file with mode: 0644]
plugins/cherry/cherry.c [new file with mode: 0644]
plugins/cherry/dependencies.txt [new file with mode: 0644]
plugins/fireball/Makefile
plugins/fireball/dependencies.txt [new file with mode: 0644]
plugins/fireball/fireball.c
plugins/game/game.c
plugins/game/game.h
plugins/healthbar/Makefile [new file with mode: 0644]
plugins/healthbar/dependencies.txt [new file with mode: 0644]
plugins/healthbar/healthbar.c [new file with mode: 0644]
plugins/inventory/Makefile [new file with mode: 0644]
plugins/inventory/dependencies.txt [new file with mode: 0644]
plugins/inventory/inventory.c [new file with mode: 0644]
plugins/inventory/inventory.h [new file with mode: 0644]
plugins/monster/Makefile
plugins/monster/dependencies.txt [new file with mode: 0644]
plugins/monster/monster.c
plugins/movement/Makefile
plugins/movement/dependencies.txt [new file with mode: 0644]
plugins/movement/movement.c
plugins/movement/movement.h [new file with mode: 0644]
plugins/score/Makefile [new file with mode: 0644]
plugins/score/dependencies.txt [new file with mode: 0644]
plugins/score/score.c [new file with mode: 0644]
plugins/score/score.h [new file with mode: 0644]
plugins/sword/Makefile [new file with mode: 0644]
plugins/sword/dependencies.txt [new file with mode: 0644]
plugins/sword/sword.c [new file with mode: 0644]

index e003dd9f244d2d849e635937819366bddbd37637..a9ca55bba30140064f9ff3c3eb1ef61c2c294658 100644 (file)
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ You might want to include the game.h file from plugins/game/game.h. Have a look
 
 Controls: WASD to move, Q to quit, Space to shoot.
 
-To build the loader and the plugins in the plugins/ folder, simply type `make` or `make all`. There are separate targets for the loader (`dungeon`) and the plugins. All Makefiles that are placed in plugin directories, so you might want to include a makefile in your plugin. The plugins target simply depends on ${PLUGINS}, so just add things to this in your plugin Makefile to add them to the plugins target (usually your plugin.so) 
+To build the loader and the plugins in the plugins/ folder, simply type `make` or `make all`. There are separate targets for the loader (`dungeon`) and the plugins. All Makefiles that are placed in plugin directories, so you might want to include a makefile in your plugin. The plugins target simply depends on ${PLUGINS}, so just add things to this in your plugin Makefile to add them to the plugins target (usually your plugin.so)
 To run the loader, type `./dungeon`. It will load all plugins including the game itself dynamically and run the game.
 
-Plugins are loaded in alphabethical order, with the exception of the game plugin that is loaded first. If you want to make a plugin that depends on another plugin, make sure the other plugin is loaded first by setting the name of your plugin accordingly. A cleaner solution to this is coming soon.
+If you want to make a plugin that depends on another plugin (including the game itself), make sure to depend on that plugin. To add dependencies to a plugin, create a file named dependencies.txt in the plugin folder. Put the names of all plugins your plugin depends on into that file. You can use spaces or newlines as seperators.
index a1b00645a8b580f3d6f8e714f12bad09bbebf12c..d461d641b6854d4ce2e4782b230faa3ce2b90be6 100644 (file)
--- a/dungeon.c
+++ b/dungeon.c
@@ -5,20 +5,63 @@
 #include <dirent.h>
 #include <string.h>
 
+struct plugin_list
+{
+       char *name;
+       void *handle;
+       struct plugin_list *next;
+};
+
+struct plugin_list *plugins = NULL;
+struct plugin_list **next = &plugins;
+
 static void *load_plugin(const char *name)
 {
+       for (struct plugin_list *ptr = plugins; ptr != NULL; ptr = ptr->next) {
+               if (strcmp(ptr->name, name) == 0)
+                       return ptr->handle;
+       }
+
        size_t len = strlen(name);
-       char filename[1 + 1 + 7 + 1 + len + 1 + len + 1 + 2 + 1];
-       sprintf(filename, "./plugins/%s/%s.so", name, name);
 
-       void *plugin_handle = dlmopen(LM_ID_BASE, filename, RTLD_NOW | RTLD_GLOBAL);
+       char dependency_file_name[1 + 1 + 7 + 1 + len + 1 + 12 + 1 + 3 + 1];
+       sprintf(dependency_file_name, "./plugins/%s/dependencies.txt", name);
+
+       FILE *dependency_file = fopen(dependency_file_name, "r");
+
+       if (dependency_file) {
+               char dependency[BUFSIZ];
+
+               while (fscanf(dependency_file, "%s", dependency) != EOF)
+                       load_plugin(dependency);
 
-       if (! plugin_handle) {
+               fclose(dependency_file);
+       }
+
+       char library_name[1 + 1 + 7 + 1 + len + 1 + len + 1 + 2 + 1];
+       sprintf(library_name, "./plugins/%s/%s.so", name, name);
+
+       void *handle = dlmopen(LM_ID_BASE, library_name, RTLD_NOW | RTLD_GLOBAL);
+
+       if (! handle) {
                printf("%s\n", dlerror());
                exit(EXIT_FAILURE);
        }
 
-       return plugin_handle;
+       char *namebuf = malloc(len + 1);
+       strcpy(namebuf, name);
+
+       *next = malloc(sizeof(struct plugin_list));
+       **next = (struct plugin_list) {
+               .name = namebuf,
+               .handle = handle,
+               .next = NULL,
+       };
+       next = &(*next)->next;
+
+       printf("Loaded %s\n", name);
+
+       return handle;
 }
 
 int main()
@@ -30,11 +73,9 @@ int main()
 
        struct dirent *dp;
 
-       while (dp = readdir(dir)) {
-               if (dp->d_name[0] != '.' && strcmp(dp->d_name, "game") != 0) {
+       while (dp = readdir(dir))
+               if (dp->d_name[0] != '.')
                        load_plugin(dp->d_name);
-               }
-       }
 
        closedir(dir);
 
index 33c330338e54907d06ea48af561f9f8ccaaec56e..f90b872a810d168cbe734247ca64c6464578a644 100644 (file)
@@ -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
index d100e45bd0381303f1e8442faba0566a6d1d5b53..795800d48ae47c5edd88a0879c77f6b9fb0f7723 100644 (file)
@@ -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 (file)
index 0000000..3914551
--- /dev/null
@@ -0,0 +1,2 @@
+game
+score
diff --git a/plugins/cherry/Makefile b/plugins/cherry/Makefile
new file mode 100644 (file)
index 0000000..edb1f44
--- /dev/null
@@ -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 (file)
index 0000000..f998770
--- /dev/null
@@ -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 (file)
index 0000000..4952514
--- /dev/null
@@ -0,0 +1,3 @@
+game
+score
+inventory
index bc0a2772e97a5f9188c655e7b7b940b4973ddf9f..117c092c248a46af33dcd084a52fb2b4dd5260f8 100644 (file)
@@ -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 (file)
index 0000000..b19003b
--- /dev/null
@@ -0,0 +1,3 @@
+game
+movement
+inventory
index 3da29a47d9e025a8efed27dbdbc27a74be46e147..39e93fb586909d7c8429e6493ad3533b0f2d9713 100644 (file)
@@ -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,
        });
 }
index 6f91ca2dc3cbe3d559c13bffc4d26ae8c1bafda3..152a5c97200e75578a3139877929e6b03277fa75 100644 (file)
@@ -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 */
 
 /*
index 6131402d65ec0d31147a546747d0136dedf81a15..60c1d615c0a1bac664dd4e537d03adcde0f234f9 100644 (file)
@@ -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 (file)
index 0000000..91c65d4
--- /dev/null
@@ -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 (file)
index 0000000..dc22e61
--- /dev/null
@@ -0,0 +1 @@
+game
diff --git a/plugins/healthbar/healthbar.c b/plugins/healthbar/healthbar.c
new file mode 100644 (file)
index 0000000..c66d3f1
--- /dev/null
@@ -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 (file)
index 0000000..a665b55
--- /dev/null
@@ -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 (file)
index 0000000..dc22e61
--- /dev/null
@@ -0,0 +1 @@
+game
diff --git a/plugins/inventory/inventory.c b/plugins/inventory/inventory.c
new file mode 100644 (file)
index 0000000..9923561
--- /dev/null
@@ -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 (file)
index 0000000..437ffb6
--- /dev/null
@@ -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
index 28edde84324dced0243eee2027258056898079d4..5cffca307d1ffd81507595bf80e687d8cce194ca 100644 (file)
@@ -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 (file)
index 0000000..3914551
--- /dev/null
@@ -0,0 +1,2 @@
+game
+score
index 59fa3806244b9b6220c9f0508ea043ce940c3535..02957da9abd6d52e2bbe5afd1c4524ff6b449c8a 100644 (file)
@@ -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,
index 9d23279ce6c15b7cda67d6c65d38f9a87162cfd2..c75d5419322c8c71bddb0451a3bf8d5657c74375 100644 (file)
@@ -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 (file)
index 0000000..dc22e61
--- /dev/null
@@ -0,0 +1 @@
+game
index 494c8b0a47d965f7b144d477ad8f83dfad15733a..25c5f939c35907a7e0bc29f7aaaa02c9c199eded 100644 (file)
@@ -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 (file)
index 0000000..c3afdac
--- /dev/null
@@ -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 (file)
index 0000000..33400e5
--- /dev/null
@@ -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 (file)
index 0000000..dc22e61
--- /dev/null
@@ -0,0 +1 @@
+game
diff --git a/plugins/score/score.c b/plugins/score/score.c
new file mode 100644 (file)
index 0000000..7ad8b66
--- /dev/null
@@ -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 (file)
index 0000000..8ee531a
--- /dev/null
@@ -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 (file)
index 0000000..ee28952
--- /dev/null
@@ -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 (file)
index 0000000..b19003b
--- /dev/null
@@ -0,0 +1,3 @@
+game
+movement
+inventory
diff --git a/plugins/sword/sword.c b/plugins/sword/sword.c
new file mode 100644 (file)
index 0000000..f399150
--- /dev/null
@@ -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,
+       });
+}