home · contact · privacy
Don't use variables for what's not variable.
[plomrogue] / roguelike.c
1 #include <stdlib.h>
2 #include <limits.h>
3 #include <stdint.h>
4 #include <ncurses.h>
5 #include <string.h>
6 #include "windows.h"
7 #include "draw_wins.h"
8 #include "roguelike.h"
9 #include "keybindings.h"
10
11 uint16_t rrand() {
12 // Pseudo-random number generator (LGC algorithm). Use instead of rand() to ensure portable predictability.
13   static uint32_t seed = 0;
14   seed = ((seed * 1103515245) + 12345) % 2147483648;   // Values as recommended by POSIX.1-2001 (see rand(3)).
15   return (seed / 65536); }                         // Ignore least significant 16 bits (they are less random).
16
17 void toggle_window (struct WinMeta * win_meta, struct Win * win) {
18 // Toggle display of window win.
19   if (0 != win->curses)
20     suspend_window(win_meta, win);
21   else
22     append_window(win_meta, win); }
23
24 void growshrink_active_window (struct WinMeta * win_meta, char change) {
25 // Grow or shrink active window horizontally or vertically by one cell size.
26   if (0 != win_meta->active) {
27     uint16_t height = win_meta->active->height;
28     uint16_t width = win_meta->active->width;
29     if      (change == '-')
30       height--;
31     else if (change == '+')
32       height++;
33     else if (change == '_')
34       width--;
35     else if (change == '*')
36       width++;
37     resize_active_window (win_meta, height, width); } }
38
39 struct Map init_map () {
40 // Initialize map with some experimental start values.
41   struct Map map;
42   map.width = 96;
43   map.height = 32;
44   map.offset_x = 0;
45   map.offset_y = 0;
46   map.cells = malloc(map.width * map.height);
47   uint16_t x, y, ran;
48   char terrain;
49   for (y = 0; y < map.height; y++)
50     for (x = 0; x < map.width; x++) {
51       terrain = '.';
52       ran = rrand();
53       if (   0 == ran % ((x*x) / 3 + 1)
54           || 0 == ran % ((y*y) / 3 + 1)
55           || 0 == ran % ((map.width - x - 1) * (map.width - x - 1) / 3 + 1)
56           || 0 == ran %((map.height - y - 1) * (map.height - y - 1) / 3 + 1))
57         terrain = ' ';
58       map.cells[(y * map.width) + x] = terrain; }
59   return map; }
60
61 void map_scroll (struct Map * map, char dir) {
62 // Scroll map into direction dir if possible by changing the offset.
63   if      ('n' == dir && map->offset_y > 0)
64     map->offset_y--;
65   else if ('s' == dir)
66     map->offset_y++;
67   else if ('w' == dir && map->offset_x > 0)
68     map->offset_x--;
69   else if ('e' == dir)
70     map->offset_x++; }
71
72 void next_turn (struct World * world) {
73 // Increment turn and move enemy.
74   world->turn++;
75   char d = rrand() % 5;
76   char ty = world->monster->y;
77   char tx = world->monster->x;
78   if (1 == d)
79     ty++;
80   else if (2 == d)
81     ty--;
82   else if (3 == d)
83     tx++;
84   else if (4 == d)
85     tx--;
86   if (tx == world->player->x && ty == world->player->y)
87     update_log(world, "\nThe monster hits you.");
88   else if (is_passable(world, tx, ty)) {
89     world->monster->y = ty;
90     world->monster->x = tx; } }
91
92 void update_log (struct World * world, char * text) {
93 // Update log with new text to be appended.
94   char * new_text;
95   uint16_t len_old = strlen(world->log);
96   uint16_t len_new = strlen(text);
97   uint16_t len_whole = len_old + len_new + 1;
98   new_text = calloc(len_whole, sizeof(char));
99   memcpy(new_text, world->log, len_old);
100   memcpy(new_text + len_old, text, len_new);
101   free(world->log);
102   world->log = new_text; }
103
104 char is_passable (struct World * world, uint16_t x, uint16_t y) {
105 // Check if coordinate on (or beyond) map is accessible to movement.
106   char passable = 0;
107   if (0 <= x && x < world->map->width && 0 <= y && y < world->map->height)
108     if ('.' == world->map->cells[y * world->map->width + x])
109       passable = 1;
110   return passable; }
111
112 void move_player (struct World * world, char d) {
113 // Move player in direction d, increment turn counter and update log.
114   static char prev = 0;
115   char success = 0;
116   char * dir;
117   char ty = world->player->y;
118   char tx = world->player->x;
119   if ('s' == d) {
120     dir = "south";
121     ty++; }
122   if ('n' == d) {
123     dir = "north";
124     ty--; }
125   if ('w' == d) {
126     dir = "west";
127     tx--; }
128   if ('e' == d) {
129     dir = "east";
130     tx++; }
131   if (ty == world->monster->y && tx == world->monster->x)
132     success = 2;
133   else if (is_passable(world, tx, ty)) {
134     success = 1;
135     world->player->y = ty;
136     world->player->x = tx; }
137   if (success * d == prev)
138     update_log (world, ".");
139   else {
140     if (2 == success)
141       update_log (world, "\nYou hit the monster.");
142     else {
143       char * msg = calloc(25, sizeof(char));
144       char * msg_content = "You fail to move";
145       if (1 == success)
146         msg_content = "You move";
147       sprintf(msg, "\n%s %s.", msg_content, dir);
148       update_log (world, msg);
149       free(msg); } }
150   prev = success * d;
151   next_turn (world); }
152
153 void player_wait (struct World * world) {
154 // Make player wait one turn.
155   next_turn (world);
156   update_log (world, "\nYou wait."); }
157
158 void save_map (struct Map * map) {
159 // Save map to file "map".
160   FILE * file = fopen("map", "w");
161   fputc(map->width / CHAR_MAX, file);
162   fputc(map->width % CHAR_MAX, file);
163   uint16_t y, x;
164   for (y = 0; y < map->height; y++)
165     for (x = 0; x < map->width; x++)
166       fputc(map->cells[y * map->width + x], file);
167   fclose(file); }
168
169 struct Map load_map () {
170 // Load map from file.
171   FILE * file = fopen("map", "r");
172   struct Map map;
173   map.width = (fgetc(file) * CHAR_MAX) + fgetc(file);
174   long pos = ftell(file);
175   int c = fgetc(file);
176   uint32_t i = 0;
177   while (EOF != c) {
178     i++;
179     c = fgetc(file); }
180   map.cells = malloc(i * sizeof(char));
181   map.height = i / map.width;
182   fseek(file, pos, SEEK_SET);
183   c = fgetc(file);
184   i = 0;
185   while (EOF != c) {
186     map.cells[i] = (char) c;
187     c = fgetc(file);
188     i++; }
189   fclose(file);
190   return map; }
191
192 int main () {
193   struct World world;
194   init_keybindings(&world);
195   world.turn = 0;
196   world.log = calloc(1, sizeof(char));
197   update_log (&world, "Start!");
198   struct Map map = init_map();
199   world.map = &map;
200   struct Player player;
201   player.y = 16;
202   player.x = 16;
203   world.player = &player;
204   struct Monster monster;
205   monster.y = 16;
206   monster.x = 80;
207   world.monster = &monster;
208
209   WINDOW * screen = initscr();
210   noecho();
211   curs_set(0);
212   keypad(screen, TRUE);
213   raw();
214   struct WinMeta win_meta = init_win_meta(screen);
215   struct Win win_keys = init_window(&win_meta, "Keys", &world, draw_keys_win);
216   struct Win win_map = init_window(&win_meta, "Map", &world, draw_map_win);
217   struct Win win_info = init_window(&win_meta, "Info", &world, draw_info_win);
218   struct Win win_log = init_window(&win_meta, "Log", &world, draw_log_win);
219
220   int key;
221   while (1) {
222     draw_all_windows (&win_meta);
223     key = getch();
224     if      (key == get_action_key(world.keybindings, "quit"))
225       break;
226     else if (key == get_action_key(world.keybindings, "scroll pad right"))
227       scroll_pad (&win_meta, '+');
228     else if (key == get_action_key(world.keybindings, "scroll pad left"))
229       scroll_pad (&win_meta, '-');
230     else if (key == get_action_key(world.keybindings, "toggle keys window"))
231       toggle_window(&win_meta, &win_keys);
232     else if (key == get_action_key(world.keybindings, "toggle map window"))
233       toggle_window(&win_meta, &win_map);
234     else if (key == get_action_key(world.keybindings, "toggle info window"))
235       toggle_window(&win_meta, &win_info);
236     else if (key == get_action_key(world.keybindings, "toggle log window"))
237       toggle_window(&win_meta, &win_log);
238     else if (key == get_action_key(world.keybindings, "cycle forwards"))
239       cycle_active_window(&win_meta, 'n');
240     else if (key == get_action_key(world.keybindings, "cycle backwards"))
241       cycle_active_window(&win_meta, 'p');
242     else if (key == get_action_key(world.keybindings, "shift forwards"))
243       shift_active_window(&win_meta, 'f');
244     else if (key == get_action_key(world.keybindings, "shift backwards"))
245       shift_active_window(&win_meta, 'b');
246     else if (key == get_action_key(world.keybindings, "grow horizontally"))
247       growshrink_active_window(&win_meta, '*');
248     else if (key == get_action_key(world.keybindings, "shrink horizontally"))
249       growshrink_active_window(&win_meta, '_');
250     else if (key == get_action_key(world.keybindings, "grow vertically"))
251       growshrink_active_window(&win_meta, '+');
252     else if (key == get_action_key(world.keybindings, "shrink vertically"))
253       growshrink_active_window(&win_meta, '-');
254     else if (key == get_action_key(world.keybindings, "save keys"))
255       save_keybindings(&world);
256     else if (key == get_action_key(world.keybindings, "keys nav up"))
257       keyswin_move_selection (&world, 'u');
258     else if (key == get_action_key(world.keybindings, "keys nav down"))
259       keyswin_move_selection (&world, 'd');
260     else if (key == get_action_key(world.keybindings, "keys mod"))
261       keyswin_mod_key (&world, &win_meta);
262     else if (key == get_action_key(world.keybindings, "load map"))
263       map = load_map();
264     else if (key == get_action_key(world.keybindings, "save map"))
265       save_map(&map);
266     else if (key == get_action_key(world.keybindings, "map up"))
267       map_scroll (&map, 'n');
268     else if (key == get_action_key(world.keybindings, "map down"))
269       map_scroll (&map, 's');
270     else if (key == get_action_key(world.keybindings, "map right"))
271       map_scroll (&map, 'e');
272     else if (key == get_action_key(world.keybindings, "map left"))
273       map_scroll (&map, 'w');
274     else if (key == get_action_key(world.keybindings, "player down"))
275       move_player(&world, 's');
276     else if (key == get_action_key(world.keybindings, "player up"))
277       move_player(&world, 'n');
278     else if (key == get_action_key(world.keybindings, "player right"))
279       move_player(&world, 'e');
280     else if (key == get_action_key(world.keybindings, "player left"))
281       move_player(&world, 'w');
282     else if (key == get_action_key(world.keybindings, "wait") )
283       player_wait (&world); }
284
285   free(map.cells);
286   for (key = 0; key <= world.keyswindata->max; key++)
287     free(world.keybindings[key].name);
288   free(world.keybindings);
289   free(world.keyswindata);
290   free(world.log);
291
292   endwin();
293   return 0; }