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