10 #include "roguelike.h"
11 #include "keybindings.h"
13 uint16_t rrand(char use_seed, uint32_t new_seed) {
14 // Pseudo-random number generator (LGC algorithm). Use instead of rand() to ensure portable predictability.
18 seed = ((seed * 1103515245) + 12345) % 2147483648; // Values as recommended by POSIX.1-2001 (see rand(3)).
19 return (seed / 65536); } // Ignore least significant 16 bits (they are less random).
21 uint16_t read_uint16_bigendian(FILE * file) {
22 // Read uint16 from file in big-endian order.
23 const uint16_t nchar = UCHAR_MAX + 1;
24 unsigned char a = fgetc(file);
25 unsigned char b = fgetc(file);
26 return (a * nchar) + b; }
28 void write_uint16_bigendian(uint16_t x, FILE * file) {
29 // Write uint16 to file in beg-endian order.
30 const uint16_t nchar = UCHAR_MAX + 1;
31 unsigned char a = x / nchar;
32 unsigned char b = x % nchar;
36 uint32_t read_uint32_bigendian(FILE * file) {
37 // Read uint32 from file in big-endian order.
38 const uint16_t nchar = UCHAR_MAX + 1;
39 unsigned char a = fgetc(file);
40 unsigned char b = fgetc(file);
41 unsigned char c = fgetc(file);
42 unsigned char d = fgetc(file);
43 return (a * nchar * nchar * nchar) + (b * nchar * nchar) + (c * nchar) + d; }
45 void write_uint32_bigendian(uint32_t x, FILE * file) {
46 // Write uint32 to file in beg-endian order.
47 const uint16_t nchar = UCHAR_MAX + 1;
48 unsigned char a = x / (nchar * nchar * nchar);
49 unsigned char b = (x - (a * nchar * nchar * nchar)) / (nchar * nchar);
50 unsigned char c = (x - ((a * nchar * nchar * nchar) + (b * nchar * nchar))) / nchar;
51 unsigned char d = x % nchar;
57 void load_game(struct World * world) {
58 // Load game data from game file.
59 FILE * file = fopen("savefile", "r");
60 world->seed = read_uint32_bigendian(file);
61 world->turn = read_uint32_bigendian(file);
62 world->player->y = read_uint16_bigendian(file);
63 world->player->x = read_uint16_bigendian(file);
64 world->monster->y = read_uint16_bigendian(file);
65 world->monster->x = read_uint16_bigendian(file);
68 void save_game(struct World * world) {
69 // Save game data to game file.
70 FILE * file = fopen("savefile", "w");
71 write_uint32_bigendian(world->seed, file);
72 write_uint32_bigendian(world->turn, file);
73 write_uint16_bigendian(world->player->y, file);
74 write_uint16_bigendian(world->player->x, file);
75 write_uint16_bigendian(world->monster->y, file);
76 write_uint16_bigendian(world->monster->x, file);
79 void toggle_window (struct WinMeta * win_meta, struct Win * win) {
80 // Toggle display of window win.
82 suspend_window(win_meta, win);
84 append_window(win_meta, win); }
86 void growshrink_active_window (struct WinMeta * win_meta, char change) {
87 // Grow or shrink active window horizontally or vertically by one cell size.
88 if (0 != win_meta->active) {
89 uint16_t height = win_meta->active->height;
90 uint16_t width = win_meta->active->width;
93 else if (change == '+')
95 else if (change == '_')
97 else if (change == '*')
99 resize_active_window (win_meta, height, width); } }
101 struct Map init_map () {
102 // Initialize map with some experimental start values.
108 uint32_t size = map.width * map.height;
109 map.cells = malloc(size);
111 for (y = 0; y < map.height; y++)
112 for (x = 0; x < map.width; x++)
113 map.cells[(y * map.width) + x] = '~';
114 map.cells[size / 2 + (map.width / 2)] = '.';
115 uint32_t repeats, root, curpos;
116 for (root = 0; root * root * root < size; root++);
117 for (repeats = 0; repeats < size * root; repeats++) {
118 y = rrand(0, 0) % map.height;
119 x = rrand(0, 0) % map.width;
120 curpos = y * map.width + x;
121 if ('~' == map.cells[curpos] &&
122 ( (curpos >= map.width && '.' == map.cells[curpos - map.width])
123 || (curpos < map.width * (map.height - 1) && '.' == map.cells[curpos + map.width])
124 || (curpos > 0 && '.' == map.cells[curpos - 1] && curpos % map.width != 0)
125 || (curpos < (map.width * map.height) && '.' == map.cells[curpos + 1] && (curpos + 1) % map.width != 0)))
126 map.cells[y * map.width + x] = '.'; }
129 void map_scroll (struct Map * map, char dir) {
130 // Scroll map into direction dir if possible by changing the offset.
131 if ('n' == dir && map->offset_y > 0)
135 else if ('w' == dir && map->offset_x > 0)
140 void next_turn (struct World * world) {
141 // Increment turn and move enemy.
143 rrand(1, world->seed * world->turn);
144 char d = rrand(0, 0) % 5;
145 uint16_t ty = world->monster->y;
146 uint16_t tx = world->monster->x;
155 if (tx == world->player->x && ty == world->player->y)
156 update_log(world, "\nThe monster hits you.");
157 else if (is_passable(world, tx, ty)) {
158 world->monster->y = ty;
159 world->monster->x = tx; } }
161 void update_log (struct World * world, char * text) {
162 // Update log with new text to be appended.
164 uint16_t len_old = strlen(world->log);
165 uint16_t len_new = strlen(text);
166 uint16_t len_whole = len_old + len_new + 1;
167 new_text = calloc(len_whole, sizeof(char));
168 memcpy(new_text, world->log, len_old);
169 memcpy(new_text + len_old, text, len_new);
171 world->log = new_text; }
173 char is_passable (struct World * world, uint16_t x, uint16_t y) {
174 // Check if coordinate on (or beyond) map is accessible to movement.
176 if (0 <= x && x < world->map->width && 0 <= y && y < world->map->height)
177 if ('.' == world->map->cells[y * world->map->width + x])
181 void record_action (char action) {
182 // Append action to game record file.
183 FILE * file = fopen("record", "a");
187 void move_player (struct World * world, char d) {
188 // Move player in direction d, increment turn counter and update log.
189 static char prev = 0;
192 uint16_t ty = world->player->y;
193 uint16_t tx = world->player->x;
206 if (ty == world->monster->y && tx == world->monster->x)
208 else if (is_passable(world, tx, ty)) {
210 world->player->y = ty;
211 world->player->x = tx; }
212 if (success * d == prev)
213 update_log (world, ".");
216 update_log (world, "\nYou hit the monster.");
218 char * msg = calloc(25, sizeof(char));
219 char * msg_content = "You fail to move";
221 msg_content = "You move";
222 sprintf(msg, "\n%s %s.", msg_content, dir);
223 update_log (world, msg);
226 if (1 == world->interactive)
230 void player_wait (struct World * world) {
231 // Make player wait one turn.
232 if (1 == world->interactive)
235 update_log (world, "\nYou wait."); }
237 void startpos(struct World * world) {
238 // Initialize some default starting values.
240 world->player->y = 8;
241 world->player->x = 8;
242 world->monster->y = 55;
243 world->monster->x = 55; }
245 unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys,
246 struct Win * win_map, struct Win * win_info, struct Win * win_log) {
247 // Call some meta program / window management actions dependent on key. Return 1 to signal quitting.
248 if (key == get_action_key(world->keybindings, "quit"))
250 else if (key == get_action_key(world->keybindings, "scroll pad right"))
251 scroll_pad (win_meta, '+');
252 else if (key == get_action_key(world->keybindings, "scroll pad left"))
253 scroll_pad (win_meta, '-');
254 else if (key == get_action_key(world->keybindings, "toggle keys window"))
255 toggle_window(win_meta, win_keys);
256 else if (key == get_action_key(world->keybindings, "toggle map window"))
257 toggle_window(win_meta, win_map);
258 else if (key == get_action_key(world->keybindings, "toggle info window"))
259 toggle_window(win_meta, win_info);
260 else if (key == get_action_key(world->keybindings, "toggle log window"))
261 toggle_window(win_meta, win_log);
262 else if (key == get_action_key(world->keybindings, "cycle forwards"))
263 cycle_active_window(win_meta, 'n');
264 else if (key == get_action_key(world->keybindings, "cycle backwards"))
265 cycle_active_window(win_meta, 'p');
266 else if (key == get_action_key(world->keybindings, "shift forwards"))
267 shift_active_window(win_meta, 'f');
268 else if (key == get_action_key(world->keybindings, "shift backwards"))
269 shift_active_window(win_meta, 'b');
270 else if (key == get_action_key(world->keybindings, "grow horizontally"))
271 growshrink_active_window(win_meta, '*');
272 else if (key == get_action_key(world->keybindings, "shrink horizontally"))
273 growshrink_active_window(win_meta, '_');
274 else if (key == get_action_key(world->keybindings, "grow vertically"))
275 growshrink_active_window(win_meta, '+');
276 else if (key == get_action_key(world->keybindings, "shrink vertically"))
277 growshrink_active_window(win_meta, '-');
278 else if (key == get_action_key(world->keybindings, "save keys"))
279 save_keybindings(world);
280 else if (key == get_action_key(world->keybindings, "keys nav up"))
281 keyswin_move_selection (world, 'u');
282 else if (key == get_action_key(world->keybindings, "keys nav down"))
283 keyswin_move_selection (world, 'd');
284 else if (key == get_action_key(world->keybindings, "keys mod"))
285 keyswin_mod_key (world, win_meta);
286 else if (key == get_action_key(world->keybindings, "map up"))
287 map_scroll (world->map, 'n');
288 else if (key == get_action_key(world->keybindings, "map down"))
289 map_scroll (world->map, 's');
290 else if (key == get_action_key(world->keybindings, "map right"))
291 map_scroll (world->map, 'e');
292 else if (key == get_action_key(world->keybindings, "map left"))
293 map_scroll (world->map, 'w');
296 int main (int argc, char *argv[]) {
298 world.interactive = 1;
301 while ((opt = getopt(argc, argv, "s::")) != -1) {
304 world.interactive = 0;
307 start_turn = atoi(optarg);
310 exit(EXIT_FAILURE); } }
312 init_keybindings(&world);
313 world.log = calloc(1, sizeof(char));
314 update_log (&world, " ");
315 struct Player player;
316 world.player = &player;
317 struct Monster monster;
318 world.monster = &monster;
321 if (0 == world.interactive) {
323 file = fopen("record", "r");
324 world.seed = read_uint32_bigendian(file); }
326 if (0 == access("savefile", F_OK))
330 world.seed = time(NULL);
331 file = fopen("record", "w");
332 write_uint32_bigendian(world.seed, file);
334 rrand(1, world.seed);
335 struct Map map = init_map();
338 WINDOW * screen = initscr();
341 keypad(screen, TRUE);
343 struct WinMeta win_meta = init_win_meta(screen);
344 struct Win win_keys = init_window(&win_meta, "Keys", &world, draw_keys_win);
345 struct Win win_map = init_window(&win_meta, "Map", &world, draw_map_win);
346 struct Win win_info = init_window(&win_meta, "Info", &world, draw_info_win);
347 struct Win win_log = init_window(&win_meta, "Log", &world, draw_log_win);
349 win_map.width = win_meta.width - win_keys.width - win_log.width - 2;
351 win_log.height = win_meta.height - 3;
352 toggle_window(&win_meta, &win_keys);
353 toggle_window(&win_meta, &win_map);
354 toggle_window(&win_meta, &win_info);
355 toggle_window(&win_meta, &win_log);
358 unsigned char quit_called;
359 if (0 == world.interactive) {
360 unsigned char still_reading_file = 1;
363 if (start_turn == world.turn)
365 if (0 == start_turn) {
366 draw_all_windows (&win_meta);
368 if (1 == still_reading_file &&
369 (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) {
373 still_reading_file = 0; }
374 else if (0 == action)
375 player_wait (&world);
376 else if ('s' == action)
377 move_player(&world, 's');
378 else if ('n' == action)
379 move_player(&world, 'n');
380 else if ('e' == action)
381 move_player(&world, 'e');
382 else if ('w' == action)
383 move_player(&world, 'w'); }
385 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
386 if (1 == quit_called)
389 uint32_t last_turn = 0;
391 if (last_turn != world.turn) {
393 last_turn = world.turn; }
394 draw_all_windows (&win_meta);
396 if (key == get_action_key(world.keybindings, "player down"))
397 move_player(&world, 's');
398 else if (key == get_action_key(world.keybindings, "player up"))
399 move_player(&world, 'n');
400 else if (key == get_action_key(world.keybindings, "player right"))
401 move_player(&world, 'e');
402 else if (key == get_action_key(world.keybindings, "player left"))
403 move_player(&world, 'w');
404 else if (key == get_action_key(world.keybindings, "wait / next turn"))
405 player_wait (&world);
407 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
408 if (1 == quit_called)
412 for (key = 0; key <= world.keyswindata->max; key++)
413 free(world.keybindings[key].name);
414 free(world.keybindings);
415 free(world.keyswindata);