10 #include "keybindings.h"
11 #include "readwrite.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 struct Map init_map () {
22 // Initialize map with some experimental start values.
28 uint32_t size = map.width * map.height;
29 map.cells = malloc(size);
31 for (y = 0; y < map.height; y++)
32 for (x = 0; x < map.width; x++)
33 map.cells[(y * map.width) + x] = '~';
34 map.cells[size / 2 + (map.width / 2)] = '.';
35 uint32_t repeats, root, curpos;
36 for (root = 0; root * root * root < size; root++);
37 for (repeats = 0; repeats < size * root; repeats++) {
38 y = rrand(0, 0) % map.height;
39 x = rrand(0, 0) % map.width;
40 curpos = y * map.width + x;
41 if ('~' == map.cells[curpos] &&
42 ( (curpos >= map.width && '.' == map.cells[curpos - map.width])
43 || (curpos < map.width * (map.height-1) && '.' == map.cells[curpos + map.width])
44 || (curpos > 0 && curpos % map.width != 0 && '.' == map.cells[curpos-1])
45 || (curpos < (map.width * map.height) && (curpos+1) % map.width != 0 && '.' == map.cells[curpos+1])))
46 map.cells[y * map.width + x] = '.'; }
49 void save_game(struct World * world) {
50 // Save game data to game file.
51 FILE * file = fopen("savefile", "w");
52 write_uint32_bigendian(world->seed, file);
53 write_uint32_bigendian(world->turn, file);
54 write_uint16_bigendian(world->player->y, file);
55 write_uint16_bigendian(world->player->x, file);
56 write_uint16_bigendian(world->monster->y, file);
57 write_uint16_bigendian(world->monster->x, file);
58 write_uint16_bigendian(world->monster->next->y, file);
59 write_uint16_bigendian(world->monster->next->x, file);
60 write_uint16_bigendian(world->monster->next->next->y, file);
61 write_uint16_bigendian(world->monster->next->next->x, file);
64 void record_action (char action) {
65 // Append action to game record file.
66 FILE * file = fopen("record", "a");
70 void next_turn (struct World * world) {
71 // Increment turn and move enemy.
73 rrand(1, world->seed * world->turn);
75 struct Monster * monster;
77 for (monster = world->monster; monster != 0; monster = monster->next) {
89 if (tx == world->player->x && ty == world->player->y)
90 update_log(world, "\nThe monster hits you.");
91 else if (is_passable(world->map, ty, tx)) {
93 monster->x = tx; } } }
95 void update_log (struct World * world, char * text) {
96 // Update log with new text to be appended.
98 uint16_t len_old = strlen(world->log);
99 uint16_t len_new = strlen(text);
100 uint16_t len_whole = len_old + len_new + 1;
101 new_text = calloc(len_whole, sizeof(char));
102 memcpy(new_text, world->log, len_old);
103 memcpy(new_text + len_old, text, len_new);
105 world->log = new_text; }
107 void move_player (struct World * world, char d) {
108 // Move player in direction d, increment turn counter and update log.
109 static char prev = 0;
112 uint16_t ty = world->player->y;
113 uint16_t tx = world->player->x;
126 struct Monster * monster;
127 for (monster = world->monster; monster != 0; monster = monster->next)
128 if (ty == monster->y && tx == monster->x) {
131 if (2 != success && is_passable(world->map, ty, tx)) {
133 world->player->y = ty;
134 world->player->x = tx; }
135 if (success * d == prev)
136 update_log (world, ".");
139 update_log (world, "\nYou hit the monster.");
141 char * msg = calloc(25, sizeof(char));
142 char * msg_content = "You fail to move";
144 msg_content = "You move";
145 sprintf(msg, "\n%s %s.", msg_content, dir);
146 update_log (world, msg);
149 if (1 == world->interactive)
153 char is_passable (struct Map * map, uint16_t y, uint16_t x) {
154 // Check if coordinate on (or beyond) map is accessible to movement.
156 if (0 <= x && x < map->width && 0 <= y && y < map->height)
157 if ('.' == map->cells[y * map->width + x])
161 void player_wait (struct World * world) {
162 // Make player wait one turn.
163 if (1 == world->interactive)
166 update_log (world, "\nYou wait."); }
168 void toggle_window (struct WinMeta * win_meta, struct Win * win) {
169 // Toggle display of window win.
170 if (0 != win->frame.curses_win)
171 suspend_win(win_meta, win);
173 append_win(win_meta, win); }
175 void scroll_pad (struct WinMeta * win_meta, char dir) {
176 // Try to scroll pad left or right.
178 reset_pad_offset(win_meta, win_meta->pad_offset + 1);
180 reset_pad_offset(win_meta, win_meta->pad_offset - 1); }
182 void growshrink_active_window (struct WinMeta * win_meta, char change) {
183 // Grow or shrink active window horizontally or vertically by one cell size.
184 if (0 != win_meta->active) {
185 uint16_t height = win_meta->active->frame.size.y;
186 uint16_t width = win_meta->active->frame.size.x;
189 else if (change == '+')
191 else if (change == '_')
193 else if (change == '*')
195 resize_active_win (win_meta, height, width); } }
197 void map_scroll (struct Map * map, char dir) {
198 // Scroll map into direction dir if possible by changing the offset.
199 if ('n' == dir && map->offset_y > 0)
203 else if ('w' == dir && map->offset_x > 0)
208 unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys,
209 struct Win * win_map, struct Win * win_info, struct Win * win_log) {
210 // Call some meta program / window management actions dependent on key. Return 1 to signal quitting.
211 if (key == get_action_key(world->keybindings, "quit"))
213 else if (key == get_action_key(world->keybindings, "scroll pad right"))
214 scroll_pad (win_meta, '+');
215 else if (key == get_action_key(world->keybindings, "scroll pad left"))
216 scroll_pad (win_meta, '-');
217 else if (key == get_action_key(world->keybindings, "toggle keys window"))
218 toggle_window(win_meta, win_keys);
219 else if (key == get_action_key(world->keybindings, "toggle map window"))
220 toggle_window(win_meta, win_map);
221 else if (key == get_action_key(world->keybindings, "toggle info window"))
222 toggle_window(win_meta, win_info);
223 else if (key == get_action_key(world->keybindings, "toggle log window"))
224 toggle_window(win_meta, win_log);
225 else if (key == get_action_key(world->keybindings, "cycle forwards"))
226 cycle_active_win(win_meta, 'n');
227 else if (key == get_action_key(world->keybindings, "cycle backwards"))
228 cycle_active_win(win_meta, 'p');
229 else if (key == get_action_key(world->keybindings, "shift forwards"))
230 shift_active_win(win_meta, 'f');
231 else if (key == get_action_key(world->keybindings, "shift backwards"))
232 shift_active_win(win_meta, 'b');
233 else if (key == get_action_key(world->keybindings, "grow horizontally"))
234 growshrink_active_window(win_meta, '*');
235 else if (key == get_action_key(world->keybindings, "shrink horizontally"))
236 growshrink_active_window(win_meta, '_');
237 else if (key == get_action_key(world->keybindings, "grow vertically"))
238 growshrink_active_window(win_meta, '+');
239 else if (key == get_action_key(world->keybindings, "shrink vertically"))
240 growshrink_active_window(win_meta, '-');
241 else if (key == get_action_key(world->keybindings, "save keys"))
242 save_keybindings(world);
243 else if (key == get_action_key(world->keybindings, "keys nav up"))
244 keyswin_move_selection (world, 'u');
245 else if (key == get_action_key(world->keybindings, "keys nav down"))
246 keyswin_move_selection (world, 'd');
247 else if (key == get_action_key(world->keybindings, "keys mod"))
248 keyswin_mod_key (world, win_meta);
249 else if (key == get_action_key(world->keybindings, "map up"))
250 map_scroll (world->map, 'n');
251 else if (key == get_action_key(world->keybindings, "map down"))
252 map_scroll (world->map, 's');
253 else if (key == get_action_key(world->keybindings, "map right"))
254 map_scroll (world->map, 'e');
255 else if (key == get_action_key(world->keybindings, "map left"))
256 map_scroll (world->map, 'w');
259 int main (int argc, char *argv[]) {
261 world.interactive = 1;
264 while ((opt = getopt(argc, argv, "s::")) != -1) {
267 world.interactive = 0;
270 start_turn = atoi(optarg);
273 exit(EXIT_FAILURE); } }
275 world.log = calloc(1, sizeof(char));
276 update_log (&world, " ");
277 struct Player player;
278 world.player = &player;
279 struct Monster monster1;
280 struct Monster monster2;
281 struct Monster monster3;
282 world.monster = &monster1;
283 monster1.next = &monster2;
284 monster2.next = &monster3;
290 if (1 == world.interactive && 0 == access("savefile", F_OK)) {
291 file = fopen("savefile", "r");
292 world.seed = read_uint32_bigendian(file);
293 world.turn = read_uint32_bigendian(file);
294 player.y = read_uint16_bigendian(file);
295 player.x = read_uint16_bigendian(file);
296 monster1.y = read_uint16_bigendian(file);
297 monster1.x = read_uint16_bigendian(file);
298 monster2.y = read_uint16_bigendian(file);
299 monster2.x = read_uint16_bigendian(file);
300 monster3.y = read_uint16_bigendian(file);
301 monster3.x = read_uint16_bigendian(file);
305 if (0 == world.interactive) {
306 file = fopen("record", "r");
307 world.seed = read_uint32_bigendian(file); }
309 file = fopen("record", "w");
310 world.seed = time(NULL);
311 write_uint32_bigendian(world.seed, file);
313 rrand(1, world.seed);
314 struct Map map = init_map();
316 if (1 == world.turn) {
317 for (player.y = player.x = 0; 0 == is_passable(&map, player.y, player.x);) {
318 player.y = rrand(0, 0) % map.height;
319 player.x = rrand(0, 0) % map.width; }
320 struct Monster * monster;
321 for (monster = world.monster; monster != 0; monster = monster->next)
322 for (monster->y = monster->x = 0; 0 == is_passable(&map, monster->y, monster->x);) {
323 monster->y = rrand(0, 0) % map.height;
324 monster->x = rrand(0, 0) % map.width; } }
326 WINDOW * screen = initscr();
329 keypad(screen, TRUE);
331 init_keybindings(&world);
332 struct WinMeta win_meta = init_win_meta(screen);
333 struct Win win_keys = init_win(&win_meta, "Keys", &world, draw_keys_win);
334 struct Win win_map = init_win(&win_meta, "Map", &world, draw_map_win);
335 struct Win win_info = init_win(&win_meta, "Info", &world, draw_info_win);
336 struct Win win_log = init_win(&win_meta, "Log", &world, draw_log_win);
337 win_keys.frame.size.x = 29;
338 win_map.frame.size.x = win_meta.pad.size.x - win_keys.frame.size.x - win_log.frame.size.x - 2;
339 win_info.frame.size.y = 1;
340 win_log.frame.size.y = win_meta.pad.size.y - 3;
341 toggle_window(&win_meta, &win_keys);
342 toggle_window(&win_meta, &win_map);
343 toggle_window(&win_meta, &win_info);
344 toggle_window(&win_meta, &win_log);
347 unsigned char quit_called = 0;
348 if (0 == world.interactive) {
349 unsigned char still_reading_file = 1;
352 if (start_turn == world.turn)
354 if (0 == start_turn) {
355 draw_all_wins (&win_meta);
357 if (1 == still_reading_file &&
358 (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) {
362 still_reading_file = 0; }
363 else if (0 == action)
364 player_wait (&world);
365 else if ('s' == action)
366 move_player(&world, 's');
367 else if ('n' == action)
368 move_player(&world, 'n');
369 else if ('e' == action)
370 move_player(&world, 'e');
371 else if ('w' == action)
372 move_player(&world, 'w'); }
374 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
375 if (1 == quit_called)
378 uint32_t last_turn = 0;
380 if (last_turn != world.turn) {
382 last_turn = world.turn; }
383 draw_all_wins (&win_meta);
385 if (key == get_action_key(world.keybindings, "player down"))
386 move_player(&world, 's');
387 else if (key == get_action_key(world.keybindings, "player up"))
388 move_player(&world, 'n');
389 else if (key == get_action_key(world.keybindings, "player right"))
390 move_player(&world, 'e');
391 else if (key == get_action_key(world.keybindings, "player left"))
392 move_player(&world, 'w');
393 else if (key == get_action_key(world.keybindings, "wait / next turn"))
394 player_wait (&world);
396 quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
397 if (1 == quit_called)
401 for (key = 0; key <= world.keyswindata->max; key++)
402 free(world.keybindings[key].name);
403 free(world.keybindings);
404 free(world.keyswindata);