home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xconq55.zip / xc5.5 / draw.c < prev    next >
C/C++ Source or Header  |  1991-06-16  |  18KB  |  600 lines

  1. /* Copyright (c) 1987, 1988, 1991  Stanley T. Shebs. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. /* Geometry is somewhat tricky because our viewports are supposed */
  6. /* to wrap around a cylinder transparently.  The general idea is that */
  7. /* if modulo opns map x coordinates onto a cylinder, adding and subtracting */
  8. /* the diameter of the cylinder "un-modulos" things.  If this doesn't make */
  9. /* any sense to you, then be careful about fiddling with the code! */
  10.  
  11. /* If that wasn't bad enough, the hexes constitute an oblique coordinate */
  12. /* where the axes form a 60 degree angle to each other.  Fortunately, no */
  13. /* trig is necessary - to convert to/from rectangular, add/subtract 1/2 of */
  14. /* the y coordinate to x, and leave the y coordinate alone. */
  15.  
  16. /* The graphical code uses mostly text drawing functions, which are more */
  17. /* likely to be efficient than is random bitblting.  (The interface may */
  18. /* implement the operations as random blitting, but that's OK.) */
  19.  
  20. #include "config.h"
  21. #include "misc.h"
  22. #include "period.h"
  23. #include "side.h"
  24. #include "unit.h"
  25. #include "map.h"
  26.  
  27. extern bool populations;        /* used to decide about running pop display */
  28.  
  29. char rowbuf[BUFSIZE];           /* buffer for terrain row drawing */
  30.  
  31.  
  32. /* Completely redo a screen, making no assumptions about appearance. */
  33. /* This one is used frequently, especially when a window is exposed. */
  34.  
  35. redraw(side)
  36. Side *side;
  37. {
  38.     if (active_display(side)) {
  39.     erase_cursor(side);
  40. #if 0
  41.     clear_window(side, side->main);
  42. #endif
  43. #if 1
  44.     show_note(side,TRUE);
  45.     show_info(side);
  46.     show_prompt(side);
  47.     show_all_sides(side,TRUE);
  48.     show_timemode(side,TRUE);
  49.     show_clock(side);
  50.     show_state(side,TRUE);
  51.     show_map(side);
  52.     show_world(side);
  53. #endif
  54.     flush_output(side);
  55.     flush_input(side);
  56.     }
  57. }
  58.  
  59. /* Put the point x, y in the center of the screen.  Limit to make sure */
  60.  /* it keeps the screen properly aligned.  */
  61.  
  62. void recenter(side, x, y)
  63. Side *side;
  64. int x, y;
  65. {
  66.   int oldx, oldy;
  67.  
  68.   oldx = side->vcx;
  69.   oldy = side->vcy;
  70.   side->vcx = wrap(x);
  71.   side->vcy = min(max(side->vh2-(1-(side->vh&1)), y),
  72.           (world.height-1)-side->vh2);
  73.   if ((side->vcx == oldx) && (side->vcy == oldy)) ;
  74.   else {
  75.     if (side->lastvcx >= 0) undraw_box(side);
  76.     draw_box(side);
  77.     show_map(side);
  78.     flush_output(side);
  79.     flush_input(side);
  80.   }
  81. }
  82. /* Ensure that given location is visible.  We also flush the input because */
  83. /* any input relating to a different screen is probably worthless. */
  84.  
  85. put_on_screen(side, x, y)
  86. Side *side;
  87. int x, y;
  88. {
  89.  
  90.     /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
  91.     if (x == 0 && y == 0) return;
  92.     if (active_display(side))
  93.     if (!in_middle(side, x, y)) 
  94.       recenter(side, x, y);
  95. }
  96.  
  97. /* Undo the wrapping effect, relative to viewport location. */
  98. /* Note that both conditions cannot both be true at the same time, */
  99. /* since viewport is smaller than map. */
  100.  
  101. /* This function has been updated to calculate the exact locations of */
  102.  /* the hex in the viewport, even if the viewport is small. */
  103.  
  104. int unwrap(side, x, y)
  105. Side *side;
  106. int x, y;
  107. {
  108.     int vcx = side->vcx, vw2 = side->vw2, odd = side->vw_odd;
  109.     int vcy = side->vcy;
  110.     int left_hex, right_hex;
  111.     
  112.     left_hex = vcx - vw2 - ((y - vcy) / 2) - (y > vcy);
  113.     right_hex = vcx + vw2 + odd - ((y - vcy) / 2) + ((y < vcy) && odd)
  114.       - ((y > vcy) && !odd);
  115.  
  116.     if ((left_hex < 0) && (x > right_hex)) x -= world.width;
  117.     else if ((right_hex >= world.width) && (x < left_hex)) x += world.width;
  118.     return x;
  119. }
  120.  
  121. /* Decide whether given location is not too close to edge of screen. */
  122. /* We do this because it's a pain to move units when half the adjacent */
  123. /* places aren't even visible.  This routine effectively places a lower */
  124. /* limit of 5x5 for the map window. (I think) */
  125.  
  126. in_middle(side, x, y)
  127. Side *side;
  128. int x, y;
  129. {
  130.     int vcx = side->vcx, vw2 = side->vw2, odd = side->vw_odd;
  131.     int vcy = side->vcy, vh2 = side->vh2;
  132.     int left_hex, right_hex;
  133.     
  134. /* changed so that units on top and bottom lines do the right thing. */
  135.     if (!between(vcy-vh2+2, y, vcy+vh2-1) && between(1, y, world.height-2))
  136.     return FALSE;
  137.     left_hex = vcx - vw2 - ((y - vcy) / 2) - (y > vcy) + 3;
  138.     right_hex = vcx + vw2 + odd - ((y - vcy) / 2) + ((y < vcy) && odd)
  139.       - ((y > vcy) && !odd) - 3;
  140.  
  141.     if (left_hex < 0) return !between(right_hex, x, left_hex + world.width);
  142.     else if (right_hex >= world.width)
  143.       return !between(right_hex - world.width, x, left_hex);
  144.     return between(left_hex, x, right_hex);
  145. }
  146.  
  147. /* Transform map coordinates into screen coordinates, relative to the given */
  148. /* side.  Allow for cylindricalness and number of pixels in a hex. */
  149.  
  150. xform(side, x, y, sxp, syp)
  151. Side *side;
  152. int x, y, *sxp, *syp;
  153. {
  154.   
  155.     *sxp = ((side->hw * (x - (side->vcx - side->vw2))) +
  156.         (side->hw * (y - side->vcy)) / 2);
  157.     *syp = side->hch * ((side->vcy + side->vh2) - y);
  158. }
  159.  
  160. /* Un-transform screen coordinates (as supplied by mouse perhaps) into */
  161. /* map coordinates.  This doesn't actually account for the details of */
  162. /* hexagonal boundaries, and actually discriminates box-shaped areas. */
  163.  
  164. deform(side, sx, sy, xp, yp)
  165. Side *side;
  166. int sx, sy, *xp, *yp;
  167. {
  168.     int vcx = side->vcx, vcy = side->vcy, adjust;
  169.  
  170.     *yp = (vcy + side->vh2) - (sy / side->hch);
  171.     adjust = (((*yp - vcy) & 1) ? ((side->hw/2) * (*yp >= vcy ? 1 : -1)) : 0);
  172.     *xp = wrap(((sx - adjust) /    side->hw) - (*yp - vcy) / 2 +
  173.            (vcx - side->vw2));
  174. }
  175.  
  176. /* Transform coordinates in the world display.  Simpler, since no moving */
  177. /* viewport nonsense. */
  178.  
  179. w_xform(side, x, y, sxp, syp)
  180. Side *side;
  181. int x, y, *sxp, *syp;
  182. {
  183.     *sxp = side->mm * x + (side->mm * y) / 2;
  184.     *syp = side->mm * (world.height - 1 - y);
  185. }
  186.  
  187. /* Translate Cartesian pixels back to map coords. */
  188.  
  189. w_deform(side, sx, sy, xp, yp)
  190. Side *side;
  191. int sx, sy, *xp, *yp;
  192. {
  193.     *yp = world.height - 1 - sy / side->mm;
  194.     *xp = sx / side->mm - (*yp) / 2;
  195.     *xp = wrap(*xp);
  196. }
  197.  
  198. /* Redraw the map of the whole world.  We use square blobs instead of icons, */
  199. /* since individual "hexes" may be as little as 1x1 pixels in size, and */
  200. /* there are lots of them.  Algorithm uses run-length encoding to find and */
  201. /* draw bars of constant color.  Monochrome just draws units - no good */
  202. /* choices for terrain display. */
  203.  
  204. show_world(side)
  205. Side *side;
  206. {
  207.     int x, y, color, barcolor, x1;
  208.  
  209.     if (active_display(side) && world_display(side)) {
  210.     clear_window(side, side->world);
  211.     for (y = world.height-1; y >= 0; --y) {
  212.         x1 = 0;
  213.         barcolor = world_color(side, x1, y);
  214.         for (x = 0; x < world.width; ++x) {
  215.         color = world_color(side, x, y);
  216.         if (color != barcolor) {
  217.             draw_bar(side, x1, y, x - x1, barcolor);
  218.             x1 = x;
  219.             barcolor = color;
  220.         }
  221.         }
  222.         draw_bar(side, x1, y, world.width - x1, barcolor);
  223.     }
  224.     if (side->vcy >= 0) draw_box(side);
  225.     }
  226. }
  227.  
  228. /* Compute the color representing a hex from the given side's point of view. */
  229.  
  230. world_color(side, x, y)
  231. Side *side;
  232. int x, y;
  233. {
  234.     viewdata view = side_view(side, x, y);
  235.     Side *side2;
  236.  
  237.     if (side->monochrome) {
  238.     return ((view == EMPTY) ? side->bgcolor : side->fgcolor);
  239.     } else {
  240.     if (view == UNSEEN) {
  241.         return (side->bgcolor);
  242.     } else if (view == EMPTY) {
  243.         return (side->hexcolor[terrain_at(x, y)]);
  244.     } else {
  245.         side2 = side_n(vside(view));
  246.         return ((side2 == NULL) ? side->neutcolor :
  247.             (allied_side(side2, side) ? side->altcolor :
  248.              side->enemycolor));
  249.     }
  250.     }
  251. }
  252.  
  253. /* Draw an outline box on the world map.  Since we adopt the dubious trick */
  254. /* of inverting through all planes, must be careful to undo before moving; */
  255. /* also, draw/undraw shiftedly so both boxes appear on both sides of world. */
  256.  
  257. draw_box(side)
  258. Side *side;
  259. {
  260.     invert_box(side, side->vcx, side->vcy);
  261.     invert_box(side, side->vcx - world.width, side->vcy);
  262.     side->lastvcx = side->vcx;  side->lastvcy = side->vcy;
  263. }
  264.  
  265. undraw_box(side)
  266. Side *side;
  267. {
  268.     invert_box(side, side->lastvcx, side->lastvcy);
  269.     invert_box(side, side->lastvcx - world.width, side->lastvcy);
  270. }
  271.  
  272. /* Draw immediate area in more detail.  We make a little effort to avoid */
  273. /* drawing hexes off the visible part of the screen, but are still somewhat */
  274. /* conservative, so as not to get holes in the display.  Implication is that */
  275. /* some lower level of routines has to be able to clip the map window. */
  276.  
  277. show_map(side)
  278. Side *side;
  279. {
  280.     int y1, y2, y, x1, x2, adj;
  281.  
  282.     if (active_display(side)) {
  283.     clear_window(side, side->map);
  284.     y1 = side->vcy + side->vh2;
  285.     y2 = side->vcy - side->vh2 + 1 - (side->vh & 1);
  286.     for (y = y1; y >= y2; --y) {
  287.         adj = (y - side->vcy) / 2;
  288.         x1 = side->vcx - side->vw2 - adj - 1;
  289.         x2 = side->vcx + side->vw2 - adj + 1 + (side->vw & 1);
  290.         draw_row(side, x1, y, x2 - x1);
  291.     }
  292.     draw_cursor(side);
  293.     flush_output(side);
  294. #if 0
  295.     draw_box(side);  /* must be after flush */
  296. #endif
  297.     }
  298. }
  299.  
  300. /* Draw an individual detailed hex, as a row of one. */
  301. /* This routine may be called in cases where the hex is not on the main */
  302. /* screen;  if so, then the world map but not the local map is drawn on. */
  303. /* (should the display be shifted to make visible?) */
  304.  
  305. draw_hex(side, x, y, flushit)
  306. Side *side;
  307. int x, y;
  308. bool flushit;
  309. {
  310.     int sx, sy;
  311.  
  312.     if (active_display(side)) {
  313.     if (side->monochrome || side->showmode == TERRICONS) {
  314.         xform(side, unwrap(side, x, y), y, &sx, &sy);
  315.         draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
  316.     }
  317.     draw_row(side, unwrap(side, x, y), y, 1);
  318.     draw_bar(side, x, y, 1, world_color(side, x, y));
  319.     if (flushit) flush_output(side);
  320.     }
  321. }
  322.  
  323. /* Return the color of the hex. (for color displays only) */
  324.  
  325. long hex_color(side, x, y)
  326. Side *side;
  327. int x, y;
  328. {
  329.   long color;
  330.  
  331.   color = ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
  332.         side->hexcolor[terrain_at(wrap(x), y)]);
  333.   return (color);
  334. }
  335.  
  336. /* The basic map drawing routine does an entire row at a time, which yields */
  337. /* order-of-magnitude speedups (!).  This routine is complicated by several */
  338. /* tricks:  1) in monochrome, the entire line can be drawn at once; 2) in */
  339. /* color, run-length encoding maximizes the length of constant-color strings */
  340. /* and 3) anything which is in the background color need not be drawn. */
  341. /* In general, this routine dominates the map viewing process, so efficiency */
  342. /* here is very important. */
  343.  
  344. draw_row(side, x0, y0, len)
  345. Side *side;
  346. int x0, y0, len;
  347. {
  348.     bool empty = TRUE;
  349.     char ch;
  350.     int i = 0, x, x1, sx, sy;
  351.     long color, segcolor;
  352.  
  353.     if (side->monochrome) {
  354.     xform(side, x0, y0, &sx, &sy);
  355.     for (x = x0; x < x0 + len; ++x) {
  356.         if (side_view(side, wrap(x), y0) == EMPTY) {
  357.         rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
  358.         empty = FALSE;
  359.         } else {
  360.         rowbuf[i++] = ' ';
  361.         }
  362.     }
  363.     if (!empty) draw_terrain_row(side, sx, sy, rowbuf, i, side->fgcolor);
  364.     } else {
  365.     x1 = x0;
  366.     segcolor = hex_color(side, x0, y0);
  367.     for (x = x0; x < x0 + len; ++x) {
  368.         color = hex_color(side, x, y0);
  369.         sx = side->bgcolor;
  370.         if (color != segcolor) {
  371.         if (segcolor != side->bgcolor) {
  372.             xform(side, x1, y0, &sx, &sy);
  373.             draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  374.         }
  375.         i = 0;
  376.         x1 = x;
  377.         segcolor = color;
  378.         }
  379.         switch(side->showmode) {
  380.         case FULLHEX:
  381.         case BOTHICONS:
  382.         ch = HEX;
  383.         break;
  384.         case BORDERHEX:
  385.         ch = OHEX;
  386.         break;
  387.         case TERRICONS:
  388.         ch = ttypes[terrain_at(wrap(x), y0)].tchar;
  389.         break;
  390.         }
  391.         rowbuf[i++] = ch;
  392.     }
  393.     if (len == 1) i = 1;
  394.     xform(side, x1, y0, &sx, &sy);
  395.     draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  396.     if (side->showmode == BOTHICONS) {
  397.         i = 0;
  398.         x1 = x0;
  399.         segcolor = terricon_color(side, x0, y0);
  400.         for (x = x0; x < x0 + len; ++x) {
  401.         color = terricon_color(side, x, y0);
  402.         if (color != segcolor) {
  403.             xform(side, x1, y0, &sx, &sy);
  404.             draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  405.             i = 0;
  406.             x1 = x;
  407.             segcolor = color;
  408.         }
  409.         rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
  410.         }
  411.         if (len == 1) i = 1;
  412.         xform(side, x1, y0, &sx, &sy);
  413.         draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  414.     }
  415.     }
  416.     /* Units are much harder to optimize - fortunately they're sparse */
  417.     for (x = x0; x < x0 + len; ++x) {
  418.     draw_unit(side, x, y0);
  419.     }
  420. }
  421.  
  422. /* Return the color of a terrain icon overlaying a colored hex. */
  423.  
  424. terricon_color(side, x, y)
  425. Side *side;
  426. int x, y;
  427. {
  428.     return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
  429.         (ttypes[terrain_at(wrap(x), y)].dark ? side->fgcolor :
  430.          side->bgcolor));
  431. }
  432.  
  433. /* Draw a single unit icon as appropriate.  This *also* has a bunch of */
  434. /* details to worry about: centering of icon in hex, clearing a rectangular */
  435. /* area for the icon, picking a color for the unit, using either a bitmap */
  436. /* or font char, and adding a side number for many-player games. */
  437. /* Must also be careful not to draw black-on-black for units in space. */
  438. /* This routine has also been drafted into drawing populace side numbers */
  439. /* for otherwise empty hexes. */
  440.  
  441. draw_unit(side, x, y)
  442. Side *side;
  443. int x, y;
  444. {
  445.     viewdata view = side_view(side, wrap(x), y);
  446.     int sx, sy, ucolor, hcolor, n;
  447.     int terr = terrain_at(wrap(x), y);
  448.     Side *side2;
  449.  
  450.     if (view != UNSEEN) {
  451.     if (view == EMPTY) {
  452. /*        if (populations) {
  453.         pop = people_at(wrap(x), y);
  454.         if (pop != NOBODY) {
  455.             side2 = side_n(pop-8);
  456.             pcolor = (allied_side(side, side2) ? side->owncolor :
  457.                   (enemy_side(side, side2) ? side->enemycolor :
  458.                    side->neutcolor));
  459.             if (pcolor == side->owncolor && 
  460.             (ttypes[terr].dark ||
  461.              side->monochrome ||
  462.              (side->showmode == TERRICONS)))
  463.             pcolor = side->fgcolor;
  464.             xform(side, x, y, &sx, &sy);
  465.             draw_side_number(side, side->map, sx, sy, pop-8, pcolor);
  466.         }
  467.         } */
  468.     } else {
  469.         xform(side, x, y, &sx, &sy);
  470.         side2 = side_n(vside(view));
  471.         ucolor = (allied_side(side, side2) ? side->owncolor :
  472.               (enemy_side(side, side2) ? side->enemycolor :
  473.                side->neutcolor));
  474.         if (ucolor == side->owncolor && 
  475.         (ttypes[terr].dark ||
  476.          side->monochrome ||
  477.          (side->showmode == TERRICONS)))
  478.         ucolor = side->fgcolor;
  479.         if (side->monochrome && side != side2)
  480.         ucolor = side->bgcolor;
  481.         hcolor = (side == side2 ? side->bgcolor : side->fgcolor);
  482.         if (side->monochrome) {
  483.         /* erasing background */
  484.         draw_hex_icon(side, side->map, sx, sy, hcolor, HEX);
  485.         } else if (side->showmode != TERRICONS) {
  486.         draw_hex_icon(side, side->map, sx, sy, hex_color(side, x, y),
  487.                   ((side->showmode == BORDERHEX) ? OHEX : HEX));
  488.         }
  489.         draw_unit_icon(side, side->map, sx, sy, vtype(view), ucolor);
  490.         n = side_number(side2);
  491.         if ((numsides > 2 || side->monochrome) && n != side_number(side)) {
  492.         draw_side_number(side, side->map, sx, sy, n, ucolor);
  493.         }
  494.     }
  495.     }
  496. }
  497.  
  498. /* Cursor drawing also draws the unit in some other color if it's not the */
  499. /* "top-level" unit in a hex, as well as getting the player's attention */
  500. /* if the new location is sufficiently far from the last. */
  501.  
  502. draw_cursor(side)
  503. Side *side;
  504. {
  505.     int sx, sy;
  506.  
  507.     if (active_display(side)) {
  508.     /* ugly hack to prevent extra cursor draw */
  509.     if (side->cury == 0) return;
  510.     xform(side, unwrap(side, side->curx, side->cury), side->cury, &sx, &sy);
  511.     if (side->curunit != NULL && side->curunit->transport != NULL) {
  512.         if (side->monochrome) {
  513.         draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
  514.         draw_unit_icon(side, side->map, sx, sy,
  515.                    side->curunit->type, side->fgcolor);
  516.         } else {
  517.         draw_unit_icon(side, side->map, sx, sy,
  518.                    side->curunit->type, side->diffcolor);
  519.         }
  520.     }
  521.     /* Flash something to draw the eye a long ways */
  522.     if (humanside(side)) {
  523.         if (distance(side->curx, side->cury, side->lastx, side->lasty) > 3)
  524.         flash_position(side, sx, sy, 100);
  525.         draw_cursor_icon(side, sx, sy);
  526.         side->lastx = side->curx;  side->lasty = side->cury;
  527.     }
  528.     }
  529. }
  530.  
  531. /* Get rid of cursor by redrawing the hex. */
  532.  
  533. erase_cursor(side)
  534. Side *side;
  535. {
  536.     if (side->lasty > 0) draw_hex(side, side->lastx, side->lasty, TRUE);
  537. }
  538.  
  539. /* Draw a splat visible to both sides at a given location.  Several splats */
  540. /* available, depending on the seriousness of the hit.  Make an extra-flashy */
  541. /* display when The Bomb goes off.  Because of the time delays involved, we */
  542. /* have to update both sides' displays more or less simultaneously. Would be */
  543. /* better to exhibit to all sides maybe, but I'm not going to bother! */
  544.  
  545. draw_blast(unit, es, hit)
  546. Unit *unit;
  547. Side *es;
  548. int hit;
  549. {
  550.     char ch;
  551.     int ux = unit->x, uy = unit->y, sx, sy, i;
  552.     Side *us = unit->side;
  553.  
  554.     if (hit >= period.nukehit) {
  555.     if (active_display(us)) invert_whole_map(us);
  556.     if (active_display(es)) invert_whole_map(es);
  557.     /* may need a time delay if X is too speedy */
  558.     if (active_display(us)) invert_whole_map(us);
  559.     if (active_display(es)) invert_whole_map(es);
  560.     for (i = 0; i < 4; ++i) {
  561.         if (active_display(us)) draw_mushroom(us, ux, uy, i);
  562.         if (active_display(es)) draw_mushroom(es, ux, uy, i);
  563.         if (i != 2 && (active_display(us) || active_display(es))) sleep(1);
  564.     }
  565.     if (active_display(us) || active_display(es)) sleep(1);
  566.     } else {
  567.     ch = ((hit >= unit->hp) ? 'd' : ((hit > 0) ? 'c' : 'b'));
  568.     if (active_display(us)) {
  569.         xform(us, unwrap(us, ux, uy), uy, &sx, &sy);
  570.         draw_blast_icon(us, us->map, sx, sy, ch, us->enemycolor);
  571.         flush_output(us);
  572.     }
  573.     if (active_display(es)) {
  574.         xform(es, unwrap(es, ux, uy), uy, &sx, &sy);
  575.         draw_blast_icon(es, es->map, sx, sy, ch, es->owncolor);
  576.         flush_output(es);
  577.     }
  578.     }
  579. }
  580.  
  581. /* Draw all the units in a column.  They should be spaced so they don't */
  582. /* overlap or get misaligned with text, and can be inverted if desired. */
  583.  
  584. draw_unit_list(side, hilite)
  585. Side *side;
  586. bool hilite[];
  587. {
  588.     int u, pos = 0, spacing = max(side->hh, side->fh);
  589.     
  590.     if (active_display(side)) {
  591.     for_all_unit_types(u) {
  592.         draw_hex_icon(side, side->state, side->margin, pos,
  593.               (hilite[u] ? side->fgcolor : side->bgcolor), OHEX);
  594.         draw_unit_icon(side, side->state, side->margin, pos, u,
  595.                (hilite[u] ? side->bgcolor : side->fgcolor));
  596.         pos += spacing;
  597.     }
  598.     }
  599. }
  600.