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