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