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 save_game(struct World * world) {
58 // Save game data to game file.
59 FILE * file = fopen("savefile", "w");
60 write_uint32_bigendian(world->seed, file);
61 write_uint32_bigendian(world->turn, file);
62 write_uint16_bigendian(world->player->y, file);
63 write_uint16_bigendian(world->player->x, file);
64 write_uint16_bigendian(world->monster->y, file);
65 write_uint16_bigendian(world->monster->x, file);
68 void toggle_window (struct WinMeta * win_meta, struct Win * win) {
69 // Toggle display of window win.
71 suspend_window(win_meta, win);
73 append_window(win_meta, win); }
75 void growshrink_active_window (struct WinMeta * win_meta, char change) {
76 // Grow or shrink active window horizontally or vertically by one cell size.
77 if (0 != win_meta->active) {
78 uint16_t height = win_meta->active->height;
79 uint16_t width = win_meta->active->width;
82 else if (change == '+')
84 else if (change == '_')
86 else if (change == '*')
88 resize_active_window (win_meta, height, width); } }
90 struct Map init_map () {
91 // Initialize map with some experimental start values.
97 uint32_t size = map.width * map.height;
98 map.cells = malloc(size);
100 for (y = 0; y < map.height; y++)
101 for (x = 0; x < map.width; x++)
102 map.cells[(y * map.width) + x] = '~';
103 map.cells[size / 2 + (map.width / 2)] = '.';
104 uint32_t repeats, root, curpos;
105 for (root = 0; root * root * root < size; root++);
106 for (repeats = 0; repeats < size * root; repeats++) {
107 y = rrand(0, 0) % map.height;
108 x = rrand(0, 0) % map.width;
109 curpos = y * map.width + x;
110 if ('~' == map.cells[curpos] &&
111 ( (curpos >= map.width && '.' == map.cells[curpos - map.width])
112 || (curpos < map.width * (map.height-1) && '.' == map.cells[curpos + map.width])
113 || (curpos > 0 && curpos % map.width != 0 && '.' == map.cells[curpos-1])
114 || (curpos < (map.width * map.height) && (curpos+1) % map.width != 0 && '.' == map.cells[curpos+1])))
115 map.cells[y * map.width + x] = '.'; }
118 void map_scroll (struct Map * map, char dir) {
119 // Scroll map into direction dir if possible by changing the offset.
120 if ('n' == dir && map->offset_y > 0)
124 else if ('w' == dir && map->offset_x > 0)
129 void next_turn (struct World * world) {
130 // Increment turn and move enemy.
132 rrand(1, world->seed * world->turn);
133 char d = rrand(0, 0) % 5;
134 uint16_t ty = world->monster->y;
135 uint16_t tx = world->monster->x;
144 if (tx == world->player->x && ty == world->player->y)
145 update_log(world, "\nThe monster hits you.");
146 else if (is_passable(world->map, ty, tx)) {
147 world->monster->y = ty;
148 world->monster->x = tx; } }
150 void update_log (struct World * world, char * text) {
151 // Update log with new text to be appended.
153 uint16_t len_old = strlen(world->log);
154 uint16_t len_new = strlen(text);
155 uint16_t len_whole = len_old + len_new + 1;
156 new_text = calloc(len_whole, sizeof(char));
157 memcpy(new_text, world->log, len_old);
158 memcpy(new_text + len_old, text, len_new);
160 world->log = new_text; }
162 char is_passable (struct Map * map, uint16_t y, uint16_t x) {
163 // Check if coordinate on (or beyond) map is accessible to movement.
165 if (0 <= x && x < map->width && 0 <= y && y < map->height)
166 if ('.' == map->cells[y * map->width + x])
170 void record_action (char action) {
171 // Append action to game record file.
172 FILE * file = fopen("record", "a");
176 void move_player (struct World * world, char d) {
177 // Move player in direction d, increment turn counter and update log.
178 static char prev = 0;
181 uint16_t ty = world->player->y;
182 uint16_t tx = world->player->x;
195 if (ty == world->monster->y && tx == world->monster->x)
197 else if (is_passable(world->map, ty, tx)) {
199 world->player->y = ty;
200 world->player->x = tx; }
201 if (success * d == prev)
202 update_log (world, ".");
205 update_log (world, "\nYou hit the monster.");
207 char * msg = calloc(25, sizeof(char));
208 char * msg_content = "You fail to move";
210 msg_content = "You move";
211 sprintf(msg, "\n%s %s.", msg_content, dir);
212 update_log (world, msg);
215 if (1 == world->interactive)
219 void player_wait (struct World * world) {
220 // Make player wait one turn.
221 if (1 == world->interactive)
224 update_log (world, "\nYou wait."); }
226 unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys,
227 struct Win * win_map, struct Win * win_info, struct Win * win_log) {
228 // Call some meta program / window management actions dependent on key. Return 1 to signal quitting.
229 if (key == get_action_key(world->keybindings, "quit"))
231 else if (key == get_action_key(world->keybindings, "scroll pad right"))
232 scroll_pad (win_meta, '+');
233 else if (key == get_action_key(world->keybindings, "scroll pad left"))
234 scroll_pad (win_meta, '-');
235 else if (key == get_action_key(world->keybindings, "toggle keys window"))
236 toggle_window(win_meta, win_keys);
237 else if (key == get_action_key(world->keybindings, "toggle map window"))
238 toggle_window(win_meta, win_map);
239 else if (key == get_action_key(world->keybindings, "toggle info window"))
240 toggle_window(win_meta, win_info);
241 else if (key == get_action_key(world->keybindings, "toggle log window"))
242 toggle_window(win_meta, win_log);
243 else if (key == get_action_key(world->keybindings, "cycle forwards"))
244 cycle_active_window(win_meta, 'n');
245 else if (key == get_action_key(world->keybindings, "cycle backwards"))
246 cycle_active_window(win_meta, 'p');
247 else if (key == get_action_key(world->keybindings, "shift forwards"))
248 shift_active_window(win_meta, 'f');
249 else if (key == get_action_key(world->keybindings, "shift backwards"))
250 shift_active_window(win_meta, 'b');
251 else if (key == get_action_key(world->keybindings, "grow horizontally"))
252 growshrink_active_window(win_meta, '*');
253 else if (key == get_action_key(world->keybindings, "shrink horizontally"))
254 growshrink_active_window(win_meta, '_');
255 else if (key == get_action_key(world->keybindings, "grow vertically"))
256 growshrink_active_window(win_meta, '+');
257 else if (key == get_action_key(world->keybindings, "shrink vertically"))
258 growshrink_active_window(win_meta, '-');
259 else if (key == get_action_key(world->keybindings, "save keys"))
260 save_keybindings(world);
261 else if (key == get_action_key(world->keybindings, "keys nav up"))
262 keyswin_move_selection (world, 'u');
263 else if (key == get_action_key(world->keybindings, "keys nav down"))
264 keyswin_move_selection (world, 'd');
265 else if (key == get_action_key(world->keybindings, "keys mod"))
266 keyswin_mod_key (world, win_meta);
267 else if (key == get_action_key(world->keybindings, "map up"))
268 map_scroll (world->map, 'n');
269 else if (key == get_action_key(world->keybindings, "map down"))
270 map_scroll (world->map, 's');
271 else if (key == get_action_key(world->keybindings, "map right"))
272 map_scroll (world->map, 'e');
273 else if (key == get_action_key(world->keybindings, "map left"))
274 map_scroll (world->map, 'w');
277 int main (int argc, char *argv[]) {
279 world.interactive = 1;
282 while ((opt = getopt(argc, argv, "s::")) != -1) {
285 world.interactive = 0;
288 start_turn = atoi(optarg);
291 exit(EXIT_FAILURE); } }
293 world.log = calloc(1, sizeof(char));
294 update_log (&world, " ");
295 struct Player player;
296 world.player = &player;
297 struct Monster monster;
298 world.monster = &monster;
300 if (1 == world.interactive && 0 == access("savefile", F_OK)) {
301 file = fopen("savefile", "r");
302 world.seed = read_uint32_bigendian(file);
303 world.turn = read_uint32_bigendian(file);
304 player.y = read_uint16_bigendian(file);
305 player.x = read_uint16_bigendian(file);
306 monster.y = read_uint16_bigendian(file);
307 monster.x = read_uint16_bigendian(file);
311 if (0 == world.interactive) {
312 file = fopen("record", "r");
313 world.seed = read_uint32_bigendian(file); }
315 file = fopen("record", "w");
316 world.seed = time(NULL);
317 write_uint32_bigendian(world.seed, file);
319 rrand(1, world.seed);
320 struct Map map = init_map();
322 if (1 == world.turn) {
323 for (player.y = player.x = 0; 0 == is_passable(&map, player.y, player.x);) {
324 player.y = rrand(0, 0) % map.height;
325 player.x = rrand(0, 0) % map.width; }
326 for (monster.y = monster.x = 0; 0 == is_passable(&map, monster.y, monster.x);) {
327 monster.y = rrand(0, 0) % map.height;
328 monster.x = rrand(0, 0) % map.width; } }
330 WINDOW * screen = initscr();
333 keypad(screen, TRUE);
335 init_keybindings(&world);
336 struct WinMeta win_meta = init_win_meta(screen);
337 struct Win win_keys = init_window(&win_meta, "Keys", &world, draw_keys_win);
338 struct Win win_map = init_window(&win_meta, "Map", &world, draw_map_win);
339 struct Win win_info = init_window(&win_meta, "Info", &world, draw_info_win);
340 struct Win win_log = init_window(&win_meta, "Log", &world, draw_log_win);
342 win_map.width = win_meta.width - win_keys.width - win_log.width - 2;
344 win_log.height = win_meta.height - 3;
345 toggle_window(&win_meta, &win_keys);
346 toggle_window(&win_meta, &win_map);
347 toggle_window(&win_meta, &win_info);
348 toggle_window(&win_meta, &win_log);
351 unsigned char quit_called = 0;
352 if (0 == world.interactive) {
353 unsigned char still_reading_file = 1;
356 if (start_turn == world.turn)
358 if (0 == start_turn) {
359 draw_all_windows (&win_meta);
361 if (1 == still_reading_file &&
362 (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) {
366 still_reading_file = 0; }
367 else if (0 == action)
368 player_wait (&world);
369 else if ('s' == action)
370 move_player(&world, 's');
371 else if ('n' == action)
372 move_player(&world, 'n');
373 else if ('e' == action)
374 move_player(&world, 'e');
375 else if ('w' == action)
376 move_player(&world, 'w'); }
378 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
379 if (1 == quit_called)
382 uint32_t last_turn = 0;
384 if (last_turn != world.turn) {
386 last_turn = world.turn; }
387 draw_all_windows (&win_meta);
389 if (key == get_action_key(world.keybindings, "player down"))
390 move_player(&world, 's');
391 else if (key == get_action_key(world.keybindings, "player up"))
392 move_player(&world, 'n');
393 else if (key == get_action_key(world.keybindings, "player right"))
394 move_player(&world, 'e');
395 else if (key == get_action_key(world.keybindings, "player left"))
396 move_player(&world, 'w');
397 else if (key == get_action_key(world.keybindings, "wait / next turn"))
398 player_wait (&world);
400 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
401 if (1 == quit_called)
405 for (key = 0; key <= world.keyswindata->max; key++)
406 free(world.keybindings[key].name);
407 free(world.keybindings);
408 free(world.keyswindata);