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 scroll_pad (struct WinMeta * win_meta, char dir) {
76 // Try to scroll pad left or right.
78 reset_pad_offset(win_meta, win_meta->pad_offset + 1);
80 reset_pad_offset(win_meta, win_meta->pad_offset - 1); }
82 void growshrink_active_window (struct WinMeta * win_meta, char change) {
83 // Grow or shrink active window horizontally or vertically by one cell size.
84 if (0 != win_meta->active) {
85 uint16_t height = win_meta->active->height;
86 uint16_t width = win_meta->active->width;
89 else if (change == '+')
91 else if (change == '_')
93 else if (change == '*')
95 resize_active_window (win_meta, height, width); } }
97 struct Map init_map () {
98 // Initialize map with some experimental start values.
104 uint32_t size = map.width * map.height;
105 map.cells = malloc(size);
107 for (y = 0; y < map.height; y++)
108 for (x = 0; x < map.width; x++)
109 map.cells[(y * map.width) + x] = '~';
110 map.cells[size / 2 + (map.width / 2)] = '.';
111 uint32_t repeats, root, curpos;
112 for (root = 0; root * root * root < size; root++);
113 for (repeats = 0; repeats < size * root; repeats++) {
114 y = rrand(0, 0) % map.height;
115 x = rrand(0, 0) % map.width;
116 curpos = y * map.width + x;
117 if ('~' == map.cells[curpos] &&
118 ( (curpos >= map.width && '.' == map.cells[curpos - map.width])
119 || (curpos < map.width * (map.height-1) && '.' == map.cells[curpos + map.width])
120 || (curpos > 0 && curpos % map.width != 0 && '.' == map.cells[curpos-1])
121 || (curpos < (map.width * map.height) && (curpos+1) % map.width != 0 && '.' == map.cells[curpos+1])))
122 map.cells[y * map.width + x] = '.'; }
125 void map_scroll (struct Map * map, char dir) {
126 // Scroll map into direction dir if possible by changing the offset.
127 if ('n' == dir && map->offset_y > 0)
131 else if ('w' == dir && map->offset_x > 0)
136 void next_turn (struct World * world) {
137 // Increment turn and move enemy.
139 rrand(1, world->seed * world->turn);
140 char d = rrand(0, 0) % 5;
141 uint16_t ty = world->monster->y;
142 uint16_t tx = world->monster->x;
151 if (tx == world->player->x && ty == world->player->y)
152 update_log(world, "\nThe monster hits you.");
153 else if (is_passable(world->map, ty, tx)) {
154 world->monster->y = ty;
155 world->monster->x = tx; } }
157 void update_log (struct World * world, char * text) {
158 // Update log with new text to be appended.
160 uint16_t len_old = strlen(world->log);
161 uint16_t len_new = strlen(text);
162 uint16_t len_whole = len_old + len_new + 1;
163 new_text = calloc(len_whole, sizeof(char));
164 memcpy(new_text, world->log, len_old);
165 memcpy(new_text + len_old, text, len_new);
167 world->log = new_text; }
169 char is_passable (struct Map * map, uint16_t y, uint16_t x) {
170 // Check if coordinate on (or beyond) map is accessible to movement.
172 if (0 <= x && x < map->width && 0 <= y && y < map->height)
173 if ('.' == map->cells[y * map->width + x])
177 void record_action (char action) {
178 // Append action to game record file.
179 FILE * file = fopen("record", "a");
183 void move_player (struct World * world, char d) {
184 // Move player in direction d, increment turn counter and update log.
185 static char prev = 0;
188 uint16_t ty = world->player->y;
189 uint16_t tx = world->player->x;
202 if (ty == world->monster->y && tx == world->monster->x)
204 else if (is_passable(world->map, ty, tx)) {
206 world->player->y = ty;
207 world->player->x = tx; }
208 if (success * d == prev)
209 update_log (world, ".");
212 update_log (world, "\nYou hit the monster.");
214 char * msg = calloc(25, sizeof(char));
215 char * msg_content = "You fail to move";
217 msg_content = "You move";
218 sprintf(msg, "\n%s %s.", msg_content, dir);
219 update_log (world, msg);
222 if (1 == world->interactive)
226 void player_wait (struct World * world) {
227 // Make player wait one turn.
228 if (1 == world->interactive)
231 update_log (world, "\nYou wait."); }
233 unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys,
234 struct Win * win_map, struct Win * win_info, struct Win * win_log) {
235 // Call some meta program / window management actions dependent on key. Return 1 to signal quitting.
236 if (key == get_action_key(world->keybindings, "quit"))
238 else if (key == get_action_key(world->keybindings, "scroll pad right"))
239 scroll_pad (win_meta, '+');
240 else if (key == get_action_key(world->keybindings, "scroll pad left"))
241 scroll_pad (win_meta, '-');
242 else if (key == get_action_key(world->keybindings, "toggle keys window"))
243 toggle_window(win_meta, win_keys);
244 else if (key == get_action_key(world->keybindings, "toggle map window"))
245 toggle_window(win_meta, win_map);
246 else if (key == get_action_key(world->keybindings, "toggle info window"))
247 toggle_window(win_meta, win_info);
248 else if (key == get_action_key(world->keybindings, "toggle log window"))
249 toggle_window(win_meta, win_log);
250 else if (key == get_action_key(world->keybindings, "cycle forwards"))
251 cycle_active_window(win_meta, 'n');
252 else if (key == get_action_key(world->keybindings, "cycle backwards"))
253 cycle_active_window(win_meta, 'p');
254 else if (key == get_action_key(world->keybindings, "shift forwards"))
255 shift_active_window(win_meta, 'f');
256 else if (key == get_action_key(world->keybindings, "shift backwards"))
257 shift_active_window(win_meta, 'b');
258 else if (key == get_action_key(world->keybindings, "grow horizontally"))
259 growshrink_active_window(win_meta, '*');
260 else if (key == get_action_key(world->keybindings, "shrink horizontally"))
261 growshrink_active_window(win_meta, '_');
262 else if (key == get_action_key(world->keybindings, "grow vertically"))
263 growshrink_active_window(win_meta, '+');
264 else if (key == get_action_key(world->keybindings, "shrink vertically"))
265 growshrink_active_window(win_meta, '-');
266 else if (key == get_action_key(world->keybindings, "save keys"))
267 save_keybindings(world);
268 else if (key == get_action_key(world->keybindings, "keys nav up"))
269 keyswin_move_selection (world, 'u');
270 else if (key == get_action_key(world->keybindings, "keys nav down"))
271 keyswin_move_selection (world, 'd');
272 else if (key == get_action_key(world->keybindings, "keys mod"))
273 keyswin_mod_key (world, win_meta);
274 else if (key == get_action_key(world->keybindings, "map up"))
275 map_scroll (world->map, 'n');
276 else if (key == get_action_key(world->keybindings, "map down"))
277 map_scroll (world->map, 's');
278 else if (key == get_action_key(world->keybindings, "map right"))
279 map_scroll (world->map, 'e');
280 else if (key == get_action_key(world->keybindings, "map left"))
281 map_scroll (world->map, 'w');
284 int main (int argc, char *argv[]) {
286 world.interactive = 1;
289 while ((opt = getopt(argc, argv, "s::")) != -1) {
292 world.interactive = 0;
295 start_turn = atoi(optarg);
298 exit(EXIT_FAILURE); } }
300 world.log = calloc(1, sizeof(char));
301 update_log (&world, " ");
302 struct Player player;
303 world.player = &player;
304 struct Monster monster;
305 world.monster = &monster;
307 if (1 == world.interactive && 0 == access("savefile", F_OK)) {
308 file = fopen("savefile", "r");
309 world.seed = read_uint32_bigendian(file);
310 world.turn = read_uint32_bigendian(file);
311 player.y = read_uint16_bigendian(file);
312 player.x = read_uint16_bigendian(file);
313 monster.y = read_uint16_bigendian(file);
314 monster.x = read_uint16_bigendian(file);
318 if (0 == world.interactive) {
319 file = fopen("record", "r");
320 world.seed = read_uint32_bigendian(file); }
322 file = fopen("record", "w");
323 world.seed = time(NULL);
324 write_uint32_bigendian(world.seed, file);
326 rrand(1, world.seed);
327 struct Map map = init_map();
329 if (1 == world.turn) {
330 for (player.y = player.x = 0; 0 == is_passable(&map, player.y, player.x);) {
331 player.y = rrand(0, 0) % map.height;
332 player.x = rrand(0, 0) % map.width; }
333 for (monster.y = monster.x = 0; 0 == is_passable(&map, monster.y, monster.x);) {
334 monster.y = rrand(0, 0) % map.height;
335 monster.x = rrand(0, 0) % map.width; } }
337 WINDOW * screen = initscr();
340 keypad(screen, TRUE);
342 init_keybindings(&world);
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 = 0;
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);