home · contact · privacy
Use standard-conformant exit.
[plomrogue] / src / main.c
1 #include "main.h"
2 #include <stdlib.h>
3 #include <ncurses.h>
4 #include <time.h>
5 #include <unistd.h>
6 #include "windows.h"
7 #include "draw_wins.h"
8 #include "keybindings.h"
9 #include "readwrite.h"
10 #include "map_objects.h"
11 #include "map_object_actions.h"
12 #include "map.h"
13 #include "misc.h"
14
15 int main (int argc, char *argv[]) {
16   struct World world;
17
18   // Read in startup options (i.e. replay option and replay start turn).
19   int opt;
20   uint32_t start_turn;
21   world.interactive = 1;
22   while ((opt = getopt(argc, argv, "s::")) != -1) {
23     switch (opt) {
24       case 's':
25         world.interactive = 0;
26         start_turn = 0;
27         if (optarg)
28           start_turn = atoi(optarg);
29         break;
30       default:
31         exit(EXIT_FAILURE); } }
32
33   // Initialize log, player, monster/item definitions and monsters/items.
34   world.log = calloc(1, sizeof(char));
35   update_log (&world, " ");
36   struct Player player;
37   player.hitpoints = 5;
38   world.player = &player;
39   world.monster = 0;
40   world.item = 0;
41   init_map_object_defs(&world, "defs");
42
43   // For interactive mode, try to load world state from savefile.
44   FILE * file;
45   if (1 == world.interactive && 0 == access("savefile", F_OK)) {
46     file = fopen("savefile", "r");
47     world.seed = read_uint32_bigendian(file);
48     world.turn = read_uint32_bigendian(file);
49     player.pos.y = read_uint16_bigendian(file) - 1;
50     player.pos.x = read_uint16_bigendian(file) - 1;
51     player.hitpoints = fgetc(file);
52     read_map_objects (&world.monster, file, sizeof(struct Monster), read_map_objects_monsterdata);
53     read_map_objects (&world.item,    file, sizeof(struct Item),    NULL);
54     fclose(file); }
55
56   // For non-interactive mode, try to load world state from record file.
57   else {
58     world.turn = 1;
59     if (0 == world.interactive) {
60       file = fopen("record", "r");
61       world.seed = read_uint32_bigendian(file); }
62
63     // For interactive-mode in newly started world, generate a start seed from the current time.
64     else {
65       file = fopen("record", "w");
66       world.seed = time(NULL);
67       write_uint32_bigendian(world.seed, file);
68       fclose(file); } }
69
70   // Generate map from seed and, if newly generated world, start positions of actors.
71   rrand(1, world.seed);
72   struct Map map = init_map();
73   world.map = &map;
74   if (1 == world.turn) {
75     player.pos = find_passable_pos(&map);
76     void * foo = build_map_objects (&world, &world.monster, 0, 1 + rrand(0,0) % 27, sizeof(struct Monster),
77                                     build_map_objects_monsterdata);
78     foo = build_map_objects (&world, foo, 1, 1 + rrand(0,0) % 9, sizeof(struct Monster),
79                              build_map_objects_monsterdata);
80     build_map_objects (&world, foo, 2, 1 + rrand(0,0) % 3, sizeof(struct Monster),
81                        build_map_objects_monsterdata);
82     foo = build_map_objects (&world, &world.item, 3, 1 + rrand(0,0) % 3, sizeof(struct Item),
83                              build_map_objects_itemdata);
84     build_map_objects (&world, foo, 4, 1 + rrand(0,0) % 3, sizeof(struct Item), build_map_objects_itemdata); }
85
86   // Initialize window system and windows.
87   WINDOW * screen = initscr();
88   noecho();
89   curs_set(0);
90   keypad(screen, TRUE);
91   raw();
92   init_keybindings(&world);
93   struct WinMeta win_meta = init_win_meta(screen);
94   struct Win win_keys = init_win(&win_meta, "Keys", &world, draw_keys_win);
95   struct Win win_map = init_win(&win_meta, "Map", &world, draw_map_win);
96   struct Win win_info = init_win(&win_meta, "Info", &world, draw_info_win);
97   struct Win win_log = init_win(&win_meta, "Log", &world, draw_log_win);
98   win_keys.frame.size.x = 29;
99   win_map.frame.size.x = win_meta.pad.size.x - win_keys.frame.size.x - win_log.frame.size.x - 2;
100   win_info.frame.size.y = 2;
101   win_log.frame.size.y = win_meta.pad.size.y - (2 + win_info.frame.size.y);
102   toggle_window(&win_meta, &win_keys);
103   toggle_window(&win_meta, &win_map);
104   toggle_window(&win_meta, &win_info);
105   toggle_window(&win_meta, &win_log);
106
107   // Replay mode.
108   int key;
109   unsigned char quit_called = 0;
110   unsigned char await_actions = 1;
111   if (0 == world.interactive) {
112     int action;
113     while (1) {
114       if (start_turn == world.turn)
115         start_turn = 0;
116       if (0 == start_turn) {
117         draw_all_wins (&win_meta);
118         key = getch(); }
119       if (1 == await_actions &&
120           (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) {
121         action = getc(file);
122         if (EOF == action) {
123           start_turn = 0;
124           await_actions = 0; }
125         else if (0 == action)
126           player_wait (&world);
127         else if (NORTH == action)
128           move_player(&world, NORTH);
129         else if (EAST  == action)
130           move_player(&world, EAST);
131         else if (SOUTH == action)
132           move_player(&world, SOUTH);
133         else if (WEST == action)
134           move_player(&world, WEST); }
135       else
136         quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
137         if (1 == quit_called)
138           break; } }
139
140   // Interactive mode.
141   else {
142     uint32_t last_turn = 0;
143     while (1) {
144       if (last_turn != world.turn) {
145         save_game(&world);
146         last_turn = world.turn; }
147       if (1 == await_actions && 0 == player.hitpoints)
148         await_actions = 0;
149       draw_all_wins (&win_meta);
150       key = getch();
151       if      (1 == await_actions && key == get_action_key(world.keybindings, "player up"))
152         move_player(&world, NORTH);
153       else if (1 == await_actions && key == get_action_key(world.keybindings, "player right"))
154         move_player(&world, EAST);
155       else if (1 == await_actions && key == get_action_key(world.keybindings, "player down"))
156         move_player(&world, SOUTH);
157       else if (1 == await_actions && key == get_action_key(world.keybindings, "player left"))
158         move_player(&world, WEST);
159       else if (1 == await_actions && key == get_action_key(world.keybindings, "wait / next turn"))
160         player_wait (&world);
161       else
162         quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log);
163         if (1 == quit_called)
164           break; } }
165
166   // Clean up and exit.
167   free(map.cells);
168   for (key = 0; key <= world.keyswindata->max; key++)
169     free(world.keybindings[key].name);
170   free(world.keybindings);
171   free(world.keyswindata);
172   free(world.log);
173   endwin();
174   exit (EXIT_SUCCESS); }