summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--corpus/map1_bromm.txt18
-rw-r--r--corpus/map1_dagna.txt18
-rw-r--r--corpus/map1_keldor.txt18
-rw-r--r--corpus/map1_skara.txt18
-rw-r--r--corpus/map1_thrain.txt18
-rw-r--r--game.c324
-rw-r--r--maps.h40
-rw-r--r--maps/map1.h6
-rw-r--r--maps/map1.txt6
-rw-r--r--nonstd.h6
-rw-r--r--vectordb.c1
-rw-r--r--vectordb.h2
13 files changed, 426 insertions, 57 deletions
diff --git a/Makefile b/Makefile
index 696534f..0bce7c3 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,9 @@ PROMPT_HEADERS := $(PROMPT_TXT:.txt=.h)
MAP_TXT := $(wildcard maps/*.txt)
MAP_HEADERS := $(MAP_TXT:.txt=.h)
+CORPUS_TXT := $(wildcard corpus/*.txt)
+CORPUS_VDB := $(CORPUS_TXT:.txt=.vdb)
+
help: .help
build/llama.cpp: .assure # Build llama.cpp libraries
@@ -37,6 +40,8 @@ build/prompts: $(PROMPT_HEADERS) # Generate prompts in C style header
build/maps: $(MAP_HEADERS) # Generate maps in C style header
+build/corpus: $(CORPUS_VDB) # Build vector DBs for all corpuses
+
run/fetch-models: .assure # Fetch GGUF models
-mkdir -p models
cd models && wget -nc -i ../models.txt
@@ -55,3 +60,6 @@ prompts/%.h: prompts/%.txt .assure
maps/%.h: maps/%.txt .assure
xxd -i $< > $@
+
+corpus/%.vdb: corpus/%.txt build/context
+ ./context -i $< -o $@
diff --git a/corpus/map1_bromm.txt b/corpus/map1_bromm.txt
new file mode 100644
index 0000000..9d2f355
--- /dev/null
+++ b/corpus/map1_bromm.txt
@@ -0,0 +1,18 @@
+Bromm is a dwarf stonemason who measures walls by touch and sound.
+Bromm keeps a black slate with chalk marks for every safe path in the ruins.
+Bromm believes the northern ruins predate the current road by two eras.
+Bromm once found a bronze hinge in the ruins and still carries it for luck.
+Bromm teaches travelers how to test a stone by tapping for a hollow ring.
+Bromm mistrusts quick repairs and prefers heavy timber bracing.
+Bromm says the ruins smell of old lime and wet ash after rain.
+Bromm trades small carvings for dried meat and lamp oil.
+Bromm thinks the marsh lights are reflections from a buried lens.
+Bromm has a friendly rivalry with Dagna about whose warnings are wiser.
+Bromm keeps his beard braided with a single iron bead from his clan.
+Bromm wants to map every chamber in the north before winter.
+Bromm is soft spoken but grows excited when discussing arches.
+Bromm believes the ruins hide a collapsed stair with carved runes.
+Bromm says the safest approach is to enter at dawn and leave by noon.
+Bromm can describe three alternate routes to avoid the broken bridge.
+Bromm worries that careless digging will wake something that sleeps in stone.
+Bromm asks visitors if they have seen mason marks shaped like a trident.
diff --git a/corpus/map1_dagna.txt b/corpus/map1_dagna.txt
new file mode 100644
index 0000000..cc80a68
--- /dev/null
+++ b/corpus/map1_dagna.txt
@@ -0,0 +1,18 @@
+Dagna is a dwarf well-keeper who knows every bucket and rope in the village.
+Dagna believes the well is safe because the water tastes of iron, not rot.
+Dagna keeps a ledger of how much water each household draws in a week.
+Dagna replaced the well crank with a dwarven gear she forged herself.
+Dagna says the well has a second shaft sealed by a stone plug.
+Dagna once pulled up a smooth glass bead that does not scratch.
+Dagna offers travelers a cup of water and a blunt warning about haste.
+Dagna can name every herb that grows within ten paces of the well.
+Dagna suspects the marsh lights are bait for thieves.
+Dagna thinks Bromm worries too much about the ruins and not enough about the road.
+Dagna keeps a small shrine to the Deep Mother near the well wall.
+Dagna loves riddles and answers only after a trade of facts.
+Dagna claims the well water calms fever if boiled with bitterroot.
+Dagna dislikes gossip but listens closely for news of caravans.
+Dagna believes a hidden aquifer feeds the village from the northern hills.
+Dagna is saving for a brass pump to replace the old rope.
+Dagna can spot forged coin by the sound it makes on stone.
+Dagna asks travelers if they have seen a faint blue glow in deep water.
diff --git a/corpus/map1_keldor.txt b/corpus/map1_keldor.txt
new file mode 100644
index 0000000..5c918bd
--- /dev/null
+++ b/corpus/map1_keldor.txt
@@ -0,0 +1,18 @@
+Keldor is a dwarf scout who watches the marsh from the old footpath.
+Keldor claims the lights in the marsh move in patterns like a slow dance.
+Keldor keeps a lantern hooded until the last moment to avoid drawing notice.
+Keldor believes the marsh hides a buried wagon sunk in peat.
+Keldor can follow frog calls to find the driest stepping stones.
+Keldor says the safest crossing is after three dry days, not two.
+Keldor carries a whistle tuned to a pitch only his hound can hear.
+Keldor tells stories of a pale heron that never casts a shadow.
+Keldor thinks the ruins and the marsh are linked by an old drainage tunnel.
+Keldor traded a silver button to learn a fisher's secret route.
+Keldor trusts Dagna's water but refuses to drink after midnight.
+Keldor marks his trail with tiny chips of white quartz.
+Keldor says the marsh lights went dark on the night the moon turned red.
+Keldor is curious about old maps and collects any scraps he finds.
+Keldor believes Bromm's trident mark is a warning, not a signature.
+Keldor is patient in silence but asks direct questions when pressed.
+Keldor wants proof that the marsh lights are not a signal to smugglers.
+Keldor asks travelers to describe any strange scents like bitter metal or smoke.
diff --git a/corpus/map1_skara.txt b/corpus/map1_skara.txt
new file mode 100644
index 0000000..00f77ff
--- /dev/null
+++ b/corpus/map1_skara.txt
@@ -0,0 +1,18 @@
+Skara is a dwarf bell-ringer who keeps time for the village with a bronze handbell.
+Skara claims the fog carries echoes that belong to no bell in town.
+Skara keeps her bell clapper wrapped in cloth to avoid false rings.
+Skara believes the marsh hides an old shrine with a cracked chime.
+Skara can tell distance by the way sound bends in wet air.
+Skara remembers every funeral toll and writes the names in a small book.
+Skara warns travelers to avoid singing in the marsh after sunset.
+Skara thinks Keldor's lights might be signals from smugglers.
+Skara says Bromm once found a bell-shaped stone near the ruins.
+Skara trades stories for thin copper wire and beeswax.
+Skara is suspicious of mirrors and keeps hers covered.
+Skara believes Dagna's well water dulls the ringing in her ears.
+Skara says the bells in fog sound like chains, not bronze.
+Skara is gentle in speech but firm about her warnings.
+Skara wants to tune the village bell to a lower, steadier note.
+Skara can teach a simple knock code used by miners.
+Skara asks travelers if they have heard three rings with no pause.
+Skara says the marsh grows quiet just before the lights appear.
diff --git a/corpus/map1_thrain.txt b/corpus/map1_thrain.txt
new file mode 100644
index 0000000..e7a79b7
--- /dev/null
+++ b/corpus/map1_thrain.txt
@@ -0,0 +1,18 @@
+Thrain is a dwarf bridge warden who inspects beams by listening for a low hum.
+Thrain keeps a pouch of pegs and wedges for emergency repairs.
+Thrain believes the old bridge was built by traders, not soldiers.
+Thrain marks safe planks with tiny chalk dots no one else notices.
+Thrain once saved a cart by spotting a hairline crack at dawn.
+Thrain says the river below turns louder right before a storm.
+Thrain trades advice for nails, tar, and braided rope.
+Thrain is skeptical of the marsh lights and calls them trick mirrors.
+Thrain respects Dagna's ledger and asks her for bridge traffic counts.
+Thrain thinks Bromm's trident mark is a builder's guild sign.
+Thrain keeps a small tin whistle for signaling across the span.
+Thrain fears rot more than storms and checks every joint twice.
+Thrain wants to replace the center beam with black oak from the hills.
+Thrain can point out a hidden ford two bends downstream.
+Thrain says the safest crossing is single file with steady steps.
+Thrain believes the bells in fog come from chains under the bridge.
+Thrain asks travelers if they have spare pitch or tar.
+Thrain is patient with questions but impatient with boasts.
diff --git a/game.c b/game.c
index d430ba3..f036fb2 100644
--- a/game.c
+++ b/game.c
@@ -6,7 +6,7 @@
#define NONSTD_IMPLEMENTATION
#include "nonstd.h"
-#include "maps/map1.h"
+#include "maps.h"
#define MIN_W 40
#define MIN_H 12
@@ -49,13 +49,21 @@ typedef struct {
Inventory inventory;
} Player;
+#define DIALOG_HISTORY_MAX 16
+
+typedef struct {
+ char prompt[128];
+ char response[256];
+} DialogEntry;
+
typedef struct {
- const unsigned char *data;
- int len;
- int width;
- int height;
- u32 *cells;
-} Map;
+ int open;
+ char input[128];
+ int input_len;
+ int npc_index;
+ DialogEntry entries[DIALOG_HISTORY_MAX];
+ int entry_count;
+} Dialog;
static int clamp(int value, int min, int max);
@@ -78,9 +86,24 @@ static void draw_border(int x, int y, int w, int h, uintattr_t fg) {
tb_set_cell(x + w - 1, y + h - 1, CP_BR, fg, TB_DEFAULT);
}
-static void draw_room(int x, int y, int w, int h, uintattr_t fg) {
- draw_border(x, y, w, h, fg);
- tb_set_cell(x + w / 2, y, '+', fg, TB_DEFAULT);
+static void draw_border_bg(int x, int y, int w, int h, uintattr_t fg,
+ uintattr_t bg) {
+ int ix;
+ int iy;
+
+ for (ix = 0; ix < w; ix++) {
+ tb_set_cell(x + ix, y, CP_H, fg, bg);
+ tb_set_cell(x + ix, y + h - 1, CP_H, fg, bg);
+ }
+ for (iy = 0; iy < h; iy++) {
+ tb_set_cell(x, y + iy, CP_V, fg, bg);
+ tb_set_cell(x + w - 1, y + iy, CP_V, fg, bg);
+ }
+
+ tb_set_cell(x, y, CP_TL, fg, bg);
+ tb_set_cell(x + w - 1, y, CP_TR, fg, bg);
+ tb_set_cell(x, y + h - 1, CP_BL, fg, bg);
+ tb_set_cell(x + w - 1, y + h - 1, CP_BR, fg, bg);
}
static void get_layout(int w, int h, int *map_x, int *map_y, int *map_w,
@@ -187,7 +210,15 @@ static void map_set(Map *map, int x, int y, u32 ch) {
static int map_is_walkable(const Map *map, int x, int y) {
u32 ch = map_get(map, x, y);
- return ch == MAP_FLOOR_CH || ch == '$';
+ return ch == MAP_FLOOR_CH || ch == '$' || ch == 'N'
+ || (ch >= '0' && ch <= '9');
+}
+
+static int npc_index_from_tile(u32 ch) {
+ if (ch >= '0' && ch <= '9') {
+ return (int)(ch - '0');
+ }
+ return -1;
}
static void map_free(Map *map) {
@@ -245,6 +276,7 @@ static void draw_map(const Map *map, int map_x, int map_y, int view_w,
int mx = cam_x + ix;
int my = cam_y + iy;
u32 ch = map_get(map, mx, my);
+ u32 draw_ch = (ch >= '0' && ch <= '9') ? 'N' : ch;
uintattr_t fg = COLOR_WHITE_256;
if (ch == MAP_FLOOR_CH) {
fg = MAP_FLOOR_FG;
@@ -254,12 +286,12 @@ static void draw_map(const Map *map, int map_x, int map_y, int view_w,
fg = COLOR_ORANGE_256;
} else if (ch == 'B' || ch == 'S' || ch == 'G') {
fg = COLOR_RED_256;
- } else if (ch == 'N') {
+ } else if (ch == 'N' || (ch >= '0' && ch <= '9')) {
fg = COLOR_CYAN_256;
} else if (ch >= MAP_BORDER_MIN && ch <= MAP_BORDER_MAX) {
fg = COLOR_BORDER_256;
}
- tb_set_cell(map_x + ix, map_y + iy, ch, fg, TB_DEFAULT);
+ tb_set_cell(map_x + ix, map_y + iy, draw_ch, fg, TB_DEFAULT);
}
}
@@ -357,8 +389,97 @@ static void update_status(const char *message) {
status_msg = message ? message : "";
}
+static void copy_truncated(char *dst, size_t dst_size, const char *src, int max_chars) {
+ int i = 0;
+ if (dst_size == 0) {
+ return;
+ }
+ if (max_chars < 0) {
+ max_chars = 0;
+ }
+ while (i < max_chars && src[i] != '\0' && i < (int)dst_size - 1) {
+ dst[i] = src[i];
+ i++;
+ }
+ dst[i] = '\0';
+}
+
+static void dialog_open(Dialog *dialog, int npc_index) {
+ dialog->open = 1;
+ dialog->input_len = 0;
+ dialog->input[0] = '\0';
+ dialog->npc_index = npc_index;
+}
+
+static void dialog_close(Dialog *dialog) {
+ dialog->open = 0;
+ dialog->npc_index = -1;
+}
+
+static void dialog_append(Dialog *dialog, uint32_t ch) {
+ if (ch < 32 || ch > 126) {
+ return;
+ }
+ if (dialog->input_len >= (int)(sizeof(dialog->input) - 1)) {
+ return;
+ }
+ dialog->input[dialog->input_len++] = (char)ch;
+ dialog->input[dialog->input_len] = '\0';
+}
+
+static void dialog_backspace(Dialog *dialog) {
+ if (dialog->input_len <= 0) {
+ return;
+ }
+ dialog->input_len--;
+ dialog->input[dialog->input_len] = '\0';
+}
+
+static void dialog_submit(Dialog *dialog, const GameMap *game_map) {
+ if (dialog->input_len == 0) {
+ return;
+ }
+ {
+ const char *demo = "Demo reply: The old ruins are north of here.";
+ const char *reply = demo;
+ if (game_map && dialog->npc_index >= 0 && dialog->npc_index < 10) {
+ const char *npc_reply = game_map->npcs[dialog->npc_index].reply;
+ if (npc_reply && npc_reply[0] != '\0') {
+ reply = npc_reply;
+ }
+ }
+ if (dialog->entry_count >= DIALOG_HISTORY_MAX) {
+ for (int i = 1; i < DIALOG_HISTORY_MAX; i++) {
+ dialog->entries[i - 1] = dialog->entries[i];
+ }
+ dialog->entry_count = DIALOG_HISTORY_MAX - 1;
+ }
+ snprintf(dialog->entries[dialog->entry_count].prompt,
+ sizeof(dialog->entries[dialog->entry_count].prompt), "%s", dialog->input);
+ snprintf(dialog->entries[dialog->entry_count].response,
+ sizeof(dialog->entries[dialog->entry_count].response), "%s", reply);
+ dialog->entry_count++;
+ }
+ dialog->input_len = 0;
+ dialog->input[0] = '\0';
+}
+
+static void update_npc_status(const GameMap *game_map, int npc_index) {
+ static char status_buf[128];
+ const char *name = NULL;
+ if (game_map && npc_index >= 0 && npc_index < 10) {
+ name = game_map->npcs[npc_index].name;
+ }
+ if (name && name[0] != '\0') {
+ snprintf(status_buf, sizeof(status_buf), "You approach %s.", name);
+ } else {
+ snprintf(status_buf, sizeof(status_buf), "You approach the NPC.");
+ }
+ update_status(status_buf);
+}
+
static void render(const Map *map, const Player *player, int *cam_x,
- int *cam_y, int *out_view_w, int *out_view_h) {
+ int *cam_y, int *out_view_w, int *out_view_h, const Dialog *dialog) {
int w;
int h;
int map_x;
@@ -436,6 +557,75 @@ static void render(const Map *map, const Player *player, int *cam_x,
tb_print(2, msg1_y, COLOR_GREEN_256, TB_DEFAULT, status_msg);
tb_print(2, msg2_y, COLOR_WHITE_256, TB_DEFAULT, "Move: arrows Quit: q/ESC");
+ if (dialog->open) {
+ int box_w = map_w - 4;
+ int box_h = 12;
+ int box_x = map_x + 2;
+ int box_y = map_y + map_h - box_h - 1;
+ if (box_w > w - 2) {
+ box_w = w - 2;
+ box_x = 1;
+ }
+ if (box_h > h - 2) {
+ box_h = h - 2;
+ box_y = 1;
+ }
+ if (box_w < 20) {
+ box_w = 20;
+ box_x = map_x + 1;
+ }
+ if (box_y < map_y + 1) {
+ box_y = map_y + 1;
+ }
+ for (int iy = 0; iy < box_h; iy++) {
+ for (int ix = 0; ix < box_w; ix++) {
+ tb_set_cell(box_x + ix, box_y + iy, ' ', COLOR_WHITE_256, 19);
+ }
+ }
+ draw_border_bg(box_x, box_y, box_w, box_h, COLOR_WHITE_256, 19);
+ {
+ int input_y = box_y + box_h - 3;
+ int footer_y = box_y + box_h - 2;
+ int log_y = box_y + 1;
+ int max_lines = input_y - log_y;
+ int max_text = box_w - 4 - 5;
+ int line = 0;
+
+ if (max_text < 0) {
+ max_text = 0;
+ }
+ int max_entries = max_lines / 2;
+ int start = dialog->entry_count - max_entries;
+ if (start < 0) {
+ start = 0;
+ }
+ for (int i = start; i < dialog->entry_count && line + 1 <= max_lines; i++) {
+ char prompt_buf[128];
+ char response_buf[256];
+ copy_truncated(prompt_buf, sizeof(prompt_buf), dialog->entries[i].prompt, max_text);
+ copy_truncated(response_buf, sizeof(response_buf), dialog->entries[i].response, max_text);
+ if (line < max_lines) {
+ tb_printf(box_x + 2, log_y + line, COLOR_WHITE_256, 19, "You: %s", prompt_buf);
+ line++;
+ }
+ if (line < max_lines) {
+ tb_printf(box_x + 2, log_y + line, COLOR_GREEN_256, 19, "NPC: %s", response_buf);
+ line++;
+ }
+ }
+
+ tb_printf(box_x + 2, input_y, COLOR_WHITE_256, 19, "Say: %s", dialog->input);
+ {
+ int cursor_x = box_x + 2 + 5 + dialog->input_len;
+ int cursor_y = input_y;
+ if (cursor_x < box_x + box_w - 1) {
+ tb_set_cell(cursor_x, cursor_y, '_', COLOR_WHITE_256 | TB_BOLD, 19);
+ }
+ }
+ tb_print(box_x + 2, footer_y, COLOR_WHITE_256, 19, "Enter: send ESC: close");
+ }
+ }
+
tb_present();
*out_view_w = view_w;
*out_view_h = view_h;
@@ -453,15 +643,22 @@ static int clamp(int value, int min, int max) {
int main(void) {
Player player = {0};
- Map map = {0};
+ array(GameMap) maps;
+ GameMap map1 = {0};
+ GameMap *current_map = NULL;
int running = 1;
int view_w = 0;
int view_h = 0;
int cam_x = 0;
int cam_y = 0;
+ Dialog dialog = {0};
player_init(&player);
- map_init(&map, maps_map1_txt, (int)maps_map1_txt_len);
+ array_init(maps);
+ map1 = make_map1();
+ array_push(maps, map1);
+ current_map = &maps.data[0];
+ map_init(&current_map->map, current_map->data, current_map->len);
if (tb_init() != TB_OK) {
fprintf(stderr, "Failed to init termbox.\n");
@@ -474,47 +671,82 @@ int main(void) {
while (running) {
struct tb_event ev;
- render(&map, &player, &cam_x, &cam_y, &view_w, &view_h);
+ render(&current_map->map, &player, &cam_x, &cam_y, &view_w, &view_h, &dialog);
tb_poll_event(&ev);
if (ev.type == TB_EVENT_KEY) {
- if (ev.key == TB_KEY_ESC || ev.ch == 'q') {
- running = 0;
- } else if (ev.key == TB_KEY_ARROW_UP) {
- int next_y = player.y - 1;
- if (map_is_walkable(&map, player.x, next_y)) {
- player.y = next_y;
- }
- } else if (ev.key == TB_KEY_ARROW_DOWN) {
- int next_y = player.y + 1;
- if (map_is_walkable(&map, player.x, next_y)) {
- player.y = next_y;
+ if (dialog.open) {
+ if (ev.key == TB_KEY_ESC) {
+ dialog_close(&dialog);
+ } else if (ev.key == TB_KEY_ENTER) {
+ dialog_submit(&dialog, current_map);
+ } else if (ev.key == TB_KEY_BACKSPACE || ev.key == TB_KEY_BACKSPACE2) {
+ dialog_backspace(&dialog);
+ } else if (ev.ch) {
+ dialog_append(&dialog, ev.ch);
}
- } else if (ev.key == TB_KEY_ARROW_LEFT) {
- int next_x = player.x - 1;
- if (map_is_walkable(&map, next_x, player.y)) {
- player.x = next_x;
+ } else {
+ if (ev.key == TB_KEY_ESC || ev.ch == 'q') {
+ running = 0;
+ } else if (ev.key == TB_KEY_ARROW_UP) {
+ int next_y = player.y - 1;
+ u32 target = map_get(&current_map->map, player.x, next_y);
+ int npc_index = npc_index_from_tile(target);
+ if (target == 'N' || npc_index >= 0) {
+ dialog_open(&dialog, npc_index);
+ update_npc_status(current_map, npc_index);
+ } else if (map_is_walkable(&current_map->map, player.x, next_y)) {
+ player.y = next_y;
+ }
+ } else if (ev.key == TB_KEY_ARROW_DOWN) {
+ int next_y = player.y + 1;
+ u32 target = map_get(&current_map->map, player.x, next_y);
+ int npc_index = npc_index_from_tile(target);
+ if (target == 'N' || npc_index >= 0) {
+ dialog_open(&dialog, npc_index);
+ update_npc_status(current_map, npc_index);
+ } else if (map_is_walkable(&current_map->map, player.x, next_y)) {
+ player.y = next_y;
+ }
+ } else if (ev.key == TB_KEY_ARROW_LEFT) {
+ int next_x = player.x - 1;
+ u32 target = map_get(&current_map->map, next_x, player.y);
+ int npc_index = npc_index_from_tile(target);
+ if (target == 'N' || npc_index >= 0) {
+ dialog_open(&dialog, npc_index);
+ update_npc_status(current_map, npc_index);
+ } else if (map_is_walkable(&current_map->map, next_x, player.y)) {
+ player.x = next_x;
+ }
+ } else if (ev.key == TB_KEY_ARROW_RIGHT) {
+ int next_x = player.x + 1;
+ u32 target = map_get(&current_map->map, next_x, player.y);
+ int npc_index = npc_index_from_tile(target);
+ if (target == 'N' || npc_index >= 0) {
+ dialog_open(&dialog, npc_index);
+ update_npc_status(current_map, npc_index);
+ } else if (map_is_walkable(&current_map->map, next_x, player.y)) {
+ player.x = next_x;
+ }
}
- } else if (ev.key == TB_KEY_ARROW_RIGHT) {
- int next_x = player.x + 1;
- if (map_is_walkable(&map, next_x, player.y)) {
- player.x = next_x;
+ if (map_get(&current_map->map, player.x, player.y) == '$') {
+ player.gold += 10;
+ map_set(&current_map->map, player.x, player.y, MAP_FLOOR_CH);
+ update_status("You pick up 10 gold.");
}
}
- if (map_get(&map, player.x, player.y) == '$') {
- player.gold += 10;
- map_set(&map, player.x, player.y, MAP_FLOOR_CH);
- update_status("You pick up 10 gold.");
- }
- player.x = clamp(player.x, 0, map.width > 1 ? map.width - 1 : 0);
- player.y = clamp(player.y, 0, map.height > 1 ? map.height - 1 : 0);
+ player.x = clamp(player.x, 0, current_map->map.width > 1 ? current_map->map.width - 1 : 0);
+ player.y = clamp(player.y, 0, current_map->map.height > 1 ? current_map->map.height - 1 : 0);
} else if (ev.type == TB_EVENT_RESIZE) {
- player.x = clamp(player.x, 0, map.width > 1 ? map.width - 1 : 0);
- player.y = clamp(player.y, 0, map.height > 1 ? map.height - 1 : 0);
+ player.x = clamp(player.x, 0, current_map->map.width > 1 ? current_map->map.width - 1 : 0);
+ player.y = clamp(player.y, 0, current_map->map.height > 1 ? current_map->map.height - 1 : 0);
}
}
player_free(&player);
- map_free(&map);
+ for (size_t i = 0; i < maps.length; i++) {
+ map_free(&maps.data[i].map);
+ }
+ array_free(maps);
tb_shutdown();
return 0;
}
diff --git a/maps.h b/maps.h
new file mode 100644
index 0000000..f3cd617
--- /dev/null
+++ b/maps.h
@@ -0,0 +1,40 @@
+#ifndef MAPS_H
+#define MAPS_H
+
+#include "nonstd.h"
+
+#include "maps/map1.h"
+
+typedef struct {
+ const unsigned char *data;
+ int len;
+ int width;
+ int height;
+ u32 *cells;
+} Map;
+
+typedef struct {
+ const char *name;
+ const char *reply;
+} NpcSettings;
+
+typedef struct {
+ const unsigned char *data;
+ int len;
+ Map map;
+ NpcSettings npcs[10];
+} GameMap;
+
+static inline GameMap make_map1(void) {
+ GameMap map = {0};
+ map.data = maps_map1_txt;
+ map.len = (int)maps_map1_txt_len;
+ map.npcs[0] = (NpcSettings){.name = "Bromm", .reply = "Bromm: The old ruins are north of here."};
+ map.npcs[1] = (NpcSettings){.name = "Dagna", .reply = "Dagna: The well is safe, mostly."};
+ map.npcs[2] = (NpcSettings){.name = "Keldor", .reply = "Keldor: I saw lights in the marsh last night."};
+ map.npcs[3] = (NpcSettings){.name = "Thrain", .reply = "Thrain: Mind the bridge; the beams sing when they're weak."};
+ map.npcs[4] = (NpcSettings){.name = "Skara", .reply = "Skara: If you hear bells in the fog, turn back."};
+ return map;
+}
+
+#endif
diff --git a/maps/map1.h b/maps/map1.h
index 110a939..b5f54dd 100644
--- a/maps/map1.h
+++ b/maps/map1.h
@@ -24,7 +24,7 @@ unsigned char maps_map1_txt[] = {
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x30, 0x31, 0x32, 0x33, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x53, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x7e,
@@ -104,7 +104,7 @@ unsigned char maps_map1_txt[] = {
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x4e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x34, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
@@ -114,7 +114,7 @@ unsigned char maps_map1_txt[] = {
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
- 0x2e, 0x2e, 0x4e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x24, 0x2e,
+ 0x2e, 0x2e, 0x35, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x24, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x24,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
diff --git a/maps/map1.txt b/maps/map1.txt
index fe3b47a..62a51fc 100644
--- a/maps/map1.txt
+++ b/maps/map1.txt
@@ -1,7 +1,7 @@
....................................................................................................
..................~~~~~~............................................................................
...................~~~~.............................................................................
-...................................S........~~~~~~~~~...............................................
+....0123...........................S........~~~~~~~~~...............................................
........G...................................~~~~~~~~~...............................................
............................................~~~~~~~~................................................
............................................~~~~~~.......G..........................................
@@ -10,8 +10,8 @@
......$..╔═══════════════╗.........................................$................................
..$......║...............║..........................................................................
.........║...............║..........................................................................
-.........║...N...........║..........................................................................
-.........║...........N...║.$............$...........................................................
+.........║...4...........║..........................................................................
+.........║...........5...║.$............$...........................................................
.........║...............║..........................................................................
.........║...............║...............................G..........................................
.........║...............║...................................................G......................
diff --git a/nonstd.h b/nonstd.h
index 146b12d..f12ba8a 100644
--- a/nonstd.h
+++ b/nonstd.h
@@ -334,8 +334,6 @@ NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ..
#define COLOR_WARNING "\033[33m"
#define COLOR_ERROR "\033[31m"
-#endif // NONSTD_H
-
#ifdef NONSTD_IMPLEMENTATION
NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) {
@@ -807,6 +805,8 @@ NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1
#endif // NONSTD_IMPLEMENTATION
+#endif // NONSTD_H
+
/*
BSD 2-Clause License
@@ -832,4 +832,4 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/ \ No newline at end of file
+*/
diff --git a/vectordb.c b/vectordb.c
index 3812ecb..c80c1fd 100644
--- a/vectordb.c
+++ b/vectordb.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <math.h>
#include <stdint.h>
+#include <errno.h>
#include "llama.h"
#include "vectordb.h"
diff --git a/vectordb.h b/vectordb.h
index 5c41a0a..786a7ed 100644
--- a/vectordb.h
+++ b/vectordb.h
@@ -3,8 +3,6 @@
#include "llama.h"
-#include <errno.h>
-
#define VDB_MAX_DOCS 1000
#define VDB_EMBED_SIZE 768
#define VDB_MAX_TEXT 1024