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