Color & save-interval implementation.
[vms-empire.git] / display.c
1 /* $Id$  - (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 #ifdef SYSV
22 #include <string.h>
23 #else
24 #include <strings.h>
25 #endif
26
27 #include <curses.h>
28 #include "empire.h"
29 #include "extern.h"
30
31 static int whose_map = UNOWNED; /* user's or computer's point of view */
32 static int ref_row; /* map loc displayed in upper-left corner */
33 static int ref_col;
34 static int save_sector; /* the currently displayed sector */
35 static int save_cursor; /* currently displayed cursor position */
36 static int change_ok = TRUE; /* true if new sector may be displayed */
37
38 #ifdef A_COLOR
39 void init_colors()
40 {
41     start_color();
42
43     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
44     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
45     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
46     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
47     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
48     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
49     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
50     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
51 }
52 #endif /* A_COLOR */
53
54 /*
55 This routine is called when the current display has been
56 trashed and no sector is shown on the screen.
57 */
58
59 void kill_display () {
60         whose_map = UNOWNED;
61 }
62
63 /*
64 This routine is called when a new sector may be displayed on the
65 screen even if the location to be displayed is already on the screen.
66 */
67
68 void sector_change () {
69         change_ok = TRUE;
70 }
71
72 /*
73 Return the currently displayed user sector, if any.  If a user
74 sector is not displayed, return -1.
75 */
76
77 int cur_sector () {
78         if (whose_map != USER) return (-1);
79         return (save_sector);
80 }
81
82 /*
83 Return the current position of the cursor.  If the user's map
84 is not on the screen, we return -1.
85 */
86
87 long cur_cursor () {
88         if (whose_map != USER) return (-1);
89         return (save_cursor);
90 }
91
92 /*
93 Display a location on the screen. We figure out the sector the
94 location is in and display that sector.  The cursor is left at
95 the requested location.
96
97 We redisplay the sector only if we either have been requested to
98 redisplay the sector, or if the location is not on the screen.
99 */
100
101 void
102 display_loc (whose, vmap, loc)
103 int whose; /* whose map to display */
104 view_map_t vmap[];
105 long loc; /* location to display */
106 {
107         void print_sector(), show_loc();
108         
109         if (change_ok || whose != whose_map || !on_screen (loc))
110                 print_sector (whose, vmap, loc_sector (loc));
111                 
112         show_loc (vmap, loc);
113 }
114
115 /*
116 Display a location iff the location is on the screen.
117 */
118
119 void
120 display_locx (whose, vmap, loc)
121 int whose; /* whose map to display */
122 view_map_t vmap[];
123 long loc; /* location to display */
124 {
125         if (whose == whose_map && on_screen (loc))
126                 show_loc (vmap, loc);
127 }
128
129 /*
130 Display a location which exists on the screen.
131 */
132
133 void
134 show_loc (vmap, loc)
135 view_map_t vmap[];
136 long loc;
137 {
138         int r, c;
139         
140         r = loc_row (loc);
141         c = loc_col (loc);
142         (void) move (r-ref_row+NUMTOPS, c-ref_col);
143         disp_square(&vmap[loc]);
144         save_cursor = loc; /* remember cursor location */
145         (void) move (r-ref_row+NUMTOPS, c-ref_col);
146 }
147
148 /*
149 Print a sector of the user's on the screen.  If it is already displayed,
150 we do nothing.  Otherwise we redraw the screen.  Someday, some intelligence
151 in doing this might be interesting.  We heavily depend on curses to update
152 the screen in a reasonable fashion.
153
154 If the desired sector
155 is not displayed, we clear the screen.  We then update the screen
156 to reflect the current map.  We heavily depend on curses to correctly
157 optimize the redrawing of the screen.
158
159 When redrawing the screen, we figure out where the
160 center of the sector is in relation to the map.  We then compute
161 the screen coordinates where we want to display the center of the
162 sector.  We will remember the sector displayed, the map displayed,
163 and the map location that appears in the upper-left corner of the
164 screen.
165 */
166  
167 void
168 print_sector (whose, vmap, sector)
169 char whose; /* USER or COMP */
170 view_map_t vmap[]; /* map to display */
171 int sector; /* sector to display */
172 {
173         void display_screen();
174
175         int first_row, first_col, last_row, last_col;
176         int display_rows, display_cols;
177         int r, c;
178
179         save_sector = sector; /* remember last sector displayed */
180         change_ok = FALSE; /* we are displaying a new sector */
181
182         display_rows = lines - NUMTOPS - 1; /* num lines to display */
183         display_cols = cols - NUMSIDES;
184
185         /* compute row and column edges of sector */
186         first_row = sector_row (sector) * ROWS_PER_SECTOR;
187         first_col = sector_col (sector) * COLS_PER_SECTOR;
188         last_row = first_row + ROWS_PER_SECTOR - 1;
189         last_col = first_col + COLS_PER_SECTOR - 1;
190
191         if (!(whose == whose_map /* correct map is on screen? */
192            && ref_row <= first_row /* top row on screen? */
193            && ref_col <= first_col /* first col on screen? */
194            && ref_row + display_rows - 1 >= last_row /* bot row on screen? */
195            && ref_col + display_cols - 1 >= last_col)) /* last col on screen? */
196         (void) clear (); /* erase current screen */
197
198         /* figure out first row and col to print; subtract half
199            the extra lines from the first line */
200
201         ref_row = first_row - (display_rows - ROWS_PER_SECTOR) / 2;
202         ref_col = first_col - (display_cols - COLS_PER_SECTOR) / 2;
203
204         /* try not to go past bottom of map */
205         if (ref_row + display_rows - 1 > MAP_HEIGHT - 1)
206                 ref_row = MAP_HEIGHT - 1 - (display_rows - 1);
207
208         /* never go past top of map */
209         if (ref_row < 0) ref_row = 0;
210
211         /* same with columns */
212         if (ref_col + display_cols - 1 > MAP_WIDTH - 1)
213                 ref_col = MAP_WIDTH - 1 - (display_cols - 1);
214
215         if (ref_col < 0) ref_col = 0;
216
217         whose_map = whose; /* remember whose map is displayed */
218         display_screen (vmap);
219
220         /* print x-coordinates along bottom of screen */
221         for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++)
222         if (c % 10 == 0) {
223                 pos_str (lines-1, c-ref_col, "%d", c);
224         }
225         /* print y-coordinates along right of screen */
226         for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++) {
227                 if (r % 2 == 0)
228                         pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "%2d", r);
229                 else pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "  ");
230         }
231         /* print round number */
232         (void) sprintf (jnkbuf, "Sector %d Round %d", sector, date);
233         for (r = 0; jnkbuf[r] != '\0'; r++) {
234                 if (r+NUMTOPS >= MAP_HEIGHT) break;
235                 (void) move (r+NUMTOPS, cols-NUMSIDES+4);
236                 (void) addch ((chtype)jnkbuf[r]);
237         }
238 }
239
240 /*
241 Display the contents of a single map square.
242
243 Fancy color hacks are done here. At the moment this is kind of bogus,
244 because the color doesn't convey any extra information, it just looks
245 pretty.
246 */
247
248
249 static disp_square(vp)
250 view_map_t *vp;
251 {
252 #ifdef A_COLOR
253         switch(vp->contents)
254         {
255         case '+':
256                 attron(COLOR_PAIR(COLOR_GREEN));
257                 break;
258         case '.':
259                 attron(COLOR_PAIR(COLOR_BLUE));
260                 break;
261         case 'a':
262         case 'f':
263         case 'p':
264         case 'd':
265         case 'b':
266         case 't':
267         case 'c':
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 #endif /* A_COLOR */
281 }
282
283
284 /*
285 Display the portion of the map that appears on the screen.
286 */
287
288 void display_screen (vmap)
289 view_map_t vmap[];
290 {
291         int display_rows, display_cols;
292         int r, c;
293         long t;
294
295         display_rows = lines - NUMTOPS - 1; /* num lines to display */
296         display_cols = cols - NUMSIDES;
297
298         for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++)
299         for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++) {
300                 t = row_col_loc (r, c);
301                 (void) move (r-ref_row+NUMTOPS, c-ref_col);
302                 disp_square(&vmap[t]);
303         }
304 }
305
306 /*
307 Move the cursor in a specified direction.  We return TRUE if the
308 cursor remains in the currently displayed screen, otherwise FALSE.
309 We display the cursor on the screen, if possible.
310 */
311
312 int
313 move_cursor (cursor, offset)
314 long *cursor; /* current cursor position */
315 int offset; /* offset to add to cursor */
316 {
317         long t;
318         int r, c;
319  
320         t = *cursor + offset; /* proposed location */
321         if (!map[t].on_board) return (FALSE); /* trying to move off map */
322         if (!on_screen (t)) return (FALSE); /* loc is off screen */
323         
324         *cursor = t; /* update cursor position */
325         save_cursor = *cursor;
326                
327         r = loc_row (save_cursor);
328         c = loc_col (save_cursor);
329         (void) move (r-ref_row+NUMTOPS, c-ref_col);
330        
331         return (TRUE);
332 }
333
334 /*
335 See if a location is displayed on the screen.
336 */
337
338 int on_screen (loc)
339 long loc;
340 {
341         int new_r, new_c;
342         
343         new_r = loc_row (loc);
344         new_c = loc_col (loc);
345
346         if (new_r < ref_row /* past top of screen */
347          || new_r - ref_row > lines - NUMTOPS - 1 /* past bot of screen? */
348          || new_c < ref_col /* past left edge of screen? */
349          || new_c - ref_col > cols - NUMSIDES) /* past right edge of screen? */
350         return (FALSE);
351
352         return (TRUE);
353 }
354
355 /* Print a view map for debugging. */
356
357 void
358 print_xzoom (vmap)
359 view_map_t *vmap;
360 {
361         print_zoom (vmap);
362 #if 0
363         prompt ("Hit a key: ");
364         (void) get_chx (); /* wait for user */
365 #endif
366 }
367
368 /*
369 Print a condensed version of the map.
370 */
371
372 char zoom_list[] = "XO*tcbsdpfaTCBSDPFAzZ+. ";
373
374 void
375 print_zoom (vmap)
376 view_map_t *vmap;
377 {
378         void print_zoom_cell();
379
380         int row_inc, col_inc;
381         int r, c;
382
383         kill_display ();
384
385         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
386         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
387
388         for (r = 0; r < MAP_HEIGHT; r += row_inc)
389         for (c = 0; c < MAP_WIDTH; c += col_inc)
390         print_zoom_cell (vmap, r, c, row_inc, col_inc);
391
392         pos_str (0, 0, "Round #%d", date);
393         
394         (void) refresh ();
395 }
396
397 /*
398 Print a single cell in condensed format.
399 */
400
401 void
402 print_zoom_cell (vmap, row, col, row_inc, col_inc)
403 view_map_t *vmap;
404 int row, col;
405 int row_inc, col_inc;
406 {
407         int r, c;
408         char cell;
409
410         cell = ' ';
411         for (r = row; r < row + row_inc; r++)
412         for (c = col; c < col + col_inc; c++)
413         if (strchr (zoom_list, vmap[row_col_loc(r,c)].contents)
414                 < strchr (zoom_list, cell))
415         cell = vmap[row_col_loc(r,c)].contents;
416         
417         (void) move (row/row_inc + NUMTOPS, col/col_inc);
418         (void) addch ((chtype)cell);
419 }
420
421 /*
422 Print a condensed version of a pathmap.
423 */
424
425 void
426 print_pzoom (s, pmap, vmap)
427 char *s;
428 path_map_t *pmap;
429 view_map_t *vmap;
430 {
431         void print_pzoom_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_pzoom_cell (pmap, vmap, r, c, row_inc, col_inc);
444
445         prompt (s);
446         (void) get_chx (); /* wait for user */
447         
448         (void) refresh ();
449 }
450
451 /*
452 Print a single cell of a pathmap in condensed format.
453 We average all squares in the cell and take the mod 10 value.
454 Squares with a value of -1 are printed with '-', squares with
455 a value of INFINITY/2 are printed with 'P', and squares with
456 a value of INFINITY are printed with 'Z'.  Squares with a value
457 between P and Z are printed as U.
458 */
459
460 void
461 print_pzoom_cell (pmap, vmap, row, col, row_inc, col_inc)
462 path_map_t *pmap;
463 view_map_t *vmap;
464 int row, col;
465 int row_inc, col_inc;
466 {
467         int r, c;
468         int sum, d;
469         char cell;
470
471         sum = 0;
472         d = 0; /* number of squares in cell */
473         
474         for (r = row; r < row + row_inc; r++)
475         for (c = col; c < col + col_inc; c++) {
476                 sum += pmap[row_col_loc(r,c)].cost;
477                 d += 1;
478         }
479         sum /= d;
480         
481         if (pmap[row_col_loc(row,col)].terrain == T_PATH) cell = '-';
482         else if (sum < 0) cell = '!';
483         else if (sum == INFINITY/2) cell = 'P';
484         else if (sum == INFINITY) cell = ' ';
485         else if (sum > INFINITY/2) cell = 'U';
486         else {
487                 sum %= 36;
488                 if (sum < 10) cell = sum + '0';
489                 else cell = sum - 10 + 'a';
490         }
491         
492         if (cell == ' ')
493                 print_zoom_cell (vmap, row, col, row_inc, col_inc);
494         else {
495                 (void) move (row/row_inc + NUMTOPS, col/col_inc);
496                 (void) addch ((chtype)cell);
497         }
498 }
499
500 /*
501 Display the score off in the corner of the screen.
502 */
503
504 void
505 display_score ()
506 {
507         pos_str (0, cols-12, " User  Comp");
508         pos_str (1, cols-12, "%5d %5d", user_score, comp_score);
509 }