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