aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am13
-rw-r--r--src/actions.zig12
-rw-r--r--src/cavegen.c103
-rw-r--r--src/cavegen.h37
-rw-r--r--src/common.h20
-rw-r--r--src/display.c223
-rw-r--r--src/display.h54
-rw-r--r--src/ecs/entity.zig3
-rw-r--r--src/entity.h24
-rw-r--r--src/frontend/ncurses.zig186
-rw-r--r--src/ht.c235
-rw-r--r--src/ht.h40
-rw-r--r--src/main.c192
-rw-r--r--src/main.zig21
-rw-r--r--src/root.zig10
15 files changed, 232 insertions, 941 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index f08ba63..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,13 +0,0 @@
-bin_PROGRAMS = urlg
-
-urlg_SOURCES = \
- cavegen.c \
- cavegen.h \
- display.c \
- display.h \
- ht.c \
- ht.h \
- common.h \
- entity.h \
- main.c
-
diff --git a/src/actions.zig b/src/actions.zig
new file mode 100644
index 0000000..a801e28
--- /dev/null
+++ b/src/actions.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+
+pub const Action = enum {
+ illegal,
+ exit,
+ move_up,
+ move_down,
+ move_left,
+ move_right,
+ down_stair,
+ up_stair,
+};
diff --git a/src/cavegen.c b/src/cavegen.c
deleted file mode 100644
index 8a74836..0000000
--- a/src/cavegen.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#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/cavegen.h b/src/cavegen.h
deleted file mode 100644
index ae5f698..0000000
--- a/src/cavegen.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#ifndef CAVEGEN_H_
-#define CAVEGEN_H_
-
-#include "common.h"
-
-#define HEIGHT 100
-#define WIDTH 180
-
-enum tile_type {
- WALL,
- GROUND,
- UP_STAIR,
- DOWN_STAIR,
-};
-
-struct map {
- enum tile_type *map;
- struct point entry_point;
-
- int width;
- int height;
-};
-
-void create_cave(struct map *map);
-
-#endif // CAVEGEN_H_
diff --git a/src/common.h b/src/common.h
deleted file mode 100644
index c36f0f2..0000000
--- a/src/common.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#ifndef COMMON_H_
-#define COMMON_H_
-
-struct point {
- int x;
- int y;
-};
-
-#endif // COMMON_H_
diff --git a/src/display.c b/src/display.c
deleted file mode 100644
index e79ed2c..0000000
--- a/src/display.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#include "display.h"
-
-#include <curses.h>
-#include <locale.h>
-#include <stdlib.h>
-#include <string.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_destroy(display_t *disp)
-{
- delwin(disp->main);
- delwin(disp->inst);
- delwin(disp->msgs);
- delwin(disp->stat);
-
- free(disp);
-}
-
-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) {
- mvwaddch(disp->main, i, j, ACS_BLOCK);
- } else if (map_i < map->width - 1 && map->map[(map_i + 1) * map->width + map_j] != WALL) {
- mvwaddch(disp->main, i, j, ACS_BLOCK);
- } else if (map_j > 0 && map->map[map_i * map->width + map_j - 1] != WALL) {
- mvwaddch(disp->main, i, j, ACS_BLOCK);
- } else if (map_j < map->width - 1 && map->map[map_i * map->width + map_j + 1] != WALL) {
- mvwaddch(disp->main, i, j, ACS_BLOCK);
- } 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) {
- mvwaddch(
- disp->main, e->p.y - camera->p.y + 1, e->p.x - camera->p.x + 1,
- e->disp_ch[0]
- );
- }
-
- 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/display.h b/src/display.h
deleted file mode 100644
index b5d7d53..0000000
--- a/src/display.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#ifndef DISPLAY_H_
-#define DISPLAY_H_
-
-#include <stdbool.h>
-
-#include "cavegen.h"
-#include "entity.h"
-#include "ht.h"
-
-#define MAIN_PANEL_WIDTH 100
-#define MAIN_PANEL_HEIGHT 41
-#define INSTRUCTION_PANEL_WIDTH 32
-#define INSTRUCTION_PANEL_HEIGHT 39
-#define MESSAGE_PANEL_WIDTH 100
-#define MESSAGE_PANEL_HEIGHT 3
-#define STATUS_PANEL_WIDTH 32
-#define STATUS_PANEL_HEIGHT 5
-
-typedef struct windows display_t;
-
-enum action {
- ACTION_NONE,
- ACTION_EXIT,
- ACTION_DOWN,
- ACTION_UP,
- ACTION_LEFT,
- ACTION_RIGHT,
- ACTION_STAIR_DOWN,
- ACTION_STAIR_UP,
- NUM_ACTIONS,
-};
-
-display_t *display_init(void);
-void display_destroy(display_t *disp);
-
-void display_map(display_t *disp, struct map *map, ht_t *entities);
-void display_instructions(display_t *disp);
-void display_message(display_t *disp, char *msg);
-void display_status(display_t *disp, struct entity *entity);
-
-enum action display_process_input(void);
-
-#endif // DISPLAY_H_
diff --git a/src/ecs/entity.zig b/src/ecs/entity.zig
new file mode 100644
index 0000000..6131cff
--- /dev/null
+++ b/src/ecs/entity.zig
@@ -0,0 +1,3 @@
+const std = @import("std");
+
+pub const Entity = struct {};
diff --git a/src/entity.h b/src/entity.h
deleted file mode 100644
index f59d868..0000000
--- a/src/entity.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#ifndef ENTITY_H_
-#define ENTITY_H_
-
-#include "common.h"
-
-struct entity {
- struct point p;
- char *disp_ch;
- bool solid;
- bool visible;
-};
-
-#endif // ENTITY_H_
diff --git a/src/frontend/ncurses.zig b/src/frontend/ncurses.zig
new file mode 100644
index 0000000..1586cf0
--- /dev/null
+++ b/src/frontend/ncurses.zig
@@ -0,0 +1,186 @@
+const std = @import("std");
+const Entity = @import("../ecs/entity.zig").Entity;
+const Action = @import("../actions.zig").Action;
+const ncurses = @cImport({
+ @cInclude("ncurses.h");
+});
+const locale = @cImport({
+ @cInclude("locale.h");
+});
+
+const MAIN_PANEL_WIDTH = 100;
+const MAIN_PANEL_HEIGHT = 41;
+const INSTRUCTION_PANEL_WIDTH = 32;
+const INSTRUCTION_PANEL_HEIGHT = 39;
+const MESSAGE_PANEL_WIDTH = 100;
+const MESSAGE_PANEL_HEIGHT = 3;
+const STATUS_PANEL_WIDTH = 32;
+const STATUS_PANEL_HEIGHT = 5;
+
+const KEY_MOVE_UP: i32 = 'k';
+const KEY_MOVE_DOWN: i32 = 'j';
+const KEY_MOVE_LEFT: i32 = 'h';
+const KEY_MOVE_RIGHT: i32 = 'l';
+const KEY_STAIR_UP: i32 = '<';
+const KEY_STAIR_DOWN: i32 = '>';
+const KEY_QUIT: i32 = ncurses.KEY_F(1);
+
+pub const Display = struct {
+ main: ?*ncurses.WINDOW,
+ inst: ?*ncurses.WINDOW,
+ msgs: ?*ncurses.WINDOW,
+ stat: ?*ncurses.WINDOW,
+ allocator: std.mem.Allocator,
+
+ fn validTermSize() bool {
+ return ncurses.LINES >= MAIN_PANEL_HEIGHT + MESSAGE_PANEL_HEIGHT and
+ ncurses.COLS >= MAIN_PANEL_WIDTH + INSTRUCTION_PANEL_WIDTH;
+ }
+
+ fn colorSupport() bool {
+ return ncurses.has_colors();
+ }
+
+ fn createNewWin(height: i32, width: i32, y: i32, x: i32) ?*ncurses.WINDOW {
+ const local_win = ncurses.newwin(height, width, y, x);
+ _ = ncurses.box(local_win, 0, 0);
+ _ = ncurses.wrefresh(local_win);
+ return local_win;
+ }
+
+ fn formatInstruction(self: *Display, pos: i32, key: i32, description: [:0]const u8) void {
+ const c_description: ?[*:0]const u8 = description.ptr;
+
+ if (ncurses.KEY_F0 <= key and key <= ncurses.KEY_F(64)) {
+ _ = ncurses.mvwprintw(self.inst, pos, 2, "F%d - %s", key - ncurses.KEY_F0, c_description);
+ } else if (key < 128) {
+ _ = ncurses.mvwprintw(self.inst, pos, 2, "%c - %s", key, c_description);
+ } else {
+ self.displayMessage("Invalid key name: {d}", .{key});
+ }
+ }
+
+ pub fn displayInstructions(self: *Display) void {
+ self.formatInstruction(1, KEY_MOVE_LEFT, "move left");
+ self.formatInstruction(2, KEY_MOVE_DOWN, "move down");
+ self.formatInstruction(3, KEY_MOVE_UP, "move up");
+ self.formatInstruction(4, KEY_MOVE_RIGHT, "move right");
+ self.formatInstruction(5, KEY_STAIR_DOWN, "move down staircase");
+ self.formatInstruction(6, KEY_STAIR_UP, "move up staircase");
+ self.formatInstruction(7, KEY_QUIT, "quit");
+ _ = ncurses.wrefresh(self.inst);
+ }
+
+ pub fn displayMessage(self: *Display, comptime fmt: []const u8, args: anytype) void {
+ for (1..MESSAGE_PANEL_WIDTH - 1) |i| {
+ const i_32 = @as(i32, @intCast(i));
+ _ = ncurses.mvwaddch(self.msgs, 1, i_32, ' ');
+ }
+
+ var buf: [MESSAGE_PANEL_WIDTH:0]u8 = undefined;
+ const data = std.fmt.bufPrint(&buf, fmt, args) catch {
+ self.displayMessage("Message too long", .{});
+ return;
+ };
+ buf[data.len] = 0;
+
+ const msg: [:0]u8 = &buf;
+
+ const c_msg: ?[*:0]const u8 = msg.ptr;
+ _ = ncurses.mvwprintw(self.msgs, 1, 1, c_msg);
+ _ = ncurses.wrefresh(self.msgs);
+ }
+
+ pub fn displayStatus(self: *Display, entity: *const Entity) void {
+ for (1..STATUS_PANEL_HEIGHT - 1) |i| {
+ for (1..STATUS_PANEL_WIDTH - 1) |j| {
+ const i_32 = @as(i32, @intCast(i));
+ const j_32 = @as(i32, @intCast(j));
+ _ = ncurses.mvwaddch(self.stat, i_32, j_32, ' ');
+ }
+ }
+
+ _ = entity;
+ _ = ncurses.wrefresh(self.stat);
+ }
+
+ pub fn processInput(self: *Display) Action {
+ _ = self;
+
+ const ch = ncurses.getch();
+ return switch (ch) {
+ KEY_MOVE_UP => Action.move_up,
+ KEY_MOVE_DOWN => Action.move_down,
+ KEY_MOVE_LEFT => Action.move_left,
+ KEY_MOVE_RIGHT => Action.move_right,
+ KEY_STAIR_DOWN => Action.down_stair,
+ KEY_STAIR_UP => Action.up_stair,
+ KEY_QUIT => Action.exit,
+ else => Action.illegal,
+ };
+ }
+
+ pub fn init() !Display {
+ _ = locale.setlocale(locale.LC_ALL, "");
+ if (ncurses.initscr() == null) {
+ return error.CursesInitFail;
+ }
+
+ if (!validTermSize()) {
+ _ = ncurses.endwin();
+ return error.InvalidTermSize;
+ }
+
+ if (!colorSupport()) {
+ _ = ncurses.endwin();
+ return error.NoColorSupport;
+ }
+
+ _ = ncurses.raw();
+ _ = ncurses.keypad(ncurses.stdscr, true);
+ _ = ncurses.noecho();
+ _ = ncurses.curs_set(0);
+ _ = ncurses.start_color();
+
+ _ = ncurses.init_pair(1, ncurses.COLOR_WHITE, ncurses.COLOR_BLACK);
+ _ = ncurses.init_pair(2, ncurses.COLOR_BLACK, ncurses.COLOR_RED);
+ _ = ncurses.wattron(ncurses.stdscr, ncurses.COLOR_PAIR(1));
+ _ = ncurses.refresh();
+
+ return Display{
+ .inst = createNewWin(
+ INSTRUCTION_PANEL_HEIGHT,
+ INSTRUCTION_PANEL_WIDTH,
+ @divTrunc(ncurses.LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT, 2) + 1,
+ @divTrunc(ncurses.COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH, 2) + MAIN_PANEL_WIDTH - 1,
+ ),
+ .msgs = createNewWin(
+ MESSAGE_PANEL_HEIGHT,
+ MESSAGE_PANEL_WIDTH,
+ @divTrunc(ncurses.LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT, 2) + MAIN_PANEL_HEIGHT,
+ @divTrunc(ncurses.COLS - MESSAGE_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH, 2),
+ ),
+ .stat = createNewWin(
+ STATUS_PANEL_HEIGHT,
+ STATUS_PANEL_WIDTH,
+ @divTrunc(ncurses.LINES - INSTRUCTION_PANEL_HEIGHT - STATUS_PANEL_HEIGHT, 2) + INSTRUCTION_PANEL_HEIGHT,
+ @divTrunc(ncurses.COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH, 2) + MAIN_PANEL_WIDTH - 1,
+ ),
+ .main = createNewWin(
+ MAIN_PANEL_HEIGHT,
+ MAIN_PANEL_WIDTH,
+ @divTrunc(ncurses.LINES - MAIN_PANEL_HEIGHT - MESSAGE_PANEL_HEIGHT, 2) + 1,
+ @divTrunc(ncurses.COLS - MAIN_PANEL_WIDTH - INSTRUCTION_PANEL_WIDTH, 2),
+ ),
+ .allocator = undefined,
+ };
+ }
+
+ pub fn deinit(self: *Display) void {
+ _ = ncurses.delwin(self.main);
+ _ = ncurses.delwin(self.inst);
+ _ = ncurses.delwin(self.msgs);
+ _ = ncurses.delwin(self.stat);
+ _ = ncurses.endwin();
+ }
+};
diff --git a/src/ht.c b/src/ht.c
deleted file mode 100644
index ff0a3a8..0000000
--- a/src/ht.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#include "ht.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-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;
-
- free(new_h);
-}
-
-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;
-}
diff --git a/src/ht.h b/src/ht.h
deleted file mode 100644
index 236ae4e..0000000
--- a/src/ht.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#ifndef HT_H_
-#define HT_H_
-
-#include <stdbool.h>
-
-typedef struct hash_table ht_t;
-
-struct kvp {
- char *key;
- void *val;
-};
-
-// construct and destructor
-ht_t *ht_create(int size);
-void ht_destroy(ht_t *h);
-
-// accessors
-void *ht_find(ht_t *h, char *key);
-void ht_insert(ht_t *h, char *key, void *val);
-void ht_delete(ht_t *h, char *key);
-
-// queries
-int ht_size(ht_t *h);
-
-// iterator
-void ht_iter_init(ht_t *h);
-struct kvp ht_iter_next(ht_t *h);
-
-#endif // HT_H_
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index 5278d3e..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-This file is part of urlg.
-urlg is free software: you can redistribute it and/or modify it under the terms
-of the GNU General Public License as published by the Free Software Foundation,
-either version 3 of the License, or (at your option) any later version. urlg is
-distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details. You should have
-received a copy of the GNU General Public License along with urlg. If not, see
-<https://www.gnu.org/licenses/>.
-*/
-#include <curses.h>
-#include <getopt.h>
-#include <locale.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include "../config.h"
-#include "cavegen.h"
-#include "common.h"
-#include "display.h"
-#include "entity.h"
-#include "ht.h"
-
-bool entity_set_pos(struct entity *e, struct point p, struct map *map)
-{
- if (e->solid) {
- if (p.y < 0 || p.x < 0 || p.y >= map->height || p.x >= map->width) {
- return false;
- }
- if (map->map[p.y * map->width + p.x] == WALL) {
- return false;
- }
- }
-
- e->p = p;
-
- return true;
-}
-
-bool game_update(
- display_t *disp, enum action action, ht_t *entities, struct map *map
-)
-{
- struct entity *player = ht_find(entities, "player");
- struct entity *camera = ht_find(entities, "camera");
-
- struct point newp = player->p;
- struct point newp_cam = camera->p;
- switch (action) {
- case ACTION_EXIT : return true;
- case ACTION_UP :
- --newp.y;
- --newp_cam.y;
- display_message(disp, "moving up");
- break;
- case ACTION_DOWN :
- ++newp.y;
- ++newp_cam.y;
- display_message(disp, "moving down");
- break;
- case ACTION_LEFT :
- --newp.x;
- --newp_cam.x;
- display_message(disp, "moving left");
- break;
- case ACTION_RIGHT :
- ++newp.x;
- ++newp_cam.x;
- display_message(disp, "moving right");
- break;
- case ACTION_STAIR_DOWN :
- if (map->map[player->p.y * map->width + player->p.x] == DOWN_STAIR) {
- free(map->map);
- create_cave(map);
-
- newp = map->entry_point;
- newp_cam.x = map->entry_point.x - MAIN_PANEL_WIDTH / 2 + 1;
- newp_cam.y = map->entry_point.y - MAIN_PANEL_HEIGHT / 2 + 1;
- display_message(disp, "moving down stairs");
- } else {
- display_message(disp, "no stairs to go down");
- }
- break;
- case ACTION_STAIR_UP :
- if (map->map[player->p.y * WIDTH + player->p.x] == UP_STAIR) {
- display_message(disp, "moving up stairs");
- return true;
- } else {
- display_message(disp, "no stairs to go up");
- }
- break;
- default : display_message(disp, "unrecognized command"); break;
- }
-
- if (entity_set_pos(player, newp, map))
- entity_set_pos(camera, newp_cam, map);
-
- return false;
-}
-
-void print_version(void)
-{
- printf("%s\n", PACKAGE_STRING);
- printf("Copyright (C) 2024 Jacob Janzen\n");
- printf("This is free software; see the source for copying conditions.\n");
- printf(
- "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
- );
- printf("PARTICULAR PURPOSE.\n");
-}
-
-int main(int argc, char **argv)
-{
- int option_index = 0;
- int ch;
- int version_flag = 0;
- struct option longopts[] = {
- {"version", no_argument, &version_flag, 'v'},
- };
- while ((ch = getopt_long(argc, argv, ":v", longopts, &option_index)) != -1
- ) {
- switch (ch) {
- case 'v' : version_flag = 1; break;
- case 0 : break;
- default : break;
- }
- }
- if (version_flag) {
- print_version();
- return EXIT_SUCCESS;
- }
-
- unsigned int seed = time(NULL);
- srand(seed);
-
- display_t *disp = display_init();
-
- if (!disp) {
- return EXIT_FAILURE;
- }
-
- // create the map
- struct map map;
- create_cave(&map);
-
- // create the entity map
- ht_t *entities = ht_create(1);
-
- // create the camera
- struct entity camera;
- camera.disp_ch = " ";
- camera.solid = false;
- camera.visible = false;
- ht_insert(entities, "camera", &camera);
-
- // create the player
- struct entity player;
- player.disp_ch = "@";
- player.solid = true;
- player.visible = true;
- ht_insert(entities, "player", &player);
-
- // set starting point
- struct point cam_p = {
- .x = map.entry_point.x - MAIN_PANEL_WIDTH / 2 + 1,
- .y = map.entry_point.y - MAIN_PANEL_HEIGHT / 2 + 1,
- };
- entity_set_pos(&player, map.entry_point, &map);
- entity_set_pos(&camera, cam_p, &map);
-
- // start displaying things
- display_map(disp, &map, entities);
- display_instructions(disp);
- display_status(disp, &player);
- display_message(disp, "");
-
- bool done = false;
- while (!done) {
- enum action action = display_process_input();
- done = game_update(disp, action, entities, &map);
- display_map(disp, &map, entities);
- }
-
- free(map.map);
- ht_destroy(entities);
- display_destroy(disp);
-
- endwin();
-
- return 0;
-}
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..63cc2db
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+const Entity = @import("ecs/entity.zig").Entity;
+const Display = @import("frontend/ncurses.zig").Display;
+const Action = @import("actions.zig").Action;
+
+pub fn main() u8 {
+ var d = Display.init() catch |err| {
+ std.log.err("{}", .{err});
+ return 1;
+ };
+ d.displayMessage("Initialized", .{});
+ d.displayInstructions();
+ d.displayStatus(&Entity{});
+ var action = Action.illegal;
+ while (action != Action.exit) {
+ action = d.processInput();
+ }
+ d.deinit();
+
+ return 0;
+}
diff --git a/src/root.zig b/src/root.zig
new file mode 100644
index 0000000..ecfeade
--- /dev/null
+++ b/src/root.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const testing = std.testing;
+
+export fn add(a: i32, b: i32) i32 {
+ return a + b;
+}
+
+test "basic add functionality" {
+ try testing.expect(add(3, 7) == 10);
+}