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