home · contact · privacy
init_window() now initializes Win.data and Win.draw, too
[plomrogue] / roguelike.c
1 #include <ncurses.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "windows.h"
6
7 struct World {
8   struct KeyBinding * keybindings;
9   struct KeysWinData * keyswindata;
10   int turn;
11   char * log;
12   struct Map * map;
13   struct Monster * monster;
14   struct Player * player; };
15
16 struct KeyBinding {
17   char * name;
18   int key; };
19
20 struct KeysWinData {
21   int max;
22   char edit;
23   int select; };
24
25 struct Map {
26   int width;
27   int height;
28   int offset_x;
29   int offset_y;
30   char * cells; };
31
32 struct Player {
33   int y;
34   int x; };
35
36 struct Monster {
37   int y;
38   int x; };
39
40 void draw_with_linebreaks (struct Win *, char *, int);
41 void draw_text_from_bottom (struct Win *, char *);
42 void draw_log (struct Win *);
43 void draw_map (struct Win *);
44 void draw_info (struct Win *);
45 void draw_keys_window (struct Win *);
46 void toggle_window (struct WinMeta *, struct Win *);
47 void growshrink_active_window (struct WinMeta *, char);
48 void init_keybindings(struct World *);
49 struct Map init_map ();
50 void map_scroll (struct Map *, char);
51 void next_turn (struct World *);
52 void update_log (struct World *, char *);
53 void save_keybindings(struct World *);
54 int get_action_key (struct KeyBinding *, char *);
55 char * get_keyname(int);
56 void keyswin_mod_key (struct World *, struct WinMeta *);
57 void keyswin_move_selection (struct World *, char);
58 char is_passable (struct World *, int, int);
59 void move_player (struct World *, char);
60 void player_wait(struct World *);
61
62 void draw_with_linebreaks (struct Win * win, char * text, int start_y) {
63 // Write text into window content space. Start on row start_y. Fill unused rows with whitespace.
64   int x, y;
65   char toggle;
66   char fin = 0;
67   int z = -1;
68   for (y = start_y; y < win->height; y++) {
69     if (0 == fin)
70       toggle = 0;
71     for (x = 0; x < win->width; x++) {
72        if (0 == toggle) {
73          z++;
74          if ('\n' == text[z]) {
75            toggle = 1;
76            continue; }
77          else
78            mvwaddch(win->curses, y, x, text[z]);
79          if ('\n' == text[z+1]) {
80            z++;
81            toggle = 1; }
82          else if (0 == text[z+1]) {
83             toggle = 1;
84             fin = 1; } } } } }
85
86 void draw_text_from_bottom (struct Win * win, char * text) {
87 // Draw text from end/bottom to the top.
88   char toggle = 0;
89   int x, y, offset;
90   int z = -1;
91   for (y = 0; 0 == toggle; y++)                           // Determine number of lines text would have in
92     for (x = 0; x < win->width; x++) {                    // a window of available width, but infinite height.
93       z++;
94       if ('\n' == text[z])            // Treat \n and \0 as control characters for incrementing y and stopping
95         break;                        // the loop. Make sure they don't count as cell space themselves.
96       if ('\n' == text[z+1]) {
97         z++;
98         break; }
99       else if (0 == text[z+1]) {
100         toggle = 1;
101         break; } }
102   z = -1;
103   int start_y = 0;
104   if (y < win->height)             // Depending on what is bigger, determine start point in window or in text.
105     start_y = win->height - y;
106   else if (y > win->height) {
107     offset = y - win->height;
108     for (y = 0; y < offset; y++)
109       for (x = 0; x < win->width; x++) {
110         z++;
111         if ('\n' == text[z])
112           break;
113         if ('\n' == text[z+1]) {
114           z++;
115           break; } }
116     text = text + (sizeof(char) * (z + 1)); }
117   draw_with_linebreaks(win, text, start_y); }
118
119 void draw_log (struct Win * win) {
120 // Draw log text from world struct in win->data from bottom to top.
121   struct World * world = (struct World *) win->data;
122   draw_text_from_bottom(win, world->log); }
123
124 void draw_map (struct Win * win) {
125 // Draw map determined by win->data Map struct into window. Respect offset.
126   struct World * world = (struct World *) win->data;
127   struct Map * map = world->map;
128   struct Player * player = world->player;
129   struct Monster * monster = world->monster;
130   char * cells = map->cells;
131   int width_map_av = map->width - map->offset_x;
132   int height_map_av = map->height - map->offset_y;
133   int x, y, z;
134   for (y = 0; y < win->height; y++) {
135     z = map->offset_x + (map->offset_y + y) * (map->width);
136     for (x = 0; x < win->width; x++) {
137       if (y < height_map_av && x < width_map_av) {
138         if (z == (map->width * player->y) + player->x)
139           mvwaddch(win->curses, y, x, '@');
140         else if (z == (map->width * monster->y) + monster->x)
141           mvwaddch(win->curses, y, x, 'M');
142         else
143           mvwaddch(win->curses, y, x, cells[z]);
144         z++; } } } }
145
146 void draw_info (struct Win * win) {
147 // Draw info window by appending win->data integer value to "Turn: " display.
148   struct World * world = (struct World *) win->data;
149   int count = world->turn;
150   char text[100];
151   snprintf(text, 100, "Turn: %d", count);
152   draw_with_linebreaks(win, text, 0); }
153
154 void draw_keys_window (struct Win * win) {
155 // Draw keybinding window.
156   struct World * world = (struct World *) win->data;
157   struct KeysWinData * keyswindata = (struct KeysWinData *) world->keyswindata;
158   struct KeyBinding * keybindings = world->keybindings;
159   int offset = 0;
160   if (keyswindata->max >= win->height) {
161     if (keyswindata->select > win->height / 2) {
162       if (keyswindata->select < (keyswindata->max - (win->height / 2)))
163         offset = keyswindata->select - (win->height / 2);
164       else
165         offset = keyswindata->max - win->height + 1; } }
166   int keydescwidth = 9 + 1; // max length assured by get_keyname() + \0
167   char * keydesc = malloc(keydescwidth);
168   attr_t attri;
169   int y, x;
170   char * keyname;
171   for (y = 0; y <= keyswindata->max && y < win->height; y++) {
172     attri = 0;
173     if (y == keyswindata->select - offset) {
174       attri = A_REVERSE;
175       if (1 == keyswindata->edit)
176         attri = attri | A_BLINK; }
177     keyname = get_keyname(keybindings[y + offset].key);
178     snprintf(keydesc, keydescwidth, "%-9s", keyname);
179     free(keyname);
180     for (x = 0; x < win->width; x++)
181       if (x < strlen(keydesc))
182         mvwaddch(win->curses, y, x, keydesc[x] | attri);
183       else if (strlen(keydesc) < x && x < strlen(keybindings[y + offset].name) + strlen(keydesc) + 1)
184         mvwaddch(win->curses, y, x, keybindings[y + offset].name[x - strlen(keydesc) - 1] | attri);
185       else
186         mvwaddch(win->curses, y, x, ' ' | attri); }
187   free(keydesc); }
188
189 void toggle_window (struct WinMeta * win_meta, struct Win * win) {
190 // Toggle display of window win.
191   if (0 != win->curses)
192     suspend_window(win_meta, win);
193   else
194     append_window(win_meta, win); }
195
196 void growshrink_active_window (struct WinMeta * win_meta, char change) {
197 // Grow or shrink active window horizontally or vertically by one cell size.
198   if (0 != win_meta->active) {
199     int height = win_meta->active->height;
200     int width = win_meta->active->width;
201     if      (change == '-')
202       height--;
203     else if (change == '+')
204       height++;
205     else if (change == '_')
206       width--;
207     else if (change == '*')
208       width++;
209     resize_active_window (win_meta, height, width); } }
210
211 void init_keybindings(struct World * world) {
212 // Initialize keybindings from file "keybindings".
213   FILE * file = fopen("keybindings", "r");
214   int lines = 0;
215   int c = 0;
216   int linemax = 0;
217   int c_count = 0;
218   while (EOF != c) {
219     c_count++;
220     c = getc(file);
221     if ('\n' == c) {
222       if (c_count > linemax)
223         linemax = c_count + 1;
224       c_count = 0;
225       lines++; } }
226   struct KeyBinding * keybindings = malloc(lines * sizeof(struct KeyBinding));
227   fseek(file, 0, SEEK_SET);
228   char * command = malloc(linemax);
229   int commcount = 0;
230   char * cmdptr;
231   while (fgets(command, linemax, file)) {
232     keybindings[commcount].key = atoi(command);
233     cmdptr = strchr(command, ' ') + 1;
234     keybindings[commcount].name = malloc(strlen(cmdptr));
235     memcpy(keybindings[commcount].name, cmdptr, strlen(cmdptr) - 1);
236     keybindings[commcount].name[strlen(cmdptr) - 1] = '\0';
237     commcount++; }
238   free(command);
239   fclose(file);
240   struct KeysWinData * keyswindata = malloc(sizeof(struct KeysWinData));
241   keyswindata->max = lines - 1;
242   keyswindata->select = 0;
243   keyswindata->edit = 0;
244   world->keybindings = keybindings;
245   world->keyswindata = keyswindata; }
246
247 struct Map init_map () {
248 // Initialize map with some experimental start values.
249   struct Map map;
250   map.width = 96;
251   map.height = 32;
252   map.offset_x = 0;
253   map.offset_y = 0;
254   map.cells = malloc(map.width * map.height);
255   int x, y, ran;
256   char terrain;
257   for (y = 0; y < map.height; y++)
258     for (x = 0; x < map.width; x++) {
259       terrain = '.';
260       ran = rand();
261       if (   0 == ran % ((x*x) / 3 + 1)
262           || 0 == ran % ((y*y) / 3 + 1)
263           || 0 == ran % ((map.width - x - 1) * (map.width - x - 1) / 3 + 1)
264           || 0 == ran %((map.height - y - 1) * (map.height - y - 1) / 3 + 1))
265         terrain = ' ';
266       map.cells[(y * map.width) + x] = terrain; }
267   return map; }
268
269 void map_scroll (struct Map * map, char dir) {
270 // Scroll map into direction dir if possible by changing the offset.
271   if      ('n' == dir && map->offset_y > 0)
272     map->offset_y--;
273   else if ('s' == dir)
274     map->offset_y++;
275   else if ('w' == dir && map->offset_x > 0)
276     map->offset_x--;
277   else if ('e' == dir)
278     map->offset_x++; }
279
280 void next_turn (struct World * world) {
281 // Increment turn and move enemy.
282   world->turn++;
283   char d = rand() % 5;
284   char ty = world->monster->y;
285   char tx = world->monster->x;
286   if (1 == d)
287     ty++;
288   else if (2 == d)
289     ty--;
290   else if (3 == d)
291     tx++;
292   else if (4 == d)
293     tx--;
294   if (tx == world->player->x && ty == world->player->y)
295     update_log(world, "\nThe monster hits you.");
296   else if (is_passable(world, tx, ty)) {
297     world->monster->y = ty;
298     world->monster->x = tx; } }
299
300 void update_log (struct World * world, char * text) {
301 // Update log with new text to be appended.
302   char * new_text;
303   int len_old = strlen(world->log);
304   int len_new = strlen(text);
305   int len_whole = len_old + len_new + 1;
306   new_text = calloc(len_whole, sizeof(char));
307   memcpy(new_text, world->log, len_old);
308   memcpy(new_text + len_old, text, len_new);
309   free(world->log);
310   world->log = new_text; }
311
312 void save_keybindings(struct World * world) {
313 // Write keybindings to keybindings file.
314   struct KeysWinData * keyswindata = (struct KeysWinData *) world->keyswindata;
315   struct KeyBinding * keybindings = world->keybindings;
316   FILE * file = fopen("keybindings", "w");
317   int linemax = 0;
318   int i;
319   for (i = 0; i <= keyswindata->max; i++)
320     if (strlen(keybindings[i].name) > linemax)
321       linemax = strlen(keybindings[i].name);
322   linemax = linemax + 6;                                // + 6 = + 3 digits + whitespace + newline + null byte
323   char * line = malloc(linemax);
324   for (i = 0; i <= keyswindata->max; i++) {
325     snprintf(line, linemax, "%d %s\n", keybindings[i].key, keybindings[i].name);
326     fwrite(line, sizeof(char), strlen(line), file); }
327   free(line);
328   fclose(file); }
329
330 int get_action_key (struct KeyBinding * keybindings, char * name) {
331 // Return key matching name in keybindings.
332   int i = 0;
333   while (strcmp(keybindings[i].name, name) )
334     i++;
335   return keybindings[i].key; }
336
337 char * get_keyname(int keycode) {
338 // Translate some keycodes to readable names of max 9 chars.
339   char * keyname;
340   keyname = malloc(15);
341   if (32 < keycode && keycode < 127)
342     sprintf(keyname, "%c", keycode);
343   else if (keycode == 9)
344     sprintf(keyname, "TAB");
345   else if (keycode == 10)
346     sprintf(keyname, "RETURN");
347   else if (keycode == 27)
348     sprintf(keyname, "ESCAPE");
349   else if (keycode == 32)
350     sprintf(keyname, "SPACE");
351   else if (keycode == KEY_UP)
352     sprintf(keyname, "UP");
353   else if (keycode == KEY_DOWN)
354     sprintf(keyname, "DOWN");
355   else if (keycode == KEY_LEFT)
356     sprintf(keyname, "LEFT");
357   else if (keycode == KEY_RIGHT)
358     sprintf(keyname, "RIGHT");
359   else if (keycode == KEY_HOME)
360     sprintf(keyname, "HOME");
361   else if (keycode == KEY_BACKSPACE)
362     sprintf(keyname, "BACKSPACE");
363   else if (keycode >= KEY_F0 && keycode <= KEY_F(63)) {
364     int f = keycode - KEY_F0;
365     sprintf(keyname, "F%d", f); }
366   else if (keycode == KEY_DC)
367     sprintf(keyname, "DELETE");
368   else if (keycode == KEY_IC)
369     sprintf(keyname, "INSERT");
370   else if (keycode == KEY_NPAGE)
371     sprintf(keyname, "NEXT PAGE");
372   else if (keycode == KEY_PPAGE)
373     sprintf(keyname, "PREV PAGE");
374   else if (keycode == KEY_END)
375     sprintf(keyname, "END");
376   else
377     sprintf(keyname, "(unknown)");
378   return keyname;  }
379
380 void keyswin_mod_key (struct World * world, struct WinMeta * win_meta) {
381 // In keybindings window, mark selection modifiable, modify key. Ensure max of three digits in key code field.
382   world->keyswindata->edit = 1;
383   draw_all_windows (win_meta);
384   int key = getch();
385   if (key < 1000)
386     world->keybindings[world->keyswindata->select].key = key;
387   world->keyswindata->edit = 0; }
388
389 void keyswin_move_selection (struct World * world, char dir) {
390 // In keybindings window, move selection upwards or downwards (if within limits of list length).
391   if ('u' == dir && world->keyswindata->select > 0)
392     world->keyswindata->select--;
393   else if ('d' == dir && world->keyswindata->select < world->keyswindata->max)
394     world->keyswindata->select++; }
395
396 char is_passable (struct World * world, int x, int y) {
397 // Check if coordinate on (or beyond) map is accessible to movement.
398   char passable = 0;
399   if (0 <= x && x < world->map->width && 0 <= y && y < world->map->height)
400     if ('.' == world->map->cells[y * world->map->width + x])
401       passable = 1;
402   return passable; }
403
404 void move_player (struct World * world, char d) {
405 // Move player in direction d, increment turn counter and update log.
406   static char prev = 0;
407   char success = 0;
408   char * dir;
409   char ty = world->player->y;
410   char tx = world->player->x;
411   if ('s' == d) {
412     dir = "south";
413     ty++; }
414   if ('n' == d) {
415     dir = "north";
416     ty--; }
417   if ('w' == d) {
418     dir = "west";
419     tx--; }
420   if ('e' == d) {
421     dir = "east";
422     tx++; }
423   if (ty == world->monster->y && tx == world->monster->x)
424     success = 2;
425   else if (is_passable(world, tx, ty)) {
426     success = 1;
427     world->player->y = ty;
428     world->player->x = tx; }
429   if (success * d == prev)
430     update_log (world, ".");
431   else {
432     if (2 == success)
433       update_log (world, "\nYou hit the monster.");
434     else {
435       char * msg = calloc(25, sizeof(char));
436       char * msg_content = "You fail to move";
437       if (1 == success)
438         msg_content = "You move";
439       sprintf(msg, "\n%s %s.", msg_content, dir);
440       update_log (world, msg);
441       free(msg); } }
442   prev = success * d;
443   next_turn (world); }
444
445 void player_wait(struct World * world) {
446 // Make player wait one turn.
447   next_turn (world);
448   update_log (world, "\nYou wait."); }
449
450 int main () {
451   struct World world;
452   init_keybindings(&world);
453   world.turn = 0;
454   world.log = calloc(1, sizeof(char));
455   update_log (&world, "Start!");
456   struct Map map = init_map();
457   world.map = &map;
458   struct Player player;
459   player.y = 16;
460   player.x = 16;
461   world.player = &player;
462   struct Monster monster;
463   monster.y = 16;
464   monster.x = 80;
465   world.monster = &monster;
466
467   WINDOW * screen = initscr();
468   noecho();
469   curs_set(0);
470   keypad(screen, TRUE);
471   raw();
472   struct WinMeta win_meta = init_win_meta(screen);
473
474   struct Win win_keys = init_window(&win_meta, "Keys", &world, draw_keys_window);
475   struct Win win_map = init_window(&win_meta, "Map", &world, draw_map);
476   struct Win win_info = init_window(&win_meta, "Info", &world, draw_info);
477   struct Win win_log = init_window(&win_meta, "Log", &world, draw_log);
478
479   int key;
480   while (1) {
481     draw_all_windows (&win_meta);
482     key = getch();
483     if      (key == get_action_key(world.keybindings, "quit"))
484       break;
485     else if (key == get_action_key(world.keybindings, "scroll pad right"))
486       scroll_pad (&win_meta, '+');
487     else if (key == get_action_key(world.keybindings, "scroll pad left"))
488       scroll_pad (&win_meta, '-');
489     else if (key == get_action_key(world.keybindings, "toggle keys window"))
490       toggle_window(&win_meta, &win_keys);
491     else if (key == get_action_key(world.keybindings, "toggle map window"))
492       toggle_window(&win_meta, &win_map);
493     else if (key == get_action_key(world.keybindings, "toggle info window"))
494       toggle_window(&win_meta, &win_info);
495     else if (key == get_action_key(world.keybindings, "toggle log window"))
496       toggle_window(&win_meta, &win_log);
497     else if (key == get_action_key(world.keybindings, "cycle forwards"))
498       cycle_active_window(&win_meta, 'n');
499     else if (key == get_action_key(world.keybindings, "cycle backwards"))
500       cycle_active_window(&win_meta, 'p');
501     else if (key == get_action_key(world.keybindings, "shift forwards"))
502       shift_active_window(&win_meta, 'f');
503     else if (key == get_action_key(world.keybindings, "shift backwards"))
504       shift_active_window(&win_meta, 'b');
505     else if (key == get_action_key(world.keybindings, "grow horizontally"))
506       growshrink_active_window(&win_meta, '*');
507     else if (key == get_action_key(world.keybindings, "shrink horizontally"))
508       growshrink_active_window(&win_meta, '_');
509     else if (key == get_action_key(world.keybindings, "grow vertically"))
510       growshrink_active_window(&win_meta, '+');
511     else if (key == get_action_key(world.keybindings, "shrink vertically"))
512       growshrink_active_window(&win_meta, '-');
513     else if (key == get_action_key(world.keybindings, "save keys"))
514       save_keybindings(&world);
515     else if (key == get_action_key(world.keybindings, "keys nav up"))
516       keyswin_move_selection (&world, 'u');
517     else if (key == get_action_key(world.keybindings, "keys nav down"))
518       keyswin_move_selection (&world, 'd');
519     else if (key == get_action_key(world.keybindings, "keys mod"))
520       keyswin_mod_key (&world, &win_meta);
521     else if (key == get_action_key(world.keybindings, "map up"))
522       map_scroll (&map, 'n');
523     else if (key == get_action_key(world.keybindings, "map down"))
524       map_scroll (&map, 's');
525     else if (key == get_action_key(world.keybindings, "map right"))
526       map_scroll (&map, 'e');
527     else if (key == get_action_key(world.keybindings, "map left"))
528       map_scroll (&map, 'w');
529     else if (key == get_action_key(world.keybindings, "player down"))
530       move_player(&world, 's');
531     else if (key == get_action_key(world.keybindings, "player up"))
532       move_player(&world, 'n');
533     else if (key == get_action_key(world.keybindings, "player right"))
534       move_player(&world, 'e');
535     else if (key == get_action_key(world.keybindings, "player left"))
536       move_player(&world, 'w');
537     else if (key == get_action_key(world.keybindings, "wait") )
538       player_wait (&world); }
539
540   free(map.cells);
541   for (key = 0; key <= world.keyswindata->max; key++)
542     free(world.keybindings[key].name);
543   free(world.keybindings);
544   free(world.keyswindata);
545   free(world.log);
546
547   endwin();
548   return 0; }