1 /* Graphics support not specific to any Xconq interface.
2    Copyright (C) 1992-2000 Stanley T. Shebs.
3    Copyright (C) 2003-2005 Eric A. McDonald.
4 
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.  See the file COPYING.  */
9 
10 /* This file includes some very general graphics-related functionality
11    that interfaces can (but are not required to) use.  For instance,
12    the size and shapes of hex cells have been precalculated to provide
13    a reasonable appearance at several magnifications.  Note that some
14    of the algorithms in this file are abstracted from code that has been
15    tuned and tweaked over many years, so it is strongly recommended that
16    all new graphical interfaces use these. */
17 
18 #include "conq.h"
19 #include "kpublic.h"
20 #include "aiutil.h"
21 #include "imf.h"
22 #include "ui.h"
23 #include "ai.h"
24 #include "aiscore.h"
25 #include "aiunit.h"
26 #include "aiunit2.h"
27 #include "aitact.h"
28 #include "aioprt.h"
29 
30 using namespace Xconq;
31 using namespace Xconq::AI;
32 
33 static void compute_q(void);
34 static int module_name_compare(CONST void *a1, CONST void *a2);
35 static int special_strcmp(char *str1, char *str2);
36 static void calc_view_misc(VP *vp);
37 static int blocking_utype(int u, int block);
38 static ImageFamily *add_default_terrain_image(ImageFamily *imf, int t);
39 static ImageFamily *add_default_material_image(ImageFamily *imf, int m);
40 static ImageFamily *add_default_unit_image(ImageFamily *imf, int u);
41 static ImageFamily *get_generic_utype_images(int u, char *name);
42 static ImageFamily *add_default_emblem_image(ImageFamily *imf, int s2);
43 static UnitView *find_unit_or_occ_view(Side *side, VP *vp, UnitView *uview,
44 				       int usx, int usy, int usw, int ush,
45 				       int sx, int sy);
46 static UnitView *find_unit_view_at(Side *side, VP *vp, int x, int y,
47 				   int sx, int sy);
48 static int can_use_oversized_img(UnitView *uview, VP *vp);
49 
50 /* The two games that should be always be available. */
51 
52 char *first_game_name = INTRO_GAME;
53 
54 char *second_game_name = STANDARD_GAME;
55 
56 char *second_game_title;
57 
58 /* The side with a display open. */
59 
60 Side *dside;
61 
62 /* The following magical arrays set all the sizes at each magnification. */
63 
64 /* This is the basic cell size. */
65 
66 short mags[NUMPOWERS] = { 1, 2, 4, 8, 16, 32, 64, 128 };
67 
68 /* These give the total dimensions of each hex cell, plus the vertical
69    distance center-to-center.  This is all carefully calculated to make
70    the cells overlap perfectly at each different magnification, assuming
71    that the icons have the right shape and size. */
72 
73 /* Note that mathematically, 12x14, 24x28, etc, is a better
74    approximation to a hexagonal shape.  But in practice, the slope of
75    the diagonal sides can't be quite as steep as it should be for a
76    true regular hexagon, and so it actually looks better to shrink the
77    height by a couple pixels. */
78 
79 short hws[NUMPOWERS] = { 1, 2, 4, 12, 24, 44, 88, 174 };
80 short hhs[NUMPOWERS] = { 1, 2, 4, 13, 26, 48, 96, 192 };
81 short hcs[NUMPOWERS] = { 1, 2, 4, 10, 20, 37, 74, 148 };
82 
83 /* Dimensions of isometric cells. */
84 
85 short iws[NUMPOWERS] = { 1, 2, 4, 13, 26, 48, 96, 192};
86 short ihs[NUMPOWERS] = { 1, 1, 2, 6, 12, 22, 44, 88 };
87 short ics[NUMPOWERS] = { 1, 1, 1, 3, 6, 11, 22, 44 };
88 
89 /* x offset of corners in isometric cells. */
90 
91 short ix1[NUMPOWERS] = { 0, 0, 1, 3, 6, 11, 22, 44 };
92 short ix2[NUMPOWERS] = { 1, 2, 3, 10, 20, 37, 74, 148 };
93 
94 /* These numbers are half the length of a vertical side. */
95 
96 short halfsides[NUMPOWERS] = { 1, 1, 2, 5, 7, 13, 26, 52 };
97 
98 /* The sizes of the unit subcells.  This is available drawing area, exact
99    unit icon sizes depends on what's available. */
100 
101 short uhs[NUMPOWERS] = { 1, 1, 3, 8, 16, 32, 64, 128 };
102 short uws[NUMPOWERS] = { 1, 1, 3, 8, 16, 32, 64, 128 };
103 
104 /* Widths of borders and connections (0 implies don't draw at all). */
105 
106 /* Full border width. */
107 
108 short bwid[NUMPOWERS] = { 0, 0, 1, 2, 3, 5, 7, 9 };
109 
110 /* Half-width, for narrower inset borders. */
111 
112 short bwid2[NUMPOWERS] = { 0, 0, 1, 1, 2, 3, 4, 5 };
113 
114 /* Full connection width. */
115 
116 short cwid[NUMPOWERS] = { 0, 0, 1, 2, 3, 5, 7, 9 };
117 
118 /* Coordinates of the hex borders. */
119 /* Note that array has extra column so don't need to wrap index. */
120 
121 short bsx[NUMPOWERS][7] = {
122     { 0 },
123     { 0 },
124     {  2,   4,   4,  2,  0,  0,  2 },
125     {  6,  12,  12,  6,  0,  0,  6 },
126     { 12,  24,  24, 12,  0,  0, 12 },
127     { 22,  44,  44, 22,  0,  0, 22 },
128     { 44,  88,  88, 44,  0,  0, 44 },
129     { 87, 174, 174, 87,  0,  0, 87 }
130 };
131 short bsy[NUMPOWERS][7] = {
132     { 0 },
133     { 0 },
134     {  0,   0,   4,   4,   4,  0,  0 },
135     {  0,   3,  10,  13,  10,  3,  0 },
136     {  0,   6,  20,  26,  20,  6,  0 },
137     {  0,  11,  37,  48,  37, 11,  0 },
138     {  0,  21,  75,  96,  75, 21,  0 },
139     {  0,  44, 148, 192, 148, 44,  0 }
140 };
141 
142 short ibsx[NUMPOWERS][7] = {
143     { 0 },
144     { 0 },
145     {  1,   3,   4,  3,  1,  0,  1 },
146     {  3,  10,  13, 10,  3,  0,  3 },
147     {  6,  20,  26, 20,  6,  0,  6 },
148     { 11,  37,  48, 37, 11,  0, 11 },
149     { 22,  74,  96, 74, 22,  0, 22 },
150     { 0 }
151 };
152 short ibsy[NUMPOWERS][7] = {
153     { 0 },
154     { 0 },
155     {  0,   0,   1,   2,   2,  1,  0 },
156     {  0,   0,   3,   6,   6,  3,  0 },
157     {  0,   0,   6,  12,  12,  6,  0 },
158     {  0,   0,  11,  22,  22, 11,  0 },
159     {  0,   0,  22,  44,  44, 22,  0 },
160     { 0 }
161 };
162 
163 /* Coords of middles of each hex border (half a connection, basically). */
164 
165 short lsx[NUMPOWERS][6] = {
166     { 0 },
167     { 0 },
168     {  1,  2,   1,  -1,  -2,  -1 },
169     {  3,  6,   3,  -3,  -6,  -3 },
170     {  6, 12,   6,  -6, -12,  -6 },
171     { 11, 22,  11, -11, -22, -11 },
172     { 22, 44,  22, -22, -44, -22 },
173     { 44, 87,  44, -44, -87, -44 }
174 };
175 short lsy[NUMPOWERS][6] = {
176     { 0 },
177     { 0 },
178     {  -2,  0,   2,   2,   0,  -2 },
179     {  -5,  0,   5,   5,   0,  -5 },
180     {  -9,  0,   9,   9,   0,  -9 },
181     { -18,  0,  18,  18,   0, -18 },
182     { -36,  0,  36,  36,   0, -36 },
183     { -74,  0,  74,  74,   0, -74 }
184 };
185 
186 /* Isometric versions. */
187 
188 short ilsx[NUMPOWERS][6] = {
189     { 0 },
190     { 0 },
191     {  0,  2,  2,  0,  -2,  -2 },
192     {  0,  5,  5,  0,  -5,  -5 },
193     {  0, 10, 10,  0, -10, -10 },
194     {  0, 19, 19,  0, -19, -19 },
195     {  0, 38, 38,  0, -38, -38 },
196     { 0 }
197 };
198 short ilsy[NUMPOWERS][6] = {
199     { 0 },
200     { 0 },
201     {  -1,  -1,  1,  1,  1,  -1 },
202     {  -3,  -2,  1,  3,  1,  -2 },
203     {  -6,  -3,  3,  6,  3,  -3 },
204     { -11,  -5,  5, 11,  5,  -5 },
205     { -22, -10, 10, 22, 10, -10 },
206     { 0 }
207 };
208 
209 short qx[NUMPOWERS][7], qy[NUMPOWERS][7];
210 
211 static int q_computed;
212 
213 int extracells = 3;
214 
215 /* Table of traditional unit widths. */
216 /*! \note This table can be dynamically calculated. */
217 
218 short uw_trad[NUMPOWERS][NUMPOWERS] = {
219     {   1,  1,  1,  1,  1,  1,  1,  1 },
220     {   1,  1,  1,  1,  1,  1,  1,  1 },
221     {   3,  1,  1,  1,  1,  1,  1,  1 },
222     {   8,  4,  2,  1,  1,  1,  1,  1 },
223     {  16,  8,  4,  2,  1,  1,  1,  1 },
224     {  32, 16,  8,  4,  2,  1,  1,  1 },
225     {  64, 32, 16,  8,  4,  2,  1,  1 },
226     { 128, 64, 32, 16,  8,  4,  2,  1 }
227 };
228 
229 /* Table of unit widths with ellipsis present. */
230 /*! \note This table can be dynamically calculated. */
231 
232 short uw_elli[NUMPOWERS][NUMPOWERS] = {
233     {   1,  1,  1,  1,  1,  1,  1,  1 },
234     {   1,  1,  1,  1,  1,  1,  1,  1 },
235     {   3,  1,  1,  1,  1,  1,  1,  1 },
236     {   8,  4,  4,  4,  4,  4,  4,  4 },
237     {  16,  8,  4,  4,  4,  4,  4,  4 },
238     {  32, 16, 16, 16, 16, 16, 16, 16 },
239     {  64, 32, 16, 16, 16, 16, 16, 16 },
240     { 128, 64, 32, 16, 16, 16, 16, 16 }
241 };
242 
243 /* The traditional direction characters. */
244 
245 char *dirchars = "ulnbhy";
246 
247 /* The unit images. */
248 
249 ImageFamily **uimages = NULL;
250 
251 /* The number of images for each unit type. */
252 
253 int *numuimages = NULL;
254 
255 /* How much space to leave for a unit image, if all images should get
256    the same amount (such as for a list of unit types). Currently used
257    only by the X11 interface. */
258 
259 int min_w_for_unit_image = 16;
260 int min_h_for_unit_image = 16;
261 
262 /* The image family for regions that are not yet discovered. */
263 
264 ImageFamily *unseen_image;
265 
266 ImageFamily *generic_transition;
267 
268 ImageFamily *generic_fuzz;
269 
270 char *unitchars = NULL;
271 char *terrchars = NULL;
272 char unseen_char_1;
273 char unseen_char_2;
274 
275 ImageFamily **recorded_imfs;
276 
277 int num_recorded_imfs;
278 
279 int max_recorded_imfs;
280 
281 void (*imf_describe_hook)(Side *side, Image *img);
282 
283 /* This flag is set if unit icons wider than 32 pixels exist in the game. */
284 
285 int big_unit_images = FALSE;
286 
287 /* Machinery to find and list all the games that should be listed as
288    choices for the user. We don't actually scan library folders
289    looking for all possible game designs therein, that would pick up
290    experimental games and modules that are only for other modules'
291    use. */
292 
293 Module **possible_games = NULL;
294 
295 int numgames = 0;
296 
297 /* The comparison function for the game list puts un-formally-named
298    modules at the end, plus the default sorting puts initial-lowercased
299    names after uppercased ones. */
300 
301 static int
module_name_compare(CONST void * a1,CONST void * a2)302 module_name_compare(CONST void *a1, CONST void *a2)
303 {
304     char *title1, *title2, *basetitle1, *basetitle2;
305     Module *mp1, *mp2, *base1, *base2;
306     int rslt;
307 
308     mp1 = *((Module **) a1);
309     mp2 = *((Module **) a2);
310     title1 = (!empty_string(mp1->title) ? mp1->title : mp1->name);
311     title2 = (!empty_string(mp2->title) ? mp2->title : mp2->name);
312     basetitle1 = title1;
313     if ((base1 = find_game_module(mp1->basemodulename)) != NULL)
314       basetitle1 = (!empty_string(base1->title) ? base1->title : base1->name);
315     basetitle2 = title2;
316     if ((base2 = find_game_module(mp2->basemodulename)) != NULL)
317       basetitle2 = (!empty_string(base2->title) ? base2->title : base2->name);
318     rslt = special_strcmp(basetitle1, basetitle2);
319     if (rslt != 0)
320       return rslt;
321     if (mp1 == base2)
322       return (-1);
323     if (mp2 == base1)
324       return 1;
325     return special_strcmp(title1, title2);
326 }
327 
328 static int
special_strcmp(char * str1,char * str2)329 special_strcmp(char *str1, char *str2)
330 {
331     if (strcmp(str1, second_game_title) == 0) {
332 	if (strcmp(str2, second_game_title) == 0)
333 	  return 0;
334 	else
335 	  return (-1);
336     } else {
337 	if (strcmp(str2, second_game_title) == 0)
338 	  return 1;
339 	else
340 	  return strcmp(str1, str2);
341     }
342 }
343 
344 static int max_possible_games;
345 
346 void
collect_possible_games(void)347 collect_possible_games(void)
348 {
349     int len;
350     char *modulename = NULL, *modulecontents = NULL;
351     Obj *lis;
352     Module *module, *basemodule;
353     FILE *fp;
354     int startline = 0, endline = 0;
355 
356     if (numgames == 0 && numutypes == 0 /* !game_already_loaded() */) {
357 	len = 0;
358 	lis = lispnil;
359 	fp = open_library_file("game.dir");
360 	if (fp != NULL) {
361 	    lis = read_form(fp, &startline, &endline);
362 	    if (consp(lis)) {
363 		len = length(lis);
364 	    } else {
365 		init_warning("Game directory has bad format, no games found");
366 	    }
367 	    fclose(fp);
368 	}
369 	max_possible_games = 2 + len * 2;
370 	/* Make enough room to record all the possible games. */
371 	possible_games =
372 	  (Module **) xmalloc(max_possible_games * sizeof(Module *));
373 	/* Collect the intro and standard game modules and put at head
374 	   of list. */
375 	module = get_game_module(first_game_name);
376 	add_to_possible_games(module);
377 	module = get_game_module(second_game_name);
378 	add_to_possible_games(module);
379 	second_game_title =
380 	  (!empty_string(module->title) ? module->title : module->name);
381 	for (; lis != lispnil; lis = cdr(lis)) {
382 	    if (!(symbolp(car(lis)) || stringp(car(lis)))) {
383 		init_warning("Bad name in game dir list, ignoring");
384 		continue;
385 	    }
386 	    modulename = c_string(car(lis));
387 	    if (modulename != NULL) {
388 		module = get_game_module(modulename);
389 		module->contents = modulecontents;
390 		add_to_possible_games(module);
391 		if (module->basemodulename != NULL) {
392 		    basemodule = get_game_module(module->basemodulename);
393 		    add_to_possible_games(basemodule);
394 		}
395 	    }
396 	}
397 	if (numgames > 1) {
398 	    /* Sort all but the first game into alphabetical order
399 	       by displayed name. */
400 	    qsort(&(possible_games[1]), numgames - 1, sizeof(Module *),
401 		  module_name_compare);
402 	}
403     }
404 }
405 
406 /* Load a game's description and add it to the list of games. */
407 
408 void
add_to_possible_games(Module * module)409 add_to_possible_games(Module *module)
410 {
411     int i;
412 
413     if (module != NULL) {
414 	if (load_game_description(module)) {
415 	    /* It might be that the module description supplies the real name,
416 	       and that the module already exists. (work on this) */
417 	    /* Don't add duplicate modules. */
418 	    for (i = 0; i < numgames; ++i) {
419 		if (possible_games[i] == module)
420 		  return;
421 	    }
422 	    if (numgames < max_possible_games) {
423 		possible_games[numgames++] = module;
424 	    }
425 	}
426     }
427 }
428 
429 /* Choose and return a reasonable location for map displays to start out
430    centered on. */
431 
432 void
pick_a_focus(Side * side,int * xp,int * yp)433 pick_a_focus(Side *side, int *xp, int *yp)
434 {
435     int tmpx, tmpy, dist, closest = area.maxdim;
436     Unit *unit, *closestunit = NULL;
437 
438     /* Explicit setting overrides any guesses. */
439     if (in_area(side->init_center_x, side->init_center_y)) {
440 	*xp = side->init_center_x;  *yp = side->init_center_y;
441 	return;
442     }
443     if (side->startx < 0 || side->starty < 0)
444       calc_start_xy(side);
445     if (side->startx < 0 || side->starty < 0) {
446 	*xp = area.width / 2 - area.height / 4;  *yp = area.halfheight;
447     } else {
448 	tmpx = side->startx;  tmpy = side->starty;
449 	/* Rescan the units to find a closest one. */
450 	for_all_side_units(side, unit) {
451 	    if (in_play(unit)) {
452 		/* If already got one right there, just return. */
453 		if (unit->x == tmpx && unit->y == tmpy) {
454 		    *xp = tmpx;  *yp = tmpy;
455 		    return;
456 		} else {
457 		    dist = distance(unit->x, unit->y, tmpx, tmpy);
458 		    if (dist < closest) {
459 			closest = dist;
460 			closestunit = unit;
461 		    }
462 		}
463 	    }
464 	}
465 	if (closestunit != NULL) {
466 	    /* Return the position of the unit closest to the avg position. */
467 	    *xp = closestunit->x;  *yp = closestunit->y;
468 	} else {
469 	    *xp = tmpx;  *yp = tmpy;
470 	}
471     }
472 }
473 
474 int
num_active_displays(void)475 num_active_displays(void)
476 {
477     int n = 0;
478     Side *side;
479 
480     for_all_sides(side) {
481 	if (active_display(side))
482 	  ++n;
483     }
484     return n;
485 }
486 
487 /* Compute positions at each hex corner, slightly inset. */
488 
489 static void
compute_q(void)490 compute_q(void)
491 {
492     int d, p, w;
493 
494     for (p = 0; p < NUMPOWERS; ++p) {
495 	if (p < 2)
496 	  continue;
497 	w = bwid[p] + 1;
498 	for_all_directions(d) {
499 	    qx[p][d] = bsx[p][d] + ((hws[p] - 2 * bsx[p][d]) * w) / (2 * mags[p]);
500 	    qy[p][d] = bsy[p][d] + ((hhs[p] - 2 * bsy[p][d]) * w) / (2 * mags[p]);
501 	}
502 	qx[p][NUMDIRS] = qx[p][0];
503 	qy[p][NUMDIRS] = qy[p][0];
504     }
505 }
506 
507 /* Viewport handling. */
508 
509 VP *
new_vp(void)510 new_vp(void)
511 {
512     int t, thickest;
513     VP *vp;
514 
515     if (!q_computed) {
516 	compute_q();
517 	q_computed = TRUE;
518     }
519     vp = (VP *) xmalloc(sizeof(VP));
520     vp->draw_aux_terrain = (short *) xmalloc(numttypes * sizeof(short));
521     /* View at a 90 degree angle by default. */
522     vp->angle = 90;
523     /* No vertical exaggeration by default. */
524     vp->vertscale = 1;
525     /* For isometric views, start out looking northeast. */
526     vp->isodir = NORTHEAST;
527     vp->cellwidth = area.cellwidth;
528     /* If the cellwidth is not reasonable for drawing elevations, use
529        an approximation based on the range of elevations and terrain
530        thicknesses. */
531     if (vp->cellwidth <= 1) {
532 	thickest = 0;
533 	for_all_terrain_types(t) {
534 	    if (t_thickness(t) > thickest)
535 	      thickest = t_thickness(t);
536 	}
537 	vp->cellwidth = ((area.maxelev + thickest) - area.avgelev) / 2;
538     }
539     if (vp->cellwidth < 1)
540       vp->cellwidth = 1;
541     set_contour_interval(vp, 0);
542     vp->draw_materials = (short *) xmalloc(nummtypes * sizeof(short));
543     vp->draw_occupants = TRUE;
544     return vp;
545 }
546 
547 /* xform_cell has been separated into two functions, a core that does
548    the xform calculations and a shell that adds on the vertical
549    offset. This makes it possible to bypass the vertical offset by
550    calling xform_cell_flat directly. */
551 
552 /* Given a viewport and a cell, figure out where its UL corner will be. */
553 
554 void
xform_cell(VP * vp,int x,int y,int * sxp,int * syp)555 xform_cell(VP *vp, int x, int y, int *sxp, int *syp)
556 {
557     int		elev, offset;
558 
559     /* First call the core function. */
560     xform_cell_flat(vp, x, y, sxp, syp);
561 
562     /* Then add vertical offset if drawing at an angle. */
563     if (vp->angle != 90 || vp->isometric) {
564 	elev = (elevations_defined() ? elev_at(x, y) : 0) - area.avgelev;
565 	/* Exaggerate the vertical scale if requested. */
566 	elev *= vp->vertscale;
567 	offset = (elev * vp->hh) / vp->cellwidth;
568 	*syp -= offset;
569     }
570 }
571 
572 void
xform_cell_top(VP * vp,int x,int y,int * sxp,int * syp)573 xform_cell_top(VP *vp, int x, int y, int *sxp, int *syp)
574 {
575     int 	elev, offset;
576 
577     /* First call the core function. */
578     xform_cell_flat(vp, x, y, sxp, syp);
579 
580     /* Then add vertical offset if drawing at an angle. */
581     if (vp->angle != 90 || vp->isometric) {
582 	elev = (elevations_defined() ? elev_at(x, y) : 0) - area.avgelev;
583 	/* We see the top of the terrain in the cell, not the bottom. */
584 	elev += t_thickness(terrain_at(x, y));
585 	/* Exaggerate the vertical scale if requested. */
586 	elev *= vp->vertscale;
587 	offset = (elev * vp->hh) / vp->cellwidth;
588 	*syp -= offset;
589     }
590 }
591 
592 /* Core calculation for xform_cell without vertical offset. */
593 
594 void
xform_cell_flat(VP * vp,int x,int y,int * sxp,int * syp)595 xform_cell_flat(VP *vp, int x, int y, int *sxp, int *syp)
596 {
597     if (vp->isometric) {
598 	/* The isometric case is funky in that each view direction
599 	   seems to need a different formula.  There may be some
600 	   unifying form that works for all directions, but I don't
601 	   know what it might look like. */
602 	switch (vp->isodir) {
603 	  case NORTHEAST:
604 	    *sxp = x * vp->hch + (vp->hh - vp->hch);
605 	    *syp = (((area.height - 1 - y) * vp->hw) / 2
606 		    + ((area.width - 1 - x) * vp->hw) / 4
607 		    - (area.halfheight * vp->hw) / 4);
608 	    break;
609 	  case EAST:
610 	    *sxp = (area.height - 1 - y) * vp->hch;
611 	    *syp = (((area.halfheight + area.width) * vp->hw) / 2
612 		    - ((x * vp->hw) / 2 + ((y + area.halfheight) * vp->hw) / 4)
613 		    - vp->hw / 2);
614 	    break;
615 	  case SOUTHEAST:
616 	    *sxp = ((area.width - 1 - x + area.height - 1 - y) * vp->hch
617 		    + (vp->hh - vp->hch));
618 	    *syp = ((area.width - 1 - x + y) * vp->hw) / 4;
619 	    break;
620 	  case SOUTHWEST:
621 	    *sxp = (area.width - 1 - x) * vp->hch + (vp->hh - vp->hch);
622 	    *syp = (y * vp->hw) / 2 + (x * vp->hw) / 4;
623 	    break;
624 	  case WEST:
625 	    *sxp = y * vp->hch;
626 	    *syp = (x * vp->hw) / 2 + ((y - area.halfheight) * vp->hw) / 4;
627 	    if (area.xwrap)
628 	      *syp += (area.halfheight * vp->hw) / 4;
629 	    break;
630 	  case NORTHWEST:
631 	    *sxp = (x + y) * vp->hch + (vp->hh - vp->hch);
632 	    *syp = ((area.height - 1 - y + x) * vp->hw) / 4;
633 	    break;
634 	  default:
635 	    case_panic("view direction", vp->isodir);
636 	    break;
637 	}
638 	/* Make viewport-relative, works the same for every view direction. */
639 	*sxp -= vp->sx;  *syp -= vp->sy;
640 	return;
641     }
642     /* Traditional overhead transformation. */
643 
644     /* Compute the scaled x. */
645     *sxp = x * vp->hw + (y * vp->hw) / 2 - vp->sx;
646     /* If the world is cylindrical, then we want to come up with a
647     	transformed point that is in the viewport if possible;
648 	either add or subtract the scaled width of the world if
649 	that will help. */
650     if (area.xwrap) {
651 	    if (*sxp < - vp->hw
652 		&& between(0, *sxp + vp->totsw, vp->pxw))
653 	      *sxp += vp->totsw;
654 	    if (*sxp > vp->pxw
655 		&& between(0, *sxp - vp->totsw, vp->pxw))
656 	      *sxp -= vp->totsw;
657     }
658     /* Compute the scaled y.  Screen coords for y run downwards, so we
659        subtract. */
660     *syp = (vp->totsh - (vp->hh + y * vp->hch)) - vp->sy;
661 }
662 
663 /* Similarly, but allowing 1/1000ths of cells as input.  Note that
664    since .001 is at the "bottom" of the cell as it appears on the
665    screen, we must subtract the fraction from 1000, similarly to how
666    we do the main y value. */
667 
668 void
xform_cell_fractional(VP * vp,int x,int y,int xf,int yf,int * sxp,int * syp)669 xform_cell_fractional(VP *vp, int x, int y, int xf, int yf, int *sxp, int *syp)
670 {
671     xform_cell(vp, x, y, sxp, syp);
672     *sxp += (xf * vp->hw) / 1000;  *syp += ((1000 - yf) * vp->hch) / 1000;
673 }
674 
675 /* Same as xform_cell_fractional but bypasses vertical offset. */
676 
677 void
xform_cell_fractional_flat(VP * vp,int x,int y,int xf,int yf,int * sxp,int * syp)678 xform_cell_fractional_flat(VP *vp, int x, int y, int xf, int yf,
679 			   int *sxp, int *syp)
680 {
681     xform_cell_flat(vp, x, y, sxp, syp);
682     *sxp += (xf * vp->hw) / 1000;  *syp += ((1000 - yf) * vp->hch) / 1000;
683 }
684 
685 void
xform_unit(VP * vp,Unit * unit,int * sxp,int * syp,int * swp,int * shp)686 xform_unit(VP *vp, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
687 {
688     int num = 0, n = -1, sq, sx, sy, sx1, sy1, sw1, sh1;
689     int x = unit->x, y = unit->y;
690     Unit *unit2;
691 
692     if (!in_play(unit)) {
693         run_warning(
694             "Tried xform_unit on an out of play unit, %s!", unit_desig(unit));
695         return;
696     }
697     if (unit->transport == NULL) {
698 	xform_cell(vp, x, y, &sx, &sy);
699 	/* Adjust to the unit box within the cell. */
700 	sx += (vp->hw - vp->uw) / 2;
701 	if (vp->isometric)
702 	  sy += (vp->hw / 2 - vp->uh);
703 	else
704 	  sy += (vp->hh - vp->uh) / 2;
705 	/* Figure out our position in this cell's stack. */
706 	for_all_stack(x, y, unit2) {
707 	    /* (should only count units visible to a given side) */
708 	    if (unit == unit2)
709 	      n = num;
710 	    ++num;
711 	}
712 	if (n < 0) {
713 	    run_warning("xform_unit weirdness with %s", unit_desig(unit));
714 	    *sxp = *syp = 0;
715 	    *swp = *shp = 1;
716 	    return;
717 	}
718 	if (num <= 1) {
719 	    sq = 1;
720 	} else if (num <= 4) {
721 	    sq = 2;
722 	} else if (num <= 16) {
723 	    sq = 4;
724 	} else if (num <= 256) {
725 	    sq = 8;
726 	} else {
727 	    /* This is room for 65,536 units in a stack. */
728 	    sq = 16;
729 	}
730 	/* Tighten up positions if using big icons. */
731 	if (big_unit_images) {
732 		*swp = vp->hw / sq;
733 		*shp = vp->hw / sq;
734 		sx -= (vp->hw - vp->uw) / 2;
735 		sy -= (vp->hw - vp->uw) / (2 * sq);
736 		*sxp = sx + *swp * (n / sq) * 3 / 4;
737 		*syp = sy + *shp * (n % sq) * 2 / 3;
738 	} else {
739 		*swp = vp->uw / sq;
740 		*shp = vp->uh / sq;
741 		*sxp = sx + *swp * (n / sq);
742 		*syp = sy + *shp * (n % sq);
743 	}
744     } else {
745 	/* Go up the transport chain to get the bounds for this unit. */
746 	xform_unit(vp, unit->transport, &sx1, &sy1, &sw1, &sh1);
747 	xform_occupant(vp, unit->transport, unit, sx1, sy1, sw1, sh1,
748 		       sxp, syp, swp, shp);
749     }
750 }
751 
752 /*! Return the maximum number of unit views in a given rectangle depending on
753    the uview display flags. */
754 /*! \todo Account for height? */
755 
756 int
max_uviews_in_rect(int w,int h,int flags)757 max_uviews_in_rect(int w, int h, int flags)
758 {
759     int maxviews = 0;
760     int i = 0;
761 
762     /* If requesting to draw a transport, then just return 1. */
763     if (flags & XFORM_UVIEW_AS_TSPT)
764       return 1;
765     /* If the UI is planning on drawing an ellipsis, then we can plan on
766        fewer unit views. */
767     if (flags & XFORM_UVIEW_ELLIPSIS) {
768 	if (w >= uws[7])
769 	  maxviews = (uws[7]/16)*(uws[7]/16);	/* smallest == 16x16 */
770 	else if (between(uws[6], w, uws[7] - 1))
771 	  maxviews = (uws[6]/16)*(uws[6]/16);	/* smallest == 16x16 */
772 	else if (between(uws[5], w, uws[6] - 1))
773 	  maxviews = (uws[5]/16)*(uws[5]/16);	/* smallest == 16x16 */
774 	else if (between(uws[4], w, uws[5] - 1))
775 	  maxviews = (uws[4]/4)*(uws[4]/4);	/* smallest == 4x4 */
776 	else if (between(uws[3], w, uws[4] - 1))
777 	  maxviews = (uws[3]/4)*(uws[3]/4);	/* smallest == 4x4 */
778 	else
779 	  maxviews = 1;
780     }
781     /* Else, traditional scaling. smallest == 1x1 */
782     else {
783 	if (w >= uws[NUMPOWERS - 1])
784 	  maxviews = uws[NUMPOWERS - 1]*uws[NUMPOWERS - 1];
785 	else {
786 	    for (i = NUMPOWERS - 1; i > 0; --i) {
787 		if (between(uws[i-1], w, uws[i] - 1)) {
788 		    maxviews = uws[i-1]*uws[i-1];
789 		    break;
790 		}
791 	    }
792 	}
793     }
794     /* If we are requesting to draw an occupant, then only the lower half of
795        the box is available. */
796     if (flags & XFORM_UVIEW_AS_OCC)
797       maxviews /= 2;
798     return maxviews;
799 }
800 
801 /* Return the maximum width of an unit view in a given rectangle with given
802    display flags and a given number of views that will be present in the
803    rectangle. */
804 
805 int
uview_width_in_rect(int w,int h,int flags,int numviews,int usestdsz)806 uview_width_in_rect(int w, int h, int flags, int numviews, int usestdsz)
807 {
808 #if (0)
809     int maxwidth = 0;
810     short *uwtab = NULL;
811     int ipower = 0;
812     int wdiv = 0;
813     int i = 0;
814 #endif
815     int vpower = 0, vpower2 = 0, vbracket = 1;
816 
817     /* The number of uviews must be artifically doubled for occupants. */
818     if (flags & XFORM_UVIEW_AS_OCC)
819       numviews *= 2;
820     /* Just return the width if only 1 uview. */
821     if (1 == numviews)
822       return w;
823     /* Determine equivalent view power given the number of units. */
824     vbracket = 1;
825     if (1 >= numviews)
826       vpower = 0;
827     else {
828 	for (vpower = 1; vpower < NUMPOWERS; ++vpower) {
829 	    if (between(vbracket + 1, numviews, vbracket * 4))
830 	      break;
831 	    else
832 	      vbracket *= 4;
833 	}
834 	if (vpower >= NUMPOWERS)
835 	  vpower = NUMPOWERS - 1;
836     }
837     /* Determine equivalent view power given the width of the rectangle. */
838     if (w >= uws[NUMPOWERS-1])
839       vpower2 = NUMPOWERS - 1;
840     else {
841 	for (vpower2 = NUMPOWERS - 1; vpower2 > 0; --vpower2) {
842 	    if (between(uws[vpower2-1], w, uws[vpower2] - 1)) {
843 		--vpower2;
844 		break;
845 	    }
846 	}
847     }
848     /* Lookup the unit width in the appropriate table. */
849     if (flags & XFORM_UVIEW_ELLIPSIS)
850       return uw_elli[vpower2][vpower];
851     else
852       return uw_trad[vpower2][vpower];
853     return w;
854 #if (0)
855     /* Choose correct lookup table. */
856     if (usestdsz)
857       uwtab = uws;
858     else
859       uwtab = hws;
860     /* The number of uviews must be artifically doubled for occupants. */
861     if (flags & XFORM_UVIEW_AS_OCC)
862       numviews *= 2;
863     /* Round numviews to nearest even power of 2. */
864     while (numviews > 1) {
865 	numviews /= 2;
866 	++ipower;
867     }
868     if (ipower % 2)
869       ++ipower;
870     /* Determine width divisor. */
871     ipower /= 2;
872     wdiv = 1;
873     for (; ipower > 0; --ipower)
874       wdiv *= 2;
875     /* Divide the width. */
876     if (w >= uwtab[NUMPOWERS - 1])
877       maxwidth = uwtab[NUMPOWERS - 1] / wdiv;
878     else {
879 	for (i = NUMPOWERS - 1; i > 0; --i) {
880 	    if (between(uwtab[i-1], w, uwtab[i] - 1)) {
881 		maxwidth = uwtab[i-1] / wdiv;
882 		break;
883 	    }
884 	}
885     }
886     /* Modify, if ellipsis is turned on. */
887     if (flags & XFORM_UVIEW_ELLIPSIS) {
888 	if (between(uwtab[3], w, uwtab[5] - 1) && (maxwidth < uwtab[2]))
889 	  maxwidth = uwtab[2];
890 	if ((w >= uwtab[5]) && (maxwidth < uwtab[4]))
891 	  maxwidth = uwtab[4];
892     }
893     /* Return the maxwidth. */
894     return max(1, maxwidth);
895 #endif
896 }
897 
898 /* Test if a given uview can use oversized image at current magnification. */
899 /* Currently assumes that the width rect is the same size as the viewport's
900    default unit width rectangle. */
901 
902 int
can_use_oversized_img(UnitView * uview,VP * vp)903 can_use_oversized_img(UnitView *uview, VP *vp)
904 {
905     Image *img = NULL;
906 
907     assert_error(vp, "Attempted to access a NULL viewport");
908     assert_error(uview, "Attempted to access a NULL unit view");
909     if (!uview->imf)
910       return FALSE;
911     for_all_images(uview->imf, img) {
912 	if (img->w == vp->hw)
913 	  return TRUE;
914     }
915     return FALSE;
916 }
917 
918 /* Transform the map coords of a given unit view into pixel coords relative
919    to its current cell. */
920 
921 int
xform_unit_view(Side * side,VP * vp,UnitView * uview,int * sxp,int * syp,int * swp,int * shp,int flags,int * piles,int sxt,int syt,int swt,int sht)922 xform_unit_view(Side *side, VP *vp, UnitView *uview,
923 		int *sxp, int *syp, int *swp, int *shp,
924 		int flags, int *piles, int sxt, int syt, int swt, int sht)
925 {
926     int x = -1, y = -1;
927     int sx = -1, sy = -1, sw = -1, sh = -1;
928     int sx1 = -1, sy1 = -1, sw1 = -1, sh1 = -1;
929     UnitView *uview2 = NULL;
930     Unit *unit2 = NULL;
931     int numviews = 0, uvpos = 0, inpile = FALSE, maxviews = -1;
932     int numrows = 0;
933     int usestdsz = FALSE;
934 
935     /* Sanity checks. */
936     assert_error(vp, "Attempted to access a NULL view port");
937     assert_error(side || (!side && vp->show_all),
938 "Attempted to access the views of a NULL side while non-designer");
939     assert_error(uview, "Attempted to access a NULL unit view");
940     /* More sanity checks. */
941     assert_warning(!piles || (piles && (flags & XFORM_UVIEW_PILES)),
942 		   "Attempted to pile unit views into NULL piles");
943     if (!piles && (flags & XFORM_UVIEW_PILES))
944       flags &= ~XFORM_UVIEW_PILES;
945     assert_warning(!(flags & XFORM_UVIEW_AS_OCC)
946 		   || ((flags & XFORM_UVIEW_AS_OCC) && uview->transport),
947 "Attempted to view an unit as an occupant of non-existent transport");
948     if (!(uview->transport) && (flags & XFORM_UVIEW_AS_OCC))
949       flags &= ~XFORM_UVIEW_AS_OCC;
950     /* If called with "don't draw" set, then just get out. */
951     if (flags & XFORM_UVIEW_DONT_DRAW)
952       return flags;
953     /* Don't attempt to draw occupants in isometric mode. */
954     if (vp->isometric && (flags & (XFORM_UVIEW_AS_OCC | XFORM_UVIEW_AS_TSPT)))
955       return (flags | XFORM_UVIEW_DONT_DRAW);
956     /* Alias some values for brevity. */
957     x = uview->x;
958     y = uview->y;
959     /* Reset the piles. */
960     if (piles)
961       memset(piles, 0, numsides * numutypes * sizeof(int));
962     *sxp = *syp = *swp = *shp = 100;
963     /* Check if the uview is in a seen transport. If so, then walk up the
964        transport chain, and then transform as occupants. */
965     if (uview->transport
966 	&& !(flags & (XFORM_UVIEW_AS_OCC | XFORM_UVIEW_AS_TSPT))) {
967 	/* First find transport's rect. */
968 	xform_unit_view(side, vp, uview->transport, &sx1, &sy1, &sw1, &sh1,
969 			flags & ~XFORM_UVIEW_AS_TSPT, piles);
970 	/* Now find occ in context of transport. */
971 	flags = xform_unit_view(side, vp, uview, sxp, syp, swp, shp,
972 				flags | XFORM_UVIEW_AS_OCC, piles,
973 				sx1, sy1, sw1, sh1);
974 	return flags;
975     }
976     /* If unit directly in cell, then adjust unit box within cell. */
977     if (!(flags & (XFORM_UVIEW_AS_TSPT | XFORM_UVIEW_AS_OCC))) {
978 	/* Get screen coordinates of cell. */
979 	xform_cell(vp, x, y, &sx, &sy);
980     }
981     /* Else, adjust the unit box within the transport. */
982     else {
983 	sx = sxt;  sy = syt;
984     }
985     /* Prepare to iterate (or skip iteration) through unit views. */
986     if (flags & XFORM_UVIEW_AS_TSPT) {
987 	uview2 = NULL;
988 	numviews = 1;
989 	uvpos = 0;
990     }
991     else if (flags & XFORM_UVIEW_AS_OCC)
992       uview2 = uview->transport->occupant;
993     else {
994 	if (side)
995 	  uview2 = unit_view_at(side, x, y);
996 	else {
997 	    unit2 = unit_at(x, y);
998 	    assert_error(unit2,
999 			 "Could not find unit in supposedly occupied cell");
1000 	    uview2 = find_unit_view(side, unit2);
1001 	}
1002     }
1003     /* Count up the current uview's position in the view stack. */
1004     for (; uview2;
1005 	 uview2 = ((flags & (XFORM_UVIEW_AS_OCC | XFORM_UVIEW_AS_TSPT))
1006 		   ? uview2->nexthere
1007 		   : unit_view_next(side, x, y, uview2))) {
1008 	/* If uview belongs to given side, then get an unit rep of it. */
1009 	if (!side || (uview->siden == side->id))
1010 	  unit2 = view_unit(uview2);
1011 	else
1012 	  unit2 = NULL;
1013 	/* Assume that it is not in pile to start with. */
1014 	inpile = FALSE;
1015 	/* If we are using piles, then increment the appropriate pile. */
1016 	if (flags & XFORM_UVIEW_PILES) {
1017 	    /* If units which have less than full ACP are to be segregated... */
1018 	    if ((flags & XFORM_UVIEW_DONT_PILE_ACP) && unit2
1019 		&& (unit2->act
1020 		    || (unit2->act->acp != unit2->act->initacp)))
1021 	      ++numviews;
1022 	    /* If units which have been damaged are to be segregated... */
1023 	    else if ((flags & XFORM_UVIEW_DONT_PILE_HP) && unit2
1024 		     && (unit2->hp != u_hp_max(unit2->type)))
1025 	      ++numviews;
1026 	    /* If units which are incomplete are to be segregated... */
1027 	    else if ((flags & XFORM_UVIEW_DONT_PILE_CP)
1028 		     && !uview2->complete)
1029 	      ++numviews;
1030 	    /* If units which have >= 1 occs are to be segregated... */
1031 	    else if ((flags & XFORM_UVIEW_DONT_PILE_OCC)
1032 		     && uview2->occupant)
1033 	      ++numviews;
1034 	    /* Else, this unit view is not distinct... */
1035 	    else {
1036 		/* If pile is empty, increment number of distinct views
1037 		   as well. */
1038 		if (!piles[uview->siden * numutypes + uview->type])
1039 		  ++numviews;
1040 		/* Else, set the marker indicating that uview is being
1041 		   added to an existing pile. */
1042 		else
1043 		  inpile = TRUE;
1044 		/* Increment the pile. */
1045 		++piles[uview2->siden * numutypes + uview2->type];
1046 	    }
1047 	}
1048 	/* Else no piling, so always increment. */
1049 	else
1050 	  ++numviews;
1051 	/* If our view is found, then record its position. */
1052 	if (uview == uview2)
1053 	  uvpos = numviews - 1;
1054     }
1055     /* Unset any flags that should not be returned to the UI. */
1056     if (inpile)
1057       flags &= ~(XFORM_UVIEW_DONT_PILE_ACP | XFORM_UVIEW_DONT_PILE_HP |
1058 		 XFORM_UVIEW_DONT_PILE_CP | XFORM_UVIEW_DONT_PILE_OCC);
1059     if (!inpile)
1060       flags &= ~XFORM_UVIEW_PILES;
1061     /* Get maximum number of views given rectangle and flags. */
1062     if (!(flags & (XFORM_UVIEW_AS_TSPT | XFORM_UVIEW_AS_OCC)))
1063       maxviews = max_uviews_in_rect(vp->hw, vp->hw, flags);
1064     else
1065       maxviews = max_uviews_in_rect(swt, sht, flags);
1066     /* Unset ellipsis flag if no dots needed. */
1067     if ((maxviews <= numviews) || (uvpos < maxviews))
1068       flags &= ~XFORM_UVIEW_ELLIPSIS;
1069     /* If unit is not to be drawn, then indicate this to the UI. */
1070     if (!maxviews)
1071       return (flags | XFORM_UVIEW_DONT_DRAW);
1072     /* Truncate numviews, if excessive. */
1073     numviews = min(numviews, maxviews);
1074     /* Truncate position, if excessive. */
1075     uvpos = min(uvpos, maxviews - 1);
1076     /* Check if unit sizes that go according to uws rather hws. */
1077     if ((uview->occupant && !(flags & XFORM_UVIEW_AS_TSPT))
1078 	|| ((numviews == 1) && !can_use_oversized_img(uview, vp))
1079 	|| (numviews > 1))
1080       usestdsz = TRUE;
1081     /* Get unit view display width (and height). */
1082     if (flags & XFORM_UVIEW_AS_TSPT)
1083       sw = swt / 2;
1084     else if (flags & XFORM_UVIEW_AS_OCC)
1085       sw = uview_width_in_rect(swt, sht, flags, numviews, usestdsz);
1086     else {
1087 	if (usestdsz)
1088 	  sw = uview_width_in_rect(vp->uw, vp->uw, flags, numviews, usestdsz);
1089 	else
1090 	  sw = vp->hw;
1091     }
1092     sh = sw;
1093     /* Calculate number of rows per col. */
1094     if (flags & XFORM_UVIEW_AS_OCC)
1095       numrows = (sht / 2) / sh;
1096     else if (flags & XFORM_UVIEW_AS_TSPT)
1097       numrows = 1;
1098     else {
1099 	if (usestdsz)
1100 	  numrows = vp->uw / sh;
1101 	else
1102 	  numrows = 1;
1103     }
1104     numrows = max(1, numrows);
1105     /* Calculate vertical offset. */
1106     sy += (uvpos % numrows) * sh;
1107     if (flags & XFORM_UVIEW_AS_OCC)
1108       sy += (sht / 2);
1109     if (!(flags & (XFORM_UVIEW_AS_OCC | XFORM_UVIEW_AS_TSPT))
1110 	&& usestdsz) {
1111 	if (vp->isometric)
1112 	  sy += (vp->hw / 2 - vp->uw);
1113 	else
1114 	  sy += (vp->hh - vp->uw) / 2;
1115     }
1116     /* Calculate horizontal offset. */
1117     sx += (uvpos / numrows) * sw;
1118     if (!(flags & (XFORM_UVIEW_AS_OCC | XFORM_UVIEW_AS_TSPT))
1119 	&& usestdsz)
1120       sx += (vp->hw - vp->uw) / 2;
1121     /* Set results. */
1122     *sxp = sx;  *syp = sy;
1123     *swp = sw;  *shp = sh;
1124     /* Return all used flags. */
1125     return flags;
1126 }
1127 
1128 #if (0)
1129 void
xform_unit_view(Side * side,VP * vp,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)1130 xform_unit_view(Side *side, VP *vp, UnitView *uview,
1131 		int *sxp, int *syp, int *swp, int *shp)
1132 {
1133     int num = 0, n = -1, sq, sx, sy, x, y, sx1, sy1, sw1, sh1;
1134     UnitView *uview2 = NULL;
1135 
1136     x = uview->x;
1137     y = uview->y;
1138     /* If unit is in transport, then go up transport chain first,
1139        and then xform as an occupant. */
1140     if (uview->transport) {
1141 	/* Go up the transport chain to get the bounds for this unit. */
1142 	xform_unit_view(side, vp, uview->transport, &sx1, &sy1, &sw1, &sh1);
1143 	xform_occupant_view(vp, uview->transport, uview, sx1, sy1, sw1, sh1,
1144 			    sxp, syp, swp, shp);
1145     }
1146     /* If unit is directly in cell... */
1147     else {
1148 	/* Get screen coordinates of cell. */
1149 	xform_cell(vp, x, y, &sx, &sy);
1150 	/* Adjust to the unit box within the cell. */
1151 	sx += (vp->hw - vp->uw) / 2;
1152 	if (vp->isometric)
1153 	  sy += (vp->hw / 2 - vp->uh);
1154 	else
1155 	  sy += (vp->hh - vp->uh) / 2;
1156 	/* Figure out our position in this cell's stack. */
1157 	for_all_view_stack(side, x, y, uview2) {
1158 	    if (uview2->transport == NULL) {
1159 		if (uview2 == uview)
1160 		  n = num;
1161 		++num;
1162 	    }
1163 	}
1164 	/* If uview not found, then something strange is going on. */
1165 	if (n < 0) {
1166 	    run_warning("xform_unit_view weirdness with");
1167 	    *sxp = *syp = 0;
1168 	    *swp = *shp = 1;
1169 	    return;
1170 	}
1171 	/* How to dice up the uview square in the cell. */
1172 	/*! \todo Replace this stuff with better limits for the
1173 		  given view size. */
1174 	if (num <= 1) {
1175 	    sq = 1;
1176 	} else if (num <= 4) {
1177 	    sq = 2;
1178 	} else if (num <= 16) {
1179 	    sq = 4;
1180 	} else if (num <= 256) {
1181 	    sq = 8;
1182 	} else {
1183 	    /* This is room for 65,536 unit views in a stack. */
1184 	    sq = 16;
1185 	}
1186 	/* Make room for a single, unoccupied oversized icon. */
1187 	if ((num == 1) && can_use_oversized_img(uview, vp)
1188 	    && (NULL == uview->occupant)) {
1189 	    *swp = vp->hw / sq;
1190 	    *shp = vp->hw / sq;
1191 	    sx -= (vp->hw - vp->uw) / 2;
1192 	    sy -= (vp->hw - vp->uw) / (2 * sq);
1193 	    *sxp = sx + *swp * (n / sq) * 3 / 4;
1194 	    *syp = sy + *shp * (n % sq) * 2 / 3;
1195 	/* Else, slice the cell normally. */
1196 	} else {
1197 	    *swp = vp->uw / sq;
1198 	    *shp = vp->uh / sq;
1199 	    *sxp = sx + *swp * (n / sq);
1200 	    *syp = sy + *shp * (n % sq);
1201 	}
1202     }
1203 }
1204 #endif
1205 
1206 void
xform_unit_self(VP * vp,Unit * unit,int * sxp,int * syp,int * swp,int * shp)1207 xform_unit_self(VP *vp, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
1208 {
1209     int sx1, sy1, sw1, sh1;
1210 
1211     if (unit->transport == NULL) {
1212 	if (unit->occupant == NULL) {
1213 	    xform_unit(vp, unit, sxp, syp, swp, shp);
1214 	} else {
1215 	    xform_unit(vp, unit, &sx1, &sy1, &sw1, &sh1);
1216 	    xform_occupant(vp, unit, unit, sx1, sy1, sw1, sh1, sxp, syp,
1217 			   swp, shp);
1218 	}
1219     } else {
1220 	xform_unit(vp, unit->transport, &sx1, &sy1, &sw1, &sh1);
1221 	xform_occupant(vp, unit->transport, unit, sx1, sy1, sw1, sh1,
1222 		       sxp, syp, swp, shp);
1223     }
1224 }
1225 
1226 /* Given a unit, compute its actual bounding box (as opposed to the
1227    box that includes both its and its occupants). */
1228 
1229 int
xform_unit_self_view(Side * side,VP * vp,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)1230 xform_unit_self_view(Side *side, VP *vp, UnitView *uview,
1231 		     int *sxp, int *syp, int *swp, int *shp)
1232 {
1233     int sx1, sy1, sw1, sh1;
1234 
1235     if (uview->transport == NULL) {
1236 	if (uview->occupant == NULL) {
1237 	    xform_unit_view(side, vp, uview, sxp, syp, swp, shp);
1238 	} else {
1239 	    xform_unit_view(side, vp, uview, &sx1, &sy1, &sw1, &sh1);
1240 	    xform_occupant_view(vp, uview, uview, sx1, sy1, sw1, sh1,
1241 				sxp, syp, swp, shp);
1242 	}
1243     } else {
1244 	xform_unit_view(side, vp, uview->transport, &sx1, &sy1, &sw1, &sh1);
1245 	xform_occupant_view(vp, uview->transport, uview, sx1, sy1, sw1, sh1,
1246 			    sxp, syp, swp, shp);
1247     }
1248     return TRUE;
1249 }
1250 
1251 void
xform_occupant(VP * vp,Unit * transport,Unit * unit,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)1252 xform_occupant(VP *vp, Unit *transport, Unit *unit,
1253 	       int sx, int sy, int sw, int sh,
1254 	       int *sxp, int *syp, int *swp, int *shp)
1255 {
1256     int num = 0, n = -1, nmx, nmy;
1257     Unit *unit2;
1258 
1259     /* Skip transformation if we are not drawing occupants. */
1260     if (!vp->draw_occupants) {
1261     	*sxp = sx;
1262     	*syp = sy;
1263     	*swp = sw;
1264     	*shp = sh;
1265 	return;
1266     }
1267     /* Figure out the position of this unit amongst all the occupants. */
1268     for_all_occupants(transport, unit2) {
1269 	if (unit2 == unit)
1270 	  n = num;
1271 	++num;
1272     }
1273     if (unit == transport) {
1274 	if (num > 0) {
1275 	    /* Transport image shrinks by half in each dimension. */
1276 	    *swp = sw / 2;  *shp = sh / 2;
1277 	}
1278 	/* Transport is always in the UL corner. */
1279 	*sxp = sx;  *syp = sy;
1280     } else {
1281 	if (n < 0)
1282 	  run_error("xform_occupant weirdness");
1283 	/* Compute how the half-box will be subdivided.  Only use
1284 	   powers of two, so image scaling works better. */
1285 	if (num <= 2) {
1286 	    nmx = 2;
1287 	} else if (num <= 8) {
1288 	    nmx = 4;
1289 	} else if (num <= 128) {
1290 	    nmx = 8;
1291 	} else {
1292 	    /* This is room for 32,768 units in a stack. */
1293 	    nmx = 16;
1294 	}
1295 	nmy = nmx / 2;
1296 	*swp = sw / nmx;
1297 	*shp = (sh / 2) / nmy;
1298 	*sxp = sx + *swp * (n / nmy) + 1;
1299 	*syp = sy + sh / 2 + *shp * (n % nmy);
1300     }
1301 }
1302 
1303 void
xform_occupant_view(VP * vp,UnitView * traview,UnitView * uview,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)1304 xform_occupant_view(VP *vp, UnitView *traview, UnitView *uview,
1305 		    int sx, int sy, int sw, int sh,
1306 		    int *sxp, int *syp, int *swp, int *shp)
1307 {
1308     int num = 0, n = -1, nmx, nmy;
1309     UnitView *uview2;
1310 
1311     /* Skip transformation if we are not drawing occupants. */
1312     if (!vp->draw_occupants) {
1313     	*sxp = sx;
1314     	*syp = sy;
1315     	*swp = sw;
1316     	*shp = sh;
1317 	return;
1318     }
1319     /* Figure out the position of this unit amongst all the occupants. */
1320     for_all_occupant_views(traview, uview2) {
1321 	if (uview2 == uview)
1322 	  n = num;
1323 	++num;
1324     }
1325     if (uview == traview) {
1326 	if (num > 0) {
1327 	    /* Transport image shrinks by half in each dimension. */
1328 	    *swp = sw / 2;
1329 	    *shp = sh / 2;
1330 	}
1331 	/* Transport is always in the UL corner. */
1332 	*sxp = sx;
1333 	*syp = sy;
1334     } else {
1335 	if (n < 0)
1336 	  run_error("xform_occupant_view weirdness");
1337 	/* Compute how the half-box will be subdivided.  Only use
1338 	   powers of two, so image scaling works better. */
1339 	if (num <= 2) {
1340 	    nmx = 2;
1341 	} else if (num <= 8) {
1342 	    nmx = 4;
1343 	} else if (num <= 128) {
1344 	    nmx = 8;
1345 	} else {
1346 	    /* This is room for 32,768 units in a stack. */
1347 	    nmx = 16;
1348 	}
1349 	nmy = nmx / 2;
1350 	*swp = sw / nmx;
1351 	*shp = (sh / 2) / nmy;
1352 	*sxp = sx + *swp * (n / nmy) + 1;
1353 	*syp = sy + sh / 2 + *shp * (n % nmy);
1354     }
1355 }
1356 
1357 /* Scale one viewport box to its position in another. */
1358 
1359 void
scale_vp(VP * vp,VP * vp2,int * sxp,int * syp,int * swp,int * shp)1360 scale_vp(VP *vp, VP *vp2, int *sxp, int *syp, int *swp, int *shp)
1361 {
1362     *sxp = (vp2->sx * vp->hw) / vp2->hw - vp->sx;
1363     *syp = (vp2->sy * vp->hch) / vp2->hch - vp->sy;
1364     *swp = (vp2->pxw * vp->hw) / vp2->hw;
1365     *shp = (vp2->pxh * vp->hch) / vp2->hch;
1366 }
1367 
1368 /* This works correctly for non-isometric maps only. */
1369 
1370 void
scale_point(VP * vp,VP * vp2,int sx,int sy,int * sx2p,int * sy2p)1371 scale_point(VP *vp, VP *vp2, int sx, int sy, int *sx2p, int *sy2p)
1372 {
1373     *sx2p = ((sx + vp2->sx) * vp->hw) / vp2->hw - vp->sx;
1374     *sy2p = ((sy + vp2->sy) * vp->hch) / vp2->hch - vp->sy;
1375 }
1376 
1377 int
nearest_cell(VP * vp,int sx,int sy,int * xp,int * yp,int * xfp,int * yfp)1378 nearest_cell(VP *vp, int sx, int sy, int *xp, int *yp, int *xfp, int *yfp)
1379 {
1380     int sxadj, syadj, sxfrac, syflipped, syfrac, tmp1;
1381 
1382     if (vp->isometric) {
1383 	/* Convert to absolute scaled coordinates. */
1384 	sx += vp->sx;
1385 	sy += vp->sy;
1386 	switch (vp->isodir) {
1387 	  case NORTHEAST:
1388 	    sy += (area.halfheight * vp->hw) / 4;
1389 	    *xp = (sx - (vp->hh - vp->hch)) / vp->hch;
1390 	    syadj = sy - ((area.width - 1 - *xp) * vp->hw) / 4;
1391 	    *yp = (area.height - 1) - (2 * syadj) / vp->hw;
1392 	    break;
1393 	  case EAST:
1394 	    sy += vp->hw / 2;
1395 	    *yp = (area.height - 1) - sx / vp->hch;
1396 	    tmp1 = ((area.halfheight + area.width) * vp->hw) / 2;
1397 	    *xp = ((tmp1 - sy - ((*yp + area.halfheight) * vp->hw) / 4) * 2)
1398 		  / vp->hw;
1399 	    break;
1400 	  case SOUTHEAST:
1401 	    *xp = area.width - 1 - (((sx - (vp->hh - vp->hch)) / vp->hch)
1402 				    + ((sy * 4) / vp->hw)
1403 				    - (area.height - 1)) / 2;
1404 	    *yp = ((sy * 4) / vp->hw) - (area.width - 1 - *xp);
1405 	    break;
1406 	  case SOUTHWEST:
1407 	    *xp = (area.width - 1) - (sx - (vp->hh - vp->hch)) / vp->hch;
1408 	    *yp = ((sy - (*xp * vp->hw) / 4) * 2) / vp->hw;
1409 	    break;
1410 	  case WEST:
1411 	    if (area.xwrap)
1412 	      sy -= (area.halfheight * vp->hw) / 4;
1413 	    *yp = sx / vp->hch;
1414 	    *xp = ((sy - ((*yp - area.halfheight) * vp->hw) / 4) * 2) / vp->hw;
1415 	    break;
1416 	  case NORTHWEST:
1417 	    *xp = ((sx + (vp->hh - vp->hch)) / vp->hch
1418 		   - (area.height - 1)
1419 		   + ((sy * 4) / vp->hw)) / 2;
1420 	    *yp = *xp - ((sy * 4) / vp->hw) + (area.height - 1);
1421 	    break;
1422 	  default:
1423 	    case_panic("view direction", vp->isodir);
1424 	    break;
1425 	}
1426 	return (in_area(*xp, *yp));
1427     }
1428     /* We don't want points below the map to be rounded off to zero and
1429        then interpreted as being within the map. */
1430     if (sy > vp->totsh) {
1431 	*yp = -1;
1432 	if (yfp) {
1433 		*yfp = 0;
1434 	}
1435     } else {
1436 	/* Flip the raw y and then scale to hex coords. */
1437 	syflipped = vp->totsh - (vp->sy + sy);
1438 	*yp = syflipped / vp->hch;
1439 	if (yfp) {
1440 	    syfrac = syflipped - (*yp * vp->hch) - (vp->hh - vp->hch);
1441 	    *yfp = (syfrac * 1000) / vp->hch;
1442 	}
1443     }
1444     /* Adjust scaled x. */
1445     sxadj = (sx + vp->sx - (*yp * vp->hw) / 2);
1446     /* The division by hw below might round towards 0, so wrap
1447        negative numbers around to positive values.  This should only
1448        ever happens for cylinder areas, but doesn't hurt to just
1449        adjust all negative values. */
1450 
1451     /* Unfortunately, it does hurt. The mac-specific meridian drawing
1452        code must be allowed to use negative values for non-cylindrical
1453        worlds! */
1454     if (area.xwrap &&  sxadj < 0) {
1455 	sxadj += (2 * vp->totsw);
1456     }
1457     /* We don't want points to the left of the map to be rounded off to zero
1458     and then interpreted as being within the map. */
1459     if (sxadj < 0) {
1460     	*xp = -1;
1461     	if (xfp) {
1462 	    *xfp = 0;
1463     	}
1464     } else {
1465 	*xp = sxadj / vp->hw;
1466 	if (xfp) {
1467 	    sxfrac = sxadj - (*xp * vp->hw);
1468 	    *xfp = (sxfrac * 1000) / vp->hw;
1469 	}
1470     }
1471     /* If the magnification of the map is large enough that the top
1472        and bottom edges of a hex are visibly sloping, then we have to
1473        take those edges into account, and accurately. */
1474     if ((vp->hh - vp->hch) / 2 > 1) {
1475 	/* (should adjust according to hex boundaries correctly here) */
1476     }
1477     /* Wrap coords as usual. */
1478     if (area.xwrap)
1479       *xp = wrapx(*xp);
1480     DGprintf("Pixel %d,%d -> hex %d.%03d,%d.%03d\n",
1481 	     sx, sy, *xp, (xfp ? *xfp : 0), *yp, (yfp ? *yfp : 0));
1482     return (in_area(*xp, *yp));
1483 }
1484 
1485 int
nearest_boundary(VP * vp,int sx,int sy,int * xp,int * yp,int * dirp)1486 nearest_boundary(VP *vp, int sx, int sy, int *xp, int *yp, int *dirp)
1487 {
1488     int sx2, sy2, ydelta, hexslope;
1489 
1490     /* Get the nearest cell... */
1491     if (nearest_cell(vp, sx, sy, xp, yp, NULL, NULL)) {
1492 	/* ... and xform it back to get the pixel coords. */
1493 	xform_cell(vp, *xp, *yp, &sx2, &sy2);
1494 	ydelta = sy - sy2;
1495 	hexslope = (vp->hh - vp->hch) / 2;
1496 	if (sx - sx2 > vp->hw / 2) {
1497 	    *dirp = ((ydelta < hexslope) ? NORTHEAST : (ydelta > vp->hch ? SOUTHEAST : EAST));
1498 	} else {
1499 	    *dirp = ((ydelta < hexslope) ? NORTHWEST : (ydelta > vp->hch ? SOUTHWEST : WEST));
1500 	}
1501 	DGprintf("Pixel %d,%d -> hex %d,%d dir %d\n", sx, sy, *xp, *yp, *dirp);
1502 	return TRUE;
1503     } else {
1504 	return FALSE;
1505     }
1506 }
1507 
1508 //! Return unit view at a certain pixel, if there is one.
1509 
1510 UnitView *
find_uview_at_pixel(Side * side,VP * vp,UnitView * uview,int usx,int usy,int usw,int ush,int sx,int sy)1511 find_uview_at_pixel(
1512     Side *side, VP *vp, UnitView *uview, int usx, int usy, int usw, int ush,
1513     int sx, int sy)
1514 {
1515     int usx1, usy1, usw1, ush1;
1516     int flags = 0;
1517     UnitView *uvocc = NULL, *rslt = NULL;
1518 
1519     /* See if the point might be over an occupant. */
1520     for_all_occupant_views(uview, uvocc) {
1521 	flags = 0;
1522 	flags = xform_unit_view(side, vp, uvocc, &usx1, &usy1, &usw1, &ush1,
1523 				XFORM_UVIEW_AS_OCC, NULL, usx, usy, usw, ush);
1524 	if (flags & XFORM_UVIEW_DONT_DRAW)
1525 	  continue;
1526 	rslt =
1527 	    find_uview_at_pixel(side, vp, uvocc, usx1, usy1, usw1, ush1,
1528 				sx, sy);
1529 	if (rslt)
1530 	  return rslt;
1531     }
1532     /* Now try the uview itself. */
1533     xform_unit_view(side, vp, uview, &usx1, &usy1, &usw1, &ush1, 0, NULL,
1534 		    usx, usy, usw, ush);
1535     if (flags & XFORM_UVIEW_DONT_DRAW)
1536       return NULL;
1537     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1))
1538       return uview;
1539     return NULL;
1540 }
1541 
1542 Unit *
find_unit_or_occ(Side * side,VP * vp,Unit * unit,int usx,int usy,int usw,int ush,int sx,int sy)1543 find_unit_or_occ(Side *side, VP *vp, Unit *unit, int usx, int usy,
1544 		 int usw, int ush, int sx, int sy)
1545 {
1546     int usx1, usy1, usw1, ush1;
1547     Unit *occ, *rslt;
1548 
1549     /* See if the point might be over an occupant. */
1550     if (unit->occupant != NULL
1551     /* See if we are drawing occs. */
1552 	&& vp->draw_occupants
1553 	&& (side_controls_unit(side, unit)
1554 	    /* If side can examine unit in detail it can also see its occs. */
1555 	    || side_sees_unit(side, unit)
1556 	    || vp->show_all
1557 	    || u_see_occupants(unit->type)
1558 	    || side_owns_occupant_of_unit(side, unit))) {
1559 	for_all_occupants(unit, occ) {
1560 	    xform_unit(vp, occ, &usx1, &usy1, &usw1, &ush1);
1561 	    rslt = find_unit_or_occ(side, vp, occ, usx1, usy1, usw1, ush1,
1562 				    sx, sy);
1563 	    if (rslt)
1564 	      return rslt;
1565 	}
1566     }
1567     /* Otherwise see if it could be the unit itself.  This has the effect of
1568        "giving" the transport everything in its box that is not in an occ. */
1569     xform_unit(vp, unit, &usx1, &usy1, &usw1, &ush1);
1570     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1))
1571       return unit;
1572     return NULL;
1573 }
1574 
1575 //! Return unit view at a certain pixel, if there is one.
1576 
1577 UnitView *
find_uview_at_pixel(Side * side,VP * vp,int x,int y,int sx,int sy)1578 find_uview_at_pixel(Side *side, VP *vp, int x, int y, int sx, int sy)
1579 {
1580     int usx, usy, usw, ush;
1581     int flags = 0;
1582     UnitView *uvstack = NULL, *uview = NULL, *rslt = NULL;
1583 
1584     /* Pick up the appropriate uvstack. */
1585     if (vp->show_all) {
1586         uvstack = query_uvstack_at(x, y);
1587         side = NULL;
1588     }
1589     else
1590       uvstack = unit_view_at(dside, x, y);
1591     /* Iterate through uvstack attempting to find uview. */
1592     for_all_uvstack(uvstack, uview) {
1593 	flags = 0;
1594 	flags = xform_unit_view(side, vp, uview, &usx, &usy, &usw, &ush, flags);
1595 	if (flags & XFORM_UVIEW_DONT_DRAW)
1596 	  continue;
1597 	rslt = find_uview_at_pixel(side, vp, uview, usx, usy, usw, ush, sx, sy);
1598 	if (rslt)
1599 	  return rslt;
1600     }
1601     return NULL;
1602 }
1603 
1604 Unit *
find_unit_at(Side * side,VP * vp,int x,int y,int sx,int sy)1605 find_unit_at(Side *side, VP *vp, int x, int y, int sx, int sy)
1606 {
1607     int usx, usy, usw, ush;
1608     Unit *unit, *rslt;
1609 
1610     for_all_stack(x, y, unit) {
1611 	xform_unit(vp, unit, &usx, &usy, &usw, &ush);
1612 	rslt = find_unit_or_occ(side, vp, unit, usx, usy, usw, ush, sx, sy);
1613 	if (rslt)
1614 	  return rslt;
1615     }
1616     return NULL;
1617 }
1618 
1619 int
nearest_unit(Side * side,VP * vp,int sx,int sy,Unit ** unitp)1620 nearest_unit(Side *side, VP *vp, int sx, int sy, Unit **unitp)
1621 {
1622     int x, y;
1623 
1624     if (!nearest_cell(vp, sx, sy, &x, &y, NULL, NULL)) {
1625 	*unitp = NULL;
1626 	DGprintf("Pixel %d,%d -> outside area\n", sx, sy);
1627 	return FALSE;
1628     }
1629     if (vp->power > 4) {
1630 	*unitp = find_unit_at(side, vp, x, y, sx, sy);
1631     } else {
1632 	*unitp = unit_at(x, y);
1633     }
1634     DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp));
1635     return TRUE;
1636 }
1637 
1638 UnitView *
find_unit_or_occ_view(Side * side,VP * vp,UnitView * uview,int usx,int usy,int usw,int ush,int sx,int sy)1639 find_unit_or_occ_view(Side *side, VP *vp, UnitView *uview, int usx, int usy,
1640 		      int usw, int ush, int sx, int sy)
1641 {
1642     int usx1, usy1, usw1, ush1;
1643 
1644 #if 0 /* until we figure this out */
1645     Unit *unit, *occ, *rslt;
1646     /* See if the point might be over an occupant. */
1647     if (unit->occupant != NULL
1648     /* See if we are drawing occs. */
1649 	&& vp->draw_occupants
1650 	&& (side_controls_unit(side, unit)
1651 	    /* If side can examine unit in detail it can also see its occs. */
1652 	    || side_sees_unit(side, unit)
1653 	    || vp->show_all
1654 	    || u_see_occupants(unit->type)
1655 	    || side_owns_occupant_of_unit(side, unit))) {
1656 	for_all_occupants(unit, occ) {
1657 	    xform_unit(vp, occ, &usx1, &usy1, &usw1, &ush1);
1658 	    rslt = find_unit_or_occ(side, vp, occ, usx1, usy1, usw1, ush1,
1659 				    sx, sy);
1660 	    if (rslt)
1661 	      return rslt;
1662 	}
1663     }
1664 #endif
1665     /* Set side to null, if it is the dside and 'show_all' is in effect. */
1666     if ((side == dside) && vp->show_all) {
1667 	side = NULL;
1668 	uview = query_uvstack_from_unit(view_unit(uview));
1669     }
1670     /* Otherwise see if it could be the unit itself.  This has the effect of
1671        "giving" the transport everything in its box that is not in an occ. */
1672     xform_unit_view(side, vp, uview, &usx1, &usy1, &usw1, &ush1);
1673     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1))
1674       return uview;
1675     return NULL;
1676 }
1677 
1678 UnitView *
find_unit_view_at(Side * side,VP * vp,int x,int y,int sx,int sy)1679 find_unit_view_at(Side *side, VP *vp, int x, int y, int sx, int sy)
1680 {
1681     int usx, usy, usw, ush;
1682     UnitView *uview, *rslt;
1683 
1684     for_all_view_stack_with_occs(side, x, y, uview) {
1685 	xform_unit_view(side, vp, uview, &usx, &usy, &usw, &ush);
1686 	rslt = find_unit_or_occ_view(side, vp, uview, usx, usy, usw, ush,
1687 				     sx, sy);
1688 	if (rslt)
1689 	  return rslt;
1690     }
1691     return NULL;
1692 }
1693 
1694 int
nearest_unit_view(Side * side,VP * vp,int sx,int sy,UnitView ** uviewp)1695 nearest_unit_view(Side *side, VP *vp, int sx, int sy, UnitView **uviewp)
1696 {
1697     int x, y;
1698 
1699     if (!nearest_cell(vp, sx, sy, &x, &y, NULL, NULL)) {
1700 	*uviewp = NULL;
1701 	DGprintf("Pixel %d,%d -> outside area\n", sx, sy);
1702 	return FALSE;
1703     }
1704     if (vp->power > 4) {
1705 #if (0)
1706 	*uviewp = find_unit_view_at(side, vp, x, y, sx, sy);
1707 #else
1708 	*uviewp = find_uview_at_pixel(side, vp, x, y, sx, sy);
1709 #endif
1710     } else {
1711 	*uviewp = unit_view_at(side, x, y);
1712     }
1713     DGprintf("Pixel %d,%d -> ", sx, sy);
1714     if (DebugG) {
1715 	if (*uviewp != NULL) {
1716 	    DGprintf("uview %d,%d,%d\n",
1717 		     (*uviewp)->id, (*uviewp)->type, (*uviewp)->siden);
1718 	} else {
1719 	    DGprintf("no uview\n");
1720 	}
1721     }
1722     return TRUE;
1723 }
1724 
1725 /* Return true if the cell is visible in the viewport. */
1726 
1727 int
cell_is_visible(VP * vp,int x,int y)1728 cell_is_visible(VP *vp, int x, int y)
1729 {
1730     int sx, sy;
1731 
1732     if (!in_area(x, y))
1733       return FALSE;
1734     xform_cell(vp, x, y, &sx, &sy);
1735     if (area.xwrap && sx > vp->totsw)
1736       sx -= vp->totsw;
1737     if (sx + vp->hw < 0)
1738       return FALSE;
1739     if (sx > vp->pxw)
1740       return FALSE;
1741     if (sy + vp->hh < 0)
1742       return FALSE;
1743     if (sy > vp->pxh)
1744       return FALSE;
1745     return TRUE;
1746 }
1747 
1748 /* Decide whether given location is away from the edge of the map's
1749    window. */
1750 
1751 int
cell_is_in_middle(VP * vp,int x,int y)1752 cell_is_in_middle(VP *vp, int x, int y)
1753 {
1754     int sx, sy, insetx1, insety1, insetx2, insety2;
1755 
1756     if (!in_area(x, y))
1757       return FALSE;
1758     xform_cell(vp, x, y, &sx, &sy);
1759     /* Adjust to be the center of the cell, more reasonable if large. */
1760     sx += vp->hw / 2;  sy += vp->hh / 2;
1761     insetx1 = min(vp->pxw / 4, 1 * vp->hw);
1762     insety1 = min(vp->pxh / 4, 1 * vp->hch);
1763     insetx2 = min(vp->pxw / 4, 2 * vp->hw);
1764     insety2 = min(vp->pxh / 4, 2 * vp->hch);
1765     if (sx < insetx2)
1766       return FALSE;
1767     if (sx > vp->pxw - insetx2)
1768       return FALSE;
1769     if (sy < (between(2, y, area.height-3) ? insety2 : insety1))
1770       return FALSE;
1771     if (sy > vp->pxh - (between(2, y, area.height-3) ? insety2 : insety1))
1772       return FALSE;
1773     return TRUE;
1774 }
1775 
1776 /* Set vcx/vcy to point to the center of the view. */
1777 
1778 void
focus_on_center(VP * vp)1779 focus_on_center(VP *vp)
1780 {
1781     vp->vcy = (vp->totsh - (vp->sy + vp->pxh / 2)) / vp->hch;
1782     vp->vcx = vp->sx / vp->hw - (vp->vcy / 2) + (vp->pxw / vp->hch) / 2;
1783     /* Restrict the focus to be *inside* the area. */
1784     vp->vcy = limitn(1, vp->vcy, area.height - 2);
1785     if (area.xwrap) {
1786 	vp->vcx = wrapx(vp->vcx);
1787     } else {
1788 	vp->vcx = limitn(1, vp->vcx, area.width - 2);
1789 	if (vp->vcx + vp->vcy < area.halfheight + 1)
1790 	  vp->vcx = area.halfheight + 1;
1791 	if (vp->vcx + vp->vcy > area.width + area.halfheight - 1)
1792 	  vp->vcx = area.width + area.halfheight - 1;
1793     }
1794 }
1795 
1796 void
center_on_focus(VP * vp)1797 center_on_focus(VP *vp)
1798 {
1799     int sx, sy, xnw = vp->vcx;
1800 
1801     if (vp->isometric) {
1802 	xform_cell(vp, vp->vcx, vp->vcy, &sx, &sy);
1803 	sx += vp->sx - vp->pxw / 2;
1804 	sy += vp->sy - vp->pxh / 2;
1805     } else {
1806 	if (area.xwrap && xnw >= (area.width - vp->vcy / 2))
1807 	  xnw -= area.width;
1808 	/* Scale, add hex offset adjustment, translate to get left edge. */
1809 	sx = xnw * vp->hw + (vp->vcy * vp->hw) / 2 - vp->pxw / 2 + vp->hw / 2;
1810 	/* Scale, translate to top edge, flip. */
1811 	sy = vp->totsh - (vp->vcy * vp->hch + vp->pxh / 2 + vp->hh / 2);
1812     }
1813     set_view_position(vp, sx, sy);
1814     DGprintf("Viewport 0x%x at %d,%d, focused at %d,%d\n",
1815 	     vp, vp->sx, vp->sy, vp->vcx, vp->vcy);
1816 }
1817 
1818 int
set_view_size(VP * vp,int w,int h)1819 set_view_size(VP *vp, int w, int h)
1820 {
1821     if (w < 1 || h < 1)
1822       run_error("Bad viewport size %dx%d", w, h);
1823     vp->pxw = w;  vp->pxh = h;
1824     calc_view_misc(vp);
1825     return TRUE;
1826 }
1827 
1828 int
set_view_position(VP * vp,int sx,int sy)1829 set_view_position(VP *vp, int sx, int sy)
1830 {
1831     vp->sx = sx;  vp->sy = sy;
1832     /* Clip to rational limits. */
1833     vp->sx = limitn(vp->sxmin, vp->sx, vp->sxmax);
1834     vp->sy = limitn(vp->symin, vp->sy, vp->symax);
1835     return TRUE;
1836 }
1837 
1838 /* Given a magnification power, calculate and cache the sizes within a cell,
1839    and the scaled size in pixels of the entire world. */
1840 
1841 int
set_view_power(VP * vp,int power)1842 set_view_power(VP *vp, int power)
1843 {
1844     vp->power = power;
1845     vp->mag = mags[power]; /* is this used?? */
1846     vp->hw = hws[power];  vp->hh = hhs[power];
1847     vp->hch = hcs[power];
1848     vp->uw = uws[power];  vp->uh = uhs[power];
1849     if (vp->angle == 30) {
1850 	vp->hh /= 2;
1851 	vp->hch /= 2;
1852     } else if (vp->angle == 15) {
1853 	vp->hh /= 4;
1854 	vp->hch /= 4;
1855     }
1856     calc_view_misc(vp);
1857     DGprintf("Viewport 0x%x power is now %d, total scaled area is %d x %d\n",
1858 	     vp, vp->power, vp->totsw, vp->totsh);
1859     return TRUE;
1860 }
1861 
1862 int
set_view_focus(VP * vp,int x,int y)1863 set_view_focus(VP *vp, int x, int y)
1864 {
1865     if (!in_area(x, y))
1866       run_error("View focus of %d,%d not in area", x, y);
1867     vp->vcx = x;  vp->vcy = y;
1868     return TRUE;
1869 }
1870 
1871 int
set_view_angle(VP * vp,int angle)1872 set_view_angle(VP *vp, int angle)
1873 {
1874     if (!(angle == 90 || angle == 30 || angle == 15)) {
1875 	run_warning("Bad angle %d, setting to 90", angle);
1876 	angle = 90;
1877     }
1878     vp->angle = angle;
1879     vp->hh = hhs[vp->power];
1880     vp->hch = hcs[vp->power];
1881     vp->uh = uhs[vp->power];
1882     if (vp->angle == 30) {
1883 	vp->hh /= 2;
1884 	vp->hch /= 2;
1885 	vp->uh /= 2;
1886     } else if (vp->angle == 15) {
1887 	vp->hh /= 4;
1888 	vp->hch /= 4;
1889 	vp->uh /= 4;
1890     }
1891     calc_view_misc(vp);
1892     DGprintf("Angle is now %d, total scaled area is %d x %d\n",
1893 	     vp->angle, vp->totsw, vp->totsh);
1894     return TRUE;
1895 }
1896 
1897 int
set_view_isometric(VP * vp,int flag,int scale)1898 set_view_isometric(VP *vp, int flag, int scale)
1899 {
1900     vp->isometric = flag;
1901     vp->vertscale = scale;
1902     calc_view_misc(vp);
1903     return TRUE;
1904 }
1905 
1906 int
set_view_direction(VP * vp,int dir)1907 set_view_direction(VP *vp, int dir)
1908 {
1909     vp->isodir = dir;
1910     calc_view_misc(vp);
1911     return TRUE;
1912 }
1913 
1914 static void
calc_view_misc(VP * vp)1915 calc_view_misc(VP *vp)
1916 {
1917     if (vp->isometric) {
1918 	/* Common stuff for NE, SW, SE & NW. */
1919 	vp->totsw = area.width * vp->hch + (vp->hh - vp->hch);
1920 	vp->totsh = area.width * vp->hw / 4 + area.height * vp->hw / 4;
1921 	vp->sxmin = vp->hw / 4;
1922 	vp->symin = 0;
1923 	if (area.xwrap) {
1924 		vp->totsh += area.height * vp->hw / 4 ;
1925 		vp->symin -= area.halfheight * vp->hw / 4;
1926 	}
1927  	switch (vp->isodir) {
1928 	  case NORTHEAST:
1929 	  	vp->symin += (area.height - 2 * area.halfheight - 1) * vp->hw / 4;
1930 		break;
1931 	  case SOUTHWEST:
1932 		vp->symin += area.halfheight * vp->hw / 4;
1933 		break;
1934 	  case SOUTHEAST:
1935 		vp->sxmin += (area.height - area.halfheight - 1) * vp->hch;
1936 		break;
1937 	  case NORTHWEST:
1938 		vp->sxmin += area.halfheight * vp->hch;
1939 		break;
1940 	  case EAST:
1941 	  case WEST:
1942 		vp->totsw = area.height * vp->hch + (vp->hh - vp->hch);
1943 		vp->totsh = (area.width * vp->hw) / 2;
1944 		vp->sxmin = 0;
1945 		vp->symin = 0;
1946 		break;
1947 	  default:
1948 		case_panic("view direction", vp->isodir);
1949 		break;
1950 	}
1951 	/* (should add any adjust due to elevated cells) */
1952    } else {
1953 	/* Calculate and cache the width in pixels of the whole area. */
1954 	vp->totsw = area.width * vp->hw;
1955 
1956 	/* This has an effect in the tcltk interface which is similar to setting
1957 	   scroll_beyond_dateline in that it becomes possible to scroll one full
1958 	   screen beyond the dateline. However, the new terrain is not drawn
1959 	   correctly (scrolling bug). */
1960 #if 0
1961 	if (area.xwrap && (vp->totsw > (vp->pxw - vp->hw)))
1962 	  vp->totsw += (vp->pxw - vp->hw);
1963 #endif
1964 	/* Total scaled height is based on center-to-center height, plus
1965 	   an adjustment to include the bottom parts of the bottom row. */
1966 	vp->totsh = area.height * vp->hch + (vp->hh - vp->hch);
1967 	vp->sxmin = (area.xwrap ? 0 : (area.halfheight * vp->hw) / 2);
1968 	vp->symin = 0;
1969     }
1970     /* Make it posssible to scroll beyond the dateline, thus scrolling around
1971     the globe. Currently supported only in the mac ppx interface. Note: if this
1972     is enabled in the tcltk interface, scrolling continues beyond the dateline,
1973     but the new terrain is not drawn (scrolling bug). In the SDL interface,
1974     scrolling continues and the terrain is drawn also beyond the dateline, but
1975     subsequent screen updates fail (black garbage appears under the cursor
1976     when moved). */
1977     if (area.xwrap && vp->scroll_beyond_dateline) {
1978 	vp->sxmax = vp->totsw;
1979     } else {
1980          vp->sxmax = vp->sxmin + vp->totsw - vp->pxw;
1981 	vp->sxmax = max(vp->sxmin, vp->sxmax);
1982     }
1983     vp->symax = vp->symin + vp->totsh - vp->pxh;
1984     vp->symax = max(vp->symin, vp->symax);
1985     vp->sx = limitn(vp->sxmin, vp->sx, vp->sxmax);
1986     vp->sy = limitn(vp->symin, vp->sy, vp->symax);
1987 }
1988 
1989 void
free_vp(VP * vp)1990 free_vp(VP *vp)
1991 {
1992     free(vp);
1993 }
1994 
1995 /* Given a row in the viewport, compute the starting cell and length
1996    of the row of cells to draw. */
1997 
1998 int
compute_x1_len(VP * vp,int vx,int vy,int y,int * x1p,int * lenp)1999 compute_x1_len(VP *vp, int vx, int vy, int y, int *x1p, int *lenp)
2000 {
2001     int x1, x2, vw, halfheight = area.height / 2;
2002 
2003     /* Compute the number of cells visible in this row. */
2004     vw = (vp->pxw + vp->hw - 1) / vp->hw;
2005     vw = min(vw, area.width);
2006     /* Adjust the right and left bounds to fill the viewport as much
2007        as possible, without going too far (the drawing code will clip,
2008        but clipped drawing is still expensive). */
2009     x1 = vx - (y - vy) / 2;
2010     /* We add 1 cell to x2 since the leftmost column bug fix in
2011     draw_map_overhead has decreased vx by 1/2 cell width, thus
2012     causing len to be too short in some cases. Note: this is probably
2013     overkill. Should try to nail down exactly when len needs to be
2014     increased by 1 cell. */
2015     x2 = x1 + vw + 1;
2016     if (1 /* should be more precise */)
2017       --x1;
2018     if (1 /* should be more precise */)
2019       ++x2;
2020     if (area.xwrap) {
2021     } else {
2022 	/* Truncate x's to stay within the area. */
2023 	x1 = max(0, min(x1, area.width-1));
2024 	x2 = max(0, min(x2, area.width));
2025 	/* If this row is entirely in the NE corner, don't draw
2026 	   anything. */
2027 	if (x1 + y > area.width + halfheight)
2028 	  return FALSE;
2029 	/* If this row is entirely in the SW corner, don't draw
2030 	   anything. */
2031 	if (x2 + y < halfheight)
2032 	  return FALSE;
2033 	/* If the row ends up in the NE corner, shorten it. */
2034 	if (x2 + y > area.width + halfheight)
2035 	  x2 = area.width + halfheight - y;
2036 	/* If the row starts out in the SW corner, shorten it. */
2037 	if (x1 + y < halfheight)
2038 	  x1 = halfheight - y;
2039     }
2040     *x1p = x1;
2041     *lenp = x2 - x1;
2042     return (*lenp > 0);
2043 }
2044 
2045 int
any_borders_in_dir(int x,int y,int dir)2046 any_borders_in_dir(int x, int y, int dir)
2047 {
2048     int b;
2049 
2050     if (!any_aux_terrain_defined())
2051       return FALSE;
2052     for_all_border_types(b) {
2053 	/* Note that border_at tests for layer existence. */
2054 	if (border_at(x, y, dir, b))
2055 	  return TRUE;
2056     }
2057     return FALSE;
2058 }
2059 
2060 int
any_coating_at(int x,int y)2061 any_coating_at(int x, int y)
2062 {
2063   int t;
2064 
2065   if (numcoattypes == 0)
2066     return NONTTYPE;
2067   for_all_terrain_types(t) {
2068     if (t_is_coating(t)
2069 	&& aux_terrain_defined(t)
2070 	&& aux_terrain_at(x, y, t) > 0)
2071       return t;
2072   }
2073   return NONTTYPE;
2074 }
2075 
2076 /* Compute the transition between two cells with different terrain,
2077    returning the positioning of the transition and which of four
2078    subimages to use. */
2079 
2080 int
compute_transition(Side * side,VP * vp,int x,int y,int dir,int * sxp,int * syp,int * swp,int * shp,int * offsetp)2081 compute_transition(Side *side, VP *vp, int x, int y, int dir,
2082 		   int *sxp, int *syp, int *swp, int *shp, int *offsetp)
2083 {
2084     int t, x1, y1, t1, x2, y2, sx, sy, sw, sh, sx2, sy2;
2085     int trite, tleft, overrite, overleft, offset;
2086     int w = vp->hw, h = vp->hh, hch = vp->hch;
2087 
2088     if (!point_in_dir(x, y, dir, &x1, &y1))
2089       return FALSE;
2090     if (!(vp->show_all || terrain_visible(side, x, y)))
2091       return FALSE;
2092     if (!(vp->show_all || terrain_visible(side, x1, y1)))
2093       return FALSE;
2094     t = terrain_at(x, y);
2095     t1 = terrain_at(x1, y1);
2096     /* We want to overlap the adjacent terrain into this cell if the
2097        terrain type is higher-numbered (meaning that land usually
2098        overlaps sea for instance), but not if there is a border,
2099        because the border itself provides the transition. */
2100     if (t1 < t || any_borders_in_dir(x, y, dir))
2101       return FALSE;
2102     /* If coatings differ we still want a transition even if the terrain
2103     is the same. The test for <= ensures that only one transition is drawn
2104     between cells with different coatings. */
2105     if (t1 == t
2106     	&& any_coating_at(x, y) <= any_coating_at(x1, y1)
2107     	&& cell_overlay(vp, x, y) <= cell_overlay(vp, x1, y1))
2108       return FALSE;
2109     /* We don't want to do overlap if there is a beach and
2110     shorelines are drawn. */
2111     if ((t_liquid(t) != t_liquid(t1))
2112         && vp->draw_shorelines)
2113       return FALSE;
2114     if (point_in_dir(x, y, right_dir(dir), &x2, &y2))
2115       trite = terrain_at(x2, y2);
2116     else
2117       trite = t;
2118     /* Overlap on the right-hand corner if the third cell's terrain is
2119        the same as the adjacent cell's terrain. */
2120     overrite = (trite == t1);
2121     /* Border on one side suppresses overlap. */
2122     if (any_borders_in_dir(x, y, right_dir(dir)))
2123       overrite = FALSE;
2124     if (point_in_dir(x, y, left_dir(dir), &x2, &y2))
2125       tleft = terrain_at(x2, y2);
2126     else
2127       tleft = t;
2128     /* Overlap on the left-hand corner if the third cell's terrain is
2129        the same as the adjacent cell's terrain. */
2130     overleft = (tleft == t1);
2131     /* Border on one side suppresses overlap. */
2132     if (any_borders_in_dir(x, y, left_dir(dir)))
2133       overleft = FALSE;
2134     xform_cell(vp, x, y, &sx, &sy);
2135     /* Now, given the overlaps on each side, choose the right piece of
2136        transition bitmap to use. */
2137     switch (dir) {
2138       case NORTHEAST:
2139 	sx2 = sx + w / 2;  sy2 = sy;
2140 	sw = w / 2;  sh = h - hch + 1;
2141 	if (overrite)
2142 	  offset = 1;
2143 	else if (overleft)
2144 	  offset = 2;
2145 	break;
2146       case EAST:
2147 	sx2 = sx + w / 2;  sy2 = sy + (h - hch) + 1;
2148 	sw = w / 2;  sh = h - 2 * (h - hch) - 2;
2149 	if (overrite)
2150 	  offset = 2;
2151 	else if (overleft)
2152 	  offset = 1;
2153 	break;
2154       case SOUTHEAST:
2155 	sx2 = sx + w / 2;  sy2 = sy + hch - 1;
2156 	sw = w / 2;  sh = h - hch + 1;
2157 	if (overrite)
2158 	  offset = 1;
2159 	else if (overleft)
2160 	  offset = 2;
2161 	break;
2162       case SOUTHWEST:
2163 	sx2 = sx;  sy2 = sy + hch - 1;
2164 	sw = w / 2;  sh = h - hch + 1;
2165 	if (overrite)
2166 	  offset = 2;
2167 	else if (overleft)
2168 	  offset = 1;
2169 	break;
2170       case WEST:
2171 	sx2 = sx;  sy2 = sy + (h - hch) + 1;
2172 	sw = w / 2;  sh = h - 2 * (h - hch) - 2;
2173 	if (overrite)
2174 	  offset = 1;
2175 	else if (overleft)
2176 	  offset = 2;
2177 	break;
2178       case NORTHWEST:
2179 	sx2 = sx;  sy2 = sy;
2180 	sw = w / 2;  sh = h - hch + 1;
2181 	if (overrite)
2182 	  offset = 2;
2183 	else if (overleft)
2184 	  offset = 1;
2185 	break;
2186     }
2187     if (overrite && overleft)
2188       offset = 0;
2189     else if (!overrite && !overleft)
2190       offset = 3;
2191     *sxp = sx2;  *syp = sy2;
2192     *swp = sw;  *shp = sh;
2193     *offsetp = offset;
2194     return TRUE;
2195 }
2196 
2197 void
compute_fire_line_segment(int sx1,int sy1,int sx2,int sy2,int i,int n,int * xx,int * yy,int * dx,int * dy)2198 compute_fire_line_segment(int sx1, int sy1, int sx2, int sy2, int i, int n,
2199 			  int *xx, int *yy, int *dx, int *dy)
2200 {
2201     /* Position one segment of a line between the locations. */
2202     *dx = (sx2 - sx1) / n;  *dy = (sy2 - sy1) / n;
2203     *xx = sx1 + ((i / 2) % n) * *dx;  *yy = sy1 + ((i / 2) % n) * *dy;
2204 }
2205 
2206 /* This routine can be used by the interface to place legends */
2207 
2208 /* orient==0 :  E (horizontal) only;
2209    orient==1 :  E, SE, NE;
2210    orient==2 :  E, SE, NE, ESE, ENE, N; */
2211 
2212 /* block==0  :  write over any unit;
2213    block==1  :  don't write over "city-like" units;
2214    block==2  :  don't write over visible units. */
2215 
2216 #if 0
2217 
2218 void
2219 place_feature_legends(Legend *legend, int nf, Side *side, int orient,
2220 		      int block)
2221 {
2222     int x, y, x1, y1, dx, dy, f, i, i3, j, d, ndirstotry, d1, dc;
2223     double dist;
2224     static int numdirstotry[] = { 1, 3, 6 };
2225     static int dirstotry[] =
2226     { EAST, SOUTHEAST, NORTHEAST, NORTHEAST, SOUTHEAST, EAST };
2227     static int da[] = { 0, -60, 60, 90, -30, 30 };
2228     unsigned char *auxf_layer, dmask;
2229 
2230     if (!features_defined())
2231       return;
2232 
2233     orient = min(orient, 2);
2234     ndirstotry = numdirstotry[orient];
2235 
2236     for (f = 1; f <= nf; f++) {
2237 	legend[f-1].ox = 0;  legend[f-1].oy = 0;
2238 	legend[f-1].dx = 0;  legend[f-1].dy = 0;
2239 	legend[f-1].angle = 0;
2240 	legend[f-1].dist  = -1;
2241     }
2242 
2243     /* Speedup: in auxf_layer we keep this information:
2244        the cell is unseen or hosts a blocking unit (bit 7);
2245        the cell has already been reached from direction id (bit id)
2246        [this avoids repeating the same path over and over;
2247 	note that directions 3,4,5 zig-zag with step 3,
2248 	so this bit is set/checked only every 3 steps.] */
2249 
2250     auxf_layer = (unsigned char *)
2251       malloc(area.height * area.width * sizeof(unsigned char));
2252 
2253     if (auxf_layer == NULL)
2254       return;
2255 
2256     for_all_cells(x, y) {
2257 	if (terrain_seen_at(side, x, y) == NONTTYPE ||
2258 	    blocking_utype(utype_seen_at(side, x, y), block)) {
2259 	    aset(auxf_layer, x, y, '\200');
2260 	} else {
2261 	    aset(auxf_layer, x, y, '\0');
2262 	}
2263     }
2264 
2265     for_all_cells(x, y) {
2266 	f = raw_feature_at(x, y);
2267 	if (f < 1 || f > nf)
2268 	  continue;
2269 
2270 	for (j = 0; j < ndirstotry; j++) {
2271 	    dmask = '\001' << j;
2272 	    d = dirstotry[j];
2273 	    d1 = ((j < 3) ? d : left_dir(left_dir(d)));
2274 	    x1 = x;  y1 = y;
2275 	    dx = dy = 0;
2276 	    i3 = i = 0;
2277 	    dist = 0;
2278 	    while (raw_feature_at(x1, y1) == f &&
2279 		   !(aref(auxf_layer, x1, y1) &
2280 		     ((j < 3 || !i3) ? ('\200' | dmask) : '\200'))) {
2281 		if (dist > legend[f-1].dist && (j < 3 || !i3)) {
2282 		    legend[f-1].ox = x;  legend[f-1].oy = y;
2283 		    legend[f-1].dx = dx;  legend[f-1].dy = dy;
2284 		    legend[f-1].angle = da[j];
2285 		    legend[f-1].dist  = dist;
2286 		}
2287 		if (j < 3 || !i3) {
2288 		    auxf_layer[area.width * y1 + x1] |= dmask;
2289 		}
2290 		dc = ((i3 == 1) ? d1 : d);
2291 		dx += dirx[dc];
2292 		x1 += dirx[dc];
2293 		dy += diry[dc];
2294 		y1 += diry[dc];
2295 		dist += ((j < 3) ? 1.0 : (i3 ? 0.5 * 1.73205080756888 : 0.0));
2296 		++i;
2297 		i3 = i % 3;
2298 	    }
2299 	}
2300     }
2301 
2302     free(auxf_layer);
2303 }
2304 
2305 #else
2306 
2307 void
place_feature_legends(Legend * legend,int nf,Side * side,int orient,int block)2308 place_feature_legends(Legend *legend, int nf, Side *side, int orient,
2309 		      int block)
2310 {
2311     int x, y, x1, dx, f;
2312     double dist;
2313     unsigned char *auxf_layer, dmask;
2314 
2315     if (!features_defined())
2316       return;
2317 
2318     for (f = 1; f <= nf; f++) {
2319 	legend[f-1].ox = 0;  legend[f-1].oy = 0;
2320 	legend[f-1].dx = 0;  legend[f-1].dy = 0;
2321 	legend[f-1].angle = 0;
2322 	legend[f-1].dist  = -1;
2323     }
2324 
2325     /* Speedup: in auxf_layer we keep this information:
2326        the cell is unseen or hosts a blocking unit (bit 7);
2327        the cell has already been reached from direction id (bit id)
2328        [this avoids repeating the same path over and over;
2329 	note that directions 3,4,5 zig-zag with step 3,
2330 	so this bit is set/checked only every 3 steps.] */
2331 
2332     auxf_layer = (unsigned char *)
2333       malloc(area.height * area.width * sizeof(unsigned char));
2334 
2335     if (auxf_layer == NULL)
2336       return;
2337 
2338     for_all_cells(x, y) {
2339 	if (terrain_seen_at(side, x, y) == NONTTYPE ||
2340 	    blocking_utype(utype_seen_at(side, x, y), block)) {
2341 	    aset(auxf_layer, x, y, '\200');
2342 	} else {
2343 	    aset(auxf_layer, x, y, '\0');
2344 	}
2345     }
2346 
2347     for_all_cells(x, y) {
2348 	f = raw_feature_at(x, y);
2349 	if (f < 1 || f > nf)
2350 	  continue;
2351 
2352 	    dmask = '\001';
2353 	    x1 = x;
2354 	    dx = 0;
2355 	    dist = 0;
2356 	    while (raw_feature_at(x1, y) == f &&
2357 		   !(aref(auxf_layer, x1, y) & ('\200' | dmask))) {
2358 		if (dist > legend[f-1].dist) {
2359 		    legend[f-1].ox = x;
2360 		    legend[f-1].oy = y;
2361 		    legend[f-1].dx = dx;
2362 		    legend[f-1].dy = 0;
2363 		    legend[f-1].angle = 0;
2364 		    legend[f-1].dist  = dist;
2365 		}
2366 		auxf_layer[area.width * y + x1] |= dmask;
2367 		dx += 1;
2368 		x1+= 1;
2369 		dist += 1;
2370 	    }
2371     }
2372 
2373     free(auxf_layer);
2374 }
2375 
2376 #endif
2377 
2378 static int
blocking_utype(int u,int block)2379 blocking_utype(int u, int block)
2380 {
2381     if (u == NONUTYPE || block == 0)
2382       return 0;
2383     if (block > 1)
2384       return 1;
2385     /* block==1:  only visible see-always unmovable units */
2386     return ((u_already_seen(u) > 99 || u_see_always(u)) && !mobile(u));
2387 }
2388 
2389 /* Set the spacing of the meridians of latitude and longitude. */
2390 
2391 void
set_meridian_interval(VP * vp,int interval)2392 set_meridian_interval(VP *vp, int interval)
2393 {
2394     vp->meridian_interval = interval;
2395     /* Don't want to compute the lat/lon label spacing yet, we don't
2396        necessarily have the window's size recorded in the viewport. */
2397     vp->lat_label_lon_interval = vp->lon_label_lat_interval = 0;
2398 }
2399 
2400 /* Compute the location of meridians desired for the given viewport
2401    and render the lines and labels using the two callback
2402    functions. */
2403 
2404 /* (should attempt to draw the lines all the way to the edge of
2405    the area, but formulas more complicated) */
2406 
2407 void vp_latlong(VP *vp, int *lat1p, int *lon1p, int *lat2p, int *lon2p);
2408 
2409 void
plot_meridians(VP * vp,void (* line_callback)(int x1,int y1,int x1f,int y1f,int x2,int y2,int x2f,int y2f),void (* text_callback)(int x1,int y1,int x1f,int y1f,char * str))2410 plot_meridians(VP *vp,
2411 	       void (*line_callback)(int x1, int y1, int x1f, int y1f,
2412 				     int x2, int y2, int x2f, int y2f),
2413 	       void (*text_callback)(int x1, int y1, int x1f, int y1f,
2414 				     char *str))
2415 {
2416     int lat1, lon1, lat2, lon2, latmin, latmax, lonmin, lonmax, incr, lat, lon;
2417     int latmid, lonmid, xmid, ymid, xmidf, ymidf;
2418     int x1, y1, x2, y2, x1f, y1f, x2f, y2f;
2419     int sx1, sy1, sx2, sy2;
2420     int latdeg, latminu, londeg, lonminu;
2421     char minbuf[10];
2422 
2423     incr = vp->meridian_interval;
2424     /* Draw only if the interval is not too small. */
2425     xy_to_latlong(area.halfwidth, area.height - 2, 0, 0, &lat, &lon);
2426     latlong_to_xy(lat, lon, &x1, &y1, &x1f, &y1f);
2427     latlong_to_xy(lat - incr, lon, &x2, &y2, &x2f, &y2f);
2428     /* If a single interval down from the top middle of the area is
2429        off the map, then there won't be any lines to plot anyway, so
2430        OK to escape. */
2431     if (!in_area(x2, y2))
2432       return;
2433     xform_cell_fractional(vp, x1, y1, x1f, y1f, &sx1, &sy1);
2434     xform_cell_fractional(vp, x2, y2, x2f, y2f, &sx2, &sy2);
2435     /* Don't draw if lines would be really closely spaced. */
2436     if (sy2 - sy1 < 20)
2437       return;
2438     /* Find the closest meridians that will be visible in the
2439        viewport. */
2440     vp_latlong(vp, &lat1, &lon1, &lat2, &lon2);
2441     /* Round to even multiples of the meridian interval. */
2442     latmin = (lat1 / incr) * incr;
2443     latmax = (lat2 / incr) * incr + incr;
2444     lonmin = (lon1 / incr) * incr;
2445     lonmax = (lon2 / incr) * incr + incr;
2446     if (lonmax <= lonmin)
2447       lonmax += 21600;
2448     vp_latlong(vp, &lat1, &lon1, &lat2, &lon2);
2449     /* Determine the spacing of the text labels.  They should be far
2450        enough apart so that only one set of each is visible at any
2451        time, although it's more OK for two sets to appear than for
2452        none to be visible. */
2453     /* This algorithm assumes that we first do a draw of the whole
2454        viewport before using smaller viewports for incremental
2455        updates. */
2456     if (vp->lon_label_lat_interval == 0) {
2457 	vp->lon_label_lat_interval = (abs(lat2 - lat1) / incr) * incr;
2458 	if (vp->lon_label_lat_interval == 0)
2459 	  vp->lon_label_lat_interval = 1;
2460 	vp->lat_label_lon_interval = (abs(lon2 - lon1) / incr) * incr;
2461 	if (vp->lat_label_lon_interval == 0)
2462 	  vp->lat_label_lon_interval = 1;
2463     }
2464     /* Draw each line as an individual segment, because meridians may
2465        not be straight lines. */
2466     for (lat = latmax; lat >= latmin; lat -= incr) {
2467 	for (lon = lonmin; lon <= lonmax; lon += incr) {
2468 	    latlong_to_xy(lat, lon, &x1, &y1, &x1f, &y1f);
2469 	    if (in_area(x1, y1)) {
2470 		latlong_to_xy(lat - incr, lon, &x2, &y2, &x2f, &y2f);
2471 		if (line_callback) {
2472 		    if (in_area(x2, y2)) {
2473 			(*line_callback)(x1, y1, x1f, y1f, x2, y2, x2f, y2f);
2474 		    }
2475 		    latlong_to_xy(lat, lon + incr, &x2, &y2, &x2f, &y2f);
2476 		    if (in_area(x2, y2)) {
2477 			(*line_callback)(x1, y1, x1f, y1f, x2, y2, x2f, y2f);
2478 		    }
2479 		}
2480 		if (text_callback) {
2481 		    if (lon % vp->lat_label_lon_interval == 0) {
2482 			lonmid = lon + incr / 2;
2483 			latlong_to_xy(lat, lonmid,
2484 				      &xmid, &ymid, &xmidf, &ymidf);
2485 			/* If adding the incr goes off the world,
2486 			   subtract instead. */
2487 			if (!in_area(xmid, ymid)) {
2488 			    lonmid = lon - incr / 2;
2489 			    latlong_to_xy(lat, lonmid,
2490 					  &xmid, &ymid, &xmidf, &ymidf);
2491 			}
2492 			/* Give up if this did not work. */
2493 			if (in_area(xmid, ymid)) {
2494 			    latdeg = abs(lat) / 60;
2495 			    latminu = abs(lat) % 60;
2496 			    minbuf[0] = '\0';
2497 			    if (latminu != 0)
2498 			      sprintf(minbuf, "%dm", latminu);
2499 			    sprintf(tmpbuf, "%dd%s%c",
2500 				    latdeg, minbuf, (lat >= 0 ? 'N' : 'S'));
2501 			    (*text_callback)(xmid, ymid, xmidf, ymidf, tmpbuf);
2502 			}
2503 		    }
2504 		    if (lat % vp->lon_label_lat_interval == 0) {
2505 			latmid = lat + incr / 2;
2506 			latlong_to_xy(latmid, lon,
2507 				      &xmid, &ymid, &xmidf, &ymidf);
2508 			/* If adding the incr goes off the world,
2509 			   subtract instead. */
2510 			if (!in_area(xmid, ymid)) {
2511 			    latmid = lat - incr / 2;
2512 			    latlong_to_xy(latmid, lon,
2513 					  &xmid, &ymid, &xmidf, &ymidf);
2514 			}
2515 			/* If this position is valid, draw the
2516                            longitude. */
2517 			if (in_area(xmid, ymid)) {
2518 			    londeg = abs(lon) / 60;
2519 			    lonminu = abs(lon) % 60;
2520 			    minbuf[0] = '\0';
2521 			    if (lonminu != 0)
2522 			      sprintf(minbuf, "%dm", lonminu);
2523 			    sprintf(tmpbuf, "%dd%s%c",
2524 				    londeg, minbuf, (lon >= 0 ? 'E' : 'W'));
2525 			    (*text_callback)(xmid, ymid, xmidf, ymidf, tmpbuf);
2526 			}
2527 		    }
2528 		}
2529 #if 0 /* use to debug meridian plotting */
2530 		sprintf(tmpbuf, "%d %d", lat, lon);
2531 		(*text_callback)(x1, y1, x1f, y1f, tmpbuf);
2532 		latlong_desc(tmpbuf, x1, y1, x1f, y1f, 3);
2533 		(*text_callback)(x1, y1, x1f, y1f, tmpbuf);
2534 #endif
2535 	    }
2536 	}
2537     }
2538 }
2539 
2540 void
vp_latlong(VP * vp,int * lat1p,int * lon1p,int * lat2p,int * lon2p)2541 vp_latlong(VP *vp, int *lat1p, int *lon1p, int *lat2p, int *lon2p)
2542 {
2543     int xmin, ymin, xmax, ymax, xminf, yminf, xmaxf, ymaxf;
2544 
2545     /* The implementation of nearest_cell still does the correct
2546        calculation, even if the pixel position is not in the area. */
2547     nearest_cell(vp, 0, vp->pxh, &xmin, &ymin, &xminf, &yminf);
2548     nearest_cell(vp, vp->pxw, 0, &xmax, &ymax, &xmaxf, &ymaxf);
2549     xy_to_latlong(xmin, ymin, xminf, yminf, lat1p, lon1p);
2550     xy_to_latlong(xmax, ymax, xmaxf, ymaxf, lat2p, lon2p);
2551 }
2552 
2553 /* Use this to set the contour interval, or, by setting to 0, let it
2554    float to a plausible value based on the actual range of
2555    elevations. */
2556 
2557 void
set_contour_interval(VP * vp,int n)2558 set_contour_interval(VP *vp, int n)
2559 {
2560     int ncontours;
2561 
2562     if (n > 0) {
2563 	/* Set contour interval, compute num contours from it. */
2564 	vp->contour_interval = n;
2565 	ncontours = (area.maxelev - area.minelev) / vp->contour_interval;
2566 	vp->contour_interval_fixed = TRUE;
2567     } else {
2568 	/* Set num contours, compute contour interval. */
2569 	ncontours = max(1, min(15, area.maxelev - area.minelev));
2570 	vp->contour_interval = (area.maxelev - area.minelev) / ncontours;
2571 	vp->contour_interval_fixed = FALSE;
2572     }
2573     if (ncontours != vp->num_contours) {
2574 	vp->num_contours = ncontours;
2575 	if (vp->linebuf != NULL) {
2576 	    free(vp->linebuf);
2577 	    vp->linebuf = NULL;
2578 	}
2579     }
2580 }
2581 
2582 /* The theory of contour lines is that each hex can be considered as
2583    six triangles, each of which has a vertex at the center and two
2584    on adjacent corners of the hex.  The elevation of the center vertex
2585    is the overall elevation of the cell, the elevations of the corners
2586    are averages with the adjacent cells.  If a particular contour
2587    elevation is between any pair of vertex elevations, then the contour
2588    line must cross that side of the triangle - and one of the other two
2589    sides.  We decide which of the two it is, interpolate to get the
2590    actual positions of each endpoint of the line segment, then draw it. */
2591 
2592 void
contour_lines_at(VP * vp,int x,int y,int sx,int sy,LineSegment ** lines,int * numlinesp)2593 contour_lines_at(VP *vp, int x, int y, int sx, int sy, LineSegment **lines,
2594 		 int *numlinesp)
2595 {
2596     int el, el2, dir, x1, y1, sum, n, lowest, liq0, liq, ecor[NUMDIRS], ec;
2597     int maxel;
2598     int sxcor[NUMDIRS], sycor[NUMDIRS], sxc, syc;
2599     int power = vp->power;
2600     int ecorr, ecorl, sxcorr, sycorr, sxcorl, sycorl;
2601     int sx1, sy1, sx2, sy2;
2602 
2603     *numlinesp = 0;
2604     if (vp->contour_interval < 1)
2605       return;
2606     if (vp->linebuf == NULL)
2607       vp->linebuf = (LineSegment *) xmalloc ((vp->num_contours + 2) * 2 * NUMDIRS * sizeof(LineSegment));
2608     *lines = vp->linebuf;
2609     /* It's possible that the contour intervals have not yet been
2610        adjusted to account for all elevations in the world, so be
2611        sure to clip. */
2612     maxel = area.minelev + vp->num_contours * vp->contour_interval;
2613     el = elev_at(x, y);
2614     if (el > maxel)
2615       el = maxel;
2616     sxc = sx + vp->hw / 2;  syc = sy + vp->hh / 2;
2617     /* Compute the elevation at each corner of the cell. */
2618     liq0 = t_liquid(terrain_at(x, y));
2619     for_all_directions(dir) {
2620 	sum = el;
2621 	n = 1;
2622 	lowest = el;
2623 	liq = liq0;
2624 	if (point_in_dir(x, y, dir, &x1, &y1)) {
2625 	    el2 = elev_at(x1, y1);
2626 	    if (el2 > maxel)
2627 	      el2 = maxel;
2628 	    sum += el2;
2629 	    ++n;
2630 	    lowest = min(lowest, el2);
2631 	    if (t_liquid(terrain_at(x1, y1)))
2632 	      liq = TRUE;
2633 	}
2634 	if (point_in_dir(x, y, left_dir(dir), &x1, &y1)) {
2635 	    el2 = elev_at(x1, y1);
2636 	    if (el2 > maxel)
2637 	      el2 = maxel;
2638 	    sum += el2;
2639 	    ++n;
2640 	    lowest = min(lowest, el2);
2641 	    if (t_liquid(terrain_at(x1, y1)))
2642 	      liq = TRUE;
2643 	}
2644 	/* Pick lowest, in the case of liquids, or average. */
2645 	if (liq)
2646 	  ecor[dir] = lowest;
2647 	else
2648 	  ecor[dir] = sum / n;
2649 	sxcor[dir] = sx + bsx[power][dir];  sycor[dir] = sy + bsy[power][dir];
2650     }
2651     /* Iterate over all the possible elevations for contour lines. */
2652     for (ec = area.minelev + vp->contour_interval; ec < area.maxelev; ec += vp->contour_interval) {
2653 	for_all_directions(dir) {
2654 	    ecorr = ecor[dir];
2655 	    ecorl = ecor[left_dir(dir)];
2656 	    sxcorr = sxcor[dir];  sycorr = sycor[dir];
2657 	    sxcorl = sxcor[left_dir(dir)];  sycorl = sycor[left_dir(dir)];
2658 	    if (el != ecorr && between(min(el, ecorr), ec, max(el, ecorr))) {
2659 		if (el != ecorl
2660 		    && between(min(el, ecorl), ec, max(el, ecorl))) {
2661 		    sx1 = sxc + ((sxcorr - sxc) * (ec - el)) / (ecorr - el);
2662 		    sy1 = syc + ((sycorr - syc) * (ec - el)) / (ecorr - el);
2663 		    sx2 = sxc + ((sxcorl - sxc) * (ec - el)) / (ecorl - el);
2664 		    sy2 = syc + ((sycorl - syc) * (ec - el)) / (ecorl - el);
2665 		    if (sx1 != sx2 || sy1 != sy2) {
2666 			vp->linebuf[*numlinesp].sx1 = sx1;
2667 			vp->linebuf[*numlinesp].sy1 = sy1;
2668 			vp->linebuf[*numlinesp].sx2 = sx2;
2669 			vp->linebuf[*numlinesp].sy2 = sy2;
2670 			++(*numlinesp);
2671 		    }
2672 		} else if (ecorl != ecorr) {
2673 		    sx1 = sxc + ((sxcorr - sxc) * (ec - el)) / (ecorr - el);
2674 		    sy1 = syc + ((sycorr - syc) * (ec - el)) / (ecorr - el);
2675 		    /* By inverting odd directions before the
2676 		       calculation we ensure that the endpoints are
2677 		       calculated in exactly the same way for the two
2678 		       line segments from adjacent cells that should
2679 		       end on the same point. This eliminates the
2680 		       small jumps in contour lines as they cross from
2681 		       one cell to another. */
2682 		    /* Line ends on outer edge. */
2683 		    if (dir == NORTHEAST || dir == SOUTHEAST || dir == WEST) {
2684 			sx2 = sxcorr + ((sxcorl - sxcorr) * (ec - ecorr)) / (ecorl - ecorr);
2685 			sy2 = sycorr + ((sycorl - sycorr) * (ec - ecorr)) / (ecorl - ecorr);
2686 		    } else {
2687 			sx2 = sxcorl + ((sxcorl - sxcorr) * (ec - ecorl)) / (ecorl - ecorr);
2688 			sy2 = sycorl + ((sycorl - sycorr) * (ec - ecorl)) / (ecorl - ecorr);
2689 	            }
2690 		    if (sx1 != sx2 || sy1 != sy2) {
2691 			vp->linebuf[*numlinesp].sx1 = sx1;
2692 			vp->linebuf[*numlinesp].sy1 = sy1;
2693 			vp->linebuf[*numlinesp].sx2 = sx2;
2694 			vp->linebuf[*numlinesp].sy2 = sy2;
2695 			++(*numlinesp);
2696 		    }
2697 		}
2698 	    }
2699 	    if (el != ecorl && between(min(el, ecorl), ec, max(el, ecorl))) {
2700 		if (ecorl != ecorr
2701 		    && between(min(ecorr, ecorl), ec, max(ecorr, ecorl))) {
2702 		    sx1 = sxc + ((sxcorl - sxc) * (ec - el)) / (ecorl - el);
2703 		    sy1 = syc + ((sycorl - syc) * (ec - el)) / (ecorl - el);
2704 		    /* Line ends on outer edge */
2705 		    if (dir == NORTHEAST || dir == SOUTHEAST || dir == WEST) {
2706 			sx2 = sxcorr + ((sxcorl - sxcorr) * (ec - ecorr)) / (ecorl - ecorr);
2707 			sy2 = sycorr + ((sycorl - sycorr) * (ec - ecorr)) / (ecorl - ecorr);
2708 		    } else {
2709 			sx2 = sxcorl + ((sxcorl - sxcorr) * (ec - ecorl)) / (ecorl - ecorr);
2710 			sy2 = sycorl + ((sycorl - sycorr) * (ec - ecorl)) / (ecorl - ecorr);
2711 		    }
2712 		    if (sx1 != sx2 || sy1 != sy2) {
2713 			vp->linebuf[*numlinesp].sx1 = sx1;
2714 			vp->linebuf[*numlinesp].sy1 = sy1;
2715 			vp->linebuf[*numlinesp].sx2 = sx2;
2716 			vp->linebuf[*numlinesp].sy2 = sy2;
2717 			++(*numlinesp);
2718 		    }
2719 		}
2720 	    }
2721 	}
2722     }
2723 }
2724 
2725 int
unit_visible(Side * side,VP * vp,Unit * unit)2726 unit_visible(Side *side, VP *vp, Unit *unit)
2727 {
2728     if (vp->show_all)
2729       return TRUE;
2730     /* Designer needs to see all units, even when show_all has been
2731        turned off - otherwise cells with units look empty. */
2732     if (is_designer(side))
2733       return TRUE;
2734     return side_sees_unit(side, unit);
2735 }
2736 
2737 /* Don't draw the temperature in every cell, only do ones with even
2738    coords or ones where the temperature in any adjacent cell is
2739    different. */
2740 
2741 int
draw_temperature_here(Side * side,int x,int y)2742 draw_temperature_here(Side *side, int x, int y)
2743 {
2744     int dir, x1, y1, temphere = temperature_view(side, x, y);
2745 
2746     /* Designers should see temperature in every cell. */
2747     if (is_designer(side))
2748       return TRUE;
2749     for_all_directions(dir) {
2750 	if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
2751 	    if (temphere != temperature_view(side, x1, y1))
2752 	      return TRUE;
2753 	    /* Always show temperature around edge of known area. */
2754 	    if (terrain_view(side, x1, y1) == UNSEEN)
2755 	      return TRUE;
2756 	}
2757     }
2758     return (x % 2 == 0 && y % 2 == 0);
2759 }
2760 
2761 /* Don't draw the winds in every cell, only do ones with odd coords or
2762    ones where the wind in any adjacent cell is different. */
2763 
2764 int
draw_winds_here(Side * side,int x,int y)2765 draw_winds_here(Side *side, int x, int y)
2766 {
2767     int dir, x1, y1, windhere = wind_view(side, x, y);
2768 
2769     /* Designers should see wind in every cell. */
2770     if (is_designer(side))
2771       return TRUE;
2772     /* Don't draw wind in unseen areas. */
2773     if (terrain_view(side, x, y) == UNSEEN)
2774       return FALSE;
2775     for_all_directions(dir) {
2776 	if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
2777 	    if (windhere != wind_view(side, x1, y1))
2778 	      return TRUE;
2779 	    /* Always show wind around edge of known area. */
2780 	    if (terrain_view(side, x1, y1) == UNSEEN)
2781 	      return TRUE;
2782 	}
2783     }
2784     return (x % 2 == 1 && y % 2 == 1);
2785 }
2786 
2787 /* Return a textual description of what is at the given pixel in the
2788    given viewport. */
2789 
2790 void
oneliner(Side * side,VP * vp,int sx,int sy)2791 oneliner(Side *side, VP *vp, int sx, int sy)
2792 {
2793     int x, y, xf, yf;
2794     Unit *unit, *unit2, *user;
2795     int t2, u, ps = NOBODY, cs = NOBODY, dep, sayin = FALSE, userid;
2796     char *peopdesc = NULL, *str;
2797     char descbuf[80], ctrlbuf[80], buf[BUFSIZE];
2798     Side *side2, *side3;
2799     Feature *feature;
2800     UnitView *uview;
2801 
2802     /* Return an empty result if the point is outside the viewport
2803        altogether. */
2804     if (!(between(0, sx, vp->pxw) && between(0, sy, vp->pxh))) {
2805 	tmpbuf[0] = '\0';
2806 	return;
2807     }
2808     nearest_cell(vp, sx, sy, &x, &y, &xf, &yf);
2809     nearest_unit(side, vp, sx, sy, &unit);
2810     nearest_unit_view(side, vp, sx, sy, &uview);
2811     if (!in_area(x, y)) {
2812 	strcpy(tmpbuf, "(nothing)");
2813 	return;
2814     } else if (vp->show_all || terrain_visible(side, x, y)) {
2815 	strcpy(tmpbuf, " ");
2816 	/* Describe the side of the people here. */
2817 	if (people_sides_defined()) {
2818 	    ps = people_side_at(x, y);
2819 	    if (ps != NOBODY) {
2820 		side2 = side_n(ps);
2821 		if (side2 == NULL) {
2822 		    peopdesc = "indep";
2823 		} else if (side2 == side) {
2824 		    peopdesc = "your";
2825 		} else {
2826 		    peopdesc = side_adjective(side2);
2827 		    if (peopdesc[0] == '\0') {
2828 			sprintf(descbuf, "s%d", side2->id);
2829 			peopdesc = descbuf;
2830 		    }
2831 		}
2832 	    }
2833 	}
2834 	if (control_sides_defined()) {
2835 	    cs = control_side_at(x, y);
2836 	    if (cs != ps) {
2837 		side3 = side_n(cs);
2838 		if (side3 == NULL) {
2839 		    strcpy(ctrlbuf, "uncontrolled");
2840 		} else if (side3 == side) {
2841 		    strcpy(ctrlbuf, "your");
2842 		} else {
2843 		    strcpy(ctrlbuf, side_adjective(side3));
2844 		    if (ctrlbuf[0] == '\0') {
2845 			sprintf(ctrlbuf, "s%d", side3->id);
2846 		    }
2847 		    strcat(ctrlbuf, "-controlled");
2848 		}
2849 		if (peopdesc != NULL) {
2850 		    strcat(ctrlbuf, " ");
2851 		    strcat(ctrlbuf, peopdesc);
2852 		}
2853 		peopdesc = ctrlbuf;
2854 	    }
2855 	}
2856 	if (vp->show_all || (uview != NULL && uview->siden == side->id)) {
2857 	    if (unit != NULL) {
2858 		if (unit->side != side) {
2859 		    if (ps != NOBODY && ps == side_number(unit->side)) {
2860 			peopdesc = "own";
2861 		    }
2862 		}
2863 		strcat(tmpbuf, unit_handle(side, unit));
2864 		sayin = TRUE;
2865 	    }
2866 	} else {
2867 	    if (uview != NULL) {
2868 		u = uview->type;
2869 		side2 = side_n(uview->siden);
2870 		if (ps != NOBODY && ps == side2->id) {
2871 		    peopdesc = "own";
2872 		}
2873 		/* Display the real name of a non-mobile unit,
2874 		   even if it's not in view coverage. */
2875 		/* (should fix this to use view stack) */
2876 		unit = NULL;
2877 		for_all_stack(x, y, unit2) {
2878 		    if (unit2->type == u
2879 			&& (!mobile(u) || type_max_acp(u) == 0)) {
2880 			unit = unit2;
2881 			break;
2882 		    }
2883 		}
2884 		if (unit != NULL) {
2885 		    strcat(tmpbuf,
2886 			   apparent_unit_handle(side, unit, side2));
2887 		} else {
2888 		    strcat(tmpbuf, side_adjective(side2));
2889 		    strcat(tmpbuf, " ");
2890 		    strcat(tmpbuf, u_type_name(u));
2891 		}
2892 		sayin = TRUE;
2893 	    }
2894 	}
2895 	if (sayin) {
2896 	    strcat(tmpbuf, " (in ");
2897 	}
2898 	if (peopdesc != NULL) {
2899 	    strcat(tmpbuf, peopdesc);
2900 	    strcat(tmpbuf, " ");
2901 	}
2902 	if (vp->show_all)
2903 	  t2 = terrain_at(x, y);
2904 	else
2905 	  t2 = vterrain(terrain_view(side, x, y));
2906 	strcat(tmpbuf, t_type_name(t2));
2907 	if (sayin) {
2908 	    strcat(tmpbuf, ")");
2909 	}
2910 	/* (should be able to display views of these) */
2911 	if (elevations_defined()) {
2912 	    tprintf(tmpbuf, " Elev %d", elev_at(x, y));
2913 	}
2914 	if (temperatures_defined()) {
2915 	    tprintf(tmpbuf, " T %ddeg", temperature_at(x, y));
2916 	}
2917 	if (numcoattypes > 0) {
2918 	    for_all_terrain_types(t2) {
2919 		if (t_is_coating(t2)
2920 		    && aux_terrain_defined(t2)
2921 		    && ((dep = aux_terrain_view(side, x, y, t2)) > 0)) {
2922 		    tprintf(tmpbuf, " %s %d", t_type_name(t2), dep);
2923 		}
2924 	    }
2925 	}
2926     } else {
2927 	sprintf(tmpbuf, "(unknown)");
2928     }
2929     strcat(tmpbuf, " @");
2930     if (1 /* drawxy */) {
2931 	tprintf(tmpbuf, "%d,%d", x, y);
2932     } else if (vp->draw_meridians) {
2933 	latlong_desc(descbuf, x, y, xf, yf, 3);
2934 	strcat(tmpbuf, descbuf);
2935     }
2936     if (vp->show_all || terrain_visible(side, x, y)) {
2937 	feature = feature_at(x, y);
2938 	if (feature != NULL) {
2939 	    if (feature->size > 0) {
2940 		str = feature_desc(feature, buf);
2941 		if (str != NULL) {
2942 		    strcat(tmpbuf, " (");
2943 		    strcat(tmpbuf, str);
2944 		    strcat(tmpbuf, ")");
2945 		}
2946 	    }
2947 	}
2948     }
2949     if (1 /* drawxy */ && vp->draw_meridians) {
2950 	latlong_desc(descbuf, x, y, xf, yf, 3);
2951 	strcat(tmpbuf, " (");
2952 	strcat(tmpbuf, descbuf);
2953 	strcat(tmpbuf, ")");
2954     }
2955     if (user_defined()
2956 	&& ((userid = user_at(x, y)) != NOUSER)) {
2957 	user = find_unit(userid);
2958 	if (in_play(user)) {
2959 	    strcat(tmpbuf, " (used by ");
2960 	    if (user->name != NULL)
2961 	      strcat(tmpbuf, user->name);
2962 	    else
2963 	      sprintf(tmpbuf+strlen(tmpbuf), "%s u#%d",
2964 		      u_type_name(user->type), user->id);
2965 	    strcat(tmpbuf, ")");
2966 	}
2967     }
2968     if (vp->draw_ai && side_has_ai(side)) {
2969 	str = ai_at_desig(side, x, y);
2970 	if (str) {
2971 	    strcat(tmpbuf, " ");
2972 	    strcat(tmpbuf, str);
2973 	}
2974     }
2975 }
2976 
2977 /* (needs a better home?) */
2978 
2979 /* Given a side and a unit, calculate the correct "next unit".  Typically
2980    used by autonext options, thus the name. */
2981 
2982 Unit *
autonext_unit(Side * side,Unit * unit)2983 autonext_unit(Side *side, Unit *unit)
2984 {
2985     int i, uniti = -1, n, numdelayed = 0;
2986     Unit *nextunit;
2987 
2988     if (!side->ingame
2989 	|| side->finishedturn
2990 	|| side->actionvector == NULL)
2991       return NULL;
2992     if (could_be_next_unit(unit) && side_controls_unit(side, unit))
2993       return unit;
2994     for (i = 0; i < side->actionvector->numunits; ++i) {
2995 	nextunit = unit_in_vector(side->actionvector, i);
2996     	if (in_play(nextunit) && side_controls_unit(side, nextunit)) {
2997 	    if (!is_active(unit) || unit == nextunit) {
2998 		uniti = i;
2999 		break;
3000 	    }
3001     	}
3002     }
3003     if (uniti < 0)
3004       return NULL;
3005   try_again:
3006     /* (should scan for both a preferred and an alternate - preferred
3007        could be within a supplied bbox so as to avoid scrolling) */
3008     for (i = uniti; i < uniti + side->actionvector->numunits; ++i) {
3009     	n = i % side->actionvector->numunits;
3010     	nextunit = unit_in_vector(side->actionvector, n);
3011     	if (could_be_next_unit(nextunit) && side_controls_unit(side, nextunit))
3012     	  return nextunit;
3013 	if (nextunit->plan && nextunit->plan->delayed && has_acp_left(nextunit))
3014 	  ++numdelayed;
3015     }
3016     /* If any units were delayed and another unit was not selected,
3017        then it is perhaps time to undelay everyone on the side and look
3018        again. */
3019     if (numdelayed) {
3020 	for (i = 0; i < side->actionvector->numunits; ++i) {
3021 	    nextunit = unit_in_vector(side->actionvector, i);
3022 	    if (nextunit->plan)
3023 	      net_delay_unit(nextunit, FALSE);
3024 	}
3025 	numdelayed = 0;
3026 	goto try_again;
3027     }
3028     return NULL;
3029 }
3030 
3031 /*
3032  * This should really be called autonext_unit and the decision
3033  * whether to check inbox or not should depend on the bbox being
3034  * valid. i.e. could be called with -1,-1,-1,-1 to disable the bbox.
3035  */
3036 Unit *
autonext_unit_inbox(Side * side,Unit * unit,VP * vp)3037 autonext_unit_inbox(Side *side, Unit *unit, VP *vp)
3038 {
3039     int i, u, mx, my, val, prefval = -999, v = 10;
3040     Unit *nextunit = NULL, *prefunit = NULL;
3041 
3042     if (!side->ingame || side->finishedturn || side->actionvector == NULL)
3043       return NULL;
3044 
3045     /* degenerate case... this unit still has stuff to do. */
3046     if (could_be_next_unit(unit) && side_controls_unit(side, unit))
3047 	return unit;
3048 
3049     if (unit == NULL) {
3050 	u = 0;
3051 	if (!nearest_cell(vp, vp->sx + vp->pxw / 2, vp->sy + vp->pxh / 2,
3052 			  &mx, &my, NULL, NULL)) {
3053 	    mx = area.width / 2;  my = area.halfheight;
3054 	}
3055     } else {
3056 	u = unit->type;
3057 	mx = unit->x;  my = unit->y;
3058     }
3059     for (i = 0; i < side->actionvector->numunits; ++i) {
3060     	nextunit = unit_in_vector(side->actionvector, i);
3061 	if (side_controls_unit(side, nextunit)
3062 	    && could_be_next_unit(nextunit)) {
3063 	    val = v - distance(nextunit->x, nextunit->y, mx, my);
3064 	    if (cell_is_in_middle(vp, nextunit->x, nextunit->y))
3065 	      val += v;
3066 	    if (nextunit->type == u)
3067 	      val += 2;
3068 
3069 	    if (val > prefval) {
3070 		prefval = val;
3071 		prefunit = nextunit;
3072 	    }
3073 	}
3074     }
3075     return prefunit;
3076 }
3077 
3078 int
could_be_next_unit(Unit * unit)3079 could_be_next_unit(Unit *unit)
3080 {
3081     return (unit != NULL
3082 	    && is_active(unit)
3083 	    && has_acp_left(unit)
3084 	    && (unit->plan
3085 	    	&& !unit->busy
3086 		&& !unit->plan->asleep
3087 		&& !unit->plan->reserve
3088 		&& !unit->plan->delayed
3089 		&& unit->plan->waitingfortasks));
3090 }
3091 
3092 /* Do a depth-first traversal of all the occupants of a unit. */
3093 
3094 Unit *
find_next_occupant(Unit * unit)3095 find_next_occupant(Unit *unit)
3096 {
3097     Unit *nextup;
3098 
3099     if (unit->occupant != NULL) {
3100 	return unit->occupant;
3101     } else if (unit->nexthere != NULL) {
3102 	return unit->nexthere;
3103     } else {
3104 	nextup = unit->transport;
3105 	if (nextup != NULL) {
3106 	    while (nextup->transport != NULL && nextup->nexthere == NULL) {
3107 		nextup = nextup->transport;
3108 	    }
3109 	    if (nextup->nexthere != NULL)
3110 	      return nextup->nexthere;
3111 	    if (nextup->transport == NULL)
3112 	      return nextup;
3113 	} else {
3114 	    /* This is a no-op if there is no stacking within a hex. */
3115 	    return unit_at(unit->x, unit->y);
3116 	}
3117     }
3118     return unit;
3119 }
3120 
3121 int
find_units_matching(Side * side,char * name,Unit ** unitp)3122 find_units_matching(Side *side, char *name, Unit **unitp)
3123 {
3124     int num = 0;
3125     Unit *unit;
3126 
3127     if (empty_string(name))
3128       return 0;
3129     for_all_units(unit) {
3130 	if (!empty_string(unit->name)
3131 	    && strstr(unit->name, name)
3132 	    && side_sees_image(side, unit)) {
3133 	    *unitp = unit;
3134 	    ++num;
3135 	}
3136     }
3137     return num;
3138 }
3139 
3140 Unit *
embarkation_unit(Unit * unit)3141 embarkation_unit(Unit *unit)
3142 {
3143     Unit *transport, *occ;
3144 
3145     /* look for the first possible transport */
3146     for_all_stack(unit->x, unit->y, transport) {
3147 	/* make sure its not the transport we're in and we can enter it */
3148 	if (transport != unit->transport &&
3149 	    valid(check_enter_action(unit, unit, transport))) {
3150 	    return transport;
3151 	}
3152 	/* check the occupants too */
3153 	for_all_occupants(transport, occ) {
3154 	    if (occ != unit->transport &&
3155 		valid(check_enter_action(unit, unit, occ))) {
3156 		return occ;
3157 	    }
3158 	}
3159     }
3160     return NULL;
3161 }
3162 
3163 /* Given a character, compute the direction(s) that it represents.
3164    Return the number of directions. */
3165 
3166 int
char_to_dir(int ch,int * dir1p,int * dir2p,int * modp)3167 char_to_dir(int ch, int *dir1p, int *dir2p, int *modp)
3168 {
3169     char basech, *rawdir;
3170     int ndirs = 0;
3171 
3172     if (isupper(ch)) {
3173 	basech = tolower(ch);
3174 	if (modp)
3175 	 *modp = 1;
3176     } else if (ch < ' ') {
3177 	basech = ch + 0x60;
3178 	if (modp)
3179 	  *modp = 2;
3180     } else {
3181 	basech = ch;
3182 	if (modp)
3183 	  *modp = 0;
3184     }
3185     rawdir = strchr(dirchars, basech);
3186     if (rawdir) {
3187 	*dir1p = rawdir - dirchars;
3188 	ndirs = 1;
3189     } else if (basech == 'k') {
3190 	if (flip_coin()) {
3191 	    *dir1p = NORTHEAST;
3192 	    if (dir2p)
3193 	      *dir2p = NORTHWEST;
3194 	} else {
3195 	    *dir1p = NORTHWEST;
3196 	    if (dir2p)
3197 	      *dir2p = NORTHEAST;
3198 	}
3199 	ndirs = 2;
3200     } else if (basech == 'j') {
3201 	if (flip_coin()) {
3202 	    *dir1p = SOUTHEAST;
3203 	    if (dir2p)
3204 	      *dir2p = SOUTHWEST;
3205 	} else {
3206 	    *dir1p = SOUTHWEST;
3207 	    if (dir2p)
3208 	      *dir2p = SOUTHEAST;
3209 	}
3210 	ndirs = 2;
3211     }
3212     return ndirs;
3213 }
3214 
3215 /* Given that the player desires to move the given unit into the given
3216    cell/other unit, prepare a "most appropriate" action. */
3217 /* (should share diff cell and same cell interaction code) */
3218 
3219 int
advance_into_cell(Side * side,Unit * unit,int x,int y,Unit * other,HistEventType * reason)3220 advance_into_cell(Side *side, Unit *unit, int x, int y, Unit *other,
3221 		  HistEventType *reason)
3222 {
3223     int z, m, rslt = H_UNDEFINED;
3224 
3225     /* Make sure we have some default value. */
3226     if (reason)
3227       *reason = (HistEventType)rslt;
3228 #ifdef DESIGNERS
3229     /* Designers use this function to push units around, bound only by the
3230        limits on occupancy. */
3231     if (is_designer(side))
3232       return net_designer_teleport(unit, x, y, other);
3233 #endif /* DESIGNERS */
3234     z = unit->z;
3235     if (x != unit->x || y != unit->y) {
3236 	/* Units that can't act/plan can't do anything to adjacent units. */
3237 	if (unit->act == NULL || unit->plan == NULL) {
3238 	    notify(side, "%s cannot act right now.", unit_handle(side, unit));
3239 	    return FALSE;
3240 	}
3241 	if (!mobile(unit->type)
3242 	    && u_advanced(unit->type)
3243 	    && distance(unit->x, unit->y, x, y) <= unit->reach) {
3244 	    net_toggle_user_at(unit->id, x, y);
3245 	    return TRUE;
3246 	}
3247 	if (distance(unit->x, unit->y, x, y) == 1) {
3248 	    /* Destination is adjacent to us. */
3249 	    /* First, handle the case where no unit was clicked on. */
3250 	    if (other == NULL) {
3251 		if (can_extract_at(unit, x, y, &m)
3252 		    || can_load_at(unit, x, y, &m)) {
3253 			net_set_collect_task(unit, m, x, y);
3254 			return TRUE;
3255 		} else if (mobile(unit->type)) {
3256 		    rslt = check_move_action(unit, unit, x, y, z);
3257 		    if (valid(rslt)) {
3258 			net_prep_move_action(unit, unit, x, y, z);
3259 			return TRUE;
3260 		    }
3261 		}
3262 		if (reason)
3263 	      	    *reason = (HistEventType)rslt;
3264 		return FALSE;
3265 	    }
3266 	    /* If a unit was clicked on, decide how to interact. */
3267 	    if (unit_trusts_unit(unit, other)
3268 		|| (other->side == indepside
3269 		    && sides_allow_entry(unit, other))) {
3270 		/* A friend, maybe get on it. */
3271 		if (can_occupy(unit, other)) {
3272 		    if (valid(check_enter_action(unit, unit, other))) {
3273 			net_prep_enter_action(unit, unit, other);
3274 		    } else {
3275 			/* (should schedule for next turn?) */
3276 		    }
3277 		} else if (can_occupy(other, unit)) {
3278 		    if (type_max_acp(other->type) > 0) {
3279 			/* Have other unit do an enter action, then
3280 			   move. */
3281 			/* Not quite right, move should happen after
3282 			   other unit is actually inside, in case it
3283 			   fills dest. */
3284 			net_prep_enter_action(other, other, unit);
3285 			net_set_move_to_task(unit, x, y, 0);
3286 		    } else {
3287 			net_prep_enter_action(unit, other, unit);
3288 			net_set_move_to_task(unit, x, y, 0);
3289 		    }
3290 		} else if (other->transport != NULL
3291 			   && can_occupy(unit, other->transport)) {
3292 		    if (valid(check_enter_action(unit, unit,
3293 						 other->transport))) {
3294 			net_prep_enter_action(unit, unit,
3295 					      other->transport);
3296 		    } else {
3297 			/* (should schedule for next turn?) */
3298 		    }
3299 		} else if (other->transport != NULL
3300 			   && other->transport->transport != NULL
3301 			   && can_occupy(unit, other->transport->transport)) {
3302 		    /* two levels up should be sufficient */
3303 		    if (valid(
3304 			    check_enter_action(unit, unit,
3305 					       other->transport->transport))) {
3306 			net_prep_enter_action(unit, unit,
3307 					      other->transport->transport);
3308 		    } else {
3309 			/* (should schedule for next turn?) */
3310 		    }
3311 		} else if (valid(check_transfer_part_action(unit, unit,
3312 							    unit->hp, other))) {
3313 		    net_prep_transfer_part_action(unit, unit,
3314 						  unit->hp, other);
3315 		} else if (can_extract_at(unit, x, y, &m)
3316 			   || can_load_at(unit, x, y, &m)) {
3317 		    /* (should test extraction from specific unit) */
3318 		    net_set_collect_task(unit, m, x, y);
3319 		} else {
3320 		    rslt = check_move_action(unit, unit, x, y, z);
3321 		    if (valid(rslt)) {
3322 			net_prep_move_action(unit, unit, x, y, z);
3323 			return TRUE;
3324 		    }
3325 		    if (reason)
3326 		      *reason = (HistEventType)rslt;
3327 		    return FALSE;
3328 		}
3329 	    } else {
3330 		/* Somebody else's unit, try to victimize it in
3331 		   various ways, trying coexistence only as a last
3332 		   resort. */
3333 		rslt = check_capture_action(unit, unit, other);
3334 		if (valid(rslt)) {
3335 		    net_prep_capture_action(unit, unit, other);
3336 		    return TRUE;
3337 		}
3338 		rslt = check_overrun_action(unit, unit, x, y, z, 100);
3339 		if (valid(rslt)) {
3340 		    net_prep_overrun_action(unit, unit, x, y, z, 100);
3341 		    return TRUE;
3342 		}
3343 		if (reason && rslt == A_ANY_NO_AMMO)
3344 		  *reason = (HistEventType)rslt;
3345 		rslt = check_attack_action(unit, unit, other, 100);
3346 		if (valid(rslt)) {
3347 		    net_prep_attack_action(unit, unit, other, 100);
3348 		    return TRUE;
3349 		}
3350 		if (reason && rslt == A_ANY_NO_AMMO)
3351 		  *reason = (HistEventType)rslt;
3352 		rslt = check_fire_at_action(unit, unit, other, -1);
3353 		if (valid(rslt)) {
3354 		    net_prep_fire_at_action(unit, unit, other, -1);
3355 		    return TRUE;
3356 		}
3357 		if (reason && rslt == A_ANY_NO_AMMO)
3358 		  *reason = (HistEventType)rslt;
3359 		rslt = check_detonate_action(unit, unit, x, y, z);
3360 		if (valid(rslt)) {
3361 		    net_prep_detonate_action(unit, unit, x, y, z);
3362 		    return TRUE;
3363 		}
3364 		if (can_extract_at(unit, x, y, &m)
3365 		    || can_load_at(unit, x, y, &m)) {
3366 		    /* (should test extraction from specific unit) */
3367 		    net_set_collect_task(unit, m, x, y);
3368 		    return TRUE;
3369 		}
3370 		rslt = check_move_action(unit, unit, x, y, z);
3371 		if (valid(rslt)) {
3372 		    net_prep_move_action(unit, unit, x, y, z);
3373 		    return TRUE;
3374 		}
3375 		/* None of the possible actions worked, so fail. */
3376 		return FALSE;
3377 	    }
3378 	} else {
3379 	    /* We're not adjacent to the destination; actions alone
3380 	       won't suffice. */
3381 	    if (can_extract_at(unit, x, y, &m)
3382 		|| can_load_at(unit, x, y, &m)) {
3383 		net_set_collect_task(unit, m, x, y);
3384 	    } else if (mobile(unit->type)) {
3385 		/* Although it's theoretically possible for a
3386 		   non-mobile unit to fulfill a move task by being
3387 		   carried all the way, in practice players are going
3388 		   to direct the transport to make the move, not its
3389 		   immobile occupants; so only allow mobile units and
3390 		   beep the player for trying to move a non-mobile
3391 		   unit. */
3392 		if (impl_move_to(side, unit, x, y, 0)) {
3393 		    return TRUE;
3394 		} else {
3395 			if (reason) {
3396 			    /* Not always true, but it suppresses the redundant
3397 			    error message. */
3398 			    *reason = (HistEventType) A_MOVE_NO_PATH;
3399 		    	}
3400 		    return FALSE;
3401 		}
3402 	    } else {
3403 		return FALSE;
3404 	    }
3405 	}
3406     /* Destination is in the unit's own cell. */
3407     } else {
3408 	/* First handle advanced units that are trying to toggle their own cell. */
3409 	if (!mobile(unit->type)
3410 	    && u_advanced(unit->type)) {
3411 		net_toggle_user_at(unit->id, x, y);
3412 		return TRUE;
3413 	} else if (other != NULL) {
3414 	    if (unit_trusts_unit(unit, other)) {
3415 		if (valid(check_transfer_part_action(unit, unit, unit->hp, other))) {
3416 		    net_prep_transfer_part_action(unit, unit, unit->hp, other);
3417 		} else if (valid(check_enter_action(unit, unit, other))) {
3418 		    net_prep_enter_action(unit, unit, other);
3419 		} else {
3420 		    return FALSE;
3421 		}
3422 	    } else {
3423 		/* Somebody else's unit, try to victimize it in various ways,
3424 		   trying coexistence only as a last resort. */
3425 		if (valid(check_capture_action(unit, unit, other))) {
3426 		    net_prep_capture_action(unit, unit, other);
3427 		} else if (valid(check_attack_action(unit, unit, other, 100))) {
3428 		    net_prep_attack_action(unit, unit, other, 100);
3429 		} else if (valid(check_fire_at_action(unit, unit, other, -1))) {
3430 		    net_prep_fire_at_action(unit, unit, other, -1);
3431 		} else if (valid(check_detonate_action(unit, unit, x, y, z))) {
3432 		    net_prep_detonate_action(unit, unit, x, y, z);
3433 		} else {
3434 		    return FALSE;
3435 		}
3436 	    }
3437 	} else if (unit->transport != NULL) {
3438 	    /* Unit is an occupant wanting to leave, but yet remain in
3439 	       the same cell as the transport. */
3440 	    rslt = check_move_action(unit, unit, x, y, z);
3441 	    if (valid(rslt)) {
3442 		net_prep_move_action(unit, unit, x, y, z);
3443 		return TRUE;
3444 	    }
3445 	    if (reason)
3446 	      *reason = (HistEventType)rslt;
3447 	    return FALSE;
3448 	/* Unit is trying to extract materials in the same cell. */
3449 	} else if (can_extract_at(unit, x, y, &m)
3450 	    || can_load_at(unit, x, y, &m)) {
3451 		net_set_collect_task(unit, m, x, y);
3452 	} else {
3453 	    /* This is a no-op, don't do anything. */
3454 	}
3455     }
3456     /* All the failures got filtered out by early returns, so anything
3457        that got here is a success. */
3458     return TRUE;
3459 }
3460 
3461 /* Return if it's possible for the given unit to attack a unit of the
3462    given type, side, and location. */
3463 /* (should return immed/later, type of attack possible) */
3464 
3465 int
unit_could_attack(Unit * unit,int u2,Side * side2,int x,int y)3466 unit_could_attack(Unit *unit, int u2, Side *side2, int x, int y)
3467 {
3468     int u = unit->type;
3469 
3470     if (trusted_side(unit->side, side2))
3471       return FALSE;
3472     switch (g_combat_model()) {
3473       case 0:
3474 	/* See if a direct attack is possible. */
3475 	if (uu_acp_to_attack(u, u2) > 0
3476 	    && uu_hit(u, u2) > 0
3477 #if 0 /* assume we can get there eventually */
3478 	    && between(uu_attack_range_min(u, u2),
3479 		       distance(unit->x, unit->y, x, y)
3480 		       uu_attack_range(u, u2))
3481 #endif
3482 	    )
3483 	  return TRUE;
3484 	/* See if attack by firing is possible. */
3485 	/* (should use this to test blocking elev) */
3486         if (fire_hit_chance(u, u2))
3487 	  return TRUE;
3488       case 1:
3489       default:
3490            return TRUE;
3491     }
3492     return FALSE;
3493 }
3494 
3495 /* Given a unit and amounts of supplies desired to transfer, move as
3496    much of them as possible into the unit's transport, or to a nearby
3497    units if there is no transport. */
3498 
3499 static int give_supplies_to_one(Unit *unit, Unit *unit2, short *amts,
3500 				short *rslts);
3501 
3502 Unit *
give_supplies(Unit * unit,short * amts,short * rslts)3503 give_supplies(Unit *unit, short *amts, short *rslts)
3504 {
3505     int dir, x1, y1, didsome;
3506     Unit *unit2;
3507 
3508     unit2 = unit->transport;
3509     if (unit2 != NULL) {
3510 	didsome = give_supplies_to_one(unit, unit2, amts, rslts);
3511 	if (didsome)
3512 	  return unit2;
3513     }
3514     for_all_occs_with_occs(unit, unit2) {
3515 	/* Note that we might have enemy spies as occupants, so test
3516 	   relationship even here. */
3517 	if (unit_trusts_unit(unit, unit2)) {
3518 	    didsome = give_supplies_to_one(unit, unit2, amts, rslts);
3519 	    if (didsome)
3520 	      return unit2;
3521 	}
3522     }
3523     for_all_stack(unit->x, unit->y, unit2) {
3524 	if (unit2 != unit && unit_trusts_unit(unit, unit2)) {
3525 	    didsome = give_supplies_to_one(unit, unit2, amts, rslts);
3526 	    if (didsome)
3527 	      return unit2;
3528 	}
3529     }
3530     /* Look around for adjacent units to give to. */
3531     for_all_directions(dir) {
3532 	if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
3533 	    for_all_stack(x1, y1, unit2) {
3534 		if (unit_trusts_unit(unit, unit2)) {
3535 		    didsome = give_supplies_to_one(unit, unit2, amts, rslts);
3536 		    if (didsome)
3537 		      return unit2;
3538 		}
3539 	    }
3540 	}
3541     }
3542     /* Now try again on adjacent occupants.  We do it this way so
3543        top-level adjacent units get tried before occupants, since
3544        for_all_stack_with_occs iterates depth-first. */
3545     for_all_directions(dir) {
3546 	if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
3547 	    for_all_stack_with_occs(x1, y1, unit2) {
3548 		if (unit_trusts_unit(unit, unit2)) {
3549 		    didsome = give_supplies_to_one(unit, unit2, amts, rslts);
3550 		    if (didsome)
3551 		      return unit2;
3552 		}
3553 	    }
3554 	}
3555     }
3556     return NULL;
3557 }
3558 
3559 /* Given a pair of units, have one give the desired amounts to the
3560    other. */
3561 
3562 static int
give_supplies_to_one(Unit * unit,Unit * unit2,short * amts,short * rslts)3563 give_supplies_to_one(Unit *unit, Unit *unit2, short *amts, short *rslts)
3564 {
3565     int m, gift, maxgift, actual, didsome = FALSE;
3566 
3567     if (!(in_play(unit2) && completed(unit2)))
3568       return FALSE;
3569     for_all_material_types(m) {
3570 	if (rslts)
3571 	  rslts[m] = 0;
3572 	maxgift = min(unit->supply[m],
3573 		      um_storage_x(unit2->type, m) - unit2->supply[m]);
3574 	gift = ((amts == NULL || amts[m] == -1) ? (maxgift / 2) : amts[m]);
3575 	if (gift > 0) {
3576 	    if (1 /* can do immed transfer */) {
3577 		/* Be stingy if giver is low */
3578 		if (2 * unit->supply[m] < um_storage_x(unit->type, m))
3579 		  gift = max(1, gift / 2);
3580 		actual = transfer_supply(unit, unit2, m, gift);
3581 		if (rslts)
3582 		  rslts[m] = actual;
3583 		if (actual > 0)
3584 		  didsome = TRUE;
3585 	    }
3586 	}
3587     }
3588 
3589     if (didsome) {
3590 	/* Recompute the supply_is_low flags for both unit and unit2.  */
3591 	if (unit->plan != NULL
3592 	    && !unit->plan->supply_is_low
3593 	    && past_halfway_point(unit)
3594 	    ) {
3595 	    unit->plan->supply_is_low = TRUE;
3596 	    update_unit_display(unit->side, unit, TRUE);
3597 	}
3598 	if (unit2->plan != NULL
3599 	    && unit2->plan->supply_is_low
3600 	    && !past_halfway_point(unit2)
3601 	    ) {
3602 	    unit2->plan->supply_is_low = FALSE;
3603 	    update_unit_display(unit2->side, unit2, TRUE);
3604 	}
3605     }
3606 
3607     return didsome;
3608 }
3609 
3610 /* Attempt to transfer the given amounts of material from the unit's
3611    transport into the unit. */
3612 
3613 int
take_supplies(Unit * unit,short * amts,short * rslts)3614 take_supplies(Unit *unit, short *amts, short *rslts)
3615 {
3616     int m, want, actual, neededsome;
3617     Unit *unit2;
3618 
3619     neededsome = FALSE;
3620     for_all_material_types(m) {
3621 	if (rslts)
3622 	  rslts[m] = 0;
3623 	want = ((amts == NULL || amts[m] == -1)
3624 	        ? (um_storage_x(unit->type, m) - unit->supply[m])
3625 	        : amts[m]);
3626 	if (want > 0) {
3627 	    neededsome = TRUE;
3628 	    unit2 = unit->transport;
3629 	    if (in_play(unit2) && completed(unit2)) {
3630 	    	/* Bug fix. Units inside a transport inside a base or
3631 	    	   city were previously unable to resupply directly
3632 	    	   from the base. Now the occ checks if the transport
3633 	    	   is inside a base and then resupplies directly from
3634 	    	   it. Moreover, the 'stingy' 50% left principle is
3635 	    	   applied only to units that need m themselves. Other
3636 	    	   units are free to give away all their supply. */
3637 
3638 		Unit *base = unit2->transport;
3639 		int request = want;
3640 
3641 		/* First take as much supplies as possible from the
3642 		   transport's transport (the base) if it exists. */
3643 		actual = 0;
3644 		if (in_play(base) && completed(base)) {
3645 		    /* Be stingy if base is low (but only if it also
3646                        needs m). */
3647 		    if (2 * base->supply[m] < um_storage_x(base->type, m)
3648 			&& (um_base_consumption(base->type, m) > 0
3649 			    || um_consumption_per_move(base->type, m) > 0)) {
3650 			request = max(1, want/2);
3651 		    }
3652 		    actual += transfer_supply(base, unit, m, request);
3653 		    want -= actual;
3654 		}
3655 		/* If still unsatisfied, also take supplies from
3656                    transport. */
3657 		if (want) {
3658 		    request = want;
3659 		    /* Be stingy if transport is low (but only if it
3660 		       also needs m). */
3661 		    if (2 * unit2->supply[m] < um_storage_x(unit2->type, m)
3662 			&& (um_base_consumption(unit2->type, m) > 0
3663 			    || um_consumption_per_move(unit2->type, m) > 0)) {
3664 			request = max(1, want/2);
3665 		    }
3666 		    actual += transfer_supply(unit2, unit, m, want);
3667 		}
3668 		if (rslts)
3669 		  rslts[m] = actual;
3670 	    }
3671 	}
3672     }
3673 
3674     if (neededsome) {
3675 	/* Recompute the supply_is_low flags for both unit and unit2.  */
3676 	if (unit2 != NULL
3677 	    && unit2->plan != NULL
3678 	    && !unit2->plan->supply_is_low
3679 	    && past_halfway_point(unit2)
3680 	    ) {
3681 	    unit2->plan->supply_is_low = TRUE;
3682 	    update_unit_display(unit2->side, unit2, TRUE);
3683 	}
3684 	if (unit->plan != NULL
3685 	    && unit->plan->supply_is_low
3686 	    && !past_halfway_point(unit)
3687 	    ) {
3688 	    unit->plan->supply_is_low = FALSE;
3689 	    update_unit_display(unit->side, unit, TRUE);
3690 	}
3691     }
3692 
3693     return neededsome;
3694 }
3695 
3696 int
impl_move_to(Side * side,Unit * unit,int x,int y,int dist)3697 impl_move_to(Side *side, Unit *unit, int x, int y, int dist)
3698 {
3699 #ifdef DESIGNERS
3700 	if (is_designer(side)) {
3701 		notify(side, "%s will teleport to (%d,%d).",
3702 					unit_handle(side, unit), x, y);
3703 		net_designer_teleport(unit, x, y, NULL);
3704 		return TRUE;
3705 	}
3706 #endif /* DESIGNERS */
3707 	/* Check that we can put the unit at the destination. */
3708 	if (!side_thinks_it_can_put_type_at(side, unit->type, x, y)) {
3709 		notify(side, "%s cannot move to (%d,%d). No room.",
3710 					unit_handle(side, unit), x, y);
3711 		notify(side,
3712 		       "Please pick a new destination (or Escape to cancel).");
3713 		return FALSE;
3714 	}
3715 	/* Disable pre-flight command checks for now, since we don't
3716 	have any path-finding. */
3717 	if (1 /*choose_move_direction(unit, x, y, 0) >= 0*/) {
3718 		notify(side, "%s will move to (%d,%d).",
3719 					unit_handle(side, unit), x, y);
3720 		net_set_move_to_task(unit, x, y, 0);
3721 		return TRUE;
3722     	} else {
3723 		notify(side, "%s cannot move to (%d,%d). No way there.",
3724 					unit_handle(side, unit), x, y);
3725 		notify(side,
3726 		       "Please pick a new destination (or Escape to cancel).");
3727 		return FALSE;
3728     	}
3729 }
3730 
3731 /* Common user interface implementation of the 'build' command. */
3732 
3733 int
impl_build(Side * side,Unit * unit,int u2,Unit * transport,int x,int y,int n)3734 impl_build(
3735     Side *side, Unit *unit, int u2, Unit *transport, int x, int y, int n)
3736 {
3737 	int range, u = unit->type;
3738 
3739 	/* This is to support the curses interface, which cannot
3740            pick places to build and therefore passes 0 as x and y. */
3741 	if (x == -1 && y == -1) {
3742 	    x = unit->x;
3743 	    y = unit->y;
3744 	}
3745 	/* Check that build is possible before proceeding. */
3746 	if (!unit_can_build_type_at(unit, u2, x, y)) {
3747 	    return FALSE;
3748 	}
3749 	/* If mobile builder, we overrule doctrines and default run
3750 	   length since we want to ask where to build each new unit. */
3751 	if (mobile(u) && n <= 0) {
3752 	    n = 1;
3753 	}
3754 	/* Check the doctrine. */
3755 	if (n < 0) {
3756 	    n = construction_run_doctrine(unit, u2);
3757 	}
3758 	/* If no doctrine, we default to 1. */
3759 	if (n <= 0) {
3760 	    n = 1;
3761 	}
3762 	notify(side, "%s will build %d %s at (%d,%d).",
3763 	       unit_handle(side, unit), n, u_type_name(u2), x, y);
3764 	if (transport && valid(can_create_in(unit, unit, u2, transport)))
3765 	    net_set_construct_task(unit, u2, n, transport->id, -1, -1);
3766 	else
3767 	    net_set_construct_task(unit, u2, n, -1, x, y);
3768 	/* We may need to move to get to where we're supposed to build.
3769 	    Just push a task to get within range. */
3770 	range = min(uu_create_range(u, u2), uu_build_range(u, u2));
3771 	if (distance(x, y, unit->x, unit->y) > range) {
3772 	    net_push_move_to_task(unit, x, y, range);
3773 	}
3774 	return TRUE;
3775 }
3776 
3777 /* Common user interface implementation of the 'change-type' command. */
3778 
3779 int
impl_change_type(Side * side,Unit * unit,int u2)3780 impl_change_type(Side *side, Unit *unit, int u2)
3781 {
3782     int rslt = -1;
3783 
3784     if (!type_can_occupy_cell_without(u2, unit->x, unit->y, unit)) {
3785         cmd_error(side, "%s cannot change into a %s: not enough room.",
3786                   unit_handle(side, unit), u_type_name(u2));
3787         return FALSE;
3788     }
3789     rslt = check_change_type_action(unit, unit, u2);
3790     if (valid(rslt)) {
3791 	notify(side, "%s changes into a %s.",
3792 		unit_handle(side, unit), u_type_name(u2));
3793         net_prep_change_type_action(unit, unit, u2);
3794     }
3795     else {
3796         cmd_error(side, "%s cannot change into a %s: %s.",
3797                   unit_handle(side, unit), u_type_name(u2),
3798                   action_result_desc(rslt));
3799         return FALSE;
3800     }
3801     return TRUE;
3802 }
3803 
3804 /* Return the type to build that dialogs should highlight initially. */
3805 
3806 int
favored_type(Unit * unit)3807 favored_type(Unit *unit)
3808 {
3809     int u;
3810 
3811     if (unit == NULL)
3812       return NONUTYPE;
3813     /* If a type is already in the works, return it. */
3814     if (unit->plan
3815 	&& unit->plan->tasks
3816 	&& unit->plan->tasks->type == TASK_BUILD)
3817       return unit->plan->tasks->args[0];
3818     /* Return the first buildable type found. */
3819     for_all_unit_types(u) {
3820 	if (unit_can_build_type(unit, u))
3821 	  return u;
3822     }
3823     return NONUTYPE;
3824 }
3825 
3826 void
set_unit_image(Unit * unit)3827 set_unit_image(Unit *unit)
3828 {
3829     int u = unit->type, choice, i = 0;
3830     ImageFamily *imf;
3831 
3832     /* Some interfaces don't have images. */
3833     if (!uimages) {
3834     	return;
3835     }
3836     /* Use the preassigned image if available. */
3837     if (!empty_string(unit->image_name)) {
3838     	imf = find_imf(unit->image_name);
3839     	if (imf) {
3840 	    unit->imf = imf;
3841 	    return;
3842     	}
3843     }
3844     /* Else pick a random image from the list for this unit type. */
3845     choice = xrandom(numuimages[u]);
3846     for (imf = uimages[u]; imf != NULL; imf = imf->next) {
3847 	if (i == choice) {
3848 	    unit->imf = imf;
3849 	    unit->image_name = imf->name;
3850 	    break;
3851     	}
3852 	++i;
3853     }
3854 }
3855 
3856 void
set_unit_view_image(UnitView * uview)3857 set_unit_view_image(UnitView *uview)
3858 {
3859     int u = uview->type, choice, i = 0;
3860     ImageFamily *imf;
3861 
3862     /* Some interfaces don't have images. */
3863     if (!uimages) {
3864     	return;
3865     }
3866     /* Use the preassigned image if available. */
3867     if (!empty_string(uview->image_name)) {
3868     	imf = find_imf(uview->image_name);
3869     	if (imf) {
3870 	    uview->imf = imf;
3871 	    uview->image_name = imf->name;
3872 	    return;
3873     	}
3874     }
3875     /* Else pick a random image from the list for this unit type. */
3876     choice = xrandom(numuimages[u]);
3877     for (imf = uimages[u]; imf != NULL; imf = imf->next) {
3878 	if (i == choice) {
3879 	    uview->imf = imf;
3880 	    uview->image_name = imf->name;
3881 	    break;
3882     	}
3883 	++i;
3884     }
3885 }
3886 
3887 ImageFamily *
get_unit_type_images(Side * side,int u)3888 get_unit_type_images(Side *side, int u)
3889 {
3890     Obj *image_name, *rest;
3891     char *name;
3892     ImageFamily *imf = NULL, *nextimf;
3893 
3894     if (!numuimages) {
3895     	numuimages = (int *) xmalloc(numutypes * sizeof(int));
3896     }
3897     image_name = u_image_name(u);
3898     if (image_name != lispnil) {
3899 	if (stringp(image_name)) {
3900 	    name = c_string(image_name);
3901 	    imf = get_generic_utype_images(u, name);
3902 	    ++numuimages[u];
3903 	} else if (consp(image_name)) {
3904 	    for_all_list(image_name, rest) {
3905 		if (stringp(car(rest))) {
3906 		    name = c_string(car(rest));
3907 		    if (imf) {
3908 			nextimf->next = get_generic_utype_images(u, name);
3909 			nextimf = nextimf->next;
3910 		    } else {
3911 			imf = get_generic_utype_images(u, name);
3912 			nextimf = imf;
3913 		    }
3914 		    ++numuimages[u];
3915 		}
3916 	    }
3917 	}
3918     } else {
3919       name = u_internal_name(u);
3920 	imf = get_generic_utype_images(u, name);
3921 	++numuimages[u];
3922     }
3923     return imf;
3924 }
3925 
3926 ImageFamily *
get_generic_utype_images(int u,char * name)3927 get_generic_utype_images(int u, char *name)
3928 {
3929     ImageFamily *imf = NULL;
3930 
3931     /* Get IMF corresponding to name. */
3932     imf = get_generic_images(name);
3933     /* If an image is void, then substitute a default one. */
3934     if (imf != NULL && imf->numsizes == 0) {
3935 	imf->ersatz = TRUE;
3936 	imf = add_default_unit_image(imf, u);
3937     }
3938     /* Register image for posterity. */
3939     record_imf_get(imf);
3940 #if (0)
3941     /* Check if the largest image is wider than 32 pixels. */
3942     if (imf->images->w > 32) {
3943     	big_unit_images = TRUE;
3944     }
3945 #endif
3946     return imf;
3947 }
3948 
3949 static ImageFamily *
add_default_unit_image(ImageFamily * imf,int u)3950 add_default_unit_image(ImageFamily *imf, int u)
3951 {
3952     int i, hi, lo;
3953     Image *img;
3954 
3955     img = get_img(imf, 16, 16);
3956     if (img == NULL)
3957       return imf;
3958     img->rawmonodata = (char *)xmalloc(32);
3959     img->rawmaskdata = (char *)xmalloc(32);
3960     hi = u >> 4;
3961     lo = (u & 0xf) << 4;
3962     for (i = 4; i < 28; i += 2) {
3963 	(img->rawmonodata)[i] = hi;
3964 	(img->rawmonodata)[i + 1] = lo;
3965 	(img->rawmaskdata)[i] = 0x7f;
3966 	(img->rawmaskdata)[i + 1] = 0xfe;
3967     }
3968     (img->rawmonodata)[14] = 0x1f;
3969     (img->rawmonodata)[15] = 0xf8;
3970     (img->rawmonodata)[16] = 0x1f;
3971     (img->rawmonodata)[17] = 0xf8;
3972     (img->rawmaskdata)[2] = 0x7f;
3973     (img->rawmaskdata)[3] = 0xfe;
3974     (img->rawmaskdata)[28] = 0x7f;
3975     (img->rawmaskdata)[29] = 0xfe;
3976     if (imf_interp_hook)
3977       imf = (*imf_interp_hook)(imf, NULL, FALSE);
3978     return imf;
3979 }
3980 
3981 /* Acquire all imagery relating to the given material type. */
3982 
3983 ImageFamily *
get_material_type_images(Side * side,int m)3984 get_material_type_images(Side *side, int m)
3985 {
3986     char *name;
3987     ImageFamily *imf;
3988 
3989     if (!empty_string(m_image_name(m)))
3990       name = m_image_name(m);
3991     else
3992       name = m_type_name(m);
3993     imf = get_generic_images(name);
3994     if (imf != NULL && imf->numsizes == 0) {
3995 	imf->ersatz = TRUE;
3996 	imf = add_default_material_image(imf, m);
3997     }
3998     record_imf_get(imf);
3999     return imf;
4000 }
4001 
4002 /* The default material image is ugly but functional; basically a
4003    binary encoding of the material type number.  Also add a shade of
4004    gray, for use when solid colors are wanted. */
4005 
4006 static ImageFamily *
add_default_material_image(ImageFamily * imf,int m)4007 add_default_material_image(ImageFamily *imf, int m)
4008 {
4009     int gray;
4010     Image *img;
4011 
4012     img = get_img(imf, 8, 8);
4013     img->istile = TRUE;
4014     img->rawmonodata = (char *)xmalloc(8);
4015     (img->rawmonodata)[1] = (m << 2);
4016     (img->rawmonodata)[2] = (m << 2);
4017     (img->rawmonodata)[3] = 0x7e;
4018     (img->rawmonodata)[4] = (m << 2);
4019     (img->rawmonodata)[5] = (m << 2);
4020     /* Also add a shade of gray. */
4021     img = get_img(imf, 1, 1);
4022     img->istile = TRUE;
4023     /* Range from dark to light gray. */
4024     gray = 15000 + ((m * 45000) / nummtypes);
4025     img->palette = cons(cons(new_number(0),
4026 			     cons(new_number(gray),
4027 				  cons(new_number(gray),
4028 				       cons(new_number(gray), lispnil)))),
4029 			lispnil);
4030     if (imf_interp_hook)
4031       imf = (*imf_interp_hook)(imf, NULL, FALSE);
4032     return imf;
4033 }
4034 
4035 /* Acquire all imagery relating to the given terrain type. */
4036 
4037 ImageFamily *
get_terrain_type_images(Side * side,int t)4038 get_terrain_type_images(Side *side, int t)
4039 {
4040     char *name;
4041     ImageFamily *imf;
4042 
4043     if (!empty_string(t_image_name(t)))
4044       name = t_image_name(t);
4045     else
4046       name = t_type_name(t);
4047     imf = get_generic_images(name);
4048     if (imf != NULL && imf->numsizes == 0) {
4049 	imf->ersatz = TRUE;
4050 	imf = add_default_terrain_image(imf, t);
4051     }
4052     record_imf_get(imf);
4053     return imf;
4054 }
4055 
4056 /* The default terrain image is ugly but functional; basically a binary
4057    encoding of the terrain type number. */
4058 
4059 static ImageFamily *
add_default_terrain_image(ImageFamily * imf,int t)4060 add_default_terrain_image(ImageFamily *imf, int t)
4061 {
4062     Image *img;
4063 
4064     img = get_img(imf, 8, 8);
4065     img->istile = TRUE;
4066     img->rawmonodata = (char *)xmalloc(8);
4067     (img->rawmonodata)[1] = (t << 2);
4068     (img->rawmonodata)[2] = (t << 2);
4069     (img->rawmonodata)[3] = 0x7e;
4070     (img->rawmonodata)[4] = (t << 2);
4071     (img->rawmonodata)[5] = (t << 2);
4072     if (imf_interp_hook)
4073       imf = (*imf_interp_hook)(imf, NULL, FALSE);
4074     return imf;
4075 }
4076 
4077 ImageFamily *
get_unseen_images(Side * side)4078 get_unseen_images(Side *side)
4079 {
4080     if (!empty_string(g_unseen_color())) {
4081 	unseen_image = get_generic_images(g_unseen_color());
4082 	if (unseen_image != NULL && unseen_image->numsizes == 0) {
4083 	    /* Appears to have failed - clear the unseen image then. */
4084 	    unseen_image = NULL;
4085 	    /* Note that we shouldn't try to free the imf, because it
4086 	       may be in use elsewhere. */
4087 	}
4088     }
4089     record_imf_get(unseen_image);
4090     return unseen_image;
4091 }
4092 
4093 ImageFamily *
get_emblem_images(Side * side,Side * side2)4094 get_emblem_images(Side *side, Side *side2)
4095 {
4096     char *s, *c, *name, tmpembuf[BUFSIZE];
4097     int s2 = side_number(side2);
4098     ImageFamily *imf;
4099 
4100     if (side2 == NULL) {
4101 	name = "s0";
4102     } else if (!empty_string(side2->emblemname)) {
4103 	name = side2->emblemname;
4104     } else if (!empty_string(side2->colorscheme)) {
4105 	/* Take the first (main) color if there are multiple colors. */
4106 	for (s = side2->colorscheme, c = tmpembuf;; ++s) {
4107 		/* Test for end of name or end of scheme. */
4108 		if (*s == ',' || *s == '\0') {
4109 			/* Terminate the name string. */
4110 			*c = '\0';
4111 			c = tmpembuf;
4112 			break;
4113 		/* Add one more character. */
4114 		} else *c++ = *s;
4115 	}
4116 	name = copy_string(tmpembuf);
4117     	/* Make sure the color emblemname is set so that it is saved
4118     	   correctly. */
4119     	net_set_side_emblemname(side, side2, name);
4120     } else {
4121 	/* There is a set of default emblems named "s1", etc. */
4122 	sprintf(tmpembuf, "s%d", s2);
4123 	name = copy_string(tmpembuf);
4124     	/* Make sure the default emblemname is set so that it is
4125     	   saved correctly. */
4126     	net_set_side_emblemname(side, side2, name);
4127     }
4128     imf = get_generic_images(name);
4129     /* If we must have an image, and none were acquired, invoke the default
4130        image getter. */
4131     if (imf != NULL && imf->numsizes == 0 && strcmp(name, "none") != 0) {
4132 	imf->ersatz = TRUE;
4133 	imf = add_default_emblem_image(imf, s2);
4134     }
4135     record_imf_get(imf);
4136     return imf;
4137 }
4138 
4139 /* For a substitute emblem, make a solid white square with stripes at
4140    top and bottom, with the encoding of the side number in the
4141    middle.  (In practice, the number emblems usually get used.) */
4142 
4143 static ImageFamily *
add_default_emblem_image(ImageFamily * imf,int s2)4144 add_default_emblem_image(ImageFamily *imf, int s2)
4145 {
4146     int i;
4147     Image *img;
4148 
4149     img = get_img(imf, 8, 8);
4150     if (img == NULL)
4151       return imf;
4152     img->rawmonodata = (char *)xmalloc(8);
4153     img->rawmaskdata = (char *)xmalloc(8);
4154     for (i = 0; i < 8; ++i) {
4155 	(img->rawmonodata)[i] = s2;
4156 	(img->rawmaskdata)[i] = 0xff;
4157     }
4158     (img->rawmonodata)[0] = 0xff;
4159     (img->rawmonodata)[7] = 0xff;
4160     if (imf_interp_hook)
4161       imf = (*imf_interp_hook)(imf, NULL, FALSE);
4162     return imf;
4163 }
4164 
4165 /* Record that the given image family was used.  This is used when
4166    saving a game, for instance. */
4167 
4168 void
record_imf_get(ImageFamily * imf)4169 record_imf_get(ImageFamily *imf)
4170 {
4171     int i;
4172     ImageFamily **new_record;
4173 
4174     if (imf == NULL)
4175       return;
4176     /* Estimate and allocate the usual amount of space needed. */
4177     if (max_recorded_imfs == 0)
4178       max_recorded_imfs = numutypes + numttypes + (MAXSIDES + 1) + 1;
4179     if (recorded_imfs == NULL) {
4180 	recorded_imfs =
4181 	  (ImageFamily **) xmalloc(max_recorded_imfs * sizeof(ImageFamily *));
4182     }
4183     /* Allocate more space if needed. */
4184     if (num_recorded_imfs >= max_recorded_imfs) {
4185 	max_recorded_imfs += max_recorded_imfs / 2;
4186 	new_record =
4187 	  (ImageFamily **) xmalloc(max_recorded_imfs * sizeof(ImageFamily *));
4188 	for (i = 0; i < num_recorded_imfs; ++i) {
4189 	    new_record[i] = recorded_imfs[i];
4190 	}
4191 	recorded_imfs = new_record;
4192     }
4193     for (i = 0; i < num_recorded_imfs; ++i) {
4194 	if (strcmp(imf->name, recorded_imfs[i]->name) == 0)
4195 	  return;
4196     }
4197     recorded_imfs[num_recorded_imfs++] = imf;
4198     /* Expand any interface-specific data into its all-interface form,
4199        so that saved games will include it.  This needs to be done
4200        now, because game saving may occur in a low-memory situation
4201        and there may not be enough memory available then. */
4202     make_generic_image_data(imf);
4203 }
4204 
4205 /* Output a general description of an image family. */
4206 
4207 void
describe_imf(Side * side,char * classname,char * imftype,ImageFamily * imf)4208 describe_imf(Side *side, char *classname, char *imftype, ImageFamily *imf)
4209 {
4210     Image *img;
4211 
4212     if (imf == NULL) {
4213 	DGprintf("No image family for %s %s for %s",
4214 		 classname, imftype, side_desig(side));
4215 	return;
4216     }
4217     DGprintf("%s %s family for %s has %d images",
4218 	     classname, imftype, side_desig(side), imf->numsizes);
4219     if (imf->location)
4220       DGprintf(" and is in %s", imf->location->name);
4221     DGprintf("\n");
4222     for_all_images(imf, img) {
4223 	DGprintf("    %dx%d", img->w, img->h);
4224 	if (img->istile)
4225 	  DGprintf(" tile");
4226 	if (img->isterrain)
4227 	  DGprintf(" terrain");
4228 	if (img->isconnection)
4229 	  DGprintf(" connection");
4230 	if (img->isborder)
4231 	  DGprintf(" border");
4232 	if (img->istransition)
4233 	  DGprintf(" transition");
4234 	if (img->numsubimages > 0)
4235 	  DGprintf(" %d subimages", img->numsubimages);
4236 	if (imf_describe_hook)
4237 	  (*imf_describe_hook)(side, img);
4238 	DGprintf("\n");
4239     }
4240 }
4241 
4242 /* Compute and cache single-char representations for things. */
4243 
4244 void
init_ui_chars(void)4245 init_ui_chars(void)
4246 {
4247     int u, t;
4248     char *str;
4249 
4250     if (unitchars == NULL) {
4251 	unitchars = (char *)xmalloc(numutypes);
4252 	for_all_unit_types(u) {
4253 	    str = u_uchar(u);
4254 	    unitchars[u] =
4255 	      (!empty_string(str) ? str[0] : utype_name_n(u, 1)[0]);
4256 	}
4257     }
4258     if (terrchars == NULL) {
4259 	terrchars = (char *)xmalloc(numttypes);
4260 	for_all_terrain_types(t) {
4261 	    str = t_char(t);
4262 	    terrchars[t] = (!empty_string(str) ? str[0] : t_type_name(t)[0]);
4263 	}
4264     }
4265     unseen_char_1 = unseen_char_2 = ' ';
4266     str = g_unseen_char();
4267     if (strlen(str) >= 1) {
4268 	unseen_char_1 = unseen_char_2 = str[0];
4269 	if (strlen(str) >= 2) {
4270 	    unseen_char_2 = str[1];
4271 	}
4272     }
4273 }
4274 
4275 void
init_unit_images(Side * side)4276 init_unit_images(Side *side)
4277 {
4278     ImageFamily *imf;
4279     Image *smallest;
4280     UnitView *uview;
4281     Unit *unit;
4282     int u;
4283 
4284     if (!uimages)
4285       uimages = (ImageFamily **) xmalloc(numutypes * sizeof(ImageFamily *));
4286     for_all_unit_types(u) {
4287 	imf = get_unit_type_images(side, u);
4288 	if (DebugG)
4289 	  describe_imf(side, "unit type", u_type_name(u), imf);
4290 	uimages[u] = imf;
4291 	/* This is used by the X11 interface only, to calculate
4292 	   image sizes for the unit type list. */
4293 	smallest = smallest_image(imf);
4294 	if (smallest) {
4295 	    if (smallest->w > min_w_for_unit_image)
4296 	      min_w_for_unit_image = smallest->w;
4297 	    if (smallest->h > min_h_for_unit_image)
4298 	      min_h_for_unit_image = smallest->h;
4299 	}
4300     }
4301     for_all_units(unit) {
4302 	if (unit->imf)
4303 	  continue;
4304 	/* Find and load any unit-specific images. */
4305 	if (unit->image_name) {
4306 	    imf = get_generic_utype_images(u, unit->image_name);
4307 	    unit->imf = imf;
4308 	}
4309 	/* Fill in any missing unit images. */
4310 	if (!unit->imf) {
4311 	    set_unit_image(unit);
4312 	}
4313     }
4314     for_all_unit_views(uview) {
4315 	if (uview->imf)
4316 	  continue;
4317 	/* Get the unit. It will provide the authoritative image name in
4318 	   the case that uview's image name is empty. */
4319 	unit = view_unit(uview);
4320 	if (!unit)
4321 	  continue;
4322 	if (empty_string(uview->image_name) && (uview->type == unit->type))
4323 	  uview->image_name = unit->image_name;
4324 	/* Find and load any unit-specific images. */
4325 	if (uview->image_name) {
4326 	    imf = get_generic_utype_images(u, uview->image_name);
4327 	    uview->imf = imf;
4328 	}
4329 	/* Fill in any missing unit view images. */
4330 	if (!uview->imf) {
4331 	    set_unit_view_image(uview);
4332 	}
4333     }
4334 }
4335 
4336 /* Write the side's view of the world, as ASCII. */
4337 
4338 /* (should be intelligent enough to cut into pages, or else document
4339    how to do it) */
4340 /* (maybe display names too somehow, perhaps as second layer?) */
4341 
4342 #define VIEWFILE "view.ccq"
4343 
4344 void
dump_text_view(Side * side,int use_both_chars)4345 dump_text_view(Side *side, int use_both_chars)
4346 {
4347     char ch1, ch2;
4348     int x, y, t, u, s, draw, i;
4349     Side *side2;
4350     Unit *unit;
4351     UnitView *uview;
4352     FILE *fp;
4353 
4354     fp = open_file(VIEWFILE, "w");
4355     if (fp != NULL) {
4356 	for (y = area.height-1; y >= 0; --y) {
4357 	    for (i = 0; i < y; ++i)
4358 	      fputc(' ', fp);
4359 	    for (x = 0; x < area.width; ++x) {
4360 		ch1 = ch2 = ' ';
4361 		if (in_area(x, y) && terrain_visible(side, x, y)) {
4362 		    t = terrain_at(x, y);
4363 		    ch1 = terrchars[t];
4364 		    ch2 = (use_both_chars ? ch1 : ' ');
4365 		    draw = FALSE;
4366 		    if (side->see_all) {
4367 			unit = unit_at(x, y);
4368 			if (unit != NULL) {
4369 			    u = unit->type;
4370 			    s = side_number(unit->side);
4371 			    draw = TRUE;
4372 			}
4373 		    } else {
4374 			uview = unit_view_at(side, x, y);
4375 			if (uview != NULL) {
4376 			    u = uview->type;
4377 			    s = uview->siden;
4378 			    draw = TRUE;
4379 			}
4380 		    }
4381 		    if (draw) {
4382 			ch1 = unitchars[u];
4383 			ch2 = ' ';
4384 			if (between(1, s, 9))
4385 			  ch2 = s + '0';
4386 			else if (s >= 10)
4387 			  /* This could get weird if s > 36, but not much
4388 			     chance of that because MAXSIDES < 31 always. */
4389 			  ch2 = s - 10 + 'A';
4390 		    }
4391 		}
4392 		fputc(ch1, fp);
4393 		fputc(ch2, fp);
4394 	    }
4395 	    fprintf(fp, "\n");
4396 	}
4397 	fprintf(fp, "\n\nTerrain Types:\n");
4398 	for_all_terrain_types(t) {
4399 	    fprintf(fp, "  %c%c  %s\n",
4400 		    terrchars[t], terrchars[t], t_type_name(t));
4401 	}
4402 	fprintf(fp, "\n\nUnit Types:\n");
4403 	for_all_unit_types(u) {
4404 	    fprintf(fp, "  %c   %s\n", unitchars[u], u_type_name(u));
4405 	}
4406 	fprintf(fp, "\n\nSides:\n");
4407 	for_all_sides(side2) {
4408 	    fprintf(fp, "  %d   %s\n", side_number(side2), side_name(side2));
4409 	}
4410 	fclose(fp);
4411 	notify(side, "Dumped area view to \"%s\".", VIEWFILE);
4412     } else {
4413 	notify(side, "Can't open \"%s\"!!", VIEWFILE);
4414     }
4415 }
4416 
4417 /* Return the type of cell terrain that the given side sees at the given
4418    location. */
4419 
4420 int
terrain_seen_at(Side * side,int x,int y)4421 terrain_seen_at(Side *side, int x, int y)
4422 {
4423     /* We are outside the map. */
4424     if (!in_area(x, y)) {
4425     	return BACKTTYPE;
4426     }
4427     /* The see all terrain cases. */
4428     if (side->see_all
4429     	|| side->show_all
4430 #ifdef DESIGNERS
4431 	|| side->designer
4432 #endif /* DESIGNERS */
4433         ) {
4434     	return terrain_at(x, y);
4435     }
4436     /* The normal case. */
4437     if (terrain_view(side, x, y) != UNSEEN) {
4438 	return terrain_at(x, y);
4439     } else {
4440 	return NONTTYPE;
4441     }
4442 }
4443 
4444 /* (should remove both of these, only used in ps.c currently) */
4445 /* Return the unit seen by the given side at the given location.  Note
4446    that this should not be used casually by interfaces, since the result
4447    is a pointer to a real unit, not a view of one. */
4448 /* (should result depend on contents of stack?) */
4449 
4450 Unit *
unit_seen_at(Side * side,int x,int y)4451 unit_seen_at(Side *side, int x, int y)
4452 {
4453     if (!in_area(x, y))
4454       return NULL;
4455     if (side->see_all
4456     	|| side->show_all
4457 #ifdef DESIGNERS
4458 	|| side->designer
4459 #endif /* DESIGNERS */
4460 	|| cover(side, x, y) > 0)
4461       return unit_at(x, y);
4462     return NULL;
4463 }
4464 
4465 int
utype_seen_at(Side * side,int x,int y)4466 utype_seen_at(Side *side, int x, int y)
4467 {
4468     Unit *unit;
4469 #if 0
4470     UnitView *uview;
4471 #endif
4472 
4473     if (!in_area(x, y))
4474       return NONUTYPE;
4475     unit = unit_seen_at(side, x, y);
4476     if (unit)
4477       return unit->type;
4478 
4479 #if 0 /* for now */
4480     uview = unit_view_at(side, x, y);
4481     if (uview != NULL)
4482       return uview->type;
4483 #endif
4484 
4485     return NONUTYPE;
4486 }
4487