1 /* Lower-level drawing routines for the X11 interface to Xconq.
2 Copyright (C) 1987-1989, 1991-1999 Stanley T. Shebs.
3
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version. See the file COPYING. */
8
9 /* The routines in this file are for drawing in maps, but only work
10 at the Xlib level, and would be the same no matter what higher-level
11 toolkit was in use. */
12
13 /* At the moment, there are three subwindows of a map that use raw Xlib;
14 the unit info, the map view itself, and the list of sides. */
15
16 #include "conq.h"
17 #include "xtconq.h"
18
19 int tmpdrawlighting;
20 int tmpdrawcoverage;
21
22 Map *tmpmap;
23
24 #define bords_to_draw(m) (numbordtypes > 0 && bwid[(m)->vp->power] > 0)
25
26 #define conns_to_draw(m) (numconntypes > 0 && cwid[(m)->vp->power] > 0)
27
28 Unit *x_find_unit_or_occ(Side *side, Map *map, Unit *unit,
29 int usx, int usy, int usw, int ush, int sx, int sy);
30 Unit *x_find_unit_at(Side *side, Map *map, int x, int y, int sx, int sy);
31
32 static void xform_fractional(Side *side, Map *map,
33 int x, int y, int xf, int yf, int *sxp, int *syp);
34
35 static void draw_feature_name(Side *side, Map *map, int fid);
36 static void draw_border_line_mult(Side *side, Map *map, Window win,
37 int sx, int sy, int bitmask, int power,
38 int t);
39 static void draw_connection_line_mult(Side *side, Window win,
40 int sx, int sy, int bitmask,
41 int power, int c);
42 static void draw_hex_polygon(Side *side, Map *map, Window win,
43 GC gc, int sx, int sy,
44 int power, int over, int dogrid);
45 static void draw_area_background(Side *side, Map *map);
46 static void meridian_line_callback(int x1, int y1, int x1f, int y1f,
47 int x2, int y2, int x2f, int y2f);
48 static void meridian_text_callback(int x1, int y1, int x1f, int y1f,
49 char *str);
50 static int cell_drawing_info(Side *side, int x, int y, int power,
51 int seeall, Pixmap *patp, long *colorp);
52 static void set_terrain_gc_for_image(Side *side, Image *timg);
53 static void draw_terrain_row(Side *side, Map *map,
54 int x0, int y0, int len, int force);
55 static void draw_contours(Side *side, Map *map, int x0, int y0, int len);
56 #if 0
57 static void draw_elevations(Side *side, Map *map, int x0, int y0, int len);
58 #endif
59 static void draw_clouds_row(Side *side, Map *map, int x0, int y0, int len);
60 static void draw_temperature_row(Side *side, Map *map, int x0, int y0,
61 int len);
62 static void draw_winds_row(Side *side, Map *map, int x0, int y0, int len);
63 static void draw_units(Side *side, Map *map, int x, int y);
64 static void draw_unit_and_occs(Side *side, Map *map, Unit *unit,
65 int sx, int sy, int sw, int sh);
66 static void draw_unit_view_and_occs(Side *side, Map *map, UnitView *uview,
67 int sx, int sy, int sw, int sh);
68 static void draw_unit_name(Side *side, Map *map, Unit *unit,
69 int sx, int sy, int sw, int sh);
70 static void draw_people(Side *side, Map *map, int x, int y);
71 static void draw_borders(Side *side, Map *map, int x, int y, int b);
72 static void draw_connections(Side *side, Map *map, int x, int y, int c);
73 static void draw_legend(Side *side, Map *map, int x, int y);
74 static void draw_country_border_line(Side *side, Window win,
75 int sx, int sy, int dir, int power);
76 static void draw_legend_text(Side *side, Window win,
77 int sx, int sy, int power, char *str,
78 int color, int maskit);
79 static void draw_feature_boundary(Side *side, Map *map, int x, int y, int fid);
80 static void draw_info_text(Side *side, Map *map, int x, int y,
81 int len, char *buf);
82
83 static void draw_meridians(Side *side, Map *map);
84
85 #define xtconq_cell_overlay(side, x, y) \
86 ((side->designer) \
87 ? ((terrain_view(side, x, y) == UNSEEN) ? -2 : 0) \
88 : (tmpdrawlighting \
89 ? (night_at(x, y) \
90 ? -2 \
91 : (tmpdrawcoverage ? (cover(side, x, y) == 0 ? -1 : 0) : 0)) \
92 : (tmpdrawcoverage \
93 ? (cover(side, x, y) == 0 ? -1 : 0) \
94 : 0)))
95
96 /* Put the point x, y in the center of the map, or at least as close
97 as possible. */
98
99 void
recenter(Side * side,Map * map,int x,int y)100 recenter(Side *side, Map *map, int x, int y)
101 {
102 int oldsx = map->vp->sx, oldsy = map->vp->sy;
103
104 set_view_focus(map->vp, x, y);
105 x_center_on_focus(side, map);
106 if (map->vp->sx != oldsx || map->vp->sy != oldsy) {
107 draw_map(side, map);
108 }
109 }
110
111 /* Ensure that given location is visible on the front map. We
112 (should) also flush the input because any input relating to a
113 different screen is probably worthless. */
114
115 void
put_on_screen(Side * side,Map * map,int x,int y)116 put_on_screen(Side *side, Map *map, int x, int y)
117 {
118
119 /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
120 if (x == 0 && y == 0)
121 return;
122 if (!in_middle(side, map, x, y))
123 recenter(side, map, x, y);
124 }
125
126 /* Decide whether given location is not too close to edge of screen.
127 We do this because it's a pain to move units when half the adjacent
128 places aren't even visible. This routine effectively places a
129 lower limit of 5x5 for the map window. (I think) */
130
131 int
in_middle(Side * side,Map * map,int x,int y)132 in_middle(Side *side, Map *map, int x, int y)
133 {
134 int sx, sy, insetx1, insety1, insetx2, insety2;
135
136 xform(side, map, x, y, &sx, &sy);
137 /* Adjust to be the center of the cell, more reasonable if large. */
138 sx += map->vp->hw / 2; sy += map->vp->hh / 2;
139 insetx1 = min(map->vp->pxw / 4, 1 * map->vp->hw);
140 insety1 = min(map->vp->pxh / 4, 1 * map->vp->hch);
141 insetx2 = min(map->vp->pxw / 4, 2 * map->vp->hw);
142 insety2 = min(map->vp->pxh / 4, 2 * map->vp->hch);
143 if (sx < insetx2)
144 return FALSE;
145 if (sx > map->vp->pxw - insetx2)
146 return FALSE;
147 if (sy < (between(2, y, area.height-3) ? insety2 : insety1))
148 return FALSE;
149 if (sy > map->vp->pxh - (between(2, y, area.height-3) ? insety2 : insety1))
150 return FALSE;
151 return TRUE;
152 }
153
154 /* Transform map coordinates into screen coordinates, relative to the
155 given side. */
156
157 void
xform(Side * side,Map * map,int x,int y,int * sxp,int * syp)158 xform(Side *side, Map *map, int x, int y, int *sxp, int *syp)
159 {
160 xform_cell(map->vp, x, y, sxp, syp);
161 }
162
163 static void
xform_fractional(Side * side,Map * map,int x,int y,int xf,int yf,int * sxp,int * syp)164 xform_fractional(Side *side, Map *map, int x, int y, int xf, int yf, int *sxp, int *syp)
165 {
166 xform_cell_fractional(map->vp, x, y, xf, yf, sxp, syp);
167 }
168
169 void
x_xform_unit(Side * side,Map * map,Unit * unit,int * sxp,int * syp,int * swp,int * shp)170 x_xform_unit(Side *side, Map *map, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
171 {
172 xform_unit(map->vp, unit, sxp, syp, swp, shp);
173 }
174
175 void
x_xform_unit_view(Side * side,Map * map,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)176 x_xform_unit_view(Side *side, Map *map, UnitView *uview, int *sxp, int *syp, int *swp, int *shp)
177 {
178 xform_unit_view(side, map->vp, uview, sxp, syp, swp, shp);
179 }
180
181 void
x_xform_unit_self(Side * side,Map * map,Unit * unit,int * sxp,int * syp,int * swp,int * shp)182 x_xform_unit_self(Side *side, Map *map, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
183 {
184 xform_unit_self(map->vp, unit, sxp, syp, swp, shp);
185 }
186
187 void
x_xform_unit_self_view(Side * side,Map * map,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)188 x_xform_unit_self_view(Side *side, Map *map, UnitView *uview, int *sxp, int *syp, int *swp, int *shp)
189 {
190 xform_unit_self_view(side, map->vp, uview, sxp, syp, swp, shp);
191 }
192
193 void
x_xform_occupant(Side * side,Map * map,Unit * transport,Unit * unit,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)194 x_xform_occupant(Side *side, Map *map, Unit *transport, Unit *unit, int sx, int sy, int sw, int sh, int *sxp, int *syp, int *swp, int *shp)
195 {
196 xform_occupant(map->vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp);
197 }
198
199 void
x_xform_occupant_view(Side * side,Map * map,UnitView * transport,UnitView * uview,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)200 x_xform_occupant_view(Side *side, Map *map, UnitView *transport, UnitView *uview, int sx, int sy, int sw, int sh, int *sxp, int *syp, int *swp, int *shp)
201 {
202 xform_occupant_view(map->vp, transport, uview, sx, sy, sw, sh, sxp, syp, swp, shp);
203 }
204
205 int
x_nearest_cell(Side * side,Map * map,int sx,int sy,int * xp,int * yp)206 x_nearest_cell(Side *side, Map *map, int sx, int sy, int *xp, int *yp)
207 {
208 return nearest_cell(map->vp, sx, sy, xp, yp, NULL, NULL);
209 }
210
211 /* Find the closest direction of the closest boundary. */
212
213 int
x_nearest_boundary(Side * side,Map * map,int sx,int sy,int * xp,int * yp,int * dirp)214 x_nearest_boundary(Side *side, Map *map, int sx, int sy, int *xp, int *yp, int *dirp)
215 {
216 return nearest_boundary(map->vp, sx, sy, xp, yp, dirp);
217 }
218
219 Unit *
x_find_unit_or_occ(Side * side,Map * map,Unit * unit,int usx,int usy,int usw,int ush,int sx,int sy)220 x_find_unit_or_occ(Side *side, Map *map, Unit *unit, int usx, int usy, int usw, int ush, int sx, int sy)
221 {
222 int usx1, usy1, usw1, ush1;
223 Unit *occ, *rslt;
224
225 /* See if the point might be over an occupant. */
226 if (unit->occupant != NULL) {
227 for_all_occupants(unit, occ) {
228 x_xform_unit(side, map, occ, &usx1, &usy1, &usw1, &ush1);
229 rslt =
230 x_find_unit_or_occ(side, map, occ, usx1, usy1, usw1, ush1, sx, sy);
231 if (rslt) {
232 return rslt;
233 }
234 }
235 }
236 /* Otherwise see if it could be the unit itself. This has the effect of
237 "giving" the transport everything in its box that is not in an occ. */
238 x_xform_unit(side, map, unit, &usx1, &usy1, &usw1, &ush1);
239 if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1)) {
240 return unit;
241 }
242 return NULL;
243 }
244
245 Unit *
x_find_unit_at(Side * side,Map * map,int x,int y,int sx,int sy)246 x_find_unit_at(Side *side, Map *map, int x, int y, int sx, int sy)
247 {
248 int usx, usy, usw, ush;
249 Unit *unit, *rslt;
250
251 for_all_stack(x, y, unit) {
252 x_xform_unit(side, map, unit, &usx, &usy, &usw, &ush);
253 rslt = x_find_unit_or_occ(side, map, unit, usx, usy, usw, ush, sx, sy);
254 if (rslt)
255 return rslt;
256 }
257 return NULL;
258 }
259
260 int
x_nearest_unit(Side * side,Map * map,int sx,int sy,Unit ** unitp)261 x_nearest_unit(Side *side, Map *map, int sx, int sy, Unit **unitp)
262 {
263 int x, y;
264
265 if (!x_nearest_cell(side, map, sx, sy, &x, &y)) {
266 *unitp = NULL;
267 } else if (map->vp->power > 4) {
268 *unitp = x_find_unit_at(side, map, x, y, sx, sy);
269 } else {
270 *unitp = unit_at(x, y);
271 }
272 DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp));
273 return TRUE;
274 }
275
276 /* Draw all the maps that are currently up. */
277
278 void
draw_all_maps(Side * side)279 draw_all_maps(Side *side)
280 {
281 Map *map;
282
283 for_all_maps(side, map) {
284 draw_map(side, map);
285 }
286 }
287
288 /* Display a map and all of its paraphernalia. */
289
290 void
draw_map(Side * side,Map * map)291 draw_map(Side *side, Map *map)
292 {
293 /* Redraw only the panes that are managed manually. */
294 draw_map_sides(side, map);
295 draw_map_info(side, map);
296 draw_map_view(side, map);
297 flush_output(side);
298 }
299
300 /* Draw the background area for the map. */
301
302 static void
draw_area_background(Side * side,Map * map)303 draw_area_background(Side *side, Map *map)
304 {
305 int sx, sy, sx2, sy2, sw, sh, aw, ah, i;
306 int llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
307 XPoint points[7];
308 Display *dpy = side->ui->dpy;
309 GC gc = side->ui->terrgc;
310
311 XSetClipMask(dpy, gc, None);
312 XSetFillStyle(dpy, gc, FillSolid);
313 XSetForeground(dpy, gc, side->ui->fgcolor);
314 XFillRectangle(dpy, map->viewwin, gc,
315 0, 0, map->vp->pxw, map->vp->pxh);
316 if (1 /* grid color matches unseen color */) {
317 XSetForeground(dpy, gc, side->ui->gridcolor);
318 }
319 XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
320
321 if (area.xwrap) {
322 /* Area is cylinder; draw a rectangle. */
323 xform(side, map, 0, area.height - 1, &sx, &sy);
324 xform(side, map, area.width - 1, 0, &sx2, &sy2);
325 sw = sx2 - sx;
326 sh = sy2 - sy;
327 XFillRectangle(dpy, map->viewwin, gc,
328 sx, sy + map->vp->hh / 2, sw, sh);
329 XSetForeground(dpy, gc, side->ui->whitecolor);
330 XDrawRectangle(dpy, map->viewwin, gc,
331 sx, sy + map->vp->hh / 2, sw, sh);
332 } else {
333 /* Area is hexagon; draw a hexagon. */
334 aw = area.width; ah = area.height;
335 xform(side, map, 0 + ah / 2, 0, &llx, &lly);
336 points[0].x = llx; points[0].y = lly;
337 xform(side, map, aw - 1, 0, &lrx, &lry);
338 points[1].x = lrx; points[1].y = lry;
339 xform(side, map, aw - 1, ah / 2, &rx, &ry);
340 points[2].x = rx; points[2].y = ry;
341 xform(side, map, aw - 1 - ah / 2, ah - 1, &urx, &ury);
342 points[3].x = urx; points[3].y = ury;
343 xform(side, map, 0, ah - 1, &ulx, &uly);
344 points[4].x = ulx; points[4].y = uly;
345 xform(side, map, 0, ah / 2, &lx, &ly);
346 points[5].x = lx; points[5].y = ly;
347 /* Offset so polygon edges run through middles of cells. */
348 for (i = 0; i < 6; ++i) {
349 points[i].x += map->vp->hw / 2; points[i].y += map->vp->hh / 2;
350 }
351 /* End up where we started. */
352 points[6].x = points[0].x; points[6].y = points[0].y;
353 XFillPolygon(dpy, map->viewwin, gc,
354 points, 6, Convex, CoordModeOrigin);
355 XSetForeground(dpy, gc, side->ui->whitecolor);
356 XDrawLines(dpy, map->viewwin, gc, points, 7, CoordModeOrigin);
357 }
358 /* This probably drew a large area; make sure it's all out there. */
359 XFlush(dpy);
360 /* Restore the foreground color. */
361 XSetForeground(dpy, gc, side->ui->fgcolor);
362 }
363
364 /* Draw the view proper. */
365
366 void
draw_map_view(Side * side,Map * map)367 draw_map_view(Side *side, Map *map)
368 {
369 int y1, y2, y, x1, x2, adj, vx, vy, vw, vh;
370 int halfheight = area.height / 2;
371
372 draw_area_background(side, map);
373
374 if (map->vp->vcx < 0 || map->vp->vcy < 0) {
375 run_warning("doing a nasty hack");
376 map->vp->vcx = map->vp->vcy = 2;
377 }
378 /* Compute the width and height. */
379 vw = min(area.width, map->pxw / map->vp->hw + 2);
380 vh = min(area.height, map->pxh / map->vp->hch + 2);
381
382 vy = ((map->vp->totsh - map->vp->sy) / map->vp->hch) - vh;
383 /* Now adjust the bottom row so it doesn't go outside the area. */
384 if (vy < 0)
385 vy = 0;
386 if (vy > area.height - vh)
387 vy = area.height - vh;
388 /* Compute the leftmost "column". */
389 vx = map->vp->sx / map->vp->hw - vy / 2 - 1;
390 DGprintf("Set map %x viewport to be %dx%d @ %d,%d (nom center %d,%d)\n",
391 map, vw, vh, vx, vy,
392 map->vp->vcx, map->vp->vcy);
393 /* Compute top and bottom rows to be displayed. */
394 /* Include rows that will only be partly drawn. */
395 y1 = min(vy + vh, area.height - 1);
396 y2 = vy;
397 for (y = y1; y >= y2; --y) {
398 /* Adjust the right and left bounds to fill the viewport as
399 much as possible, without going too far (the drawing code
400 will clip, but clipped drawing is still expensive). */
401 /* could test by drawing viewport rect as lines... */
402 adj = (y - vy) / 2;
403 /* If the area doesn't wrap, then we might have to stop
404 drawing before we reach the edge of the viewport. */
405 /* (is this really reliable?) */
406 x1 = vx - (y - vy) / 2;
407 x2 = x1 + vw + 2 /* bleah, shouldn't be necessary */;
408 if (area.xwrap) {
409 } else {
410 /* Truncate x's to stay within the area. */
411 x1 = max(0, min(x1, area.width-1));
412 x2 = max(0, min(x2, area.width));
413 /* If this row is entirely in the NE corner, don't draw
414 anything. */
415 if (x1 + y > area.width + halfheight)
416 continue;
417 /* If this row is entirely in the SW corner, don't draw
418 anything. */
419 if (x2 + y < halfheight)
420 continue;
421 /* If the row ends up in the NE corner, shorten it. */
422 if (x2 + y > area.width + halfheight)
423 x2 = area.width + halfheight - y;
424 /* If the row starts out in the SW corner, shorten it. */
425 if (x1 + y < halfheight)
426 x1 = halfheight - y;
427 }
428 #if 0
429 /* I don't understand the wrapx below, but Massimo says it
430 fixes bugs. -sts */
431 draw_row(side, map, wrapx(x1), y, x2 - x1, FALSE);
432 #endif
433 draw_row(side, map, x1, y, x2 - x1, FALSE);
434 /* (should test for input events here, would respond better) */
435 }
436 /* Now draw the lat-long grid if asked to do so. */
437 if (map->vp->draw_meridians && map->vp->meridian_interval > 0)
438 draw_meridians(side, map);
439 draw_current(side, map);
440 }
441
442 static void
draw_meridians(Side * side,Map * map)443 draw_meridians(Side *side,Map * map)
444 {
445 Display *dpy = side->ui->dpy;
446 GC gc = side->ui->gc;
447
448 tmpside = side;
449 tmpmap = map;
450 XSetForeground(dpy, gc, side->ui->meridian_color);
451 XSetBackground(dpy, gc, side->ui->bgcolor);
452 XSetClipMask(dpy, gc, None);
453 XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
454 plot_meridians(map->vp, meridian_line_callback, meridian_text_callback);
455 }
456
457 static void
meridian_line_callback(int x1,int y1,int x1f,int y1f,int x2,int y2,int x2f,int y2f)458 meridian_line_callback(int x1, int y1, int x1f, int y1f, int x2, int y2, int x2f, int y2f)
459 {
460 int sx1, sy1, sx2, sy2;
461
462 xform_fractional(tmpside, tmpmap, x1, y1, x1f, y1f, &sx1, &sy1);
463 xform_fractional(tmpside, tmpmap, x2, y2, x2f, y2f, &sx2, &sy2);
464 XDrawLine(tmpside->ui->dpy, tmpmap->viewwin, tmpside->ui->gc,
465 sx1, sy1, sx2, sy2);
466 }
467
468 static void
meridian_text_callback(int x1,int y1,int x1f,int y1f,char * str)469 meridian_text_callback(int x1, int y1, int x1f, int y1f, char *str)
470 {
471 int sx1, sy1;
472
473 xform_fractional(tmpside, tmpmap, x1, y1, x1f, y1f, &sx1, &sy1);
474 draw_text(tmpside, tmpmap->viewwin, sx1 + 1, sy1 + 1, str,
475 tmpside->ui->fgcolor);
476 }
477
478 /* The basic map drawing routine does an entire row at a time, which yields
479 order-of-magnitude speedups. */
480
481 void
draw_row(Side * side,Map * map,int x0,int y0,int len,int clearit)482 draw_row(Side *side, Map *map, int x0, int y0, int len, int clearit)
483 {
484 int x, b, c, i;
485
486 if (map->vp->draw_terrain) {
487 draw_terrain_row(side, map, x0, y0, len, (clearit == -1));
488 /* The relative ordering of these is quite important. Note that
489 each should be prepared to run independently also, since the
490 other displays might have been turned off. */
491 if (elevations_defined()
492 && map->vp->draw_elevations
493 && map->vp->angle == 90) {
494 draw_contours(side, map, x0, y0, len);
495 }
496 if (any_aux_terrain_defined()) {
497 if (bords_to_draw(map)) {
498 for_all_terrain_types(b) {
499 if (t_is_border(b) && aux_terrain_defined(b)) {
500 for (x = x0; x < x0 + len; ++x) {
501 draw_borders(side, map, x, y0, b);
502 }
503 }
504 }
505 }
506 /* Draw the connections on top of the borders. */
507 if (conns_to_draw(map)) {
508 for_all_terrain_types(c) {
509 if (t_is_connection(c) && aux_terrain_defined(c)) {
510 for (x = x0; x < x0 + len; ++x) {
511 draw_connections(side, map, x, y0, c);
512 }
513 }
514 }
515 }
516 }
517 }
518 if (clouds_defined() && map->vp->draw_clouds) {
519 draw_clouds_row(side, map, x0, y0, len);
520 }
521 if (temperatures_defined() && map->vp->draw_temperature && map->vp->hw > 10) {
522 draw_temperature_row(side, map, x0, y0, len);
523 }
524 if (winds_defined() && map->vp->draw_winds && map->vp->hw > 10) {
525 draw_winds_row(side, map, x0, y0, len);
526 }
527 /* Skip the top and bottom rows if they are edge rows. */
528 if (!between(1, y0, area.height - 2))
529 return;
530 /* Skip the rightmost and leftmost cells if on the edge. */
531 if (!inside_area(x0 + len - 1, y0))
532 --len;
533 if (!inside_area(x0, y0)) {
534 ++x0;
535 --len;
536 }
537 if (len <= 0)
538 return;
539 if (features_defined() && map->vp->draw_feature_boundaries) {
540 for (x = x0; x < x0 + len; ++x) {
541 draw_feature_boundary(side, map, x, y0, raw_feature_at(x, y0));
542 }
543 }
544 if (features_defined() && map->vp->draw_feature_names && side->ui->legends) {
545 for (i = 0; i < numfeatures; ++i) {
546 if (side->ui->legends[i].oy == y0) {
547 draw_feature_name(side, map, i);
548 }
549 }
550 }
551 /* Draw sparse things on top of the basic row. */
552 if (people_sides_defined() && map->vp->draw_people && map->vp->hw >= 8) {
553 for (x = x0; x < x0 + len; ++x) {
554 draw_people(side, map, x, y0);
555 }
556 }
557 /* Draw units. */
558 /* (Do names first, so that unit images don't disappear behind names
559 when densely packed) */
560 if (map->vp->draw_names && map->vp->hh >= 8) {
561 for (x = x0; x < x0 + len; ++x) {
562 draw_legend(side, map, x, y0);
563 }
564 }
565 if (map->vp->draw_units && map->vp->hw > 2) {
566 for (x = x0; x < x0 + len; ++x) {
567 draw_units(side, map, x, y0);
568 }
569 }
570 }
571
572 char buffer[BUFSIZE];
573
574 static void
draw_feature_name(Side * side,Map * map,int f)575 draw_feature_name(Side *side, Map *map, int f)
576 {
577 Legend *leg = &side->ui->legends[f];
578 int y = leg->oy;
579 int x = leg->ox;
580 int d = ((leg->dx + 1) * map->vp->hw * 9) / 10;
581 int i, b, l, lnew, sx0, sy0, sxc2, syc;
582 char *pad, *name;
583 XCharStruct *bounds;
584
585 #if 0 /* uses slots that no longer exist */
586 if (leg->dist < 0 || y < map->vy || y > map->vy + map->vh)
587 return;
588 #endif
589
590 name = feature_desc(find_feature(f + 1), buffer);
591 if (empty_string(name))
592 return;
593
594 XSetFillStyle(side->ui->dpy, side->ui->gc, FillSolid);
595 XSetForeground(side->ui->dpy, side->ui->gc, side->ui->feature_color);
596 xform(side, map, x, y, &sx0, &sy0);
597 /* xform returns coordinates of the upper-left corner of the cell */
598 sxc2 = 2 * sx0 + (leg->dx + 1) * map->vp->hw; /* twice center x */
599 syc = sy0 + map->vp->hh / 2; /* center y */
600 if (sxc2 + 2 * d < 0)
601 return;
602
603 l = XTextWidth(side->ui->flegendfonts[0], name, strlen(name));
604 for (i = 1; i < 5; i++) {
605 /* Check if the font is too tall for the current magnification. */
606 bounds = &side->ui->flegendfonts[i]->max_bounds;
607 if (bounds->ascent+bounds->descent > map->vp->hch)
608 break;
609 lnew = XTextWidth(side->ui->flegendfonts[i], name, strlen(name));
610 if (lnew > d) {
611 XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i-1]);
612 bounds = &side->ui->flegendfonts[i-1]->max_bounds;
613 XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
614 (sxc2 - l) / 2, syc + (bounds->ascent - bounds->descent) / 2,
615 name, strlen(name));
616 return;
617 }
618 l = lnew;
619 }
620 /* Retain the biggest usable font. */
621 if (i)
622 --i;
623
624 XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i]);
625 bounds = &side->ui->flegendfonts[i]->max_bounds;
626 for (b = 1; b < 21; b++) {
627 pad = pad_blanks(name, b);
628 lnew = XTextWidth(side->ui->flegendfonts[i], pad, strlen(pad));
629 if (lnew > d || b == 20) {
630 if (b == 20) {
631 l = lnew;
632 b++;
633 }
634 pad = pad_blanks(name, b - 1);
635 /* map->pxw is the window width */
636 if (sxc2 - l > 2 * map->pxw)
637 return;
638 XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
639 (sxc2 - l) / 2,
640 syc + (bounds->ascent - bounds->descent) / 2,
641 pad, strlen(pad));
642 return;
643 }
644 l = lnew;
645 }
646 }
647
648 static int
cell_drawing_info(Side * side,int x,int y,int power,int seeall,Pixmap * patp,long * colorp)649 cell_drawing_info(Side *side, int x, int y, int power, int seeall, Pixmap *patp, long *colorp)
650 {
651 int t;
652 enum whattouse rslt;
653
654 *patp = None;
655 *colorp = side->ui->whitecolor;
656 if (seeall || terrain_visible(side, x, y)) {
657 t = terrain_at(x, y);
658 *patp = side->ui->terrpics[power][t];
659 *colorp = side->ui->cellcolor[t];
660 if (*colorp < 0)
661 *colorp = side->ui->blackcolor;
662 rslt = side->ui->usewhat[power][t];
663 } else {
664 rslt = dontdraw;
665 }
666 return rslt;
667 }
668
669 static void
set_terrain_gc_for_image(Side * side,Image * timg)670 set_terrain_gc_for_image(Side *side, Image *timg)
671 {
672 X11Image *ximg;
673 Display *dpy = side->ui->dpy;
674 GC gc = side->ui->terrgc;
675
676 if (timg != NULL) {
677 ximg = (X11Image *) timg->hook;
678 if (ximg != NULL) {
679 if (!side->ui->monochrome) {
680 if (side->ui->dflt_color_terr_images) {
681 if (ximg->colr != None) {
682 XSetFillStyle(dpy, gc, FillTiled);
683 XSetTile(dpy, gc, ximg->colr);
684 } else if (ximg->mono != None) {
685 XSetFillStyle(dpy, gc, FillOpaqueStippled);
686 XSetStipple(dpy, gc, ximg->mono);
687 } else {
688 XSetFillStyle(dpy, gc, FillSolid);
689 }
690 } else {
691 XSetFillStyle(dpy, gc, FillSolid);
692 }
693 } else if (ximg->mono != None) {
694 XSetFillStyle(dpy, gc, FillOpaqueStippled);
695 XSetStipple(dpy, gc, ximg->mono);
696 } else {
697 /* No pattern, no color - what to do? */
698 XSetFillStyle(dpy, gc, FillSolid);
699 }
700 } else {
701 XSetFillStyle(dpy, gc, FillSolid);
702 }
703 } else {
704 XSetFillStyle(dpy, gc, FillSolid);
705 }
706 }
707
708 /* This interfaces higher-level drawing decisions to the rendition of
709 individual pieces of display. The rendering technique chosen
710 depends on what the init code has decided is appropriate given what
711 it found during init and what magnification the display is at.
712
713 This routine is performance-critical; any improvements will
714 probably have a noticeable effect on the display. But also note
715 that X's main bottleneck is the network connection, so it's more
716 useful to eliminate roundtrips to the server than anything else. */
717
718 /* (should cache best images, never have to look up in here) */
719
720 static void
draw_terrain_row(Side * side,Map * map,int x0,int y0,int len,int force)721 draw_terrain_row(Side *side, Map *map, int x0, int y0, int len, int force)
722 {
723 int x1, x, t, sx, sy, i = 0, j;
724 int w = map->vp->hw, h = map->vp->hh, p = map->vp->power;
725 int seeall = map->seeall, dogrid = map->vp->draw_grid;
726 int over, segover;
727 long color, segcolor;
728 enum whattouse drawmethod, segdrawmethod;
729 Pixmap pat, segpat;
730 Image *timg;
731 Display *dpy = side->ui->dpy;
732 GC gc = side->ui->terrgc;
733
734 tmpdrawlighting = map->vp->draw_lighting;
735 tmpdrawcoverage = (!side->see_all && map->vp->draw_cover);
736
737 x1 = x0;
738 segdrawmethod = (enum whattouse)cell_drawing_info(side, x0, y0, p, seeall,
739 &segpat, &segcolor);
740 segover = xtconq_cell_overlay(side, x0, y0);
741 for (x = x0; x < x0 + len + 1; ++x) {
742 t = terrain_at(x, y0);
743 drawmethod = (enum whattouse)cell_drawing_info(side, x, y0, p, seeall, &pat, &color);
744 over = xtconq_cell_overlay(side, x, y0);
745 /* Decide if the run is over and we need to dump some output. */
746 if (x == x0 + len
747 || x == area.width
748 || drawmethod != segdrawmethod
749 || color != segcolor
750 || pat != segpat
751 || over != segover
752 || segdrawmethod == usepolygons
753 || force) {
754 /* Note: we might end up drawing something that matches
755 the background color, which wastes time, but apparently
756 the test "(segdrawmethod != dontdraw && segcolor !=
757 side->ui->bgcolor)" is not completely sufficient.
758 (should figure this one out sometime) */
759 t = terrain_at(x1, y0);
760 timg = best_image(side->ui->timages[t], w, h);
761 xform(side, map, x1, y0, &sx, &sy);
762 /* Last-minute fixup for when we're erasing a cell. */
763 if (force && segdrawmethod == dontdraw) {
764 segdrawmethod = side->ui->usewhat[p][t];
765 /* this must match the bg set in draw_area_background */
766 segcolor = side->ui->gridcolor /* bgcolor */;
767 timg = NULL;
768 }
769 XSetForeground(dpy, gc, segcolor);
770 XSetBackground(dpy, gc, side->ui->whitecolor);
771 switch (segdrawmethod) {
772 case dontdraw:
773 /* Don't do anything. */
774 break;
775 case useblocks:
776 XSetClipMask(dpy, gc, None);
777 set_terrain_gc_for_image(side, timg);
778 XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
779 if (segover < 0) {
780 enum grayshade shade = gray;
781
782 if (segover == -2)
783 shade = darkgray;
784 XSetFillStyle(dpy, gc, FillStippled);
785 XSetStipple(dpy, gc, side->ui->grays[shade]);
786 XSetForeground(dpy, gc, side->ui->blackcolor);
787 XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
788 XSetFillStyle(dpy, gc, FillSolid);
789 }
790 break;
791 case usepictures:
792 if (dogrid) {
793 XSetClipMask(dpy, gc, side->ui->bhexpics[p]);
794 } else {
795 XSetClipMask(dpy, gc, side->ui->hexpics[p]);
796 }
797 set_terrain_gc_for_image(side, timg);
798 for (j = 0; j < i; ++j) {
799 xform(side, map, x1 + j, y0, &sx, &sy);
800 XSetClipOrigin(dpy, gc, sx, sy);
801
802 /* In tcltk, this is actually a user preference.
803 For now, we'll hardcode images (which is the tcltk
804 default) - does that increase X server traffic or
805 have some other subtle reason for being a
806 preference? */
807 /* Upon startup (not the very first call but a few
808 in), the XCopyArea was crashing with a Bad
809 Drawable, but only if there were two players on
810 different X displays. I guess we were passing
811 the window for the wrong player (?). Anyway, until
812 that is fixed, turn off terrain images. */
813 if (/* we should display images */ 0) {
814 int quasirand = abs(((x1 + j) * (x1 + j) + y0 * y0 ) % 101);
815 X11Image *ximg;
816 Image *subimg;
817
818 subimg = timg;
819 if (timg->numsubimages > 0 && timg->subimages) {
820 subimg =
821 timg->subimages[quasirand % timg->numsubimages];
822 }
823 ximg = (X11Image*) subimg->hook;
824 if (subimg
825 && ximg
826 && ximg->colr != None
827 && !timg->istile) {
828 XCopyArea(dpy, ximg->colr, map->viewwin, gc,
829 0, 0, w, h, sx, sy);
830 } else {
831 XFillRectangle(dpy, map->viewwin, gc, sx, sy, w, h);
832 }
833 } else {
834 /* User doesn't want to see images. */
835 XFillRectangle(dpy, map->viewwin, gc, sx, sy, w, h);
836 }
837
838 if (segover < 0) {
839 enum grayshade shade = gray;
840
841 if (segover == -2)
842 shade = darkgray;
843 XSetFillStyle(dpy, gc, FillStippled);
844 XSetStipple(dpy, gc, side->ui->grays[shade]);
845 XSetForeground(dpy, gc, side->ui->blackcolor);
846 XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
847 XSetFillStyle(dpy, gc, FillSolid);
848 XSetForeground(dpy, gc, segcolor);
849 }
850 }
851 break;
852 case usepolygons:
853 /* No clipping when we're drawing big polygons. */
854 XSetClipMask(dpy, gc, None);
855 set_terrain_gc_for_image(side, timg);
856 draw_hex_polygon(side, map, map->viewwin, gc, sx, sy, p, segover, dogrid);
857 }
858 /* Reset everything for the next run. */
859 i = 0;
860 x1 = x;
861 segdrawmethod = drawmethod;
862 segpat = pat;
863 segcolor = color;
864 segover = over;
865 }
866 ++i;
867 }
868 }
869
870 static void
draw_contours(Side * side,Map * map,int x0,int y0,int len)871 draw_contours(Side *side, Map *map, int x0, int y0, int len)
872 {
873 int x, y;
874 int i, sx, sy, numlines;
875 LineSegment *lines;
876 Display *dpy = side->ui->dpy;
877 Window win = map->viewwin;
878 GC gc = side->ui->gc;
879
880 if (map->vp->contour_interval < 1)
881 return;
882 XSetForeground(dpy, gc, side->ui->contour_color);
883 XSetClipMask(dpy, gc, None);
884 XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
885 y = y0;
886 for (x = x0; x < x0 + len; ++x) {
887 if (terrain_visible(side, x, y0)) {
888 xform(side, map, x, y, &sx, &sy);
889 contour_lines_at(map->vp, x, y, sx, sy, &lines, &numlines);
890 for (i = 0; i < numlines; ++i) {
891 XDrawLine(dpy, win, gc,
892 lines[i].sx1, lines[i].sy1, lines[i].sx2, lines[i].sy2);
893 }
894 }
895 }
896 }
897
898 #if 0
899 static void
900 draw_elevations(Side *side, Map *map, int x0, int y0, int len)
901 {
902 int x, sx, sy;
903
904 if (map->vp->hw < 20)
905 return;
906 for (x = x0; x < x0 + len - 1; ++x) {
907 if (terrain_visible(side, x, y0)) {
908 sprintf(spbuf, "%d", elev_at(x, y0));
909 xform(side, map, x, y0, &sx, &sy);
910 draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
911 spbuf, side->ui->fgcolor);
912 }
913 }
914 }
915 #endif
916
917 static void
draw_clouds_row(Side * side,Map * map,int x0,int y0,int len)918 draw_clouds_row(Side *side, Map *map, int x0, int y0, int len)
919 {
920 int x, sx, sy;
921
922 for (x = x0; x < x0 + len - 1; ++x) {
923 if (map->seeall || terrain_visible(side, x, y0)) {
924 sprintf(spbuf, "%d", cloud_view(side, x, y0));
925 xform(side, map, x, y0, &sx, &sy);
926 draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
927 spbuf, side->ui->fgcolor);
928 }
929 }
930 }
931
932 static void
draw_temperature_row(Side * side,Map * map,int x0,int y0,int len)933 draw_temperature_row(Side *side, Map *map, int x0, int y0, int len)
934 {
935 int x, sx, sy;
936
937 for (x = x0; x < x0 + len - 1; ++x) {
938 if (map->seeall || terrain_visible(side, x, y0)) {
939 sprintf(spbuf, "%d", temperature_view(side, x, y0));
940 xform(side, map, x, y0, &sx, &sy);
941 draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
942 spbuf, side->ui->fgcolor);
943 }
944 }
945 }
946
947 static void
draw_winds_row(Side * side,Map * map,int x0,int y0,int len)948 draw_winds_row(Side *side, Map *map, int x0, int y0, int len)
949 {
950 int x, sx, sy;
951
952 for (x = x0; x < x0 + len - 1; ++x) {
953 if (map->seeall || terrain_visible(side, x, y0)) {
954 sprintf(spbuf, "%d", wind_view(side, x, y0));
955 xform(side, map, x, y0, &sx, &sy);
956 draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
957 spbuf, side->ui->fgcolor);
958 }
959 }
960 }
961
962 /* Draw a single unit icon as appropriate. This *also* has a bunch of
963 details to worry about: centering of icon in cell, clearing a rectangular
964 area for the icon, picking a color for the unit, using either a bitmap
965 or font char, and adding a side emblem. */
966
967 static void
draw_units(Side * side,Map * map,int x,int y)968 draw_units(Side *side, Map *map, int x, int y)
969 {
970 int sx, sy, sw, sh, u, s, osx, osy, numvis = 0;
971 int uw = map->vp->uw, uh = map->vp->uh, didone;
972 Unit *unit, *unit2;
973 UnitView *uview, *uview2;
974 Display *dpy = side->ui->dpy;
975 GC gc = side->ui->unitgc;
976
977 /* This case is for the display of all units. */
978 if (map->vp->show_all) {
979 if (uw <= 16) {
980 /* At smaller mags we choose a single unit to display. */
981 unit = unit_at(x, y);
982 if (unit == NULL)
983 return;
984 /* Prefer to display one of our own units. */
985 for_all_stack(x, y, unit2) {
986 if (unit2->transport)
987 continue;
988 if (unit2->side == side) {
989 unit = unit2;
990 break;
991 }
992 }
993 s = side_number(unit->side);
994 xform(side, map, x, y, &sx, &sy);
995 if (big_unit_images) {
996 sw = map->vp->hw;
997 sh = map->vp->hw;
998 } else {
999 sw = map->vp->uw;
1000 sh = map->vp->uh;
1001 }
1002 /* Adjust to unit part of cell. */
1003 sx += (map->vp->hw - sw) / 2;
1004 sy += (map->vp->hh - sh) / 2;
1005 if (sw >= 8
1006 /* Always draw advanced units in boxes at low magnifications. */
1007 && (
1008 #if 0
1009 (u_advanced(unit->type) && sw < 16)
1010 /* Always draw boxes if there are visible occupants. */
1011 ||
1012 #endif
1013 (unit->occupant
1014 && map->vp->draw_occupants))) {
1015 /* Draw a "grouping box", in white, but with no occs
1016 actually drawn. */
1017 XSetClipMask(dpy, gc, None);
1018 XSetForeground(dpy, gc, side->ui->whitecolor);
1019 XFillRectangle(dpy, map->viewwin, gc,
1020 sx + 1, sy + 1, uw - 2, uh - 2);
1021 /* Put a black border around it, for better contrast. */
1022 XSetForeground(dpy, gc, side->ui->blackcolor);
1023 XDrawRectangle(dpy, map->viewwin, gc,
1024 sx + 1, sy + 1, uw - 2, uh - 2);
1025 }
1026 draw_unit_image(unit->imf, side, map->viewwin, sx, sy, uw, uh,
1027 s, -1, -1, !completed(unit));
1028 /* Indicate that other units are stacked here also. */
1029 if (unit->nexthere != NULL && uw > 8) {
1030 osx = sx + uw / 2 - 6;
1031 osy = sy + uh - 2;
1032 /* (should be able to do with one fill?) */
1033 XSetClipOrigin(dpy, gc, osx, osy - 1);
1034 XSetForeground(dpy, gc, side->ui->whitecolor);
1035 XSetClipMask(dpy, gc, None);
1036 XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1037 XSetForeground(dpy, gc, side->ui->blackcolor);
1038 XSetClipMask(dpy, gc, side->ui->dots);
1039 XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1040 }
1041 if (map->vp->draw_names) {
1042 draw_unit_name(side, map, unit, sx, sy, uw, uh);
1043 }
1044 } else {
1045 /* At 32x32 and up, we can display several units in the stack. */
1046 for_all_stack(x, y, unit) {
1047 x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
1048 draw_unit_and_occs(side, map, unit, sx, sy, sw, sh);
1049 }
1050 }
1051 } else {
1052 /* Count the visible units at the top level. */
1053 numvis = 0;
1054 for_all_view_stack(side, x, y, uview) {
1055 if (uview->transport == NULL)
1056 ++numvis;
1057 }
1058 if (uw <= 16) {
1059 /* At smaller mags we choose a single unit to display. */
1060 uview = unit_view_at(side, x, y);
1061 if (uview == NULL)
1062 return;
1063 /* Prefer to display one of our own units. */
1064 for_all_view_stack(side, x, y, uview2) {
1065 if (uview2->transport)
1066 continue;
1067 if (uview2->siden == side->id) {
1068 uview = uview2;
1069 break;
1070 }
1071 }
1072 s = uview->siden;
1073 xform(side, map, x, y, &sx, &sy);
1074 if (big_unit_images) {
1075 sw = map->vp->hw;
1076 sh = map->vp->hw;
1077 } else {
1078 sw = map->vp->uw;
1079 sh = map->vp->uh;
1080 }
1081 /* Adjust to unit part of cell. */
1082 sx += (map->vp->hw - sw) / 2;
1083 sy += (map->vp->hh - sh) / 2;
1084 if (sw >= 8
1085 /* Always draw advanced units in boxes at low magnifications. */
1086 && (
1087 #if 0
1088 (u_advanced(uview->type) && sw < 16)
1089 /* Always draw boxes if there are visible occupants. */
1090 ||
1091 #endif
1092 (uview->occupant
1093 && map->vp->draw_occupants))) {
1094 /* Draw a "grouping box", in white, but with no occs
1095 actually drawn. */
1096 XSetClipMask(dpy, gc, None);
1097 XSetForeground(dpy, gc, side->ui->whitecolor);
1098 XFillRectangle(dpy, map->viewwin, gc,
1099 sx + 1, sy + 1, uw - 2, uh - 2);
1100 /* Put a black border around it, for better contrast. */
1101 XSetForeground(dpy, gc, side->ui->blackcolor);
1102 XDrawRectangle(dpy, map->viewwin, gc,
1103 sx + 1, sy + 1, uw - 2, uh - 2);
1104 }
1105 draw_unit_image(uview->imf, side, map->viewwin, sx, sy, uw, uh,
1106 s, -1, -1, !uview->complete);
1107 /* Indicate that other units are stacked here also. */
1108 if (numvis > 1 && sw > 8) {
1109 osx = sx + uw / 2 - 6;
1110 osy = sy + uh - 2;
1111 /* (should be able to do with one fill?) */
1112 XSetClipOrigin(dpy, gc, osx, osy - 1);
1113 XSetForeground(dpy, gc, side->ui->whitecolor);
1114 XSetClipMask(dpy, gc, None);
1115 XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1116 XSetForeground(dpy, gc, side->ui->blackcolor);
1117 XSetClipMask(dpy, gc, side->ui->dots);
1118 XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1119 }
1120 if (map->vp->draw_names && uview->unit) {
1121 draw_unit_name(side, map, uview->unit, sx, sy, uw, uh);
1122 }
1123 } else {
1124 /* At 32x32 and up, we can display several units in the stack. */
1125 for_all_view_stack(side, x, y, uview) {
1126 x_xform_unit_view(side, map, uview, &sx, &sy, &sw, &sh);
1127 draw_unit_view_and_occs(side, map, uview, sx, sy, sw, sh);
1128 }
1129 }
1130 }
1131 }
1132
1133 static void
draw_unit_and_occs(Side * side,Map * map,Unit * unit,int sx,int sy,int sw,int sh)1134 draw_unit_and_occs(Side *side, Map *map, Unit *unit, int sx, int sy, int sw, int sh)
1135 {
1136 int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
1137 Unit *occ;
1138 Display *dpy = side->ui->dpy;
1139 GC gc = side->ui->unitgc;
1140
1141 /* If an occupant's side is the same as its transport's, then there's
1142 really no need to draw its side emblem, since the transport's emblem
1143 will also be visible. */
1144 if (unit->transport && unit->side == unit->transport->side)
1145 s = -1;
1146 if (sw > 8
1147 && unit->occupant
1148 && map->vp->draw_occupants) {
1149 /* Draw a white box to indicate the grouping. */
1150 XSetClipMask(dpy, gc, None);
1151 XSetForeground(dpy, gc, side->ui->whitecolor);
1152 XFillRectangle(dpy, map->viewwin, gc,
1153 sx + 1, sy + 1, sw - 2, sh - 2);
1154 /* Put a black border around it, for better contrast. */
1155 XSetForeground(dpy, gc, side->ui->blackcolor);
1156 XDrawRectangle(dpy, map->viewwin, gc,
1157 sx + 1, sy + 1, sw - 2, sh - 2);
1158 /* Draw the transport's image. */
1159 x_xform_occupant(side, map, unit, unit, sx, sy, sw, sh,
1160 &sx2, &sy2, &sw2, &sh2);
1161 draw_unit_image(unit->imf, side, map->viewwin, sx2, sy2, sw2, sh2, s,
1162 -1, -1, !completed(unit));
1163 if (map->vp->draw_names) {
1164 draw_unit_name(side, map, unit, sx2, sy2, sw2, sh2);
1165 }
1166 for_all_occupants(unit, occ) {
1167 x_xform_occupant(side, map, unit, occ, sx, sy, sw, sh,
1168 &sx2, &sy2, &sw2, &sh2);
1169 draw_unit_and_occs(side, map, occ, sx2, sy2, sw2, sh2);
1170 }
1171 } else {
1172 draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh, s,
1173 -1, -1, !completed(unit));
1174 if (map->vp->draw_names) {
1175 draw_unit_name(side, map, unit, sx, sy, sw, sh);
1176 }
1177 }
1178 }
1179
1180 static void
draw_unit_view_and_occs(Side * side,Map * map,UnitView * uview,int sx,int sy,int sw,int sh)1181 draw_unit_view_and_occs(Side *side, Map *map, UnitView *uview, int sx, int sy, int sw, int sh)
1182 {
1183 int u = uview->type, s = uview->siden, sx2, sy2, sw2, sh2;
1184 UnitView *occview;
1185 Display *dpy = side->ui->dpy;
1186 GC gc = side->ui->unitgc;
1187
1188 /* If an occupant's side is the same as its transport's, then there's
1189 really no need to draw its side emblem, since the transport's emblem
1190 will also be visible. */
1191 if (uview->transport && uview->siden == uview->transport->siden)
1192 s = -1;
1193 if (sw > 8
1194 && uview->occupant
1195 && map->vp->draw_occupants) {
1196 /* Draw a white box to indicate the grouping. */
1197 XSetClipMask(dpy, gc, None);
1198 XSetForeground(dpy, gc, side->ui->whitecolor);
1199 XFillRectangle(dpy, map->viewwin, gc,
1200 sx + 1, sy + 1, sw - 2, sh - 2);
1201 /* Put a black border around it, for better contrast. */
1202 XSetForeground(dpy, gc, side->ui->blackcolor);
1203 XDrawRectangle(dpy, map->viewwin, gc,
1204 sx + 1, sy + 1, sw - 2, sh - 2);
1205 /* Draw the transport's image. */
1206 x_xform_occupant_view(side, map, uview, uview, sx, sy, sw, sh,
1207 &sx2, &sy2, &sw2, &sh2);
1208 draw_unit_image(uview->imf, side, map->viewwin, sx2, sy2, sw2, sh2, s,
1209 -1, -1, !uview->complete);
1210 if (map->vp->draw_names && uview->unit) {
1211 draw_unit_name(side, map, uview->unit, sx2, sy2, sw2, sh2);
1212 }
1213 for_all_occupant_views(uview, occview) {
1214 x_xform_occupant_view(side, map, uview, occview, sx, sy, sw, sh,
1215 &sx2, &sy2, &sw2, &sh2);
1216 draw_unit_view_and_occs(side, map, occview, sx2, sy2, sw2, sh2);
1217 }
1218 } else {
1219 draw_unit_image(uview->imf, side, map->viewwin, sx, sy, sw, sh, s,
1220 -1, -1, !uview->complete);
1221 if (map->vp->draw_names && uview->unit) {
1222 draw_unit_name(side, map, uview->unit, sx, sy, sw, sh);
1223 }
1224 }
1225 }
1226
1227 static void
draw_unit_name(Side * side,Map * map,Unit * unit,int sx,int sy,int sw,int sh)1228 draw_unit_name(Side *side, Map *map, Unit *unit, int sx, int sy, int sw, int sh)
1229 {
1230 if (!empty_string(unit->name)) {
1231 draw_legend_text(side, map->viewwin,
1232 sx + sw,
1233 sy + sh / 2 - 5,
1234 map->vp->power, unit->name, side->ui->fgcolor, TRUE);
1235 }
1236 }
1237
1238
1239 /* Indicate what kind of people are living in the given cell. */
1240
1241 static void
draw_people(Side * side,Map * map,int x,int y)1242 draw_people(Side *side, Map *map, int x, int y)
1243 {
1244 int pop, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
1245 int bordercell = FALSE;
1246 Side *side2;
1247
1248 if (!terrain_visible(side, x, y))
1249 return;
1250 pop = people_side_at(x, y);
1251 side2 = side_n(pop);
1252 if (!side2)
1253 return;
1254 xform(side, map, x, y, &sx, &sy);
1255 /* Decide which edges are borders of the country. */
1256 for_all_directions(dir) {
1257 if (point_in_dir(x, y, dir, &x1, &y1)) {
1258 pop1 = people_side_at(x1, y1);
1259 if (pop != pop1) {
1260 if (pop1 != NOBODY) {
1261 draw_country_border_line(side, map->viewwin,
1262 sx, sy, dir, map->vp->power);
1263 } else {
1264 /* should draw in gray instead? */
1265 draw_country_border_line(side, map->viewwin,
1266 sx, sy, dir, map->vp->power);
1267 }
1268 bordercell = TRUE;
1269 }
1270 }
1271 }
1272 /* Draw an emblem for the people in the cell. */
1273 if (map->vp->draw_people && bordercell) {
1274 sw = map->vp->hw; sh = map->vp->hh;
1275 ew = min(sw, max(8, sw / 4)); eh = min(sh, max(8, sh / 4));
1276 ex = sx + sw / 2 - ew / 2; ey = sy + sh / 2 - eh / 2;
1277 draw_side_emblem(side, map->viewwin, ex, ey, ew, eh,
1278 side_number(side2), 0);
1279 }
1280 }
1281
1282 /* Draw three borders of the given cell. */
1283
1284 /* (do we need another routine to repair all six borders?) */
1285
1286 static void
draw_borders(Side * side,Map * map,int x,int y,int b)1287 draw_borders(Side *side, Map *map, int x, int y, int b)
1288 {
1289 int dir, bitmask = 0, sx, sy;
1290
1291 if (!terrain_visible(side, x, y)
1292 || !any_borders_at(x, y, b))
1293 return;
1294 for_all_directions(dir) {
1295 if (border_at(x, y, dir, b) && seen_border(side, x, y, dir)) {
1296 bitmask |= 1 << dir;
1297 }
1298 }
1299 if (bitmask != 0) {
1300 xform(side, map, x, y, &sx, &sy);
1301 draw_border_line_mult(side, map, map->viewwin, sx, sy,
1302 bitmask, map->vp->power, b);
1303 }
1304 }
1305
1306 /* Draw three connections of the given cell. */
1307
1308 /* Actually this draws all six half-connections. It also only draws the
1309 connection if the underlying terrain is different. */
1310
1311 static void
draw_connections(Side * side,Map * map,int x,int y,int t)1312 draw_connections(Side *side, Map *map, int x, int y, int t)
1313 {
1314 int dir, bitmask = 0, sx, sy;
1315
1316 if (bwid[map->vp->power] == 0)
1317 return;
1318 if (!terrain_visible(side, x, y))
1319 return;
1320 xform(side, map, x, y, &sx, &sy);
1321 for_all_directions(dir) {
1322 if (connection_at(x, y, dir, t)) {
1323 bitmask |= 1 << dir;
1324 }
1325 }
1326 if (bitmask != 0) {
1327 draw_connection_line_mult(side, map->viewwin,
1328 sx, sy, bitmask,
1329 map->vp->power, t);
1330 }
1331 }
1332
1333 /* Draw any text that should be associated with this cell. */
1334
1335 /* (could precompute what the string will lap over and move or truncate str),
1336 should be deterministic for each mag, so redraw doesn't scramble */
1337
1338 /* do features, label at a cell with nothing else, and declared as the
1339 feature's "center" */
1340
1341 /* Black/white text should be consistent for each period, use mask only
1342 to fix difficulties. */
1343
1344 static void
draw_legend(Side * side,Map * map,int x,int y)1345 draw_legend(Side *side, Map *map, int x, int y)
1346 {
1347 int sx, sy, pixlen;
1348 char *featname;
1349 Feature *feature;
1350
1351 if (!inside_area(x, y))
1352 return;
1353 if (!terrain_visible(side, x, y))
1354 return;
1355 /* feature object should specify legend's position */
1356 feature = feature_at(x, y);
1357 if (feature != NULL) {
1358 if (feature->size == 1) {
1359 featname = feature_name_at(x, y);
1360 if (featname != NULL) {
1361 pixlen = strlen(featname) * 8;
1362 xform(side, map, x, y, &sx, &sy);
1363 draw_legend_text(side, map->viewwin,
1364 sx + 1 + (pixlen > map->vp->hw ? 2 :
1365 (map->vp->hw/2 - pixlen / 2)),
1366 sy - 6 + map->vp->hh / 2,
1367 map->vp->power, featname,
1368 side->ui->fgcolor, TRUE);
1369 }
1370 }
1371 }
1372 }
1373
1374 /* Cursor drawing also draws the unit in some other color if it's not the
1375 "top-level" unit in a cell. */
1376
1377 /* "color unders" here are not correct */
1378
1379 void
draw_current(Side * side,Map * map)1380 draw_current(Side *side, Map *map)
1381 {
1382 int sx, sy, sw, sh;
1383 int uw = map->vp->uw, uh = map->vp->uh;
1384 enum grayshade shade = black;
1385 Unit *unit = NULL;
1386 Display *dpy = side->ui->dpy;
1387 GC gc = side->ui->unitgc;
1388 Window win;
1389
1390 if (in_play(map->curunit)) {
1391 unit = map->curunit;
1392 /* Compute the bounding box we're going to hilite. */
1393 if (map->vp->power >= 5) { /* not ideal test */
1394 x_xform_unit_self(side, map, unit, &sx, &sy, &sw, &sh);
1395 } else {
1396 xform(side, map, unit->x, unit->y, &sx, &sy);
1397 /* Adjust to unit part of cell. */
1398 sx += (map->vp->hw - uw) / 2; sy += (map->vp->hh - uh) / 2;
1399 sw = uw; sh = uh;
1400 }
1401 /* Maybe redraw the unit that the cursor is showing. */
1402 if (unit->transport != NULL) {
1403 if (side->ui->monochrome) {
1404 draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh,
1405 -1, -1, -1, !completed(unit));
1406 } else {
1407 /* Leave any underlying image alone, but draw over it
1408 in a different color. */
1409 draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh,
1410 -1, side->ui->diffcolor, -1, !completed(unit));
1411 }
1412 }
1413 } else if (inside_area(map->curx, map->cury)) {
1414 xform(side, map, map->curx, map->cury, &sx, &sy);
1415 /* Adjust to unit part of cell. */
1416 sx += (map->vp->hw - uw) / 2; sy += (map->vp->hh - uh) / 2;
1417 sw = uw; sh = uh;
1418 } else {
1419 /* Nothing to draw, get out of here. */
1420 return;
1421 }
1422 /* Draw the cursor icon proper. */
1423 win = map->viewwin;
1424 /* Black is for units that can still act, dark gray for actors
1425 that used all acp, gray if the unit can't do anything. */
1426 shade = (unit ?
1427 ((unit->act && unit->act->initacp > 0) ?
1428 (has_acp_left(unit) ? black : darkgray) :
1429 gray) :
1430 black);
1431 /* Box at larger sizes need to come in slightly, otherwise we
1432 get "dirt" pixels in the hex grid. */
1433 if (sw > 16 && sh > 16)
1434 sh -= 1;
1435 XSetClipMask(dpy, gc, None);
1436 XSetForeground(dpy, gc, (map->curtool == movetool && map->anim_state % 2 == 1) ? side->ui->blackcolor : side->ui->whitecolor);
1437 if (sw >= 2 && sh >= 2) {
1438 XDrawRectangle(dpy, win, gc, sx, sy, sw - 1, sh - 1);
1439 } else {
1440 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1441 }
1442 if (sw >= 4 && sh >= 4) {
1443 if (shade != black) {
1444 XSetFillStyle(dpy, gc, FillOpaqueStippled);
1445 XSetStipple(dpy, gc, side->ui->grays[shade]);
1446 }
1447 XSetForeground(dpy, gc, side->ui->blackcolor);
1448 XDrawRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 3, sh - 3);
1449 /* If the box is not too small, thicken the inner rectangle. */
1450 if (sw >= 8 && sh >= 8
1451 && (unit ?
1452 !(unit->plan && (unit->plan->asleep || unit->plan->reserve))
1453 : TRUE)) {
1454 if (sw > 8 && sh > 8 && unit == NULL)
1455 XSetForeground(dpy, gc, side->ui->whitecolor);
1456 XDrawRectangle(dpy, win, gc, sx + 2, sy + 2, sw - 5, sh - 5);
1457 }
1458 }
1459 if (shade != black) {
1460 XSetFillStyle(dpy, gc, FillSolid);
1461 }
1462 }
1463
1464 /* Get rid of curunit indicator by redrawing the cell. */
1465
1466 void
erase_current(Side * side,Map * map,int x,int y,Unit * unit)1467 erase_current(Side *side, Map *map, int x, int y, Unit *unit)
1468 {
1469 /* (should use unit to decide whether to redraw only its part of
1470 the cell, instead of doing whole cell) */
1471 if (in_area(x, y)) {
1472 draw_row(side, map, x, y, 1, -1);
1473 } else if (unit != NULL && in_area(unit->x, unit->y)) {
1474 draw_row(side, map, unit->x, unit->y, 1, -1);
1475 }
1476 }
1477
1478 void
draw_blast_image(Side * side,Map * map,int sx,int sy,int sw,int sh,int blasttype)1479 draw_blast_image(Side *side, Map *map, int sx, int sy, int sw, int sh, int blasttype)
1480 {
1481 int sx2, sy2;
1482 Display *dpy = side->ui->dpy;
1483 GC gc = side->ui->gc;
1484
1485 if (sw >= 16 && sh >= 16) {
1486 sx2 = sx + (sw - 16) / 2; sy2 = sy + (sh - 16) / 2;
1487 XSetClipMask(dpy, gc, side->ui->hitpics[blasttype]);
1488 XSetClipOrigin(dpy, gc, sx2 + 1, sy2 + 1);
1489 XSetForeground(dpy, gc, side->ui->blackcolor);
1490 XFillRectangle(dpy, map->viewwin, gc, sx2 + 1, sy2 + 1, 16, 16);
1491 flush_output(side);
1492 XSetClipOrigin(dpy, gc, sx2, sy2);
1493 XSetForeground(dpy, gc, side->ui->badcolor);
1494 XFillRectangle(dpy, map->viewwin, gc, sx2, sy2, 16, 16);
1495 flush_output(side);
1496 } else {
1497 XSetForeground(dpy, gc, side->ui->badcolor);
1498 XFillRectangle(dpy, map->viewwin, gc, sx, sy, sw, sh);
1499 flush_output(side);
1500 XSetForeground(dpy, gc, side->ui->blackcolor);
1501 XFillRectangle(dpy, map->viewwin, gc, sx, sy, sw, sh);
1502 flush_output(side);
1503 XSetFunction(dpy, gc, GXcopy);
1504 }
1505 }
1506
1507 /* Flash an area of the screen. */
1508
1509 void
invert_unit_subarea(Side * side,Map * map,int x,int y)1510 invert_unit_subarea(Side *side, Map *map, int x, int y)
1511 {
1512 int sx, sy;
1513
1514 xform(side, map, x, y, &sx, &sy);
1515 sx += (map->vp->hw - map->vp->uw) / 2;
1516 sy += (map->vp->hh - map->vp->uh) / 2;
1517 XSetFunction(side->ui->dpy, side->ui->gc, GXinvert);
1518 XFillRectangle(side->ui->dpy, map->viewwin, side->ui->gc,
1519 sx, sy, map->vp->uw, map->vp->uh);
1520 flush_output(side);
1521 XSetFunction(side->ui->dpy, side->ui->gc, GXcopy);
1522 }
1523
1524 static void
draw_feature_boundary(Side * side,Map * map,int x,int y,int fid)1525 draw_feature_boundary(Side *side, Map *map, int x, int y, int fid)
1526 {
1527 int wid, p, wid2, d, color, fid0, x1, y1, sx, sy;
1528 Display *dpy = side->ui->dpy;
1529 GC gc = side->ui->bdrygc;
1530 Pixmap graylev;
1531
1532 if (!terrain_visible(side, x, y))
1533 return;
1534 p = map->vp->power;
1535 wid = bwid[p];
1536 fid0 = raw_feature_at(x, y);
1537 if (fid0 == 0)
1538 return;
1539 xform(side, map, x, y, &sx, &sy);
1540 if (wid == 0)
1541 return;
1542 wid2 = wid / 2;
1543
1544 /* for now: */
1545 if (fid0 == side->ui->curfid) {
1546 color = side->ui->badcolor;
1547 } else {
1548 color = side->ui->graycolor;
1549 }
1550 XSetForeground(dpy, gc, color);
1551 if (side->ui->monochrome) {
1552 if (fid0 == side->ui->curfid) {
1553 graylev = side->ui->grays[darkgray];
1554 } else {
1555 graylev = side->ui->grays[gray];
1556 }
1557 XSetFillStyle(dpy, gc, FillStippled);
1558 XSetStipple(dpy, gc, graylev);
1559 } else {
1560 XSetFillStyle(dpy, gc, FillSolid);
1561 }
1562 XSetClipMask(dpy, gc, None);
1563 XSetLineAttributes(dpy, gc, bwid[p], LineSolid, CapButt, JoinMiter);
1564 for_all_directions(d) {
1565 if (point_in_dir(x, y, d, &x1, &y1)) {
1566 if (raw_feature_at(x1,y1) != fid0) {
1567 XDrawLine(dpy, map->viewwin, gc,
1568 sx + qx[p][d], sy + qy[p][d],
1569 sx + qx[p][d+1], sy + qy[p][d+1]);
1570 }
1571 }
1572 }
1573 }
1574
1575 /* Do the grody work of drawing very large polygons accurately. */
1576
1577 static void
draw_hex_polygon(Side * side,Map * map,Window win,GC gc,int sx,int sy,int power,int over,int dogrid)1578 draw_hex_polygon(Side *side, Map *map, Window win, GC gc, int sx, int sy, int power, int over, int dogrid)
1579 {
1580 XPoint points[6];
1581 int hw = hws[power], hh = hhs[power], delt = (hhs[power] - hcs[power]);
1582 int ew = (dogrid ? 1 : 0);
1583 enum grayshade shade = gray;
1584 Display *dpy = side->ui->dpy;
1585
1586 points[0].x = sx + hw / 2; points[0].y = sy;
1587 points[1].x = hw / 2 - ew; points[1].y = delt /*- ew*/;
1588 points[2].x = 0; points[2].y = hh - 2 * delt - ew;
1589 points[3].x = 0 - (hw / 2 - ew); points[3].y = delt - ew;
1590 points[4].x = 0 - (hw / 2 - ew); points[4].y = 0 - (delt - ew);
1591 points[5].x = 0; points[5].y = 0 - (hh - 2 * delt - ew);
1592 XFillPolygon(dpy, win, gc, points, 6, Convex, CoordModePrevious);
1593 XFlush(dpy);
1594 if (over < 0) {
1595 if (over == -2)
1596 shade = darkgray;
1597 XSetFillStyle(dpy, gc, FillStippled);
1598 XSetStipple(dpy, gc, side->ui->grays[shade]);
1599 XSetClipMask(dpy, gc, None);
1600 XSetForeground(dpy, gc, side->ui->blackcolor);
1601 XFillPolygon(dpy, win, gc, points, 6, Convex, CoordModePrevious);
1602 XFlush(dpy);
1603 XSetFillStyle(dpy, gc, FillSolid);
1604 }
1605 }
1606
1607 /* Draw a mask of borders for the given location. */
1608
1609 static void
draw_border_line_mult(Side * side,Map * map,Window win,int sx,int sy,int bitmask,int power,int t)1610 draw_border_line_mult(Side *side, Map *map, Window win, int sx, int sy, int bitmask, int power, int t)
1611 {
1612 int wid = bwid[power], wid2, dir, color, sx1, sy1, sx2, sy2;
1613 Image *timg;
1614 Display *dpy = side->ui->dpy;
1615 GC gc = side->ui->terrgc;
1616
1617 if (wid == 0)
1618 return;
1619 wid2 = wid / 2;
1620 color = side->ui->cellcolor[t];
1621 if (color < 0)
1622 color = side->ui->blackcolor;
1623 XSetForeground(dpy, gc, color);
1624 XSetBackground(dpy, gc, side->ui->whitecolor);
1625 timg = best_image(side->ui->timages[t], wid, wid);
1626 set_terrain_gc_for_image(side, timg);
1627 XSetClipMask(dpy, gc, None);
1628 XSetLineAttributes(dpy, gc, bwid[power], LineSolid, CapButt, JoinMiter);
1629
1630 for_all_directions(dir) {
1631 if (bitmask & (1 << dir)) {
1632 sx1 = bsx[power][dir]; sy1 = bsy[power][dir];
1633 sx2 = bsx[power][dir+1]; sy2 = bsy[power][dir+1];
1634 XDrawLine(dpy, win, gc,
1635 sx + sx1 - wid2, sy + sy1 - wid2,
1636 sx + sx2 - wid2, sy + sy2 - wid2);
1637 }
1638 }
1639 }
1640
1641 /* Draw a mask of connection terrain at the given location. */
1642
1643 static void
draw_connection_line_mult(Side * side,Window win,int sx,int sy,int bitmask,int power,int t)1644 draw_connection_line_mult(Side *side, Window win, int sx, int sy, int bitmask, int power, int t)
1645 {
1646 int wid = cwid[power], wid2, cx = hws[power] / 2, cy = hhs[power] / 2;
1647 int color, dir;
1648 Image *timg;
1649 Display *dpy = side->ui->dpy;
1650 GC gc = side->ui->terrgc;
1651
1652 if (wid == 0 || lsx[power][0] == 0)
1653 return;
1654 wid2 = wid / 2;
1655 color = side->ui->cellcolor[t];
1656 if (color < 0)
1657 color = side->ui->blackcolor;
1658 XSetForeground(dpy, gc, color);
1659 XSetBackground(dpy, gc, side->ui->whitecolor);
1660 timg = best_image(side->ui->timages[t], wid, wid);
1661 set_terrain_gc_for_image(side, timg);
1662 XSetClipMask(dpy, gc, None);
1663 XSetLineAttributes(dpy, gc, wid, LineSolid, CapButt, JoinMiter);
1664
1665 for_all_directions(dir) {
1666 if (bitmask & (1 << dir)) {
1667 XDrawLine(dpy, win, gc,
1668 sx + cx - wid2, sy + cy - wid2,
1669 sx + cx + lsx[power][dir] - wid2,
1670 sy + cy + lsy[power][dir] - wid2);
1671 }
1672 }
1673 }
1674
1675 /* Map legends are stencils usually. */
1676
1677 static void
draw_legend_text(Side * side,Window win,int sx,int sy,int power,char * str,int color,int maskit)1678 draw_legend_text(Side *side, Window win, int sx, int sy, int power, char *str, int color, int maskit)
1679 {
1680 sy += (side->ui->ulegendfonts[power][0])->max_bounds.ascent;
1681 XSetFont(side->ui->dpy, side->ui->ltextgc,
1682 (side->ui->ulegendfonts[power][0])->fid);
1683 XSetForeground(side->ui->dpy, side->ui->ltextgc, color);
1684 if (maskit) {
1685 XSetBackground(side->ui->dpy, side->ui->ltextgc,
1686 (color == side->ui->bgcolor ? side->ui->fgcolor :
1687 side->ui->bgcolor));
1688 XDrawImageString(side->ui->dpy, win, side->ui->ltextgc,
1689 sx, sy, str, strlen(str));
1690 } else {
1691 XDrawString(side->ui->dpy, win, side->ui->ltextgc,
1692 sx, sy, str, strlen(str));
1693 }
1694 }
1695
1696 /* Splash a unit image (either bitmap or font char) onto some window. */
1697
1698 void
draw_unit_image(ImageFamily * imf,Side * side,Window win,int sx,int sy,int sw,int sh,int s2,int fg,int bg,int mod)1699 draw_unit_image(ImageFamily *imf, Side *side, Window win, int sx, int sy, int sw, int sh, int s2, int fg, int bg, int mod)
1700 {
1701 char *ename;
1702 int sx2, sy2, ex, ey, ew, eh, desperate = FALSE;
1703 long imagecolor = side->ui->blackcolor, maskcolor = side->ui->whitecolor;
1704 Image *uimg;
1705 X11Image *ximg;
1706 Display *dpy = side->ui->dpy;
1707 GC gc = side->ui->unitgc;
1708
1709 /* Filter out very small images. */
1710 if (sw <= 1)
1711 return;
1712 /* Just draw a small box at 4x4 and below. */
1713 if (sw <= 4) {
1714 XSetClipMask(dpy, gc, None);
1715 /* (should draw with a side color if possible) */
1716 XSetForeground(dpy, gc, side->ui->blackcolor);
1717 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1718 return;
1719 }
1720 uimg = best_image(imf, sw, sh);
1721 if (uimg != NULL) {
1722 /* Offset the image to draw in the middle of its area,
1723 whether larger or smaller than the given area. */
1724 sx2 = sx + (sw - uimg->w) / 2; sy2 = sy + (sh - uimg->h) / 2;
1725 /* Only change the size of the rectangle being drawn if it's
1726 smaller than what was passed in. */
1727 if (uimg->w < sw) {
1728 sx = sx2;
1729 sw = uimg->w;
1730 }
1731 if (uimg->h < sh) {
1732 sy = sy2;
1733 sh = uimg->h;
1734 }
1735 /* Figure out what colors to use. */
1736 imagecolor = ((fg != -1) ? fg : side->ui->blackcolor);
1737 if (side->ui->unitcolors) {
1738 #if 0
1739 (side->ui->numcolors[s2] > 2) ? side->ui->colors[s2][2] :
1740 (side->ui->numcolors[s2] > 0) ? side->ui->colors[s2][0] :
1741 #endif
1742 }
1743 maskcolor = ((bg != -1) ? bg : side->ui->whitecolor);
1744 if (side->ui->unitcolors) {
1745 #if 0
1746 (side->ui->numcolors[s2] > 1) ? side->ui->colors[s2][1] :
1747 side->ui->whitecolor;
1748 #endif
1749 }
1750 ximg = (X11Image *) uimg->hook;
1751 if (ximg != NULL) {
1752 if (!side->ui->monochrome
1753 && side->ui->dflt_color_unit_images
1754 && ximg->colr != None) {
1755 if (ximg->mask != None) {
1756 /* set the clip mask */
1757 XSetClipOrigin(dpy, gc, sx2, sy2);
1758 XSetClipMask(dpy, gc, ximg->mask);
1759 }
1760 /* Draw the color image. */
1761 XCopyArea(dpy, ximg->colr, win, gc, 0, 0, sw, sh, sx, sy);
1762 } else if (ximg->mono != None || ximg->mask != None) {
1763 /* Set the origin for any subsequent clipping. */
1764 XSetClipOrigin(dpy, gc, sx2, sy2);
1765 /* Set the color we're going to use for the mask; use
1766 the imagecolor if we'll be using the mask as the
1767 only image. */
1768 XSetForeground(dpy, gc,
1769 (ximg->mono == None ? imagecolor : maskcolor));
1770 /* Set the clip mask to be explicit mask or unit's image. */
1771 if (ximg->mask)
1772 XSetClipMask(dpy, gc, ximg->mask);
1773 else
1774 XSetClipMask(dpy, gc, ximg->mono);
1775 /* Draw the mask. */
1776 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1777 /* Draw the image proper. */
1778 if (ximg->mono != None) {
1779 XSetForeground(dpy, gc, imagecolor);
1780 XSetClipMask(dpy, gc, ximg->mono);
1781 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1782 }
1783 }
1784 } else {
1785 desperate = TRUE;
1786 }
1787 } else {
1788 desperate = TRUE;
1789 }
1790 if (desperate) {
1791 /* For some reason, we have no useful image; draw a blank box
1792 instead. */
1793 XSetClipOrigin(dpy, gc, sx, sy);
1794 XSetClipMask(dpy, gc, None);
1795 /* Draw an edge, at least for larger mags. */
1796 if (sw >= 16) {
1797 XSetForeground(dpy, gc, maskcolor);
1798 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1799 }
1800 /* Draw a filled box. */
1801 XSetForeground(dpy, gc, imagecolor);
1802 XFillRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 2, sh - 2);
1803 }
1804 if (mod != 0) {
1805 XSetFillStyle(dpy, gc, FillStippled);
1806 XSetStipple(dpy, gc, side->ui->grays[gray]);
1807 XSetClipMask(dpy, gc, None);
1808 XSetForeground(dpy, gc, side->ui->whitecolor);
1809 XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1810 XSetFillStyle(dpy, gc, FillSolid);
1811 }
1812 /* Draw a side emblem if one was requested. */
1813 if (between(0, s2, numsides)) {
1814 ename = (side_n(s2) ? side_n(s2)->emblemname : NULL);
1815 if (emblem_position(uimg, ename, NULL /* eimg */, sw, sh,
1816 &ex, &ey, &ew, &eh)) {
1817 draw_side_emblem(side, win, sx + ex, sy + ey, ew, eh, s2, 0);
1818 }
1819 }
1820 }
1821
1822 /* Draw an emblem identifying the given side. If a side does not have a
1823 distinguishing emblem, fall back on some defaults. */
1824
1825 void
draw_side_emblem(Side * side,Window win,int ex,int ey,int ew,int eh,int s2,int style)1826 draw_side_emblem(Side *side, Window win, int ex, int ey, int ew, int eh, int s2, int style)
1827 {
1828 int ex2, ey2, offset;
1829 long imagecolor, maskcolor;
1830 Image *eimg;
1831 X11Image *ximg;
1832 Display *dpy = side->ui->dpy;
1833 GC gc = side->ui->emblgc;
1834
1835 /* Draw the emblem's mask, or else an enclosing box. */
1836 eimg = best_image(side->ui->eimages[s2], ew, eh);
1837 if (eimg != NULL) {
1838 /* Offset the image to draw in the middle of its area,
1839 whether larger or smaller than the given area. */
1840 ex2 = ex + (ew - eimg->w) / 2; ey2 = ey + (eh - eimg->h) / 2;
1841 /* Only change the size of the rectangle being drawn if it's
1842 smaller than what was passed in. */
1843 if (eimg->w < ew) {
1844 ex = ex2;
1845 ew = eimg->w;
1846 }
1847 if (eimg->h < eh) {
1848 ey = ey2;
1849 eh = eimg->h;
1850 }
1851 ximg = (X11Image *) eimg->hook;
1852 /* Maybe draw with a gray shadow. */
1853 if (style == 1) {
1854 offset = (ew >= 16 ? 2 : 1);
1855 XSetForeground(dpy, gc, side->ui->graycolor);
1856 XSetClipOrigin(dpy, gc, ex + offset, ey + offset);
1857 XSetClipMask(dpy, gc, (ximg ? ximg->mask : None));
1858 XFillRectangle(dpy, win, gc, ex + offset, ey + offset, ew, eh);
1859 }
1860 /* Decide on the colors to use with the emblem. */
1861 if (side->ui->numcolors[s2] > 0) {
1862 imagecolor = side->ui->colors[s2][0];
1863 } else {
1864 imagecolor = side->ui->blackcolor;
1865 }
1866 if (side->ui->numcolors[s2] > 1) {
1867 maskcolor = side->ui->colors[s2][1];
1868 } else {
1869 maskcolor = side->ui->whitecolor;
1870 }
1871 /* Draw the mask. */
1872 XSetForeground(dpy, gc, maskcolor);
1873 XSetClipOrigin(dpy, gc, ex, ey);
1874 if (ximg != NULL && ximg->mask != None) {
1875 XSetClipMask(dpy, gc, ximg->mask);
1876 XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
1877 } else {
1878 XSetClipMask(dpy, gc, None);
1879 XFillRectangle(dpy, win, gc, ex - 1, ey, ew + 1, eh + 1);
1880 }
1881 /* Now draw the emblem proper. */
1882 /* (should fix so some color emblems can be used with mono displays) */
1883 if (!side->ui->monochrome
1884 && side->ui->dflt_color_embl_images
1885 && ximg != NULL
1886 && ximg->colr != None) {
1887 /* Draw the color image. */
1888 XCopyArea(dpy, ximg->colr, win, gc, 0, 0, ew, eh, ex, ey);
1889 } else {
1890 XSetForeground(dpy, gc, imagecolor);
1891 XSetClipMask(dpy, gc, (ximg != NULL ? ximg->mono : None));
1892 XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
1893 }
1894 }
1895 }
1896
1897 static void
draw_country_border_line(Side * side,Window win,int sx,int sy,int dir,int power)1898 draw_country_border_line(Side *side, Window win, int sx, int sy, int dir, int power)
1899 {
1900 int wid = bwid[power];
1901 GC gc = side->ui->bdrygc;
1902 Display *dpy = side->ui->dpy;
1903
1904 if (wid == 0)
1905 return;
1906 wid = max(1, wid / 2);
1907 XSetForeground(dpy, gc, side->ui->country_border_color);
1908 XSetClipMask(dpy, gc, None);
1909 XSetLineAttributes(dpy, gc, wid, LineSolid, CapButt, JoinMiter);
1910 XDrawLine(dpy, win, gc,
1911 sx + bsx[power][dir], sy + bsy[power][dir],
1912 sx + bsx[power][dir+1], sy + bsy[power][dir+1]);
1913 }
1914
1915 /* Describe the state of the given unit, in maximal detail. */
1916
1917 void
draw_map_info(Side * side,Map * map)1918 draw_map_info(Side *side, Map *map)
1919 {
1920 char infobuf[BUFSIZE];
1921 int u, s, mrow, x = map->curx, y = map->cury, len;
1922 Unit *unit, *unit2;
1923 Side *side2 = NULL; /* init to keep GCC quiet */
1924 UnitView *uview;
1925
1926 len = 40;
1927 XClearWindow(side->ui->dpy, map->infowin);
1928 unit = map->curunit;
1929 if (!in_play(unit)) {
1930 if (inside_area(x, y)) {
1931 /* (should pick the "most visible" unit at x,y) */
1932 unit2 = unit_at(x, y);
1933 if (unit2 != NULL
1934 && unit_visible(side, map->vp, unit2)) {
1935 sprintf(infobuf, "%s", unit_handle(side, unit2));
1936 draw_info_text(side, map, 0, 0, len, infobuf);
1937 location_desc(infobuf, side, unit2, unit2->type, x, y);
1938 draw_info_text(side, map, 0, 1, len, infobuf);
1939 } else {
1940 u = NONUTYPE;
1941 uview = unit_view_at(side, x, y);
1942 if (uview != NULL) {
1943 u = uview->type;
1944 side2 = side_n(uview->siden);
1945 }
1946 if (u != NONUTYPE) {
1947 sprintf(infobuf, "%s %s",
1948 side_adjective(side2), u_type_name(u));
1949 draw_info_text(side, map, 0, 0, len, infobuf);
1950 }
1951 location_desc(infobuf, side, NULL, u, x, y);
1952 draw_info_text(side, map, 0, 1, len, infobuf);
1953 }
1954 }
1955 return;
1956 }
1957 u = unit->type;
1958 /* Say which unit this is. */
1959 sprintf(infobuf, "%s", unit_handle(side, unit));
1960 draw_info_text(side, map, 0, 0, len, infobuf);
1961 location_desc(infobuf, side, unit, u, x, y);
1962 draw_info_text(side, map, 0, 1, len, infobuf);
1963 /* Very briefly list the numbers and types of the occupants. */
1964 occupants_desc(infobuf, unit);
1965 draw_info_text(side, map, 0, 2, len, infobuf);
1966 /* Display the "important" parameters. */
1967 /* (should say something about parts?) */
1968 hp_desc(infobuf, unit, TRUE);
1969 strcat(infobuf, " ");
1970 acp_desc(tmpbuf, unit, TRUE);
1971 strcat(infobuf, tmpbuf);
1972 cxp_desc(tmpbuf, unit, TRUE);
1973 strcat(infobuf, tmpbuf);
1974 morale_desc(tmpbuf, unit, TRUE);
1975 strcat(infobuf, tmpbuf);
1976 /* Crude hack, should be replaced with something better */
1977 if (supply_system_in_use())
1978 sprintf(&infobuf[strlen(infobuf)],
1979 " Supply inflow=%d%% connectedness=%d%%",
1980 supply_inflow(unit), supply_connectedness(unit));
1981 draw_info_text(side, map, 50, 0, -1, infobuf);
1982 /* List other stack members here. */
1983 others_here_desc(infobuf, unit);
1984 if (strlen(infobuf) > 0)
1985 draw_info_text(side, map, 50, 1, -1, infobuf);
1986 /* Describe the state of all the supplies. */
1987 mrow = 0;
1988 while (supply_desc(infobuf, unit, mrow)) {
1989 draw_info_text(side, map, 50, mrow + 2, -1, infobuf);
1990 ++mrow;
1991 }
1992 /* Describe the current plan and task agenda. */
1993 if (unit->plan) {
1994 int row = 4;
1995 Task *task;
1996
1997 plan_desc(infobuf, unit);
1998 draw_info_text(side, map, 0, 3, len, infobuf);
1999 for (task = unit->plan->tasks; task != NULL; task = task->next) {
2000 task_desc(infobuf, unit->side, unit, task);
2001 draw_info_text(side, map, 0, row++, len, infobuf);
2002 }
2003 }
2004 }
2005
2006 /* Display improvement can be achieved by padding out lines with blanks,
2007 then the lines need not be cleared before redrawing. */
2008
2009 static void
draw_info_text(Side * side,Map * map,int x,int y,int len,char * buf)2010 draw_info_text(Side *side, Map *map, int x, int y, int len, char *buf)
2011 {
2012 int sx, sy;
2013
2014 /* Translate a 0-100 value for x to pixels. */
2015 if (x == 50)
2016 sx = map->pxw / 2;
2017 else
2018 sx = 2;
2019 sy = y * side->ui->fh;
2020 if (len > 0 && strlen(buf) > len)
2021 buf[len-1] = '\0';
2022 if (buf[0] == '\0')
2023 return;
2024 draw_text(side, map->infowin, sx, sy, buf, side->ui->fgcolor);
2025 }
2026
2027 /* Write onto the list of sides. */
2028
2029 void
draw_map_sides(Side * side,Map * map)2030 draw_map_sides(Side *side, Map *map)
2031 {
2032 Side *side2;
2033
2034 if (map == NULL)
2035 return;
2036 for_all_sides(side2) {
2037 draw_side_info(side, map, side2);
2038 }
2039 }
2040
2041 /* Show info about a single side to some other side. */
2042
2043 void
draw_side_info(Side * side,Map * map,Side * side2)2044 draw_side_info(Side *side, Map *map, Side *side2)
2045 {
2046 char sidebuf[BUFSIZE];
2047 int sx, sy, i, fh = side->ui->fh;
2048 Display *dpy = side->ui->dpy;
2049 Window win;
2050 GC gc = side->ui->gc;
2051
2052 if (map == NULL || side2 == NULL)
2053 return;
2054 win = map->sideswin;
2055 sx = 2 + 16 + 2;
2056 sy = (side_number(side2) - 1) * map->sidespacing;
2057 draw_side_emblem(side, win, 1, sy + 2, 16, 16, side_number(side2), 1);
2058 /* Build up and write the textual description of the side. */
2059 sidebuf[0] = '\0';
2060 #ifdef DESIGNERS
2061 if (side2->designer)
2062 strcat(sidebuf, "(designer)");
2063 #endif /* DESIGNERS */
2064 strcat(sidebuf, short_side_title(side2));
2065 if (side2->willingtodraw) {
2066 strcat(sidebuf, "[draw]");
2067 }
2068 if (side2->player) {
2069 strcat(sidebuf, "(");
2070 short_player_title(sidebuf+strlen(sidebuf), side2->player, NULL);
2071 strcat(sidebuf, ")");
2072 }
2073 draw_text(side, win, sx, sy, sidebuf,
2074 (side2 == side ? side->ui->bgcolor : side->ui->fgcolor));
2075 if (keeping_score() && side2->everingame) {
2076 Scorekeeper *sk;
2077 char *scoredesc;
2078
2079 XSetForeground(dpy, gc, side->ui->bgcolor);
2080 XFillRectangle(dpy, win, gc,
2081 0, sy + 2 * fh, 500, map->sidespacing - 2 * fh);
2082 i = 0;
2083 for_all_scorekeepers(sk) {
2084 scoredesc = side_score_desc(spbuf, side2, sk);
2085 sx = (((i & 1) == 1) ? 50 : 0) + 2 + 16 + 2;
2086 draw_text(side, win, sx, sy + 2 * fh + (i / 2) * 15, scoredesc,
2087 side->ui->fgcolor);
2088 ++i;
2089 }
2090 }
2091 if (side_won(side2)) {
2092 /* (should do something more interesting than this) */
2093 XSetForeground(dpy, gc, side->ui->fgcolor);
2094 XDrawRectangle(dpy, win, gc, 0, sy, 500, map->sidespacing);
2095 XDrawRectangle(dpy, win, gc, 1, sy + 1, 500 - 2, map->sidespacing - 2);
2096 XDrawRectangle(dpy, win, gc, 2, sy + 2, 500 - 4, map->sidespacing - 4);
2097 } else if (side_lost(side2)) {
2098 /* Draw the line of ignominy through sides that have lost. */
2099 XSetForeground(dpy, gc, side->ui->fgcolor);
2100 XDrawLine(dpy, win, gc, 0, sy + fh / 2, 500, sy + fh / 2);
2101 } else if (side2->ingame) {
2102 draw_side_progress(side, map, side2);
2103 } else {
2104 /* what to do here? */
2105 }
2106 }
2107
2108 /* Display how far along the side is with moving its units. */
2109
2110 void
draw_side_progress(Side * side,Map * map,Side * side2)2111 draw_side_progress(Side *side, Map *map, Side *side2)
2112 {
2113 int sx, sy, totacp, percentleft, percentresv;
2114 int barhgt = 7;
2115 Display *dpy = side->ui->dpy;
2116 Window win;
2117 GC gc = side->ui->gc;
2118
2119 if (map == NULL || side2 == NULL)
2120 return;
2121 win = map->sideswin;
2122 sx = 2 + 16 + 2;
2123 sy = (side_number(side2) - 1) * map->sidespacing + side->ui->fh + 2;
2124 XSetClipMask(dpy, gc, None);
2125 /* (should make generic) */
2126 percentresv = percentleft = 0;
2127 if (side2->ingame && !endofgame) {
2128 totacp = side_initacp(side2);
2129 if (totacp > 0) {
2130 percentleft = (100 * side_acp(side2)) / totacp;
2131 percentleft = limitn(0, percentleft, 100);
2132 percentresv = (100 * side_acp_reserved(side2)) / totacp;
2133 percentresv = limitn(0, percentresv, percentleft);
2134 }
2135 }
2136 /* Clear the bar's area. */
2137 XSetForeground(dpy, gc, side->ui->bgcolor);
2138 XFillRectangle(dpy, win, gc, sx, sy, 100 + 2, barhgt + 2);
2139
2140 /* And show the acp we haven't used yet. */
2141 if (percentleft > 0) {
2142 XSetForeground(dpy, gc, side->ui->fgcolor);
2143 if (side2->finishedturn
2144 || !(side_has_ai(side2) || side_has_display(side2))) {
2145 XSetFillStyle(dpy, gc, FillOpaqueStippled);
2146 XSetStipple(dpy, gc, side->ui->grays[gray]);
2147 }
2148 XFillRectangle(dpy, win, gc, sx + 1, sy + 1, percentleft, barhgt);
2149 /* Display the acp in reserve using dark gray. */
2150 if (percentresv > 0) {
2151 XSetFillStyle(dpy, gc, FillOpaqueStippled);
2152 XSetStipple(dpy, gc, side->ui->grays[darkgray]);
2153 XFillRectangle(dpy, win, gc, sx + 1, sy + 1, percentresv, barhgt);
2154 XSetFillStyle(dpy, gc, FillSolid);
2155 }
2156 if (side2->finishedturn
2157 || !(side_has_ai(side2) || side_has_display(side2))) {
2158 XSetFillStyle(dpy, gc, FillSolid);
2159 }
2160 }
2161 /* Frame the progress bar if progress is meaningful. */
2162 if (side2->ingame && !endofgame) {
2163 XSetForeground(dpy, gc, side->ui->fgcolor);
2164 XDrawRectangle(dpy, win, gc, sx, sy, 100 + 1, barhgt + 1);
2165 }
2166 }
2167