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