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