home · contact · privacy
Built error checking into file reading/writing routines and calls of them.
[plomrogue] / src / main.c
1 /* main.c */
2
3 #include "main.h"
4 #include <stdlib.h> /* for atoi(), exit(), EXIT_FAILURE, calloc() */
5 #include <stdio.h> /* for FILE typedef, F_OK */
6 #include <ncurses.h> /* for initscr(), noecho(), curs_set(), keypad(), raw() */
7 #include <time.h> /* for time() */
8 #include <unistd.h> /* for getopt(), optarg */
9 #include "windows.h" /* for structs WinMeta, Win, init_win(), init_win_meta(),
10                       * draw_all_wins()
11                       */
12 #include "draw_wins.h" /* for draw_keys_win(), draw_map_win(), draw_info_win(),
13                         * draw_log_win()
14                         */
15 #include "keybindings.h" /* for initkeybindings(), get_action_key() */
16 #include "readwrite.h" /* for [read/write]_uint[8/16/32][_bigendian]() */
17 #include "map_objects.h" /* for structs Monster, Item, Player,
18                           * init_map_object_defs(), read_map_objects(),
19                           * build_map_objects()
20                           */
21 #include "map_object_actions.h" /* for player_wait(), move_player() */
22 #include "map.h" /* for struct Map, init_map() */
23 #include "misc.h" /* for update_log(), toggle_window(), find_passable_pos(),
24                    * meta_keys(), save_game()
25                    */
26 #include "yx_uint16.h" /* for dir enum */
27 #include "rrand.h" /* for rrand(), rrand_seed() */
28 #include "rexit.h" /* for exit_game() */
29
30 int main(int argc, char *argv[])
31 {
32     struct World world;
33
34     /* Read in startup options (i.e. replay option and replay start turn). */
35     int opt;
36     uint32_t start_turn;
37     world.interactive = 1;
38     while ((opt = getopt(argc, argv, "s::")) != -1)
39     {
40         switch (opt)
41         {
42             case 's':
43                 world.interactive = 0;
44                 start_turn = 0;
45                 if (optarg)
46                     start_turn = atoi(optarg);
47                  break;
48             default:
49                 exit(EXIT_FAILURE);
50         }
51     }
52
53     /* Initialize log, player, monster/item definitions and monsters/items. */
54     world.log = calloc(1, sizeof(char));
55     set_cleanup_flag(CLEANUP_LOG);
56     update_log (&world, " ");
57     struct Player player;
58     player.hitpoints = 5;
59     world.player = &player;
60     world.monster = 0;
61     world.item = 0;
62     init_map_object_defs(&world, "defs");
63     uint8_t fail = 0;
64
65     /* For interactive mode, try to load world state from savefile. */
66     FILE * file;
67     if (1 == world.interactive && 0 == access("savefile", F_OK))
68     {
69         file = fopen("savefile", "r");
70         fail = fail | read_uint32_bigendian(file, &world.seed);
71         fail = fail | read_uint32_bigendian(file, &world.turn);
72         fail = fail | read_uint16_bigendian(file, &player.pos.y);
73         fail = fail | read_uint16_bigendian(file, &player.pos.x);
74         player.pos.y--;
75         player.pos.x--;
76         fail = fail | read_uint8(file, &player.hitpoints);
77         fail = fail | read_map_objects(&world, &world.monster, file);
78         fail = fail | read_map_objects(&world, &world.item,    file);
79         fclose(file);
80     }
81
82     /* For non-interactive mode, try to load world state from record file. */
83     else
84     {
85         world.turn = 1;
86         if (0 == world.interactive)
87         {
88             file = fopen("record", "r");
89             fail = fail | read_uint32_bigendian(file, &world.seed);
90         }
91
92         /* For interactive-mode in newly started world, generate a start seed
93          * from the current time.
94          */
95         else
96         {
97             file = fopen("record", "w");
98             world.seed = time(NULL);
99             fail = fail | write_uint32_bigendian(world.seed, file);
100             fclose(file);
101         }
102     }
103
104     exit_err(fail, &world, "Failure initializing game.");
105
106
107     /* Generate map from seed and, if newly generated world, start positions of
108      * actors.
109      */
110     rrand_seed(world.seed);
111     struct Map map = init_map();
112     world.map = &map;
113     set_cleanup_flag(CLEANUP_MAP);
114     if (1 == world.turn)
115     {
116         player.pos = find_passable_pos(world.map);
117         void * foo;
118         foo = build_map_objects(&world, &world.monster, 1, 1 + rrand() % 27);
119         foo = build_map_objects(&world, foo, 2, 1 + rrand() % 9);
120         build_map_objects(&world, foo, 3, 1 + rrand() % 3);
121         foo = build_map_objects(&world, &world.item, 4, 1 + rrand() % 3);
122         build_map_objects(&world, foo, 5, 1 + rrand() % 3);
123     }
124
125     /* Initialize window system and windows. */
126     WINDOW * screen = initscr();
127     set_cleanup_flag(CLEANUP_NCURSES);
128     noecho();
129     curs_set(0);
130     keypad(screen, TRUE);
131     raw();
132     init_keybindings(&world);
133     set_cleanup_flag(CLEANUP_KEYBINDINGS);
134     struct WinMeta win_meta = init_win_meta(screen);
135     struct Win win_keys = init_win(&win_meta, "Keys",
136                                    0, 29, &world, draw_keys_win);
137     struct Win win_info = init_win(&win_meta, "Info",
138                                    2, 20, &world, draw_info_win);
139     uint16_t height_logwin = win_meta.padframe.size.y
140                              - (2 + win_info.frame.size.y);
141     struct Win win_log = init_win(&win_meta, "Log",
142                                   height_logwin, 20, &world, draw_log_win);
143     uint16_t width_mapwin = win_meta.padframe.size.x - win_keys.frame.size.x
144                              - win_log.frame.size.x - 2;
145     struct Win win_map = init_win(&win_meta, "Map",
146                                   0, width_mapwin, &world, draw_map_win);
147     toggle_window(&win_meta, &win_keys);
148     toggle_window(&win_meta, &win_map);
149     toggle_window(&win_meta, &win_info);
150     toggle_window(&win_meta, &win_log);
151
152     /* Replay mode. */
153     int key;
154     unsigned char quit_called = 0;
155     unsigned char await_actions = 1;
156     if (0 == world.interactive)
157     {
158         int action;
159         while (1)
160         {
161             if (start_turn == world.turn)
162             {
163                 start_turn = 0;
164             }
165             if (0 == start_turn)
166             {
167                 draw_all_wins (&win_meta);
168                key = getch();
169             }
170             if (1 == await_actions
171                 && (world.turn < start_turn
172                     || key == get_action_key(world.keybindings,
173                                              "wait / next turn")) )
174             {
175                 action = getc(file);
176                 if (EOF == action)
177                 {
178                     start_turn = 0;
179                     await_actions = 0;
180                 }
181                 else if (0 == action)
182                 {
183                     player_wait (&world);
184                 }
185                 else if (NORTH == action)
186                 {
187                     move_player(&world, NORTH);
188                 }
189                 else if (EAST  == action)
190                 {
191                     move_player(&world, EAST);
192                 }
193                 else if (SOUTH == action)
194                 {
195                     move_player(&world, SOUTH);
196                 }
197                 else if (WEST == action)
198                 {
199                     move_player(&world, WEST);
200                 }
201             }
202             else
203             {
204                 quit_called = meta_keys(key, &world, &win_meta, &win_keys,
205                                              &win_map, &win_info, &win_log);
206                 if (1 == quit_called)
207                 {
208                     exit_game(&world);
209                 }
210             }
211         }
212     }
213
214     /* Interactive mode. */
215     else
216     {
217         uint32_t last_turn = 0;
218         while (1)
219         {
220             if (last_turn != world.turn)
221             {
222                 save_game(&world);
223                 last_turn = world.turn;
224             }
225             if (1 == await_actions && 0 == player.hitpoints)
226             {
227                 await_actions = 0;
228             }
229             draw_all_wins (&win_meta);
230             key = getch();
231             if      (1 == await_actions
232                      && key == get_action_key(world.keybindings,
233                                               "player up"))
234             {
235                 move_player(&world, NORTH);
236             }
237             else if (1 == await_actions
238                      && key == get_action_key(world.keybindings,
239                                               "player right"))
240             {
241                 move_player(&world, EAST);
242             }
243             else if (1 == await_actions
244                      && key == get_action_key(world.keybindings,
245                                               "player down"))
246             {
247                 move_player(&world, SOUTH);
248             }
249             else if (1 == await_actions
250                      && key == get_action_key(world.keybindings,
251                                               "player left"))
252             {
253                 move_player(&world, WEST);
254             }
255             else if (1 == await_actions
256                      && key == get_action_key(world.keybindings,
257                                               "wait / next turn"))
258             {
259                 player_wait (&world);
260             }
261             else
262             {
263                 quit_called = meta_keys(key, &world, &win_meta, &win_keys,
264                                              &win_map, &win_info, &win_log);
265                 if (1 == quit_called)
266                 {
267                     exit_game(&world);
268                 }
269             }
270         }
271     }
272 }