aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cavegen.c92
-rw-r--r--src/display.c201
-rw-r--r--src/ht.c224
3 files changed, 517 insertions, 0 deletions
diff --git a/src/cavegen.c b/src/cavegen.c
new file mode 100644
index 0000000..1365e9d
--- /dev/null
+++ b/src/cavegen.c
@@ -0,0 +1,92 @@
+#include "cavegen.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAX_STEPS 200
+#define NUM_WALKERS 100
+
+enum direction {
+ UP,
+ LEFT,
+ RIGHT,
+ DOWN,
+ NUM_DIRS,
+};
+
+void create_cave(struct map *map)
+{
+ map->width = WIDTH;
+ map->height = HEIGHT;
+
+ // create a map consisting entirely of walls
+ map->map = malloc(sizeof(enum tile_type) * HEIGHT * WIDTH);
+ for (int i = 0; i < HEIGHT; ++i) {
+ for (int j = 0; j < WIDTH; ++j) {
+ map->map[i * WIDTH + j] = WALL;
+ }
+ }
+
+ // start in the middle of the screen
+ int start_x = WIDTH / 2;
+ int start_y = HEIGHT / 2;
+
+ // make the starting point GROUND
+ map->map[start_y * WIDTH + start_x] = GROUND;
+
+ // setup the open tiles
+ struct point *open_tiles = malloc(sizeof(struct point) * HEIGHT * WIDTH);
+
+ int num_open_tiles = 1;
+ open_tiles[0].x = start_x;
+ open_tiles[0].y = start_y;
+
+ for (int i = 0; i < NUM_WALKERS; ++i) {
+ // get a random open point
+ struct point curr_point = open_tiles[rand() % num_open_tiles];
+
+ int x_pos = curr_point.x;
+ int y_pos = curr_point.y;
+
+ // iterate until the walk exits the array or MAX_STEPS is reached
+ for (int j = 1; j < MAX_STEPS - 1 && x_pos < WIDTH - 1 && x_pos >= 1 &&
+ y_pos < HEIGHT - 1 && y_pos >= 1;
+ ++j) {
+ // add new open point if the current point is still a wall
+ if (map->map[y_pos * WIDTH + x_pos] == WALL) {
+ open_tiles[num_open_tiles].x = x_pos;
+ open_tiles[num_open_tiles].y = y_pos;
+ ++num_open_tiles;
+ }
+
+ map->map[y_pos * WIDTH + x_pos] = GROUND; // assign ground
+
+ // move in a random direction
+ enum direction dir = rand() % NUM_DIRS;
+ switch (dir) {
+ case UP : --y_pos; break;
+ case LEFT : --x_pos; break;
+ case RIGHT : ++x_pos; break;
+ case DOWN : ++y_pos; break;
+ default : exit(EXIT_FAILURE); // should not occur
+ }
+ }
+ }
+
+ // assign the up stair and remove from open tiles
+ int in = rand() % num_open_tiles;
+ map->entry_point = open_tiles[in];
+ open_tiles[in] = open_tiles[num_open_tiles - 1];
+ --num_open_tiles;
+ map->map[map->entry_point.y * WIDTH + map->entry_point.x] = UP_STAIR;
+
+ // assign the down stair and remove from open tiles
+ in = rand() % num_open_tiles;
+ struct point down = open_tiles[in];
+ open_tiles[in] = open_tiles[num_open_tiles - 1];
+ --num_open_tiles;
+ map->map[down.y * WIDTH + down.x] = DOWN_STAIR;
+
+ free(open_tiles);
+}
diff --git a/src/display.c b/src/display.c
new file mode 100644
index 0000000..0b08390
--- /dev/null
+++ b/src/display.c
@@ -0,0 +1,201 @@
+#include "display.h"
+
+#include <curses.h>
+#include <locale.h>
+#include <stdlib.h>
+
+#include "entity.h"
+
+struct windows {
+ WINDOW *main;
+ WINDOW *inst;
+ WINDOW *msgs;
+ WINDOW *stat;
+};
+
+static WINDOW *create_newwin(int height, int width, int y, int x)
+{
+ WINDOW *local_win = newwin(height, width, y, x);
+ box(local_win, 0, 0);
+ wrefresh(local_win);
+
+ return local_win;
+}
+
+static display_t *create_windows(void)
+{
+ display_t *wins = malloc(sizeof(display_t));
+
+ wins->inst = create_newwin(
+ INSTRUCTION_PANEL_HEIGHT, INSTRUCTION_PANEL_WIDTH,
+ (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 + 1,
+ (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 +
+ MAIN_PANEL_WIDTH - 1
+ );
+ wins->msgs = create_newwin(
+ MESSAGE_PANEL_HEIGHT, MESSAGE_PANEL_WIDTH,
+ (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 +
+ MAIN_PANEL_HEIGHT,
+ (COLS - MESSAGE_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2
+ );
+ wins->stat = create_newwin(
+ STATUS_PANEL_HEIGHT, STATUS_PANEL_WIDTH,
+ (LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT) / 2 +
+ INSTRUCTION_PANEL_HEIGHT,
+ (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2 +
+ MAIN_PANEL_WIDTH - 1
+ );
+ wins->main = create_newwin(
+ MAIN_PANEL_HEIGHT, MAIN_PANEL_WIDTH,
+ (LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT) / 2 + 1,
+ (COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH) / 2
+ );
+
+ return wins;
+}
+
+display_t *display_init(void)
+{
+ setlocale(LC_ALL, ""); // allow extended ASCII
+
+ initscr(); // initialize curses
+
+ // exit on unsupported consoles
+ if (LINES < MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT ||
+ COLS < MAIN_PANEL_WIDTH + INSTRUCTION_PANEL_WIDTH || !has_colors()) {
+ endwin();
+ fprintf(
+ stderr,
+ "a color terminal is required with at least %dx%d characters\n",
+ INSTRUCTION_PANEL_WIDTH + MAIN_PANEL_WIDTH,
+ MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT
+ );
+ return NULL;
+ }
+
+ // configure curses if startup was successful
+ raw(); // disable line buffering
+ keypad(stdscr, TRUE); // enable reading function keys
+ noecho(); // don't print input
+ curs_set(0); // disable the cursor
+ start_color(); // enable colours
+
+ // setup colours
+ init_pair(1, COLOR_WHITE, COLOR_BLACK);
+ init_pair(2, COLOR_BLACK, COLOR_RED);
+ wattron(stdscr, COLOR_PAIR(1));
+ refresh();
+
+ return create_windows();
+}
+
+void display_map(display_t *disp, struct map *map, ht_t *entities)
+{
+ // print map
+ struct entity *camera = ht_find(entities, "camera");
+
+ for (int i = 1; i < MAIN_PANEL_HEIGHT - 1; ++i) {
+ for (int j = 1; j < MAIN_PANEL_WIDTH - 1; ++j) {
+ int map_i = i - 1 + camera->p.y;
+ int map_j = j - 1 + camera->p.x;
+
+ if (map_i > map->height || map_j > map->width || map_i < 0 ||
+ map_j < 0) {
+ mvwaddch(disp->main, i, j, ' ');
+ } else {
+ switch (map->map[map_i * map->width + map_j]) {
+ case GROUND : mvwaddch(disp->main, i, j, '.'); break;
+ case UP_STAIR : mvwaddch(disp->main, i, j, '<'); break;
+ case DOWN_STAIR :
+ wattron(disp->main, COLOR_PAIR(2));
+ mvwaddch(disp->main, i, j, '>');
+ wattroff(disp->main, COLOR_PAIR(2));
+ break;
+ case WALL :
+ if (map_i > 0 &&
+ map->map[(map_i - 1) * map->width + map_j] != WALL) {
+ mvwprintw(disp->main, i, j, "█");
+ } else if (map_i < map->width - 1 && map->map[(map_i + 1) * map->width + map_j] != WALL) {
+ mvwprintw(disp->main, i, j, "█");
+ } else if (map_j > 0 && map->map[map_i * map->width + map_j - 1] != WALL) {
+ mvwprintw(disp->main, i, j, "█");
+ } else if (map_j < map->width - 1 && map->map[map_i * map->width + map_j + 1] != WALL) {
+ mvwprintw(disp->main, i, j, "█");
+ } else {
+ mvwaddch(disp->main, i, j, ' ');
+ }
+ break;
+ default : mvwaddch(disp->main, i, j, ' ');
+ }
+ }
+ }
+ }
+
+ // print entities
+ ht_iter_init(entities);
+ struct kvp kvp = ht_iter_next(entities);
+ while (kvp.key) {
+ struct entity *e = kvp.val;
+ if (e->visible) {
+ mvwprintw(
+ disp->main, e->p.y - camera->p.y + 1, e->p.x - camera->p.x + 1,
+ e->disp_ch
+ );
+ }
+
+ kvp = ht_iter_next(entities);
+ }
+
+ wrefresh(disp->main);
+}
+
+void display_instructions(display_t *disp)
+{
+ mvwprintw(disp->inst, 1, 2, "h - move left");
+ mvwprintw(disp->inst, 2, 2, "j - move down");
+ mvwprintw(disp->inst, 3, 2, "k - move up");
+ mvwprintw(disp->inst, 4, 2, "l - move right");
+ mvwprintw(disp->inst, 5, 2, "> - move down staircase");
+ mvwprintw(disp->inst, 6, 2, "< - exit via staircase");
+ wrefresh(disp->inst);
+}
+
+void display_message(display_t *disp, char *msg)
+{
+ for (int i = 1; i < MESSAGE_PANEL_WIDTH - 1; ++i) {
+ mvwaddch(disp->msgs, 1, i, ' ');
+ }
+
+ mvwprintw(disp->msgs, 1, 1, msg);
+ wrefresh(disp->msgs);
+}
+
+void display_status(display_t *disp, struct entity *entity)
+{
+ for (int i = 1; i < STATUS_PANEL_HEIGHT - 1; ++i) {
+ for (int j = 1; j < STATUS_PANEL_WIDTH - 1; ++j) {
+ mvwaddch(disp->stat, i, j, ' ');
+ }
+ }
+
+ mvwprintw(disp->stat, 1, 2, "HP:");
+ mvwprintw(disp->stat, 2, 2, "STAMINA:");
+ mvwprintw(disp->stat, 3, 2, "MANA:");
+
+ wrefresh(disp->stat);
+}
+
+enum action display_process_input(void)
+{
+ int ch = getch();
+ switch (ch) {
+ case 'k' : return ACTION_UP;
+ case 'j' : return ACTION_DOWN;
+ case 'h' : return ACTION_LEFT;
+ case 'l' : return ACTION_RIGHT;
+ case '>' : return ACTION_STAIR_DOWN;
+ case '<' : return ACTION_STAIR_UP;
+ case KEY_F(1) : return ACTION_EXIT;
+ default : return ACTION_NONE;
+ }
+}
diff --git a/src/ht.c b/src/ht.c
new file mode 100644
index 0000000..39093eb
--- /dev/null
+++ b/src/ht.c
@@ -0,0 +1,224 @@
+#include "ht.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SIZE 1024
+
+struct node {
+ char *key;
+ void *val;
+ struct node *next;
+};
+
+struct hash_table {
+ struct node **vals;
+ int max_size;
+ int size;
+
+ int curr_index;
+ struct node *curr_node;
+ bool iterating;
+};
+
+static void rehash(ht_t *h, int newsize)
+{
+ ht_t *new_h = ht_create(newsize);
+
+ ht_iter_init(h);
+
+ struct kvp kvp = ht_iter_next(h);
+ while (kvp.key) {
+ ht_insert(new_h, kvp.key, kvp.val);
+ kvp = ht_iter_next(h);
+ }
+
+ // only destroy vals if it isn't NULL
+ if (h->vals) {
+ // iterate through the hash values
+ for (int i = 0; i < h->max_size; ++i) {
+ // iterate through the linked list and remove the value
+ struct node *curr = h->vals[i];
+ struct node *prev;
+ while (curr) {
+ prev = curr;
+ curr = curr->next;
+ free(prev->key);
+ free(prev);
+ }
+ }
+ free(h->vals);
+ }
+
+ h->max_size = newsize;
+ h->vals = new_h->vals;
+ h->curr_index = 0;
+ h->curr_node = NULL;
+ h->iterating = false;
+}
+
+static unsigned long djb2_hash(char *str)
+{
+ unsigned long hash = 5381;
+ int c;
+
+ // hash * 33 + c
+ while ((c = *str++)) hash = ((hash << 5) + hash) + c;
+
+ return hash;
+}
+
+ht_t *ht_create(int max_size)
+{
+ ht_t *h = malloc(sizeof(ht_t));
+ h->max_size = max_size;
+ h->size = 0;
+ h->vals = malloc(sizeof(struct node) * max_size);
+ h->curr_index = 0;
+ h->curr_node = NULL;
+ h->iterating = false;
+
+ return h;
+}
+
+void ht_destroy(ht_t *h)
+{
+ // only destroy if h isn't NULL
+ if (h) {
+ // only destroy vals if it isn't NULL
+ if (h->vals) {
+ // iterate through the hash values
+ for (int i = 0; i < h->max_size; ++i) {
+ // iterate through the linked list and remove the value
+ struct node *curr = h->vals[i];
+ struct node *prev;
+ while (curr) {
+ prev = curr;
+ curr = curr->next;
+ free(prev->key);
+ free(prev);
+ }
+ }
+ free(h->vals);
+ }
+ free(h);
+ }
+}
+
+void *ht_find(ht_t *h, char *key)
+{
+ unsigned int hash = djb2_hash(key) % h->max_size;
+
+ // exit early if hash isn't in table
+ if (!h->vals[hash])
+ return NULL;
+
+ // iterate through values stored at `hash`; exit early if found
+ struct node *curr = h->vals[hash];
+ while (curr) {
+ if (strcmp(key, curr->key) == 0)
+ return curr->val;
+ curr = curr->next;
+ }
+
+ return NULL;
+}
+
+void ht_insert(ht_t *h, char *key, void *val)
+{
+ if (h->size > h->max_size * 0.75)
+ rehash(h, h->max_size * 2);
+
+ unsigned int hash = djb2_hash(key) % h->max_size;
+
+ // create a node
+ int len = strlen(key);
+ struct node *new = malloc(sizeof(struct node));
+ new->key = malloc(sizeof(char) * (len + 1));
+ for (int i = 0; i < len; ++i) {
+ new->key[i] = key[i];
+ }
+ new->key[len] = 0;
+ new->val = val;
+
+ // insert node at beginning of list
+ if (h->vals[hash]) {
+ new->next = h->vals[hash];
+ }
+ h->vals[hash] = new;
+
+ ++(h->size);
+}
+
+void ht_delete(ht_t *h, char *key)
+{
+ if (h->size < h->max_size * 0.25)
+ rehash(h, h->max_size * 0.5);
+
+ unsigned int hash = djb2_hash(key) % h->max_size;
+
+ if (!h->vals[hash])
+ return; // exit if the hash is not found
+
+ // remove the key from the front of the list if it's there
+ if (strcmp(key, h->vals[hash]->key) == 0) {
+ struct node *temp = h->vals[hash];
+ h->vals[hash] = h->vals[hash]->next;
+ free(temp->key);
+ free(temp);
+ } else {
+ // iterate through list and remove once a match is found
+ struct node *prev = h->vals[hash];
+ struct node *curr = h->vals[hash]->next;
+ while (curr) {
+ if (strcmp(key, curr->key) == 0) {
+ prev->next = curr->next;
+ free(curr->key);
+ free(curr);
+ return;
+ }
+
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+}
+
+int ht_size(ht_t *h)
+{
+ return h->size;
+}
+
+void ht_iter_init(ht_t *h)
+{
+ h->curr_index = 0;
+ h->curr_node = h->vals[0];
+}
+
+struct kvp ht_iter_next(ht_t *h)
+{
+ struct kvp out = {
+ .key = NULL,
+ .val = NULL,
+ };
+
+ // return NULL if we've reached the end
+ if (!h->curr_node && h->curr_index >= h->max_size)
+ return out;
+
+ // look for next index with node if current node is NULL
+ if (!h->curr_node) {
+ while (!h->curr_node) {
+ h->curr_node = h->vals[h->curr_index++];
+ if (h->curr_index >= h->max_size)
+ return out;
+ }
+ }
+
+ // get the value and move to the next node
+ out.key = h->curr_node->key;
+ out.val = h->curr_node->val;
+ h->curr_node = h->curr_node->next;
+ return out;
+}