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