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