first basic build, pathfinding, maze generation, enemy+loot spawning, no memleaks, no major bugs

AuthorKonata <konata@posteo.jp>
Date
Commit5aaf1fdf10e092087ede7d34c19a12153239c485
Parent5dd6f78
15 files changed, 1651 insertions(+), 9 deletions(-)
A.gitignore
@@ -0,0 +1,3 @@
1+.kdev4
2+heaptrack*
3+build/
MCMakeLists.txt
@@ -1,5 +1,21 @@
1-project(orcsmasher)
1+cmake_minimum_required(VERSION 3.0)
22
3-add_executable(orcsmasher main.c)
3+project(orcsmasher LANGUAGES C)
44
5-install(TARGETS orcsmasher RUNTIME DESTINATION bin)
5+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Wno-sign-compare")
6+
7+if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
8+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g -fsanitize=address")
9+endif()
10+
11+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
12+include_directories(include)
13+file(GLOB SOURCES "src/*.c")
14+
15+set(CURSES_NEED_NCURSES TRUE)
16+set(CURSES_NEED_WIDE TRUE)
17+find_package(Curses REQUIRED)
18+include_directories(${CURSES_INCLUDE_DIR})
19+
20+add_executable(orcsmasher ${SOURCES})
21+target_link_libraries(orcsmasher ${CURSES_LIBRARIES} panelw)
Ainclude/algorithm.h
@@ -0,0 +1,15 @@
1+#ifndef ALGORITHM_H
2+#define ALGORITHM_H
3+#include <stddef.h>
4+#include <game.h>
5+
6+struct game make_maze(size_t y, size_t x);
7+typedef darray(struct point) pointarr;
8+
9+//algorithm terminates with no path if target is not found within maxdist
10+//result contains target as well as the source point
11+//so even if next to the target, the result arr is at least 2 items big
12+pointarr astar(struct game *g, struct point start, struct point target, size_t maxdist);
13+
14+
15+#endif //ALGORITHM_H
Ainclude/config.h
@@ -0,0 +1,17 @@
1+#ifndef CONFIG_H
2+#define CONFIG_H
3+
4+#define AGGRO_RANGE 4
5+#define GAME_MIN_Y 10
6+#define GAME_MIN_X 10
7+#define GAME_MAX_Y 30
8+#define GAME_MAX_X 30
9+
10+#define MIN_ENEMIES 5
11+#define MAX_ENEMIES 15
12+
13+#define MIN_LOOT 5
14+#define MAX_LOOT 15
15+
16+
17+#endif //COFNIG_H
Ainclude/darray.h
@@ -0,0 +1,362 @@
1+/*
2+ * Copyright (C) 2011 Joseph Adams <joeyadams3.14159@gmail.com>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef CCAN_DARRAY_H
24+#define CCAN_DARRAY_H
25+
26+#include <stdlib.h>
27+#include <string.h>
28+
29+/*
30+ * SYNOPSIS
31+ *
32+ * Life cycle of a darray (dynamically-allocated array):
33+ *
34+ * darray(int) a = darray_new();
35+ * darray_free(a);
36+ *
37+ * struct {darray(int) a;} foo;
38+ * darray_init(foo.a);
39+ * darray_free(foo.a);
40+ *
41+ * Typedefs for darrays of common types:
42+ *
43+ * darray_char, darray_schar, darray_uchar
44+ * darray_short, darray_int, darray_long
45+ * darray_ushort, darray_uint, darray_ulong
46+ *
47+ * Access:
48+ *
49+ * T darray_item(darray(T) arr, size_t index);
50+ * size_t darray_size(darray(T) arr);
51+ * size_t darray_alloc(darray(T) arr);
52+ * bool darray_empty(darray(T) arr);
53+ *
54+ * Insertion (single item):
55+ *
56+ * void darray_append(darray(T) arr, T item);
57+ * void darray_prepend(darray(T) arr, T item);
58+ * void darray_insert(darray(T) arr, size_t index, T item);
59+ * void darray_push(darray(T) arr, T item); // same as darray_append
60+ *
61+ * Insertion (multiple items):
62+ *
63+ * void darray_append_items(darray(T) arr, T *items, size_t count);
64+ * void darray_prepend_items(darray(T) arr, T *items, size_t count);
65+ *
66+ * void darray_appends(darray(T) arr, [T item, [...]]);
67+ * void darray_prepends(darray(T) arr, [T item, [...]]);
68+ *
69+ * // Same functionality as above, but does not require typeof.
70+ * void darray_appends_t(darray(T) arr, #T, [T item, [...]]);
71+ * void darray_prepends_t(darray(T) arr, #T, [T item, [...]]);
72+ *
73+ * Removal:
74+ *
75+ * T darray_pop(darray(T) arr | darray_size(arr) != 0);
76+ * T* darray_pop_check(darray(T*) arr);
77+ * void darray_remove(darray(T) arr, size_t index);
78+ *
79+ * Replacement:
80+ *
81+ * void darray_from_items(darray(T) arr, T *items, size_t count);
82+ * void darray_from_c(darray(T) arr, T c_array[N]);
83+ *
84+ * String buffer:
85+ *
86+ * void darray_append_string(darray(char) arr, const char *str);
87+ * void darray_append_lit(darray(char) arr, char stringLiteral[N+1]);
88+ *
89+ * void darray_prepend_string(darray(char) arr, const char *str);
90+ * void darray_prepend_lit(darray(char) arr, char stringLiteral[N+1]);
91+ *
92+ * void darray_from_string(darray(T) arr, const char *str);
93+ * void darray_from_lit(darray(char) arr, char stringLiteral[N+1]);
94+ *
95+ * Size management:
96+ *
97+ * void darray_resize(darray(T) arr, size_t newSize);
98+ * void darray_resize0(darray(T) arr, size_t newSize);
99+ *
100+ * void darray_realloc(darray(T) arr, size_t newAlloc);
101+ * void darray_growalloc(darray(T) arr, size_t newAlloc);
102+ *
103+ * void darray_make_room(darray(T) arr, size_t room);
104+ *
105+ * Traversal:
106+ *
107+ * darray_foreach(T *&i, darray(T) arr) {...}
108+ * darray_foreach_reverse(T *&i, darray(T) arr) {...}
109+ *
110+ * Except for darray_foreach, darray_foreach_reverse, and darray_remove,
111+ * all macros evaluate their non-darray arguments only once.
112+ */
113+
114+/*** Life cycle ***/
115+
116+#define darray(type) struct {type *item; size_t size; size_t alloc;}
117+
118+#define darray_new() {0,0,0}
119+#define darray_init(arr) do {(arr).item=0; (arr).size=0; (arr).alloc=0;} while(0)
120+#define darray_free(arr) do {free((arr).item);} while(0)
121+
122+
123+/*
124+ * Typedefs for darrays of common types. These are useful
125+ * when you want to pass a pointer to an darray(T) around.
126+ *
127+ * The following will produce an incompatible pointer warning:
128+ *
129+ * void foo(darray(int) *arr);
130+ * darray(int) arr = darray_new();
131+ * foo(&arr);
132+ *
133+ * The workaround:
134+ *
135+ * void foo(darray_int *arr);
136+ * darray_int arr = darray_new();
137+ * foo(&arr);
138+ */
139+
140+typedef darray(char) darray_char;
141+typedef darray(signed char) darray_schar;
142+typedef darray(unsigned char) darray_uchar;
143+
144+typedef darray(short) darray_short;
145+typedef darray(int) darray_int;
146+typedef darray(long) darray_long;
147+
148+typedef darray(unsigned short) darray_ushort;
149+typedef darray(unsigned int) darray_uint;
150+typedef darray(unsigned long) darray_ulong;
151+
152+
153+/*** Access ***/
154+
155+#define darray_item(arr, i) ((arr).item[i])
156+#define darray_size(arr) ((arr).size)
157+#define darray_alloc(arr) ((arr).alloc)
158+#define darray_empty(arr) ((arr).size == 0)
159+
160+
161+/*** Insertion (single item) ***/
162+
163+#define darray_append(arr, ...) do { \
164+ darray_resize(arr, (arr).size+1); \
165+ (arr).item[(arr).size-1] = (__VA_ARGS__); \
166+ } while(0)
167+#define darray_prepend(arr, ...) do { \
168+ darray_resize(arr, (arr).size+1); \
169+ memmove((arr).item+1, (arr).item, ((arr).size-1)*sizeof(*(arr).item)); \
170+ (arr).item[0] = (__VA_ARGS__); \
171+ } while(0)
172+#define darray_insert(arr, i, ...) do { \
173+ size_t index_ = (i); \
174+ darray_resize(arr, (arr).size+1); \
175+ memmove((arr).item+index_+1, (arr).item+index_, ((arr).size-index_-1)*sizeof(*(arr).item)); \
176+ (arr).item[index_] = (__VA_ARGS__); \
177+ } while(0)
178+#define darray_push(arr, ...) darray_append(arr, __VA_ARGS__)
179+
180+
181+/*** Insertion (multiple items) ***/
182+
183+#define darray_append_items(arr, items, count) do { \
184+ size_t count_ = (count), oldSize_ = (arr).size; \
185+ darray_resize(arr, oldSize_ + count_); \
186+ memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
187+ } while(0)
188+
189+#define darray_prepend_items(arr, items, count) do { \
190+ size_t count_ = (count), oldSize_ = (arr).size; \
191+ darray_resize(arr, count_ + oldSize_); \
192+ memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
193+ memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
194+ } while(0)
195+
196+#define darray_append_items_nullterminate(arr, items, count) do { \
197+ size_t count_ = (count), oldSize_ = (arr).size; \
198+ darray_resize(arr, oldSize_ + count_ + 1); \
199+ memcpy((arr).item + oldSize_, items, count_ * sizeof(*(arr).item)); \
200+ (arr).item[--(arr).size] = 0; \
201+ } while(0)
202+
203+#define darray_prepend_items_nullterminate(arr, items, count) do { \
204+ size_t count_ = (count), oldSize_ = (arr).size; \
205+ darray_resize(arr, count_ + oldSize_ + 1); \
206+ memmove((arr).item + count_, (arr).item, oldSize_ * sizeof(*(arr).item)); \
207+ memcpy((arr).item, items, count_ * sizeof(*(arr).item)); \
208+ (arr).item[--(arr).size] = 0; \
209+ } while(0)
210+
211+#if HAVE_TYPEOF
212+#define darray_appends(arr, ...) darray_appends_t(arr, typeof((*(arr).item)), __VA_ARGS__)
213+#define darray_prepends(arr, ...) darray_prepends_t(arr, typeof((*(arr).item)), __VA_ARGS__)
214+#endif
215+
216+#define darray_appends_t(arr, type, ...) do { \
217+ type src_[] = {__VA_ARGS__}; \
218+ darray_append_items(arr, src_, sizeof(src_)/sizeof(*src_)); \
219+ } while(0)
220+#define darray_prepends_t(arr, type, ...) do { \
221+ type src_[] = {__VA_ARGS__}; \
222+ darray_prepend_items(arr, src_, sizeof(src_)/sizeof(*src_)); \
223+ } while(0)
224+
225+
226+/*** Removal ***/
227+
228+/* Warning: Do not call darray_pop on an empty darray. */
229+#define darray_pop(arr) ((arr).item[--(arr).size])
230+#define darray_pop_check(arr) ((arr).size ? darray_pop(arr) : NULL)
231+/* Warning, slow: Requires copying all elements after removed item. */
232+#define darray_remove(arr, i) do { \
233+ size_t index_ = (i); \
234+ if (index_ < arr.size-1) \
235+ memmove(&(arr).item[index_], &(arr).item[index_+1], ((arr).size-1-index_)*sizeof(*(arr).item)); \
236+ (arr).size--; \
237+ } while(0)
238+
239+
240+/*** Replacement ***/
241+
242+#define darray_from_items(arr, items, count) do {size_t count_ = (count); darray_resize(arr, count_); memcpy((arr).item, items, count_*sizeof(*(arr).item));} while(0)
243+#define darray_from_c(arr, c_array) darray_from_items(arr, c_array, sizeof(c_array)/sizeof(*(c_array)))
244+
245+
246+/*** String buffer ***/
247+
248+#define darray_append_string(arr, str) do {const char *str_ = (str); darray_append_items(arr, str_, strlen(str_)+1); (arr).size--;} while(0)
249+#define darray_append_lit(arr, stringLiteral) do {darray_append_items(arr, stringLiteral, sizeof(stringLiteral)); (arr).size--;} while(0)
250+
251+#define darray_prepend_string(arr, str) do { \
252+ const char *str_ = (str); \
253+ darray_prepend_items_nullterminate(arr, str_, strlen(str_)); \
254+ } while(0)
255+#define darray_prepend_lit(arr, stringLiteral) \
256+ darray_prepend_items_nullterminate(arr, stringLiteral, sizeof(stringLiteral) - 1)
257+
258+#define darray_from_string(arr, str) do {const char *str_ = (str); darray_from_items(arr, str_, strlen(str_)+1); (arr).size--;} while(0)
259+#define darray_from_lit(arr, stringLiteral) do {darray_from_items(arr, stringLiteral, sizeof(stringLiteral)); (arr).size--;} while(0)
260+
261+
262+/*** Size management ***/
263+
264+#define darray_resize(arr, newSize) darray_growalloc(arr, (arr).size = (newSize))
265+#define darray_resize0(arr, newSize) do { \
266+ size_t oldSize_ = (arr).size, newSize_ = (newSize); \
267+ (arr).size = newSize_; \
268+ if (newSize_ > oldSize_) { \
269+ darray_growalloc(arr, newSize_); \
270+ memset(&(arr).item[oldSize_], 0, (newSize_ - oldSize_) * sizeof(*(arr).item)); \
271+ } \
272+ } while(0)
273+
274+#define darray_realloc(arr, newAlloc) do { \
275+ (arr).item = xrealloc((arr).item, ((arr).alloc = (newAlloc)) * sizeof(*(arr).item)); \
276+ } while(0)
277+#define darray_growalloc(arr, need) do { \
278+ size_t need_ = (need); \
279+ if (need_ > (arr).alloc) \
280+ darray_realloc(arr, darray_next_alloc((arr).alloc, need_)); \
281+ } while(0)
282+
283+#if HAVE_STATEMENT_EXPR==1
284+#define darray_make_room(arr, room) ({size_t newAlloc = (arr).size+(room); if ((arr).alloc<newAlloc) darray_realloc(arr, newAlloc); (arr).item+(arr).size; })
285+#endif
286+
287+static inline size_t darray_next_alloc(size_t alloc, size_t need)
288+{
289+ if (alloc == 0)
290+ alloc = 1;
291+ while (alloc < need)
292+ alloc *= 2;
293+ return alloc;
294+}
295+
296+
297+/*** Traversal ***/
298+
299+/*
300+ * darray_foreach(T *&i, darray(T) arr) {...}
301+ *
302+ * Traverse a darray. `i` must be declared in advance as a pointer to an item.
303+ */
304+#define darray_foreach(i, arr) \
305+ for ((i) = &(arr).item[0]; (i) < &(arr).item[(arr).size]; (i)++)
306+
307+/*
308+ * darray_foreach_reverse(T *&i, darray(T) arr) {...}
309+ *
310+ * Like darray_foreach, but traverse in reverse order.
311+ */
312+#define darray_foreach_reverse(i, arr) \
313+ for ((i) = &(arr).item[(arr).size]; (i)-- > &(arr).item[0]; )
314+
315+
316+#endif /* CCAN_DARRAY_H */
317+
318+/*
319+
320+darray_growalloc(arr, newAlloc) sees if the darray can currently hold newAlloc items;
321+ if not, it increases the alloc to satisfy this requirement, allocating slack
322+ space to avoid having to reallocate for every size increment.
323+
324+darray_from_string(arr, str) copies a string to an darray_char.
325+
326+darray_push(arr, item) pushes an item to the end of the darray.
327+darray_pop(arr) pops it back out. Be sure there is at least one item in the darray before calling.
328+darray_pop_check(arr) does the same as darray_pop, but returns NULL if there are no more items left in the darray.
329+
330+darray_make_room(arr, room) ensures there's 'room' elements of space after the end of the darray, and it returns a pointer to this space.
331+Currently requires HAVE_STATEMENT_EXPR, but I plan to remove this dependency by creating an inline function.
332+
333+The following require HAVE_TYPEOF==1 :
334+
335+darray_appends(arr, item0, item1...) appends a collection of comma-delimited items to the darray.
336+darray_prepends(arr, item0, item1...) prepends a collection of comma-delimited items to the darray.\
337+
338+
339+Examples:
340+
341+ darray(int) arr;
342+ int *i;
343+
344+ darray_appends(arr, 0,1,2,3,4);
345+ darray_appends(arr, -5,-4,-3,-2,-1);
346+ darray_foreach(i, arr)
347+ printf("%d ", *i);
348+ printf("\n");
349+
350+ darray_free(arr);
351+
352+
353+ typedef struct {int n,d;} Fraction;
354+ darray(Fraction) fractions;
355+ Fraction *i;
356+
357+ darray_appends(fractions, {3,4}, {3,5}, {2,1});
358+ darray_foreach(i, fractions)
359+ printf("%d/%d\n", i->n, i->d);
360+
361+ darray_free(fractions);
362+*/
Ainclude/game.h
@@ -0,0 +1,180 @@
1+#ifndef GAME_H
2+#define GAME_H
3+#include <stddef.h>
4+#include <stdbool.h>
5+#include <assert.h>
6+#include <helper.h>
7+#include <darray.h>
8+#define MAXITEMS 10
9+
10+extern unsigned m_id;
11+
12+enum objtype {NOOBJ = 0, MONSTER, ITEM, WALL, TARGET};
13+enum itemtype {NOITEM = 0, EQUIP, USE};
14+
15+struct equip
16+{
17+ unsigned hp;
18+ unsigned dmg;
19+ bool isequipped;
20+};
21+
22+struct use
23+{
24+ unsigned restorehp;
25+};
26+
27+struct item
28+{
29+ const char *name;
30+ enum itemtype type;
31+ union
32+ {
33+ struct equip e;
34+ struct use u;
35+ }u;
36+};
37+
38+
39+struct monster
40+{
41+ const char *name;
42+ unsigned id;
43+ unsigned current_hp;
44+ unsigned max_hp;
45+ unsigned dmg;
46+ unsigned level;
47+ unsigned xp;
48+ darray(struct item) items;
49+ bool isplayer;
50+};
51+
52+static inline size_t item_len(const struct monster *m)
53+{
54+ for(size_t i = 0; i < MAXITEMS; i++)
55+ {
56+ if(darray_item(m->items, i).type == NOITEM)
57+ return i;
58+ }
59+ return MAXITEMS;
60+}
61+
62+
63+struct object
64+{
65+ enum objtype type;
66+ char symbol;
67+ union
68+ {
69+ struct monster m;
70+ struct item i;
71+ } u;
72+};
73+
74+struct game
75+{
76+ struct object *map;
77+ size_t xs, ys;
78+};
79+
80+static inline struct object *game_at(const struct game *g, struct point p)
81+{
82+ return &g->map[p.y * g->xs + p.x];
83+}
84+
85+
86+static inline struct game create_game(size_t y, size_t x)
87+{
88+ struct game ret;
89+ ret.map = xmalloc(x * y * sizeof * ret.map);
90+ ret.xs = x;
91+ ret.ys = y;
92+
93+ return ret;
94+}
95+
96+static inline void delete_game(struct game *g)
97+{
98+ for(size_t y = 0; y < g->ys; y++)
99+ {
100+ for(size_t x = 0; x < g->xs; x++)
101+ {
102+ if(game_at(g, (struct point){y,x})->type == MONSTER)
103+ darray_free(game_at(g, (struct point){y,x})->u.m.items);
104+ }
105+ }
106+ free(g->map);
107+}
108+
109+enum action {MOVE, SPAWN, QUIT, INVENTORY, REDRAW, INVALID};
110+enum direction {UP,DOWN,LEFT,RIGHT};
111+
112+struct choice
113+{
114+ enum action a;
115+ union
116+ {
117+ enum direction d;
118+ }u;
119+};
120+
121+#define MAX(x,y) (x >= y ? x : y)
122+#define MIN(x,y) (x <= y ? x : y)
123+
124+#define ORC (struct object)\
125+ {\
126+ .type = MONSTER,\
127+ .symbol = 'O',\
128+ .u.m = {"Zog Zog", m_id++, 10, 10, 1, 5 , 0, darray_new(), false}\
129+ }
130+
131+#define EMPTYFIELD (struct object)\
132+ {\
133+ .type = NOOBJ,\
134+ .symbol = ' ',\
135+ }
136+#define WALLFIELD (struct object)\
137+ {\
138+ .type = WALL,\
139+ .symbol = '#',\
140+ }
141+
142+static inline struct point relative(struct point p, enum direction d)
143+{
144+ switch(d)
145+ {
146+ case UP:
147+ return (struct point){p.y - 1, p.x};
148+ case DOWN:
149+ return (struct point){p.y + 1, p.x};
150+ case LEFT:
151+ return (struct point){p.y, p.x - 1};
152+ case RIGHT:
153+ return (struct point){p.y, p.x + 1};
154+ default:
155+ assert(false);
156+ }
157+}
158+
159+static inline bool in_bounds(struct game *g, struct point p)
160+{
161+ if(p.y >= 0 && p.y < g->ys && p.x >= 0 && p.x < g->xs)
162+ return true;
163+ else
164+ return false;
165+}
166+
167+static inline void spawn_enemy(struct game *g)
168+{
169+ while(true)
170+ {
171+ struct point p = {randrange(0, g->ys), randrange(0, g->xs)};
172+ if(in_bounds(g, p) && game_at(g, p)->type == NOOBJ) {
173+ *game_at(g, p) = ORC;
174+ break;
175+ }
176+ }
177+}
178+
179+
180+#endif
Ainclude/helper.h
@@ -0,0 +1,70 @@
1+#ifndef HELPER_H
2+#define HELPER_H
3+
4+#include <stdlib.h>
5+#include <stdio.h>
6+#include <stdbool.h>
7+
8+struct point
9+{
10+ int y;
11+ int x;
12+};
13+
14+static inline bool pointeq(struct point p1, struct point p2)
15+{
16+ return p1.y == p2.y && p1.x == p2.x;
17+}
18+
19+static inline unsigned int iabs(int i)
20+{
21+ return i < 0 ? -i : i;
22+}
23+
24+static inline int ipow(int base, int exp)
25+{
26+ int result = 1;
27+ for (;;)
28+ {
29+ if (exp & 1)
30+ result *= base;
31+ exp >>= 1;
32+ if (!exp)
33+ break;
34+ base *= base;
35+ }
36+
37+ return result;
38+}
39+
40+static inline int randrange(int min, int max)
41+{
42+ return rand() % (max + 1 - min) + min;
43+}
44+
45+static inline void *xmalloc(size_t n)
46+{
47+ void *ret = malloc(n);
48+ if(!ret)
49+ {
50+ fprintf(stderr, "malloc failed\n");
51+ abort();
52+ }
53+
54+ return ret;
55+}
56+
57+static inline void *xrealloc(void *ptr, size_t n)
58+{
59+ void *ret = realloc(ptr, n);
60+ if(!ret)
61+ {
62+ fprintf(stderr, "realloc failed\n");
63+ abort();
64+ }
65+
66+ return ret;
67+}
68+
69+#endif //HELPER_H
70+
Ainclude/items.h
@@ -0,0 +1,32 @@
1+#ifndef ITEMS_H
2+#define ITEMS_H
3+
4+#include <game.h>
5+
6+#define DAGGER_OF_DARKNESS *game_at(&g, (struct point){8,3}) = (struct object)\
7+ {\
8+ .type = ITEM,\
9+ .symbol = 'T',\
10+ .u.i = {"Dagger of Darkness", EQUIP, {{0, 2, 0}}}\
11+ };
12+
13+#define HP_POTION (struct object)\
14+ {\
15+ .type = ITEM,\
16+ .symbol = 'p',\
17+ .u.i = {"HP Potion", USE, {{5, 0, 0}}}\
18+ };
19+
20+static inline void spawn_item(struct game *g)
21+{
22+ while(true)
23+ {
24+ struct point p = {randrange(0, g->ys), randrange(0, g->xs)};
25+ if(in_bounds(g, p) && game_at(g, p)->type == NOOBJ) {
26+ *game_at(g, p) = HP_POTION;
27+ break;
28+ }
29+ }
30+}
31+
32+#endif //ITEMS_H
Ainclude/loop.h
@@ -0,0 +1,7 @@
1+#ifndef LOOP_H
2+#define LOOP_H
3+
4+void loop(struct game *g);
5+
6+
7+#endif //LOOP_H
Ainclude/tui.h
@@ -0,0 +1,25 @@
1+#ifndef TUI_H
2+#define TUI_H
3+
4+#include <game.h>
5+#include <stdbool.h>
6+#include <stddef.h>
7+
8+bool setup(); //setups screens, doesn't redaw contents
9+void printlog();
10+void logstr(const char *format, ...); //echoes printf string to the logwindow TODO: fix multiline log
11+void charclear();
12+void charprint(const char *format, ...); //echoes printf string to the charwindow
13+void log_scroll(enum direction d); //scrolls the log up or down
14+void draw_map(const struct game *g, struct point p); //draws the map around the player
15+bool messagebox(const char *str, int y, int x); //shows a centered messagebox of at least the given size
16+
17+typedef bool(itemselectfn)(struct monster *m, size_t i); //returns true if cursor should be reset
18+void itemselect(struct monster *m, itemselectfn f); //provides an inventory screen, calls f when the space is pressed on an item
19+//TODO: create general select screen
20+
21+extern size_t logindex;
22+extern size_t loglength;
23+extern char **logs;
24+
25+#endif //TUI_H
Dmain.c
-6
@@ -1,6 +0,0 @@
1-#include <stdio.h>
2-
3-int main(int argc, char **argv) {
4- printf("Hello World");
5- return 0;
6-}
Asrc/algorithm.c
@@ -0,0 +1,212 @@
1+#include <helper.h>
2+#include <stdbool.h>
3+#include <string.h>
4+#include <stddef.h>
5+#include <darray.h>
6+
7+#include <algorithm.h>
8+
9+#define index(x) ((x)*2+1)
10+#define indexpoint(p) ((struct point){index(p.y), index(p.x)})
11+#define unindex(x) (((x)-1)/2)
12+#define unindexpoint(p) ((struct point){unindex(p.y), unindex(p.x)})
13+
14+
15+#define UNVISITED '#'
16+#define VISITED ' '
17+#define WALL '#'
18+#define NOWALL ' '
19+
20+typedef unsigned long size_t;
21+
22+static char *maze_at(char *m, size_t xs, struct point p)
23+{
24+ return &m[p.y*xs+p.x];
25+}
26+
27+static bool maze_in_bounds(size_t ys, size_t xs, struct point p)
28+{
29+ return p.y > 0 && p.y < ys-1 &&
30+ p.x > 0 && p.x < xs-1;
31+}
32+
33+struct game make_maze(size_t y, size_t x)
34+{
35+ bool yeven = (y%2 == 0);
36+ bool xeven = (x%2 == 0);
37+ y += yeven; //it must be an uneven number to look fine
38+ x += xeven;
39+ struct game g = create_game(y, x);
40+
41+ char *maze = xmalloc(y * x + 1);
42+ memset(maze, WALL, y * x);
43+ maze[y * x] = '\0';
44+ darray(struct point) stack = darray_new();
45+
46+ struct point current = {0, 0};
47+ *maze_at(maze, x, indexpoint(current)) = VISITED;
48+
49+ while(true)
50+ {
51+ struct point nb[4] =
52+ {
53+ {index(current.y), index(current.x-1)},
54+ {index(current.y), index(current.x+1)},
55+ {index(current.y-1), index(current.x)},
56+ {index(current.y+1), index(current.x)},
57+ };
58+
59+ if((maze_in_bounds(y,x,nb[0]) && (*maze_at(maze,x,nb[0]) == UNVISITED)) ||
60+ (maze_in_bounds(y,x,nb[1]) && (*maze_at(maze,x,nb[1]) == UNVISITED)) ||
61+ (maze_in_bounds(y,x,nb[2]) && (*maze_at(maze,x,nb[2]) == UNVISITED)) ||
62+ (maze_in_bounds(y,x,nb[3]) && (*maze_at(maze,x,nb[3]) == UNVISITED)))
63+ {
64+ int c;
65+ darray_append(stack, current);
66+
67+ while(!maze_in_bounds(y,x,nb[c=randrange(0,3)]) || *maze_at(maze, x, nb[c]) != UNVISITED);
68+ *maze_at(maze, x, nb[c]) = VISITED;
69+
70+ struct point choice = unindexpoint(nb[c]);
71+ struct point diff = {choice.y-current.y, choice.x-current.x};
72+ *maze_at(maze, x, (struct point){index(current.y)+diff.y, index(current.x)+diff.x}) = NOWALL;
73+ current = choice;
74+ } else if(!darray_empty(stack)) {
75+ current = darray_pop(stack);
76+ } else {
77+ break;
78+ }
79+ }
80+
81+ for(size_t i = 0; i < y; i++)
82+ {
83+ for(size_t j = 0; j < x; j++)
84+ {
85+ if(*maze_at(maze, x, (struct point){i,j}) == NOWALL)
86+ *game_at(&g, (struct point){i,j}) = EMPTYFIELD;
87+ else
88+ *game_at(&g, (struct point){i,j}) = WALLFIELD;
89+ }
90+ }
91+
92+ game_at(&g, (struct point){y-2,x-2})->symbol = '*';
93+ game_at(&g, (struct point){y-2,x-2})->type = TARGET;
94+
95+ darray_free(stack);
96+ free(maze);
97+ return g;
98+}
99+
100+struct node
101+{
102+ struct node *parent;
103+ struct point p;
104+ size_t g;
105+ size_t h;
106+ size_t f;
107+};
108+
109+pointarr astar(struct game *g, struct point start, struct point target, size_t maxdist)
110+{
111+ darray(struct node*) open = darray_new();
112+ darray(struct node*) closed = darray_new();
113+ pointarr result = darray_new();
114+ struct node *startnode = xmalloc(sizeof(*startnode));
115+ startnode->p = start;
116+ startnode->parent = NULL;
117+ startnode->f = 0;
118+ startnode->g = 0;
119+ startnode->h = 0;
120+
121+ darray_append(open, startnode);
122+ while(!darray_empty(open))
123+ {
124+ size_t currenti = 0;
125+ struct node *currentnode = darray_item(open, currenti);
126+ for(size_t i = 0; i < open.size; i++)
127+ {
128+ if(darray_item(open, i)->f < currentnode->f) {
129+ currenti = i;
130+ currentnode = darray_item(open, i);
131+ }
132+ }
133+
134+ darray_append(closed, darray_item(open, currenti));
135+ darray_remove(open, currenti);
136+ if(pointeq(currentnode->p, target))
137+ {
138+ struct node *this = currentnode;
139+ darray_append(result, this->p);
140+ while((this = this->parent) != NULL)
141+ {
142+ darray_append(result, this->p);
143+ }
144+ }
145+
146+ darray(struct point) children = darray_new();
147+ struct point up = (struct point){currentnode->p.y-1, currentnode->p.x};
148+ struct point down = (struct point){currentnode->p.y+1, currentnode->p.x};
149+ struct point left = (struct point){currentnode->p.y, currentnode->p.x-1};
150+ struct point right = (struct point){currentnode->p.y, currentnode->p.x+1};
151+
152+ if(game_at(g,left)->type == NOOBJ || game_at(g,left)->type == MONSTER)
153+ darray_append(children, left);
154+ if(game_at(g,right)->type == NOOBJ || game_at(g,right)->type == MONSTER)
155+ darray_append(children, right);
156+ if(game_at(g,up)->type == NOOBJ || game_at(g,up)->type == MONSTER)
157+ darray_append(children, up);
158+ if(game_at(g,down)->type == NOOBJ || game_at(g,down)->type == MONSTER)
159+ darray_append(children, down);
160+
161+
162+ for(size_t i = 0; i < children.size; i++)
163+ {
164+ struct point child = darray_item(children, i);
165+ struct node **n;
166+ darray_foreach(n, closed)
167+ {
168+ if(pointeq((*n)->p, child))
169+ goto next;
170+ }
171+
172+ if(maxdist != 0 && currentnode->g+1 > maxdist)
173+ goto next;
174+
175+ darray_foreach(n, open)
176+ {
177+ if(pointeq((*n)->p, child) && currentnode->g+1 > (*n)->g)
178+ goto next;
179+ }
180+ darray_foreach(n, closed)
181+ {
182+ if(pointeq((*n)->p, child) &&currentnode->g+1 > (*n)->g)
183+ goto next;
184+ }
185+
186+ struct node *childnode = xmalloc(sizeof(*childnode));
187+ childnode->p = child;
188+ childnode->parent = currentnode;
189+ childnode->g = currentnode->g + 1;
190+ childnode->h = ipow(childnode->p.y - target.y, 2) + ipow(childnode->p.x - target.x, 2);
191+ childnode->f = childnode->g + childnode->h;
192+
193+ darray_append(open, childnode);
194+
195+
196+ next:;
197+ }
198+ darray_free(children);
199+
200+ }
201+
202+ struct node **n;
203+ darray_foreach(n, closed)
204+ {
205+ free(*n);
206+ }
207+
208+ darray_free(open);
209+ darray_free(closed);
210+
211+ return result;
212+}
Asrc/loop.c
@@ -0,0 +1,298 @@
1+#define _POSIX_C_SOURCE 200809L
2+#define _XOPEN_SOURCE_EXTENDED
3+#include <stdio.h>
4+#include <stdlib.h>
5+#include <string.h>
6+#include <assert.h>
7+#include <stdbool.h>
8+#include <unistd.h>
9+
10+#include <ncurses.h>
11+
12+#include <game.h>
13+#include <helper.h>
14+#include <tui.h>
15+#include <algorithm.h>
16+#include <config.h>
17+
18+#define NOOBJ_OBJECT (struct object){.type = NOOBJ, .symbol = ' '}
19+
20+unsigned m_id = 0;
21+
22+struct choice get_input(void)
23+{
24+ struct choice c;
25+
26+start:;
27+ int res = getch();
28+ assert(res != ERR);
29+ switch(res)
30+ {
31+ case 'w':
32+ c.a = MOVE;
33+ c.u.d = UP;
34+ break;
35+ case 'a':
36+ c.a = MOVE;
37+ c.u.d = LEFT;
38+ break;
39+ case 's':
40+ c.a = MOVE;
41+ c.u.d = DOWN;
42+ break;
43+ case 'd':
44+ c.a = MOVE;
45+ c.u.d = RIGHT;
46+ break;
47+ case 'f':
48+ c.a = SPAWN;
49+ break;
50+ case 'q':
51+ c.a = QUIT;
52+ break;
53+ case 'i':
54+ c.a = INVENTORY;
55+ break;
56+ case KEY_RESIZE:
57+ c.a = REDRAW;
58+ break;
59+ case KEY_UP:
60+ log_scroll(UP);
61+ goto start;
62+ case KEY_DOWN:
63+ log_scroll(DOWN);
64+ goto start;
65+ default:
66+ c.a = INVALID;
67+ }
68+
69+ return c;
70+}
71+
72+static void addxp(struct monster *m, unsigned xp)
73+{
74+ m->xp += xp;
75+ if(m->xp >= m->level *100) {
76+ m->xp -= m->level*100;
77+ m->level++;
78+ m->dmg++;
79+ m->max_hp += 10;
80+ m->current_hp += 10;
81+ const char *str = "!You leveled up!";
82+ messagebox(str, 1, strlen(str));
83+ }
84+}
85+
86+static void attack(struct object *src, struct object *target)
87+{
88+ assert(src->type == MONSTER);
89+
90+ if(target->type == MONSTER) {
91+ if(src->u.m.isplayer)
92+ logstr("%s hit %s for %d damage", src->u.m.name, target->u.m.name, src->u.m.dmg);
93+
94+ if(src->u.m.dmg >= target->u.m.current_hp) {
95+ logstr("%s killed %s", src->u.m.name ,target->u.m.name);
96+
97+ if(src->u.m.isplayer) {
98+ addxp(&src->u.m, target->u.m.level*10);
99+ } else {
100+ messagebox("U done did dedden", 1, 0);
101+ exit(0);
102+ }
103+ darray_free(target->u.m.items);
104+ *target = NOOBJ_OBJECT;
105+ } else {
106+ target->u.m.current_hp -= src->u.m.dmg;
107+ }
108+ } else {
109+ logstr("Target is not a monster or player");
110+ }
111+}
112+
113+static bool additem(struct monster *m, struct item *it)
114+{
115+ darray_append(m->items, *it);
116+ return true;
117+}
118+
119+static void rmvitem(struct monster *m, size_t i)
120+{
121+ darray_remove(m->items, i);
122+}
123+
124+bool inventorycallback(struct monster *m, size_t i)
125+{
126+ struct item *it = &darray_item(m->items,i);
127+ if(it->type == USE) {
128+ m->current_hp = MIN(m->max_hp, m->current_hp+it->u.u.restorehp);
129+ rmvitem(m, i);
130+ return true;
131+ } else if(it->type == EQUIP) {
132+ if(it->u.e.isequipped) {
133+ m->max_hp -= it->u.e.hp;
134+ m->current_hp = MAX(1, m->current_hp-it->u.e.hp);
135+ m->dmg -= it->u.e.dmg;
136+ } else {
137+ m->max_hp += it->u.e.hp;
138+ m->current_hp += it->u.e.hp;
139+ m->dmg += it->u.e.dmg;
140+ }
141+
142+ it->u.e.isequipped = !it->u.e.isequipped;
143+ }
144+ return false;
145+}
146+
147+void inventory(struct monster *m)
148+{
149+ itemselect(m, inventorycallback);
150+}
151+
152+
153+//TODO: player moves on target, create new map
154+struct point move_object(struct game *g, struct point src, struct point target)
155+{
156+ if(game_at(g, target)->type == NOOBJ) {
157+ *game_at(g, target) = *game_at(g, src);
158+ *game_at(g, src) = NOOBJ_OBJECT;
159+ return target;
160+ } else if(game_at(g, target)->type == ITEM && game_at(g, src)->type == MONSTER) {
161+ if(!additem(&game_at(g, src)->u.m, &game_at(g, target)->u.i)) {
162+ logstr("Inventory is full");
163+ return src;
164+ }
165+ *game_at(g, target) = *game_at(g, src);
166+ *game_at(g, src) = NOOBJ_OBJECT;
167+ return target;
168+ } else {
169+ return src;
170+ }
171+}
172+
173+static inline struct point attack_move(struct game *g, struct point src, struct point target)
174+{
175+ struct object *t = game_at(g, target);
176+
177+ if(t->type == MONSTER) {
178+ attack(game_at(g, src), t);
179+ } else {
180+ return move_object(g, src, target);
181+ }
182+ return src;
183+}
184+
185+void run_ai(struct game *g, struct point player)
186+{
187+ darray(unsigned) checked = darray_new();
188+ for(size_t y = 0; y < g->ys; y++)
189+ {
190+ for(size_t x = 0; x < g->xs; x++)
191+ {
192+ pointarr res;
193+ if(game_at(g, (struct point){y,x})->type == MONSTER && !game_at(g, (struct point){y,x})->u.m.isplayer) {
194+ unsigned *id;
195+ darray_foreach(id, checked)
196+ {
197+ if(*id == game_at(g, (struct point){y,x})->u.m.id)
198+ goto next;
199+ }
200+ darray_append(checked, game_at(g, (struct point){y,x})->u.m.id);
201+
202+ res = astar(g, (struct point){y,x}, player, AGGRO_RANGE);
203+ if(res.size >= 2) {
204+ attack_move(g, (struct point){y,x}, darray_item(res, res.size-2));
205+ } else {
206+ struct point ps[4] =
207+ {
208+ {y, x-1},
209+ {y, x+1},
210+ {y-1, x},
211+ {y+1, x}
212+ };
213+ move_object(g, (struct point){y,x}, ps[randrange(0,3)]);
214+ }
215+ darray_free(res);
216+
217+ }
218+ next:;
219+ }
220+ }
221+ darray_free(checked);
222+}
223+
224+
225+void loop(struct game *g)
226+{
227+/*
228+"\
229+███████╗██████╗ ██╗ ██╗ ███╗ ██╗███████╗ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ███╗██╗ ██╗███████╗███╗ ██╗██████╗ \n\
230+██╔════╝██╔══██╗██║ ██║ ████╗ ██║██╔════╝██╔════╝██║ ██╔╝██╔══██╗██╔══██╗██╔════╝ ██╔════╝████╗ ████║██║ ██║██╔════╝████╗ ██║██╔══██╗\n\
231+███████╗██████╔╝███████║ ██╔██╗ ██║█████╗ ██║ █████╔╝ ███████║██████╔╝██║ ███╗█████╗ ██╔████╔██║██║ ██║█████╗ ██╔██╗ ██║██║ ██║\n\
232+╚════██║██╔══██╗██╔══██║ ██║╚██╗██║██╔══╝ ██║ ██╔═██╗ ██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██║\n\
233+███████║██║ ██║██║ ██║ ██║ ╚████║███████╗╚██████╗██║ ██╗██║ ██║██║ ██║╚██████╔╝███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚████║██████╔╝\n\
234+"
235+*/
236+ *game_at(g, (struct point){1,1}) = (struct object)
237+ {
238+ .type = MONSTER,
239+ .symbol = 'P',
240+ .u.m =
241+ {
242+ .id = -1,
243+ .name = "Player",
244+ .max_hp = 20,
245+ .current_hp = 20,
246+ .dmg = 2,
247+ .level = 1,
248+ .xp = 0,
249+ .items = darray_new(),
250+ .isplayer = true
251+ }
252+ };
253+
254+ struct point player = {1,1};
255+
256+ while(true)
257+ {
258+ charclear();
259+ charprint("(づ。◕‿‿◕。)づ");
260+ charprint("Name: %s", game_at(g, player)->u.m.name);
261+ charprint("HP : %u/%u", game_at(g, player)->u.m.current_hp, game_at(g, player)->u.m.max_hp);
262+ charprint("DMG: %u", game_at(g, player)->u.m.dmg);
263+ charprint("LVL: %u", game_at(g, player)->u.m.level);
264+ charprint("XP : %u", game_at(g, player)->u.m.xp);
265+ draw_map(g, player);
266+
267+ struct choice c = get_input();
268+ switch(c.a)
269+ {
270+ case INVALID:
271+ logstr("Invalid input, try again");
272+ continue;
273+ case QUIT:
274+ return;
275+ case MOVE:;
276+ struct point targetpos = relative(player, c.u.d);
277+
278+ if(!in_bounds(g, targetpos)) {
279+ logstr("Target not in bounds");
280+ continue;
281+ }
282+
283+ player = attack_move(g, player, targetpos);
284+ run_ai(g, player);
285+ break;
286+ case SPAWN:
287+ spawn_enemy(g);
288+ break;
289+ case INVENTORY:
290+ inventory(&game_at(g, player)->u.m);
291+ break;
292+ case REDRAW:
293+ setup();
294+ printlog();
295+ break;
296+ }
297+ }
298+}
Asrc/main.c
@@ -0,0 +1,45 @@
1+
2+#include <stdio.h>
3+#include <stdlib.h>
4+#include <string.h>
5+#include <assert.h>
6+#include <stdbool.h>
7+#include <locale.h>
8+#include <time.h>
9+
10+
11+#include <helper.h>
12+#include <tui.h>
13+#include <loop.h>
14+#include <game.h>
15+#include <algorithm.h>
16+#include <config.h>
17+#include <items.h>
18+
19+
20+
21+#include <unistd.h>
22+int main()
23+{
24+ srand(time(NULL));
25+
26+ setup();
27+ logstr("Game started");
28+
29+ struct game g = make_maze(randrange(GAME_MIN_Y, GAME_MAX_Y), randrange(GAME_MIN_X, GAME_MAX_X));
30+ size_t num_enemies = randrange(MIN_ENEMIES, MAX_ENEMIES);
31+ for(size_t i = 0; i < num_enemies; i++)
32+ {
33+ spawn_enemy(&g);
34+ }
35+
36+ size_t num_items = randrange(MIN_LOOT, MAX_LOOT);
37+ for(size_t i = 0; i < num_items; i++)
38+ {
39+ spawn_item(&g);
40+ }
41+
42+ loop(&g);
43+
44+ delete_game(&g);
45+}
Asrc/tui.c
@@ -0,0 +1,366 @@
1+#include <tui.h>
2+#define NCURSES_WIDECHAR 1
3+#include <curses.h>
4+#include <sys/ioctl.h>
5+#include <unistd.h>
6+#include <time.h>
7+#include <locale.h>
8+#include <stdlib.h>
9+#include <stdarg.h>
10+#include <panel.h>
11+#include <string.h>
12+
13+#include <helper.h>
14+
15+static PANEL *stdpanel = NULL;
16+static WINDOW *logbox = NULL;
17+static WINDOW *logwin = NULL;
18+static WINDOW *charbox = NULL;
19+static WINDOW *charwin = NULL;
20+static WINDOW *mapbox = NULL;
21+static WINDOW *mapwin = NULL;
22+static bool initialized = false;
23+
24+static bool setup_log(struct winsize w)
25+{
26+ int ystart = 0;
27+ int xstart = w.ws_col/2+1;
28+ int ysize = w.ws_row;
29+ int xsize = w.ws_col/2-1;
30+ logbox = subwin(stdscr, ysize, xsize, ystart, xstart);
31+ if(!logbox)
32+ return false;
33+ box(logbox,'|', '-');
34+ wmove(logbox, 0,1);
35+ wprintw(logbox, "Log");
36+ logwin = subwin(logbox, ysize-2, xsize-2, ystart+1, xstart+1);
37+ if(!logwin)
38+ return false;
39+// scrollok(logwin, true);
40+ return true;
41+}
42+
43+static bool setup_char(struct winsize w)
44+{
45+ int ystart = 0;
46+ int xstart = 0;
47+ int ysize = w.ws_row/3;
48+ int xsize = w.ws_col/2;
49+ charbox = subwin(stdscr, ysize, xsize, ystart, xstart);
50+ if(!charbox)
51+ return false;
52+ box(charbox,'|', '-');
53+ wmove(charbox, 0,1);
54+ wprintw(charbox, "Character");
55+ charwin = subwin(charbox, ysize-2, xsize-2, ystart+1, xstart+1);
56+ if(!charwin)
57+ return false;
58+ return true;
59+}
60+
61+static bool setup_map(struct winsize w)
62+{
63+ int ystart = w.ws_row/3+1;
64+ int xstart = 0;
65+ int ysize = w.ws_row-w.ws_row/3-1;
66+ int xsize = w.ws_col/2;
67+ mapbox = subwin(stdscr, ysize, xsize, ystart, xstart);
68+ if(!mapbox)
69+ return false;
70+ box(mapbox,'|', '-');
71+ wmove(mapbox, 0,1);
72+ wprintw(mapbox, "Map");
73+ mapwin = subwin(mapbox, ysize-2, xsize-2, ystart+1, xstart+1);
74+ if(!mapwin)
75+ return false;
76+ return true;
77+}
78+
79+void quit(void)
80+{
81+ endwin();
82+}
83+
84+bool setup()
85+{
86+ if(initialized) {
87+ erase();
88+
89+ if(logwin != NULL)
90+ delwin(logwin);
91+ if(logbox != NULL)
92+ delwin(logbox);
93+
94+ if(charwin != NULL)
95+ delwin(charwin);
96+ if(charbox != NULL)
97+ delwin(charbox);
98+
99+ if(mapwin != NULL)
100+ delwin(mapwin);
101+ if(mapbox != NULL)
102+ delwin(mapbox);
103+
104+ } else {
105+ atexit(quit);
106+ setlocale(LC_ALL,"");
107+ stdscr = initscr();
108+ stdpanel = new_panel(stdscr);
109+ assert(stdpanel != NULL);
110+ noecho();
111+ cbreak();
112+ curs_set(0);
113+ keypad(stdscr, true);
114+ initialized = true;
115+ }
116+
117+
118+ struct winsize w;
119+ assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
120+ if(w.ws_col < 25 || w.ws_row < 10) {
121+ endwin();
122+ puts("Terminal too small");
123+ abort();
124+ }
125+
126+ if(!setup_char(w)) {
127+ fprintf(stderr, "char\n");
128+ return false;
129+ }
130+ if(!setup_log(w)) {
131+ fprintf(stderr, "log\n");
132+ return false;
133+ }
134+
135+ if(!setup_map(w)) {
136+ fprintf(stderr, "map\n");
137+ return false;
138+ }
139+ update_panels();
140+ doupdate();
141+ return true;
142+}
143+
144+size_t logindex = 0;
145+size_t loglength;
146+char **logs = NULL;
147+
148+void printlog() //TODO: fix logs if they go multiline
149+{
150+ werase(logwin);
151+ wmove(logwin, 0, 0);
152+ int ymax = getmaxy(logwin);
153+ for(int i = 0; i < ymax && i+logindex < loglength; i++)
154+ {
155+ waddstr(logwin, logs[i+logindex]);
156+ waddstr(logwin, "\n");
157+ }
158+ wrefresh(logwin);
159+}
160+
161+void logstr(const char *format, ...)
162+{
163+ static size_t capacity;
164+
165+ if(logs == NULL) {
166+ logs = xmalloc(8*sizeof(*logs));
167+ capacity = 8;
168+ loglength = 0;
169+ }
170+
171+ if(capacity == loglength++) {
172+ logs = xrealloc(logs, (capacity*=2) * sizeof(*logs));
173+ }
174+
175+ va_list ap;
176+ va_start(ap, format);
177+ va_list ap2;
178+ va_copy(ap2, ap);
179+ int bufsz = vsnprintf(NULL, 0, format, ap);
180+ va_end(ap);
181+ struct tm mytime = *localtime(&(time_t){time(NULL)});
182+ logs[loglength-1] = xmalloc(bufsz+11+2); //11 == size needed for the time header;
183+ strftime(logs[loglength-1], 11, "%T> ", &mytime);
184+ vsprintf(logs[loglength-1]+10, format, ap2);
185+ va_end(ap2);
186+
187+ if(loglength - logindex == getmaxy(logwin))
188+ logindex++;
189+
190+ printlog();
191+}
192+
193+void charclear()
194+{
195+ werase(charwin);
196+ wrefresh(charwin);
197+}
198+
199+
200+void charprint(const char* format, ...)
201+{
202+ va_list ap;
203+ va_start(ap, format);
204+
205+ vw_printw(charwin, format, ap);
206+ wprintw(charwin, "\n");
207+ wrefresh(charwin);
208+
209+ va_end(ap);
210+}
211+
212+
213+void log_scroll(enum direction d)
214+{
215+ if(d == UP && logindex != 0)
216+ logindex--;
217+ else if(d == DOWN && logindex != loglength-1)
218+ logindex++;
219+
220+ printlog();
221+
222+ wrefresh(logwin);
223+}
224+
225+
226+void draw_map(const struct game *g, struct point p)
227+{
228+ int x, y;
229+ getmaxyx(mapwin, y, x);
230+ size_t starty = MAX(0, p.y-y/2);
231+ size_t endy = MIN(starty + y, g->ys);
232+ size_t startx = MAX(0, p.x-x/2);
233+ size_t endx = MIN(startx + x, g->xs);
234+
235+ werase(mapwin);
236+ int line = 0;
237+ for(size_t i = starty; i < endy; i++)
238+ {
239+ wmove(mapwin, line++, 0);
240+ for(size_t j = startx; j < endx; j++)
241+ {
242+ waddch(mapwin, game_at(g, (struct point){.y = i, .x = j})->symbol);
243+ }
244+ }
245+
246+ wrefresh(mapwin);
247+}
248+
249+bool messagebox(const char *str, int y, int x)
250+{
251+ int yorig = y, xorig = x;
252+ struct winsize w;
253+ assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
254+
255+ const char *returnstr = "Press return to continue";
256+ x = MAX(strlen(returnstr), x)+2;
257+ y += 4;
258+
259+ if(y > w.ws_row || x > w.ws_col)
260+ return false;
261+
262+ int ystart = w.ws_row/2-y/2;
263+ int xstart = w.ws_col/2-x/2;
264+
265+ WINDOW *msgbox = newwin(y, x, ystart, xstart);
266+ PANEL *msgboxpanel = new_panel(msgbox);
267+ box(msgbox, '|', '-');
268+ WINDOW *msgwin = newwin(y-2, x-2, ystart+1, xstart+1);
269+ PANEL *msgwinpanel = new_panel(msgwin);
270+ waddstr(msgwin, str);
271+ waddstr(msgwin, "\n\n");
272+ waddstr(msgwin, returnstr);
273+ update_panels();
274+ doupdate();
275+ int c;
276+ while((c = getch()) != '\n' && c != KEY_RESIZE);
277+
278+ del_panel(msgwinpanel);
279+ del_panel(msgboxpanel);
280+
281+ delwin(msgbox);
282+ delwin(msgwin);
283+
284+ update_panels();
285+ doupdate();
286+
287+ if(c == KEY_RESIZE)
288+ {
289+ while(!setup());
290+ return messagebox(str, yorig, xorig);
291+ }
292+ printlog(); //incase we resized
293+ return true;
294+
295+}
296+
297+void itemselect(struct monster *m, itemselectfn f)
298+{
299+ WINDOW *menu = newwin(0, 0, 0, 0);
300+ PANEL *menupanel = new_panel(menu);
301+
302+ size_t i = 0; //we put the index before the label so we keep it between redraws
303+
304+redraw:;
305+ struct winsize w;
306+ assert(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1);
307+
308+ for(size_t i = 0; i < m->items.size; i++)
309+ {
310+ struct item *it = &darray_item(m->items, i);
311+ if(it->type != NOITEM) {
312+ if(it->type == EQUIP) {
313+ mvwprintw(menu, i, 0, "%.*s (%c)", w.ws_col, it->name, it->u.e.isequipped ? '*' : ' ');
314+ } else {
315+ mvwprintw(menu, i, 0, "%.*s", w.ws_col, it->name);
316+ }
317+ } else {
318+ mvwaddstr(menu, i, 0, "EMPTY");
319+ }
320+ }
321+
322+ mvwchgat(menu, i, 0, -1, A_REVERSE, 0, NULL);
323+ update_panels();
324+ doupdate();
325+
326+ int c;
327+ while((c = getch()) != 'i' && c != 'q')
328+ {
329+ switch(c)
330+ {
331+ case ' ':
332+ if(f(m, i))
333+ i = 0;
334+ werase(menu);
335+ goto redraw; //items might have changed, so we redraw the list
336+ break;
337+ case 's':
338+ if(i != (m->items.size == 0 ? 0 :m->items.size-1)) {
339+ wchgat(menu , -1, A_NORMAL, 0, NULL);
340+ mvwchgat(menu, ++i, 0, -1, A_REVERSE, 0, NULL);
341+ }
342+ break;
343+ case 'w':
344+ if(i != 0) {
345+ wchgat(menu, -1, A_NORMAL, 0, NULL);
346+ mvwchgat(menu, --i, 0, -1, A_REVERSE, 0, NULL);
347+ }
348+ break;
349+ case KEY_RESIZE:
350+ werase(menu);
351+ goto redraw;
352+ break;
353+ }
354+ update_panels();
355+ doupdate();
356+ }
357+
358+
359+ del_panel(menupanel);
360+ delwin(menu);
361+ update_panels();
362+ doupdate();
363+}
364+
365+
366+