--- /dev/null
+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
--- /dev/null
+#include <stddef.h>
+#include <stdlib.h>
+#include "../game/game.h"
+#include "../score/score.h"
+#include "../inventory/inventory.h"
+
+static struct entity cherry_entity;
+
+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,
+};
+
+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 void spawn_cherry(int x, int y)
+{
+ spawn(cherry_entity, x, y, NULL);
+}
+
+__attribute__((constructor)) static void init()
+{
+ cherry_entity = (struct entity) {
+ .name = "cherry",
+ .x = 0,
+ .y = 0,
+ .color = get_color("#FF2A53"),
+ .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,
+ };
+
+ register_air_function((struct generator_function) {
+ .chance = 100,
+ .callback = &spawn_cherry,
+ });
+}
+
--- /dev/null
+game
+score
+inventory
#include <stdio.h>
-#include <stdbool.h>
#include <stdlib.h>
-#include <stddef.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>
#include <termios.h>
#include <math.h>
#include <pthread.h>
+#include <string.h>
#include "game.h"
-#define MAKEBUF(type) type *buf = malloc(sizeof(type)); *buf = arg;
/* Shared variables */
return a < b ? a : b;
}
+void *make_buffer(void *ptr, size_t size)
+{
+ void *buf = malloc(size);
+ memcpy(buf, ptr, size);
+
+ return buf;
+}
+
/* Game-related utility functions */
void quit()
/* Register callback functions */
-void register_air_function(struct generator_function arg)
+void register_air_function(struct generator_function func)
{
- MAKEBUF(struct generator_function);
- 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 arg)
+void register_input_handler(unsigned char c, struct input_handler handler)
{
if (input_handlers[c])
return;
- MAKEBUF(struct input_handler);
- input_handlers[c] = buf;
+ input_handlers[c] = make_buffer(&handler, sizeof(struct input_handler));
}
-void register_render_component(void (*arg)(struct winsize ws))
+void register_render_component(void (*callback)(struct winsize ws))
{
- render_components = add_element(render_components, arg);
+ render_components = add_element(render_components, callback);
};
/* Player */
{
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;
void game()
{
- srand(time(0));
-
struct sigaction sa;
sa.sa_handler = &handle_interrupt;
sigaction(SIGINT, &sa, NULL);
__attribute__ ((constructor)) static void init()
{
+ srand(time(0));
+
wall = (struct material) {
.solid = true,
.color = get_color("#5B2F00"),
#include <stdbool.h>
#include <sys/ioctl.h>
+#include <stddef.h>
#define MAP_HEIGHT 1000
#define MAP_WIDTH 1000
#define LIGHT 10
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);
void quit();
bool player_dead();
bool move(struct entity *entity, int xoff, int yoff);
void add_health(struct entity *entity, int health);
-void register_air_function(struct generator_function arg);
-void register_input_handler(unsigned char c, struct input_handler arg);
-void register_render_component(void (*arg)(struct winsize ws));
+void register_air_function(struct generator_function func);
+void register_input_handler(unsigned char c, struct input_handler handler);
+void register_render_component(void (*callback)(struct winsize ws));
#endif
--- /dev/null
+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
--- /dev/null
+#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;
+ }
+ }
+ }
+
+ *ptr = add_element(*ptr, make_buffer(&stack, sizeof(struct itemstack)));
+}
+
+/*
+
+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);
+
+ 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,
+ });
+}
--- /dev/null
+#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);
+};
+
+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