3d2d29b419d4598814403361241b899fdefcc21d
[vms-empire.git] / display.c
1 /*
2  *    Copyright (C) 1987, 1988 Chuck Simmons
3  * 
4  * See the file COPYING, distributed with empire, for restriction
5  * and warranty information.
6  */
7
8 /*
9 display.c -- This file contains routines for displaying sectors and
10 moving the cursor about in a sector.  We need to remember the following
11 information:
12
13         the current map portion displayed on the screen;
14
15         whether the displayed portion is from the user's or the computer's
16         point of view;
17 */
18
19 #include <string.h>
20 #include <curses.h>
21 #include <stdarg.h>
22 #include "empire.h"
23 #include "extern.h"
24
25 static int whose_map = UNOWNED; /* user's or computer's point of view */
26 static int ref_row; /* map loc displayed in upper-left corner */
27 static int ref_col;
28 static int save_sector; /* the currently displayed sector */
29 static int save_cursor; /* currently displayed cursor position */
30 static bool change_ok = true; /* true if new sector may be displayed */
31
32 static void show_loc(view_map_t vmap[],loc_t loc);
33 static void disp_square(view_map_t *vp);
34 static bool on_screen(loc_t loc);
35
36 #ifdef A_COLOR
37 void init_colors(void)
38 {
39     start_color();
40
41     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
42     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
43     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
44     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
45     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
46     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
47     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
48     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
49     attron(A_BOLD);     /* otherwise we get gray for white */
50     keypad(stdscr, TRUE);
51 }
52 #endif /* A_COLOR */
53
54 /*
55 Used for win announcements 
56  */
57 void announce (char *msg) {
58     (void) addstr (msg);
59 }
60
61
62 /*
63  * Map input character to direction offset.
64  * Attempts to enable arrow and keypad keys.
65  */
66 int direction(chtype c)
67 {
68     switch (c)
69     {
70     case 'w':
71     case 'W':
72     case KEY_UP:
73         return 0;
74
75     case 'e':
76     case 'E':
77     case KEY_A3:
78     case KEY_PPAGE:
79         return 1;
80
81     case 'd':
82     case 'D':
83     case KEY_RIGHT:
84         return 2;
85
86     case 'c':
87     case 'C':
88     case KEY_C3:
89     case KEY_NPAGE:
90         return 3;
91
92     case 'x':
93     case 'X':
94     case KEY_DOWN:
95         return 4;
96
97     case 'z':
98     case 'Z':
99     case KEY_C1:
100     case KEY_END:
101         return 5;
102
103     case 'a':
104     case 'A':
105     case KEY_LEFT:
106         return 6;
107
108     case 'q':
109     case 'Q':
110     case KEY_A1:
111     case KEY_HOME:
112         return 7;
113
114     default:
115         return -1;
116     }
117 }
118
119 /*
120 This routine is called when the current display has been
121 trashed and no sector is shown on the screen.
122 */
123
124 void kill_display (void) {
125         whose_map = UNOWNED;
126 }
127
128 /*
129 This routine is called when a new sector may be displayed on the
130 screen even if the location to be displayed is already on the screen.
131 */
132
133 void sector_change (void) {
134         change_ok = true;
135 }
136
137 /*
138 Return the currently displayed user sector, if any.  If a user
139 sector is not displayed, return -1.
140 */
141
142 int cur_sector (void) {
143         if (whose_map != USER) return (-1);
144         return (save_sector);
145 }
146
147 /*
148 Return the current position of the cursor.  If the user's map
149 is not on the screen, we return -1.
150 */
151
152 loc_t cur_cursor (void) {
153         if (whose_map != USER) return (-1);
154         return (save_cursor);
155 }
156
157 /*
158 Display a location on the screen. We figure out the sector the
159 location is in and display that sector.  The cursor is left at
160 the requested location.
161
162 We redisplay the sector only if we either have been requested to
163 redisplay the sector, or if the location is not on the screen.
164 */
165
166 void
167 display_loc (int whose, view_map_t vmap[], loc_t loc)
168 /* whose is whose map to display; loc is location to display */
169 {
170         if (change_ok || whose != whose_map || !on_screen (loc))
171                 print_sector (whose, vmap, loc_sector (loc));
172                 
173         show_loc (vmap, loc);
174 }
175
176 /*
177 Display a location iff the location is on the screen.
178 */
179
180 void
181 display_locx (int whose, view_map_t vmap[], loc_t loc)
182 /* whose is whose map to display; loc is location to display */
183 {
184         if (whose == whose_map && on_screen (loc))
185                 show_loc (vmap, loc);
186 }
187
188 /*
189 Display a location which exists on the screen.
190 */
191
192 void
193 show_loc (view_map_t vmap[], loc_t loc)
194 {
195         int r, c;
196         
197         r = loc_row (loc);
198         c = loc_col (loc);
199         (void) move (r-ref_row+NUMTOPS, c-ref_col);
200         disp_square(&vmap[loc]);
201         save_cursor = loc; /* remember cursor location */
202         (void) move (r-ref_row+NUMTOPS, c-ref_col);
203 }
204
205 /*
206 Print a sector of the user's on the screen.  If it is already displayed,
207 we do nothing.  Otherwise we redraw the screen.  Someday, some intelligence
208 in doing this might be interesting.  We heavily depend on curses to update
209 the screen in a reasonable fashion.
210
211 If the desired sector
212 is not displayed, we clear the screen.  We then update the screen
213 to reflect the current map.  We heavily depend on curses to correctly
214 optimize the redrawing of the screen.
215
216 When redrawing the screen, we figure out where the
217 center of the sector is in relation to the map.  We then compute
218 the screen coordinates where we want to display the center of the
219 sector.  We will remember the sector displayed, the map displayed,
220 and the map location that appears in the upper-left corner of the
221 screen.
222 */
223  
224 void
225 print_sector(int whose, view_map_t vmap[], int sector)
226 /* whose is USER or COMP, vmap is map to display, sector is sector to display */
227 {
228         void display_screen();
229
230         int first_row, first_col, last_row, last_col;
231         int display_rows, display_cols;
232         int r, c;
233
234         save_sector = sector; /* remember last sector displayed */
235         change_ok = false; /* we are displaying a new sector */
236
237         display_rows = lines - NUMTOPS - 1; /* num lines to display */
238         display_cols = cols - NUMSIDES;
239
240         /* compute row and column edges of sector */
241         first_row = sector_row (sector) * ROWS_PER_SECTOR;
242         first_col = sector_col (sector) * COLS_PER_SECTOR;
243         last_row = first_row + ROWS_PER_SECTOR - 1;
244         last_col = first_col + COLS_PER_SECTOR - 1;
245
246         if (!(whose == whose_map /* correct map is on screen? */
247            && ref_row <= first_row /* top row on screen? */
248            && ref_col <= first_col /* first col on screen? */
249            && ref_row + display_rows - 1 >= last_row /* bot row on screen? */
250            && ref_col + display_cols - 1 >= last_col)) /* last col on screen? */
251         (void) clear (); /* erase current screen */
252
253         /* figure out first row and col to print; subtract half
254            the extra lines from the first line */
255
256         ref_row = first_row - (display_rows - ROWS_PER_SECTOR) / 2;
257         ref_col = first_col - (display_cols - COLS_PER_SECTOR) / 2;
258
259         /* try not to go past bottom of map */
260         if (ref_row + display_rows - 1 > MAP_HEIGHT - 1)
261                 ref_row = MAP_HEIGHT - 1 - (display_rows - 1);
262
263         /* never go past top of map */
264         if (ref_row < 0) ref_row = 0;
265
266         /* same with columns */
267         if (ref_col + display_cols - 1 > MAP_WIDTH - 1)
268                 ref_col = MAP_WIDTH - 1 - (display_cols - 1);
269
270         if (ref_col < 0) ref_col = 0;
271
272         whose_map = whose; /* remember whose map is displayed */
273         display_screen (vmap);
274
275         /* print x-coordinates along bottom of screen */
276         for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++)
277         if (c % 10 == 0) {
278                 pos_str (lines-1, c-ref_col, "%d", c,0,0,0,0,0,0,0);
279         }
280         /* print y-coordinates along right of screen */
281         for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++) {
282                 if (r % 2 == 0)
283                         pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "%2d", r,0,0,0,0,0,0,0);
284                 else pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "  ",0,0,0,0,0,0,0,0);
285         }
286         /* print round number */
287         (void) sprintf (jnkbuf, "Sector %d Round %ld", sector, date);
288         for (r = 0; jnkbuf[r] != '\0'; r++) {
289                 if (r+NUMTOPS >= MAP_HEIGHT) break;
290                 (void) move (r+NUMTOPS, cols-NUMSIDES+4);
291                 (void) addch ((chtype)jnkbuf[r]);
292         }
293 }
294
295 /*
296 Display the contents of a single map square.
297
298 Fancy color hacks are done here. At the moment this is kind of bogus,
299 because the color doesn't convey any extra information, it just looks
300 pretty.
301 */
302
303
304 static void disp_square(view_map_t *vp)
305 {
306 #ifdef A_COLOR
307         chtype attr;
308
309         switch(vp->contents)
310         {
311         case '+':
312             attr = COLOR_PAIR(COLOR_GREEN);
313                 break;
314         case '.':
315                 attr = COLOR_PAIR(COLOR_CYAN);
316                 break;
317         case 'a':
318         case 'f':
319         case 'p':
320         case 'd':
321         case 'b':
322         case 't':
323         case 'c':
324         case 's':
325         case 'z':
326         case 'X':
327                 attr = COLOR_PAIR(COLOR_RED);
328                 break;
329         default:
330                 attr = COLOR_PAIR(COLOR_WHITE);
331                 break;
332         }
333         attron(attr);
334 #endif /* A_COLOR */
335         (void) addch ((chtype)vp->contents);
336 #ifdef A_COLOR
337         attroff(attr);
338         attron(COLOR_PAIR(COLOR_WHITE));
339 #endif /* A_COLOR */
340 }
341
342
343 /*
344 Display the portion of the map that appears on the screen.
345 */
346
347 void display_screen(view_map_t vmap[])
348 {
349         int display_rows, display_cols;
350         int r, c;
351         loc_t t;
352
353         display_rows = lines - NUMTOPS - 1; /* num lines to display */
354         display_cols = cols - NUMSIDES;
355
356         for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++)
357         for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++) {
358                 t = row_col_loc (r, c);
359                 (void) move (r-ref_row+NUMTOPS, c-ref_col);
360                 disp_square(&vmap[t]);
361         }
362 }
363
364 /*
365 Move the cursor in a specified direction.  We return true if the
366 cursor remains in the currently displayed screen, otherwise false.
367 We display the cursor on the screen, if possible.
368 */
369
370 bool
371 move_cursor(loc_t *cursor, int offset)
372 /* cursor is current cursor position, offset is offset to add to cursor */
373 {
374         loc_t t;
375         int r, c;
376  
377         t = *cursor + offset; /* proposed location */
378         if (!map[t].on_board) return (false); /* trying to move off map */
379         if (!on_screen (t)) return (false); /* loc is off screen */
380         
381         *cursor = t; /* update cursor position */
382         save_cursor = *cursor;
383                
384         r = loc_row (save_cursor);
385         c = loc_col (save_cursor);
386         (void) move (r-ref_row+NUMTOPS, c-ref_col);
387        
388         return (true);
389 }
390
391 /*
392 See if a location is displayed on the screen.
393 */
394
395 bool on_screen (loc_t loc)
396 {
397         int new_r, new_c;
398         
399         new_r = loc_row (loc);
400         new_c = loc_col (loc);
401
402         if (new_r < ref_row /* past top of screen */
403          || new_r - ref_row > lines - NUMTOPS - 1 /* past bot of screen? */
404          || new_c < ref_col /* past left edge of screen? */
405          || new_c - ref_col > cols - NUMSIDES) /* past right edge of screen? */
406         return (false);
407
408         return (true);
409 }
410
411 /* Print a view map for debugging. */
412
413 void
414 print_xzoom(view_map_t *vmap)
415 {
416         print_zoom (vmap);
417 #if 0
418         prompt ("Hit a key: ",0,0,0,0,0,0,0,0);
419         (void) get_chx (); /* wait for user */
420 #endif
421 }
422
423 /*
424 Print a condensed version of the map.
425 */
426
427 char zoom_list[] = "XO*tcbsdpfaTCBSDPFAzZ+. ";
428
429 void
430 print_zoom(view_map_t *vmap)
431 {
432         void print_zoom_cell ();
433
434         int row_inc, col_inc;
435         int r, c;
436
437         kill_display ();
438
439         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
440         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
441
442         for (r = 0; r < MAP_HEIGHT; r += row_inc)
443         for (c = 0; c < MAP_WIDTH; c += col_inc)
444         print_zoom_cell (vmap, r, c, row_inc, col_inc);
445
446         pos_str (0, 0, "Round #%d", date,0,0,0,0,0,0,0);
447         
448         (void) refresh ();
449 }
450
451 /*
452 Print a single cell in condensed format.
453 */
454
455 void
456 print_zoom_cell(view_map_t *vmap, 
457                  int row, int col, int row_inc, int col_inc)
458 {
459         int r, c;
460         char cell;
461
462         cell = ' ';
463         for (r = row; r < row + row_inc; r++)
464         for (c = col; c < col + col_inc; c++)
465         if (strchr (zoom_list, vmap[row_col_loc(r,c)].contents)
466                 < strchr (zoom_list, cell))
467         cell = vmap[row_col_loc(r,c)].contents;
468         
469         (void) move (row/row_inc + NUMTOPS, col/col_inc);
470         (void) addch ((chtype)cell);
471 }
472
473 /*
474 Print a condensed version of a pathmap.
475 */
476
477 void
478 print_pzoom(char *s, path_map_t *pmap, view_map_t *vmap)
479 {
480         void print_pzoom_cell();
481
482         int row_inc, col_inc;
483         int r, c;
484
485         kill_display ();
486
487         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
488         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
489
490         for (r = 0; r < MAP_HEIGHT; r += row_inc)
491         for (c = 0; c < MAP_WIDTH; c += col_inc)
492         print_pzoom_cell (pmap, vmap, r, c, row_inc, col_inc);
493
494         prompt (s,0,0,0,0,0,0,0,0);
495         (void) get_chx (); /* wait for user */
496         
497         (void) refresh ();
498 }
499
500 /*
501 Print a single cell of a pathmap in condensed format.
502 We average all squares in the cell and take the mod 10 value.
503 Squares with a value of -1 are printed with '-', squares with
504 a value of INFINITY/2 are printed with 'P', and squares with
505 a value of INFINITY are printed with 'Z'.  Squares with a value
506 between P and Z are printed as U.
507 */
508
509 void
510 print_pzoom_cell(path_map_t *pmap, view_map_t *vmap, 
511                   int row, int col, int row_inc, int col_inc)
512 {
513         int r, c;
514         int sum, d;
515         char cell;
516
517         sum = 0;
518         d = 0; /* number of squares in cell */
519         
520         for (r = row; r < row + row_inc; r++)
521         for (c = col; c < col + col_inc; c++) {
522                 sum += pmap[row_col_loc(r,c)].cost;
523                 d += 1;
524         }
525         sum /= d;
526         
527         if (pmap[row_col_loc(row,col)].terrain == T_PATH) cell = '-';
528         else if (sum < 0) cell = '!';
529         else if (sum == INFINITY/2) cell = 'P';
530         else if (sum == INFINITY) cell = ' ';
531         else if (sum > INFINITY/2) cell = 'U';
532         else {
533                 sum %= 36;
534                 if (sum < 10) cell = sum + '0';
535                 else cell = sum - 10 + 'a';
536         }
537         
538         if (cell == ' ')
539                 print_zoom_cell (vmap, row, col, row_inc, col_inc);
540         else {
541                 (void) move (row/row_inc + NUMTOPS, col/col_inc);
542                 (void) addch ((chtype)cell);
543         }
544 }
545
546 /*
547 Display the score off in the corner of the screen.
548 */
549
550 void
551 display_score(void)
552 {
553         pos_str (1, cols-12, " User  Comp",0,0,0,0,0,0,0,0);
554         pos_str (2, cols-12, "%5d %5d", user_score, comp_score,0,0,0,0,0,0);
555 }
556
557 /*
558 Clear the end of a specified line starting at the specified column.
559 */
560
561 void
562 clreol(int linep, int colp)
563 {
564         (void) move (linep, colp);
565         (void) clrtoeol();
566 }
567
568 /*
569 Initialize the terminal.
570 */
571
572 void
573 ttinit(void)
574 {
575         (void) initscr();
576         (void) noecho();
577         (void) crmode();
578 #ifdef A_COLOR
579         init_colors();
580 #endif /* A_COLOR */
581         lines = LINES;
582         cols = COLS;
583         if (lines > MAP_HEIGHT + NUMTOPS + 1)
584                 lines = MAP_HEIGHT + NUMTOPS + 1;
585         if (cols > MAP_WIDTH + NUMSIDES)
586                 cols = MAP_WIDTH + NUMSIDES;
587 }
588
589
590 /*
591 Clear the screen.  We must also kill information maintained about the
592 display.
593 */
594
595 void
596 clear_screen(void)
597 {
598         (void) clear ();
599         (void) refresh ();
600         kill_display ();
601 }
602
603 /*
604 Audible complaint.
605 */
606
607 void 
608 complain(void)
609 {
610         (void) beep ();
611 }
612
613 /*
614 Redraw the screen.
615 */
616
617 void 
618 redisplay(void)
619 {
620         (void) refresh ();
621 }
622
623 void
624 redraw(void)
625 {
626         (void) clearok (curscr, TRUE);
627         (void) refresh ();
628 }
629
630 /*
631 Wait a little bit to give user a chance to see a message.  We refresh
632 the screen and pause for a few milliseconds.
633 */
634
635 void
636 delay(void)
637 {
638         int t = delay_time;
639         int i = 500;
640         (void) refresh ();
641         if (t > i) {
642           (void) move (LINES - 1, 0);
643         }
644         for (; t > 0; t -= i) {
645           (void) napms ((t > i) ? i : t); /* pause a bit */
646           if (t > i) {
647             addstr ("*");
648             refresh (); 
649           }
650         }
651 }
652
653 /*
654 Clean up the display.  This routine gets called as we leave the game.
655 */
656
657 void
658 close_disp(void)
659 {
660         (void) move (LINES - 1, 0);
661         (void) clrtoeol ();
662         (void) refresh ();
663         (void) endwin ();
664 }
665
666 /*
667 Position the cursor and output a string.
668 */
669
670 void
671 pos_str1(int row, int col, char *str, ...)
672 {
673         va_list ap;
674         char junkbuf[STRSIZE];
675
676         va_start(ap, str);
677         (void) move (row, col);
678         vsprintf(junkbuf, str, ap);
679         (void) addstr (junkbuf);
680         va_end(ap);
681 }
682
683 void
684 pos_str(int row, int col, char *str, ...)
685 {
686         va_list ap;
687         char junkbuf[STRSIZE];
688
689         va_start(ap, str);
690         (void) move (row, col);
691         vsprintf(junkbuf, str, ap);
692         (void) addstr (junkbuf);
693         va_end(ap);
694 }
695
696 void
697 /* VARARGS1 */
698 addprintf(char *str, int a, int b, int c, int d, int e, int f, int g, int h)
699 {
700         char junkbuf[STRSIZE];
701         
702         (void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
703         (void) addstr (junkbuf);
704 }
705 void
706 /* VARARGS1 */
707 addprintf1(char *str, char *a, int b, int c, int d, int e, int f, int g, int h)
708 {
709         char junkbuf[STRSIZE];
710         
711         (void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
712         (void) addstr (junkbuf);
713 }
714
715 void
716 /* VARARGS1 */
717 addprintf2(char *str, char *a, int b, int c, int d, char *e, char *f, int g, int h)
718 {
719         char junkbuf[STRSIZE];
720         
721         (void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
722         (void) addstr (junkbuf);
723 }
724
725 /*
726 Print a single cell in condensed format.
727 */
728
729 extern char zoom_list[];
730
731 void
732 print_movie_cell(char *mbuf, int row, int col, int row_inc, int col_inc)
733 {
734         int r, c;
735         char cell;
736
737         cell = ' ';
738         for (r = row; r < row + row_inc; r++)
739         for (c = col; c < col + col_inc; c++)
740         if (strchr (zoom_list, mbuf[row_col_loc(r,c)])
741                 < strchr (zoom_list, cell))
742         cell = mbuf[row_col_loc(r,c)];
743         
744         (void) move (row/row_inc + NUMTOPS, col/col_inc);
745         (void) addch ((chtype)cell);
746 }