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