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