first basic build, pathfinding, maze generation, enemy+loot spawning, no memleaks, no major bugs
MCMakeLists.txt
| @@ -1,5 +1,21 @@ | |||
|---|---|---|---|
| 1 | - | project(orcsmasher) | |
| 1 | + | cmake_minimum_required(VERSION 3.0) | |
| 2 | 2 | ||
| 3 | - | add_executable(orcsmasher main.c) | |
| 3 | + | project(orcsmasher LANGUAGES C) | |
| 4 | 4 | ||
| 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) &¤tnode->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 | + | ||