Reindent the code.
[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,0,0,0,0,0,0,0);
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,0,0,0,0,0,0,0);
291         else pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "  ",0,0,0,0,0,0,0,0);
292     }
293     /* print round number */
294     (void) sprintf (jnkbuf, "Sector %d Round %ld", sector, date);
295     for (r = 0; jnkbuf[r] != '\0'; r++) {
296         if (r+NUMTOPS >= MAP_HEIGHT) break;
297         (void) move (r+NUMTOPS, cols-NUMSIDES+4);
298         (void) addch ((chtype)jnkbuf[r]);
299     }
300 }
301
302 /*
303 Display the contents of a single map square.
304
305 Fancy color hacks are done here. At the moment this is kind of bogus,
306 because the color doesn't convey any extra information, it just looks
307 pretty.
308 */
309
310
311 static void disp_square(view_map_t *vp)
312 {
313 #ifdef A_COLOR
314     chtype attr;
315
316     switch(vp->contents)
317     {
318     case '+':
319         attr = COLOR_PAIR(COLOR_GREEN);
320         break;
321     case '.':
322         attr = COLOR_PAIR(COLOR_CYAN);
323         break;
324     case 'a':
325     case 'f':
326     case 'p':
327     case 'd':
328     case 'b':
329     case 't':
330     case 'c':
331     case 's':
332     case 'z':
333     case 'X':
334         attr = COLOR_PAIR(COLOR_RED);
335         break;
336     default:
337         attr = COLOR_PAIR(COLOR_WHITE);
338         break;
339     }
340     attron(attr);
341 #endif /* A_COLOR */
342     (void) addch ((chtype)vp->contents);
343 #ifdef A_COLOR
344     attroff(attr);
345     attron(COLOR_PAIR(COLOR_WHITE));
346 #endif /* A_COLOR */
347 }
348
349
350 /*
351 Display the portion of the map that appears on the screen.
352 */
353
354 void display_screen(view_map_t vmap[])
355 {
356     int display_rows, display_cols;
357     int r, c;
358     loc_t t;
359
360     display_rows = lines - NUMTOPS - 1; /* num lines to display */
361     display_cols = cols - NUMSIDES;
362
363     for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++)
364         for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++) {
365             t = row_col_loc (r, c);
366             (void) move (r-ref_row+NUMTOPS, c-ref_col);
367             disp_square(&vmap[t]);
368         }
369 }
370
371 /*
372 Move the cursor in a specified direction.  We return true if the
373 cursor remains in the currently displayed screen, otherwise false.
374 We display the cursor on the screen, if possible.
375 */
376
377 bool
378 move_cursor(loc_t *cursor, int offset)
379 /* cursor is current cursor position, offset is offset to add to cursor */
380 {
381     loc_t t;
382     int r, c;
383  
384     t = *cursor + offset; /* proposed location */
385     if (!map[t].on_board) return (false); /* trying to move off map */
386     if (!on_screen (t)) return (false); /* loc is off screen */
387         
388     *cursor = t; /* update cursor position */
389     save_cursor = *cursor;
390                
391     r = loc_row (save_cursor);
392     c = loc_col (save_cursor);
393     (void) move (r-ref_row+NUMTOPS, c-ref_col);
394        
395     return (true);
396 }
397
398 /*
399 See if a location is displayed on the screen.
400 */
401
402 bool on_screen (loc_t loc)
403 {
404     int new_r, new_c;
405         
406     new_r = loc_row (loc);
407     new_c = loc_col (loc);
408
409     if (new_r < ref_row /* past top of screen */
410         || new_r - ref_row > lines - NUMTOPS - 1 /* past bot of screen? */
411         || new_c < ref_col /* past left edge of screen? */
412         || new_c - ref_col > cols - NUMSIDES) /* past right edge of screen? */
413         return (false);
414
415     return (true);
416 }
417
418 /* Print a view map for debugging. */
419
420 void
421 print_xzoom(view_map_t *vmap)
422 {
423     print_zoom (vmap);
424 #if 0
425     prompt ("Hit a key: ",0,0,0,0,0,0,0,0);
426     (void) get_chx (); /* wait for user */
427 #endif
428 }
429
430 /*
431 Print a condensed version of the map.
432 */
433
434 char zoom_list[] = "XO*tcbsdpfaTCBSDPFAzZ+. ";
435
436 void
437 print_zoom(view_map_t *vmap)
438 {
439     void print_zoom_cell ();
440
441     int row_inc, col_inc;
442     int r, c;
443
444     kill_display ();
445
446     row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
447     col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
448
449     for (r = 0; r < MAP_HEIGHT; r += row_inc)
450         for (c = 0; c < MAP_WIDTH; c += col_inc)
451             print_zoom_cell (vmap, r, c, row_inc, col_inc);
452
453     pos_str (0, 0, "Round #%d", date,0,0,0,0,0,0,0);
454         
455     (void) refresh ();
456 }
457
458 /*
459 Print a single cell in condensed format.
460 */
461
462 void
463 print_zoom_cell(view_map_t *vmap, 
464                  int row, int col, int row_inc, int col_inc)
465 {
466     int r, c;
467     char cell;
468
469     cell = ' ';
470     for (r = row; r < row + row_inc; r++)
471         for (c = col; c < col + col_inc; c++)
472             if (strchr (zoom_list, vmap[row_col_loc(r,c)].contents)
473                 < strchr (zoom_list, cell))
474                 cell = vmap[row_col_loc(r,c)].contents;
475         
476     (void) move (row/row_inc + NUMTOPS, col/col_inc);
477     (void) addch ((chtype)cell);
478 }
479
480 /*
481 Print a condensed version of a pathmap.
482 */
483
484 void
485 print_pzoom(char *s, path_map_t *pmap, view_map_t *vmap)
486 {
487     void print_pzoom_cell();
488
489     int row_inc, col_inc;
490     int r, c;
491
492     kill_display ();
493
494     row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
495     col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
496
497     for (r = 0; r < MAP_HEIGHT; r += row_inc)
498         for (c = 0; c < MAP_WIDTH; c += col_inc)
499             print_pzoom_cell (pmap, vmap, r, c, row_inc, col_inc);
500
501     prompt (s,0,0,0,0,0,0,0,0);
502     (void) get_chx (); /* wait for user */
503         
504     (void) refresh ();
505 }
506
507 /*
508 Print a single cell of a pathmap in condensed format.
509 We average all squares in the cell and take the mod 10 value.
510 Squares with a value of -1 are printed with '-', squares with
511 a value of INFINITY/2 are printed with 'P', and squares with
512 a value of INFINITY are printed with 'Z'.  Squares with a value
513 between P and Z are printed as U.
514 */
515
516 void
517 print_pzoom_cell(path_map_t *pmap, view_map_t *vmap, 
518                   int row, int col, int row_inc, int col_inc)
519 {
520     int r, c;
521     int sum, d;
522     char cell;
523
524     sum = 0;
525     d = 0; /* number of squares in cell */
526         
527     for (r = row; r < row + row_inc; r++)
528         for (c = col; c < col + col_inc; c++) {
529             sum += pmap[row_col_loc(r,c)].cost;
530             d += 1;
531         }
532     sum /= d;
533         
534     if (pmap[row_col_loc(row,col)].terrain == T_PATH) cell = '-';
535     else if (sum < 0) cell = '!';
536     else if (sum == INFINITY/2) cell = 'P';
537     else if (sum == INFINITY) cell = ' ';
538     else if (sum > INFINITY/2) cell = 'U';
539     else {
540         sum %= 36;
541         if (sum < 10) cell = sum + '0';
542         else cell = sum - 10 + 'a';
543     }
544         
545     if (cell == ' ')
546         print_zoom_cell (vmap, row, col, row_inc, col_inc);
547     else {
548         (void) move (row/row_inc + NUMTOPS, col/col_inc);
549         (void) addch ((chtype)cell);
550     }
551 }
552
553 /*
554 Display the score off in the corner of the screen.
555 */
556
557 void
558 display_score(void)
559 {
560     pos_str (1, cols-12, " User  Comp");
561     pos_str (2, cols-12, "%5d %5d", user_score, comp_score);
562 }
563
564 /*
565 Clear the end of a specified line starting at the specified column.
566 */
567
568 void
569 clreol(int linep, int colp)
570 {
571     (void) move (linep, colp);
572     (void) clrtoeol();
573 }
574
575 /*
576 Initialize the terminal.
577 */
578
579 void
580 ttinit(void)
581 {
582     (void) initscr();
583     (void) noecho();
584     (void) crmode();
585 #ifdef A_COLOR
586     init_colors();
587 #endif /* A_COLOR */
588     lines = LINES;
589     cols = COLS;
590     if (lines > MAP_HEIGHT + NUMTOPS + 1)
591         lines = MAP_HEIGHT + NUMTOPS + 1;
592     if (cols > MAP_WIDTH + NUMSIDES)
593         cols = MAP_WIDTH + NUMSIDES;
594 }
595
596
597 /*
598 Clear the screen.  We must also kill information maintained about the
599 display.
600 */
601
602 void
603 clear_screen(void)
604 {
605     (void) clear ();
606     (void) refresh ();
607     kill_display ();
608 }
609
610 /*
611 Audible complaint.
612 */
613
614 void 
615 complain(void)
616 {
617     (void) beep ();
618 }
619
620 /*
621 Redraw the screen.
622 */
623
624 void 
625 redisplay(void)
626 {
627     (void) refresh ();
628 }
629
630 void
631 redraw(void)
632 {
633     (void) clearok (curscr, TRUE);
634     (void) refresh ();
635 }
636
637 /*
638 Wait a little bit to give user a chance to see a message.  We refresh
639 the screen and pause for a few milliseconds.
640 */
641
642 void
643 delay(void)
644 {
645     int t = delay_time;
646     int i = 500;
647     (void) refresh ();
648     if (t > i) {
649         (void) move (LINES - 1, 0);
650     }
651     for (; t > 0; t -= i) {
652         (void) napms ((t > i) ? i : t); /* pause a bit */
653         if (t > i) {
654             addstr ("*");
655             refresh (); 
656         }
657     }
658 }
659
660 /*
661 Clean up the display.  This routine gets called as we leave the game.
662 */
663
664 void
665 close_disp(void)
666 {
667     (void) move (LINES - 1, 0);
668     (void) clrtoeol ();
669     (void) refresh ();
670     (void) endwin ();
671 }
672
673 /*
674 Position the cursor and output a string.
675 */
676
677 void
678 pos_str(int row, int col, char *str, ...)
679 {
680     va_list ap;
681     char junkbuf[STRSIZE];
682
683     va_start(ap, str);
684     (void) move (row, col);
685     vsprintf(junkbuf, str, ap);
686     (void) addstr (junkbuf);
687     va_end(ap);
688 }
689
690 void
691 pos_str1(int row, int col, char *str, ...)
692 {
693     va_list ap;
694     char junkbuf[STRSIZE];
695
696     va_start(ap, str);
697     (void) move (row, col);
698     vsprintf(junkbuf, str, ap);
699     (void) addstr (junkbuf);
700     va_end(ap);
701 }
702
703 /*
704 Print a single cell in condensed format.
705 */
706
707 extern char zoom_list[];
708
709 void
710 print_movie_cell(char *mbuf, int row, int col, int row_inc, int col_inc)
711 {
712     int r, c;
713     char cell;
714
715     cell = ' ';
716     for (r = row; r < row + row_inc; r++)
717         for (c = col; c < col + col_inc; c++)
718             if (strchr (zoom_list, mbuf[row_col_loc(r,c)])
719                 < strchr (zoom_list, cell))
720                 cell = mbuf[row_col_loc(r,c)];
721         
722     (void) move (row/row_inc + NUMTOPS, col/col_inc);
723     (void) addch ((chtype)cell);
724 }
725
726 /* end */