home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / mac / macmap2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-02  |  22.3 KB  |  944 lines  |  [TEXT/KAHL]

  1. /* Map interaction for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11. extern int do_fire_into_command(void);
  12.  
  13. /* This is a temporary used by the map scroll proc. */
  14.  
  15. Map *curmap;
  16.  
  17. /* This scroll proc is shared by both the horizontal and vertical scrollbars. */
  18.  
  19. pascal void
  20. map_scroll_proc(ControlHandle control, short code)
  21. {
  22.     int curvalue, pagesize, jump;
  23.     int oldsx, oldsy;
  24.     RgnHandle tmprgn;
  25.     Rect tmprect;
  26.  
  27.     /* The page jump should be most but not all of a screenful. */
  28.     if (control == curmap->hscrollbar) {
  29.         pagesize = (3 * curmap->vp->pxw) / 4;
  30.     } else {
  31.         pagesize = (3 * curmap->vp->pxh) / 4;
  32.     }
  33.     /* Adjust the pagesize to always be a multiple of 4. */
  34.     pagesize = ((pagesize + 3) / 4) * 4;
  35.     switch (code) {
  36.         case inPageDown:
  37.             jump = pagesize;
  38.             break;
  39.         case inDownButton:
  40.             jump = 4;
  41.             break;
  42.         case inPageUp:
  43.             jump = 0 - pagesize;
  44.             break;
  45.         case inUpButton:
  46.             jump = -4;
  47.             break;
  48.         default:
  49.             jump = 0;
  50.             break;
  51.     }
  52.     /* Letting control's code do the max/min hacking. */
  53.     SetCtlValue(control, GetCtlValue(control) + jump);
  54.     curvalue = GetCtlValue(control);
  55.     /* Tweak the map's own variables to match. */
  56.     oldsx = curmap->vp->sx;  oldsy = curmap->vp->sy;
  57.     if (control == curmap->hscrollbar) {
  58.         set_view_position(curmap->vp, curvalue, curmap->vp->sy);
  59.     } else {
  60.         set_view_position(curmap->vp, curmap->vp->sx, curvalue);
  61.     }
  62.     m_focus_on_center(curmap);
  63.     set_content_rect(curmap);
  64.     /* Scroll the already-drawn bits. */
  65.     tmprgn = NewRgn();
  66.     SetRect(&tmprect, conwid, curmap->toph, conwid + curmap->vp->pxw, curmap->toph + curmap->vp->pxh);
  67.     ScrollRect(&tmprect, oldsx - curmap->vp->sx, oldsy - curmap->vp->sy, tmprgn);
  68.     InvalRgn(tmprgn);
  69.     /* Do the update now, because we won't get back to the main event loop
  70.        until the mouse button is released. */
  71.     update_window(curmap->window);
  72.     DisposeRgn(tmprgn);
  73. }
  74.  
  75. /* Handle a mouse down in the map window. */
  76.  
  77. void
  78. do_mouse_down_map(Map *map, Point mouse, int mods)
  79. {
  80.     ControlHandle control;
  81.     short part;
  82.     long oldsx = map->vp->sx, oldsy = map->vp->sy;
  83.     WindowPtr window = map->window;
  84.     RgnHandle tmprgn;
  85.     Rect tmprect;
  86.  
  87.     part = FindControl(mouse, window, &control);
  88.     if (control == map->hscrollbar) {
  89.         /* Handle the horizontal scrollbar. */
  90.         switch (part) {
  91.             case inThumb:
  92.                 /* (should add power thumb ability?) */
  93.                 part = TrackControl(control, mouse, NULL);
  94.                 if (part == inThumb) {
  95.                     map->vp->sx = GetCtlValue(control);
  96.                     if (oldsx != map->vp->sx) {
  97.                         m_focus_on_center(map);
  98.                         set_content_rect(map);
  99.                         /* Scroll the already-drawn bits. */
  100.                         tmprgn = NewRgn();
  101.                         SetRect(&tmprect, conwid, map->toph, conwid + map->vp->pxw, map->toph + map->vp->pxh);
  102.                         ScrollRect(&tmprect, oldsx - map->vp->sx, oldsy - map->vp->sy, tmprgn);
  103.                         InvalRgn(tmprgn);
  104.                         DisposeRgn(tmprgn);
  105.                         draw_related_maps(map);
  106.                     }
  107.                 }
  108.                 break;
  109.             default:
  110.                 curmap = map;
  111.                 part = TrackControl(control, mouse, (ProcPtr) map_scroll_proc);
  112.                 break;
  113.         }
  114.     } else if (control == map->vscrollbar) {
  115.         /* Handle the vertical scrollbar. */
  116.         switch (part) {
  117.             case inThumb:
  118.                 /* (should add power thumb ability?) */
  119.                 part = TrackControl(control, mouse, NULL);
  120.                 if (part == inThumb) {
  121.                     map->vp->sy = GetCtlValue(control);
  122.                     if (oldsy != map->vp->sy) {
  123.                         m_focus_on_center(map);
  124.                         set_content_rect(map);
  125.                         /* Scroll the already-drawn bits. */
  126.                         tmprgn = NewRgn();
  127.                         SetRect(&tmprect, conwid, map->toph, conwid + map->vp->pxw, map->toph + map->vp->pxh);
  128.                         ScrollRect(&tmprect, oldsx - map->vp->sx, oldsy - map->vp->sy, tmprgn);
  129.                         InvalRgn(tmprgn);
  130.                         DisposeRgn(tmprgn);
  131.                         draw_related_maps(map);
  132.                     }
  133.                 }
  134.                 break;
  135.             default:
  136.                 curmap = map;
  137.                 part = TrackControl(control, mouse, (ProcPtr) map_scroll_proc);
  138.                 break;
  139.         }
  140.     } else if (mouse.h <= conwid) {
  141.         /* Interpret as a control panel hit. */
  142.         do_mouse_down_map_control_panel(map, mouse.h, mouse.v, mods);
  143.     } else {
  144.         do_mouse_down_map_content(map, mouse.h, mouse.v, mods);
  145.     }
  146. }
  147.  
  148. void
  149. do_mouse_down_map_control_panel(Map *map, int h, int v, int mods)
  150. {
  151.     int winh = map->window->portRect.bottom - map->window->portRect.top;
  152.  
  153.     /* (should better organize tests here) */
  154.     if (between(winh - 2 * sbarwid, v, winh)) {
  155.         switch ((winh - v) / sbarwid) {
  156.             case 0:
  157.                 magnify_map(map, ((h < conwid / 2) ? -1 : 1));
  158.                 break;
  159. #if 0
  160.             /* This was too confusing here, so it's now flushed.  However, the
  161.                capability still seems worthwhile, so it should reappear elsewhere. */
  162.             case 1:
  163.                 map_modal = ZOOM_MODAL;
  164.                 break;
  165. #endif
  166.         }
  167.     } else if (v < 32) {
  168.         toggle_survey(map);
  169.     } else if ((v - 32) < 5 * 15) {
  170.         switch ((v - 32) / 15) {
  171.             case 0:
  172.                 if (h < conwid / 2) {
  173.                     select_previous_awake_mover(map);
  174.                 } else {
  175.                     select_next_awake_mover(map);
  176.                 }
  177.                 break;
  178.             case 1:
  179.                 if (h < conwid / 2) {
  180.                     select_previous_mover(map);
  181.                 } else {
  182.                     select_next_mover(map);
  183.                 }
  184.                 break;
  185.             case 2:
  186.                 if (h < conwid / 2) {
  187.                     select_previous_actor(map);
  188.                 } else {
  189.                     select_next_actor(map);
  190.                 }
  191.                 break;
  192.             case 3:
  193.                 if (h < conwid / 2) {
  194.                     select_previous_unit(map);
  195.                 } else {
  196.                     select_next_unit(map);
  197.                 }
  198.                 break;
  199.             case 4:
  200.                 beep();
  201.                 break;
  202.         }
  203.     } else if (v - 32 - 5*15 - 2 - 5/*why?*/ < 5 * 11) {
  204.         switch ((v - 32 - 5*15 - 2 - 5/*why?*/) / 11) {
  205.             case 0:
  206.                 toggle_map_grid(map);
  207.                 break;
  208.             case 1:
  209.                 toggle_map_names(map);
  210.                 break;
  211.             case 2:
  212.                 if (people_sides_defined()) {
  213.                     toggle_map_people(map);
  214.                 }
  215.                 break;
  216.             case 3:
  217.                 toggle_map_plans(map);
  218.                 break;
  219.             case 4:
  220.                 toggle_map_ai(map);
  221.                 break;
  222.         }
  223.     } else if (mayseeall) {
  224.         weseeall = !weseeall;
  225.         calc_vision();
  226.         force_map_update(map);
  227.     }
  228. }
  229.  
  230. void
  231. toggle_survey(Map *map)
  232. {
  233.     int i;
  234.     Unit *unit;
  235.  
  236.     map->moveonclick = !map->moveonclick;
  237.     map->autoselect = !map->autoselect;
  238.     draw_control_panel(map);
  239.     if (map->autoselect) {
  240.         if (map->numselections > 0) {
  241.             for (i = 0; i < map->numselections; ++i) {
  242.                 unit = map->selections[i];
  243.                 if (unit != NULL) {
  244.                     map->curunit = autonext_unit(dside, unit);
  245.                     select_exactly_one_unit(map, map->curunit);
  246.                 }
  247.             }
  248.         }
  249.     }
  250. }
  251.  
  252. void
  253. magnify_map(Map *map, int inout)
  254. {
  255.     set_map_mag(map, map->vp->power + inout);
  256. }
  257.  
  258. /* This sets the map's magnification directly and updates it. */
  259.  
  260. void
  261. set_map_mag(Map *map, int newpower)
  262. {
  263.     newpower = clip_to_limits(0, newpower, NUMPOWERS-1);
  264.     if (map->vp->power != newpower) {
  265.         set_map_power(map, newpower);
  266.         m_center_on_focus(map);
  267.         set_map_scrollbars(map);
  268.         force_map_update(map);
  269.         draw_related_maps(map);
  270.     }
  271. }
  272.  
  273. void
  274. toggle_map_grid(Map *map)
  275. {
  276.     map->drawgrid = !map->drawgrid;
  277.     /* (should not do a total redraw?) */
  278.     force_map_update(map);
  279. }
  280.  
  281. void
  282. toggle_map_topline(Map *map)
  283. {
  284. #if 0
  285.     Rect tmprect;
  286.     RgnHandle tmprgn;
  287. #endif
  288.     GrafPtr oldport;
  289.  
  290.     map->toph = (map->toph ? 0 : tophgt);
  291.     set_content_rect(map);
  292.     if (map->toph) {
  293.          GetPort(&oldport);
  294.         SetPort(map->window);
  295. #if 0 /* this would be better than redrawing everything */
  296.         tmprect = map->window->portRect;
  297.         tmprect.left += conwid;
  298.         tmprect.right -= sbarwid;  tmprect.bottom -= sbarwid;
  299. /*        ScrollRect(&tmprect, 0, map->toph, tmprgn); */
  300.         /* (should draw the topline here now) */
  301. #endif
  302.         SetPort(oldport);
  303.         force_map_update(map);
  304.     } else {
  305.         force_map_update(map);
  306.     }
  307. }
  308.  
  309. void
  310. toggle_map_other_maps(Map *map)
  311. {
  312.     map->drawothermaps = !map->drawothermaps;
  313.     /* (should not do a total redraw) */
  314.     force_map_update(map);
  315. }
  316.  
  317. void
  318. toggle_map_lighting(Map *map)
  319. {
  320.     map->drawlighting = !map->drawlighting;
  321.     /* We have to do a total redraw. */
  322.     force_map_update(map);
  323. }
  324.  
  325. void
  326. toggle_map_coverage(Map *map)
  327. {
  328.     map->drawcover = !map->drawcover;
  329.     /* (should only change newly dimmed/lightened cells) */
  330.     force_map_update(map);
  331. }
  332.  
  333. void
  334. toggle_map_names(Map *map)
  335. {
  336.     map->drawnames = !map->drawnames;
  337.     /* (if now on, should draw names on top of everything, don't redraw everything) */
  338.     if (map->vp->hh > 5) {
  339.         force_map_update(map);
  340.     } else {
  341.         /* (should be a force update on control panel alone) */
  342.         draw_control_panel(map);
  343.     }
  344. }
  345.  
  346. void
  347. toggle_map_people(Map *map)
  348. {
  349.     map->drawpeople = !map->drawpeople;
  350.     if (bwid2[map->vp->power] > 0) {
  351.         force_map_update(map);
  352.     } else {
  353.         /* (should be a force update on control panel alone) */
  354.         draw_control_panel(map);
  355.     }
  356. }
  357.  
  358. void
  359. toggle_map_elevations(Map *map)
  360. {
  361.     map->drawelevations = !map->drawelevations;
  362.     force_map_update(map);
  363. }
  364.  
  365. void
  366. toggle_map_materials(Map *map, int m)
  367. {
  368.     map->drawmaterials[m] = !map->drawmaterials[m];
  369.     map->nummaterialstodraw += (map->drawmaterials[m] ? 1 : -1);
  370.     force_map_update(map);
  371. }
  372.  
  373. void
  374. toggle_map_aux_terrain(Map *map, int t)
  375. {
  376.     map->drawauxterrain[t] = !map->drawauxterrain[t];
  377.     force_map_update(map);
  378. }
  379.  
  380. void
  381. toggle_map_temperature(Map *map)
  382. {
  383.     map->drawtemperature = !map->drawtemperature;
  384.     force_map_update(map);
  385. }
  386.  
  387. void
  388. toggle_map_winds(Map *map)
  389. {
  390.     map->drawwinds = !map->drawwinds;
  391.     force_map_update(map);
  392. }
  393.  
  394. void
  395. toggle_map_clouds(Map *map)
  396. {
  397.     map->drawclouds = !map->drawclouds;
  398.     force_map_update(map);
  399. }
  400.  
  401. void
  402. toggle_map_storms(Map *map)
  403. {
  404.     map->drawstorms = !map->drawstorms;
  405.     force_map_update(map);
  406. }
  407.  
  408. void
  409. toggle_map_plans(Map *map)
  410. {
  411.     map->drawplans = !map->drawplans;
  412.     if (map->numselections > 0) {
  413.         force_map_update(map);
  414.     } else {
  415.         /* (should be a force update on control panel alone) */
  416.         draw_control_panel(map);
  417.     }
  418. }
  419.  
  420. void
  421. toggle_map_ai(Map *map)
  422. {
  423.     if (!side_has_ai(dside)) return;
  424.     map->drawai = !map->drawai;
  425.     force_map_update(map);
  426. }
  427.  
  428. static int selrect;
  429. static int downx, downy, downdir;
  430.  
  431. void
  432. do_mouse_down_map_content(Map *map, int h, int v, int mods)
  433. {
  434.     int i, rslt, anysuccess;
  435.     Unit *unit;
  436.     
  437.     /* Remember this cell. */
  438.     m_nearest_cell(map, h, v, &downx, &downy);
  439.     /* Assume that last place clicked is a reasonable focus. */
  440.     if (inside_area(downx, downy)) {
  441.         map->vp->vcx = downx;  map->vp->vcy = downy;
  442.     }
  443.     if (map_modal > 0) {
  444.         switch (map_modal) {
  445.             case ZOOM_MODAL:
  446.                 select_area_and_zoom(map, h, v, mods);
  447.                 /* Done being modal. */
  448.                 map_modal = NO_MODAL;
  449.                 break;
  450.             case FIRE_MODAL:
  451.                 do_fire_command();
  452.                 /* Reset modality whether or not the command succeeded, otherwise
  453.                    the player can get caught here if no fire commands can succeed. */
  454.                 map_modal = NO_MODAL;
  455.                 break;
  456.             case FIRE_INTO_MODAL:
  457.                 do_fire_into_command();
  458.                 /* Reset modality whether or not the command succeeded, otherwise
  459.                    the player can get caught here if no fire commands can succeed. */
  460.                 map_modal = NO_MODAL;
  461.                 break;
  462.             default:
  463.                 /* (should error out) */
  464.                 break;
  465.         }
  466.     } else if (mods & cmdKey) {
  467.         if (map->moveonclick && map->autoselect) {
  468.             unselect_all(map);
  469.             m_nearest_unit(map, h, v, &unit);
  470.             if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
  471.                 /* The nearest unit will become the "current unit". */
  472.                 map->curunit = unit;
  473.                 select_unit_on_map(map, unit);
  474.                 draw_selections(map);
  475.                 move_on_drag(map, unit, mods);
  476.             } else {
  477.                 select_all_dragged_over(map, h, v, mods);
  478.                 /* Pick the first of the multiple selection as the "current unit". */
  479.                 if (map->numselections > 0) {
  480.                     map->curunit = map->selections[0];
  481.                 }
  482.             }
  483.         } else {
  484.             anysuccess = FALSE;
  485.             for (i = 0; i < map->numselections; ++i) {
  486.                 if ((unit = map->selections[i]) != NULL) {
  487.                     rslt = move_the_selected_unit(map, unit, h, v);
  488.                     if (rslt) anysuccess = TRUE;
  489.                 }
  490.             }
  491.             if (!anysuccess)
  492.               beep();
  493.         }
  494.     } else if (mods & optionKey) {
  495.         for (i = 0; i < map->numselections; ++i) {
  496.             if ((unit = map->selections[i]) != NULL) {
  497.                 fire_the_selected_unit(map, unit, h, v);
  498.             }
  499.         }
  500.     } else if (mods & shiftKey) {
  501.         m_nearest_unit(map, h, v, &unit);
  502.         if (unit && side_sees_unit(dside, unit)) {
  503.             /* Invert the selection status of the unit. */
  504.             if (unit_is_selected(map, unit)) {
  505.                 unselect_unit_on_map(map, unit);
  506.                 erase_selection(map, unit);
  507.             } else {
  508.                 select_unit_on_map(map, unit);
  509.                 draw_selections_at(map, unit->x, unit->y);
  510.             }
  511.         } else {
  512.             select_all_dragged_over(map, h, v, mods);
  513.         }
  514.     } else {
  515.         /* Interpret an unmodified mouse down. */
  516. #ifdef DESIGNERS
  517.         if (dside->designer && tooltype != notool) {
  518.             apply_designer_tool(map, h, v, mods);
  519.         } else
  520. #endif /* DESIGNERS */
  521.         if (map->moveonclick && !dside->designer) {
  522.             /* Usually will only be one to move, but be general anyway. */
  523.             for (i = 0; i < map->numselections; ++i) {
  524.                 if ((unit = map->selections[i]) != NULL) {
  525.                     move_the_selected_unit(map, unit, h, v);
  526.                 }
  527.             }
  528.             map->scrolltocurunit = TRUE;
  529.         } else {
  530.             unselect_all(map);
  531.             m_nearest_unit(map, h, v, &unit);
  532.             if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
  533.                 select_unit_on_map(map, unit);
  534.                 draw_selections(map);
  535.                 move_on_drag(map, unit, mods);
  536.             } else {
  537.                 select_all_dragged_over(map, h, v, mods);
  538.             }
  539.         }
  540.     }
  541. }
  542.  
  543. void
  544. select_all_dragged_over(Map *map, int h0, int v0, int mods)
  545. {
  546.     Point pt0, pt1, newmouse;
  547.     int drawn = FALSE;
  548.     Rect tmprect;
  549.  
  550.     SetPt(&pt0, h0, v0);
  551.     SetPt(&pt1, h0, v0);
  552.     SetRect(&tmprect, h0, v0, h0, v0);
  553.     /* (should be a generic subr?) */
  554.     PenMode(patXor);
  555.     PenPat(QDPat(gray));
  556.     while (WaitMouseUp()) {
  557.         GetMouse(&newmouse);
  558.         if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
  559.             if (drawn) {
  560.                 tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  561.                 tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  562.                 FrameRect(&tmprect);
  563.             }
  564.             pt1 = newmouse;
  565.             tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  566.             tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  567.             FrameRect(&tmprect);
  568.             drawn = TRUE;
  569.         }
  570.     }
  571.     if (drawn) {
  572.         tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  573.         tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  574.         FrameRect(&tmprect);
  575.     }
  576.     PenNormal();
  577.     select_all_units_in_rect(map, &tmprect);
  578. }
  579.  
  580. void
  581. select_area_and_zoom(Map *map, int h0, int v0, int mods)
  582. {
  583.     Point pt0, pt1, newmouse;
  584.     int drawn = FALSE, x, y;
  585.     Rect tmprect;
  586.  
  587.     SetPt(&pt0, h0, v0);
  588.     SetPt(&pt1, h0, v0);
  589.     /* (should be a generic subr) */
  590.     PenMode(patXor);
  591.     PenPat(QDPat(gray));
  592.     while (WaitMouseUp()) {
  593.         GetMouse(&newmouse);
  594.         if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
  595.             if (drawn) {
  596.                 tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  597.                 tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  598.                 FrameRect(&tmprect);
  599.             }
  600.             pt1 = newmouse;
  601.             tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  602.             tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  603.             FrameRect(&tmprect);
  604.             drawn = TRUE;
  605.         }
  606.     }
  607.     if (drawn) {
  608.         tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  609.         tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  610.         FrameRect(&tmprect);
  611.     }
  612.     PenNormal();
  613.     m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
  614.     if (x != downx && y != downy) {
  615.         magnify_to_fit(map, downx, downy, x, y);
  616.     }
  617. }
  618.  
  619. void
  620. move_on_drag(Map *map, Unit *unit, int mods)
  621. {
  622.     int sx, sy, sw, sh, h0, v0, drawn = FALSE, x, y;
  623.     Point pt0, pt1, newmouse;
  624.  
  625.     m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  626.     h0 = sx + sw / 2;  v0 = sy + sh / 2;
  627.     SetPt(&pt0, h0, v0);
  628.     SetPt(&pt1, h0, v0);
  629.     /* (should be a generic subr?) */
  630.     PenMode(patXor);
  631.     while (WaitMouseUp()) {
  632.         GetMouse(&newmouse);
  633.         /* should scroll, then abort if we drag outside the window */
  634.         if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
  635.         }
  636.         if (!EqualPt(pt1, newmouse)) {
  637.             if (drawn) {
  638.                 MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  639.             }
  640.             pt1 = newmouse;
  641.             MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  642.             drawn = TRUE;
  643.         }
  644.     }
  645.     /* Erase the last drawn line. */
  646.     if (drawn) {
  647.         MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  648.     }
  649.     PenNormal();
  650.     m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
  651.     if (x != downx || y != downy) {
  652.         if (!move_the_selected_unit(map, unit, pt1.h, pt1.v))
  653.           beep();
  654.     } else {
  655.         /* (should try to enter another unit in this cell) */
  656.     }
  657. }
  658.  
  659. void
  660. unselect_all(Map *map)
  661. {
  662.     int num = map->numselections;
  663.  
  664.     if (map->numselections > 0) {
  665.         erase_selections(map);
  666.         map->numselections = 0;
  667.     }
  668. }
  669.  
  670. /* Add the given unit to the array of units selected in the given map.  If we need
  671.    more space, then grow the array by 50%. */
  672.  
  673. void
  674. select_unit_on_map(Map *map, Unit *unit)
  675. {
  676.     if (map->numselections >= map->maxselections) {
  677.         int newsize = map->maxselections + map->maxselections / 2;
  678.         Unit **newarray = (Unit **) realloc((char *) map->selections, newsize * sizeof(Unit *));
  679.  
  680.         if (newarray == NULL) {
  681.             run_warning("couldn't realloc map selection array");
  682.             return;
  683.         }
  684.         map->maxselections = newsize;
  685.         map->selections = newarray;
  686.     }
  687.     map->selections[map->numselections++] = unit;
  688. }
  689.  
  690. int
  691. unit_is_selected(Map *map, Unit *unit)
  692. {
  693.     int i;
  694.  
  695.     for (i = 0; i < map->numselections; ++i) {
  696.         if (map->selections[i] == unit) return TRUE;
  697.     }
  698.     return FALSE;
  699. }
  700.  
  701. void
  702. unselect_unit_on_map(Map *map, Unit *unit)
  703. {
  704.     int i, j;
  705.  
  706.     for (i = 0; i < map->numselections; ++i) {
  707.         if (map->selections[i] == unit) {
  708.             /* Keep selection list contiguous, move other units down. */
  709.             for (j = i + 1; j < map->numselections; ++j) {
  710.                 map->selections[j - 1] = map->selections[j];
  711.             }
  712.             --map->numselections;
  713.             return;
  714.         }
  715.     }
  716. }
  717.  
  718. /* Given a map and a rectangle in it, select all the units whose images touch on
  719.    that rectangle. */
  720.  
  721. void
  722. select_all_units_in_rect(Map *map, Rect *rectptr)
  723. {
  724.     int rectissmall = FALSE;
  725.     int sx, sy, sw, sh;
  726.     Unit *unit;
  727.     Rect unitrect, tmprect;
  728.     
  729.     /* First see if we're selecting over a large area or within a single cell. */
  730.     if (rectptr->right - rectptr->left < map->vp->hw
  731.         && rectptr->bottom - rectptr->top < map->vp->hh) rectissmall = TRUE;
  732.     /* Now look at all the plausible units and see if any's image intersects the rect. */
  733.     for_all_units(unit) {
  734.         if (in_play(unit)
  735.             && (side_controls_unit(dside, unit) || endofgame)
  736.             && (rectissmall || unit->transport == NULL)) {
  737.             m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  738.             SetRect(&unitrect, sx, sy, sx + sw, sy + sh);
  739.             if (SectRect(&unitrect, rectptr, &tmprect)) {
  740.                 select_unit_on_map(map, unit);
  741.                 /* (could do setport etc once...) */
  742.                 draw_selected_unit_setport(map, unit);
  743.             }
  744.         }
  745.     }
  746. }
  747.  
  748. /* This translates the user's "go to here" into appropriate tasks and/or actions. */
  749.  
  750. int
  751. move_the_selected_unit(Map *map, Unit *unit, int h, int v)
  752. {
  753.     int x, y, rslt;
  754.     Unit *other = NULL;
  755.  
  756.     m_nearest_cell(map, h, v, &x, &y);
  757.     if (unit_at(x, y) != NULL) {
  758.         m_nearest_unit(map, h, v, &other);
  759.     }
  760.     rslt = advance_into_cell(dside, unit, x, y, other);
  761.     return rslt;
  762. }
  763.  
  764. void
  765. fire_the_selected_unit(Map *map, Unit *unit, int h, int v)
  766. {
  767.     int x, y;
  768.     Unit *other;
  769.  
  770.     m_nearest_cell(map, h, v, &x, &y);
  771.     if (x != unit->x || y != unit->y) {
  772.         if (unit->act && unit->plan) { /* (should be more sophisticated test?) */
  773.             if ((other = unit_at(x, y)) != NULL) {
  774.                 /* There's a unit to fire at. */
  775.                 if (other->side == unit->side) {
  776.                     beep();
  777.                 } else {
  778.                     prep_fire_at_action(unit, unit, other, -1);
  779.                 }
  780.             } else {
  781.                 beep();
  782.             }
  783.         }
  784.     }
  785. }
  786.  
  787. void
  788. select_exactly_one_unit(Map *map, Unit *unit)
  789. {
  790.     Unit *thisunit;
  791.  
  792.     if (map->numselections > 0) {        
  793.         thisunit = map->selections[0];
  794.         if (thisunit == unit) return;
  795.     }
  796.     unselect_all(map);
  797.     select_unit_on_map(map, unit);
  798.     scroll_to_unit(map, unit);
  799.     draw_selections(map);
  800. }
  801.  
  802. void
  803. select_next_unit(Map *map)
  804. {
  805.     select_another(map, find_next_unit);
  806. }
  807.  
  808. void
  809. select_previous_unit(Map *map)
  810. {
  811.     select_another(map, find_prev_unit);
  812. }
  813.  
  814. void
  815. select_next_actor(Map *map)
  816. {
  817.     select_another(map, find_next_actor);
  818. }
  819.  
  820. void
  821. select_previous_actor(Map *map)
  822. {
  823.     select_another(map, find_prev_actor);
  824. }
  825.  
  826. void
  827. select_next_mover(Map *map)
  828. {
  829.     select_another(map, find_next_mover);
  830. }
  831.  
  832. void
  833. select_previous_mover(Map *map)
  834. {
  835.     select_another(map, find_prev_mover);
  836. }
  837.  
  838. void
  839. select_next_awake_mover(Map *map)
  840. {
  841.     select_another(map, find_next_awake_mover);
  842. }
  843.  
  844. void
  845. select_previous_awake_mover(Map *map)
  846. {
  847.     select_another(map, find_prev_awake_mover);
  848. }
  849.  
  850. /* Given a map and a searching function, go find the "next" matching unit and select it. */
  851.  
  852. void
  853. select_another(Map *map, Unit *(*fn)(Side *side, Unit *unit))
  854. {
  855.     Unit *thisunit, *nextunit;
  856.  
  857.     if (fn == NULL) {
  858.         beep();
  859.         return;
  860.     }
  861.     if (map->numselections > 0) {
  862.         thisunit = map->selections[0];
  863.     } else {
  864.         thisunit = NULL;
  865.     }
  866.     nextunit = (*fn)(dside, thisunit);
  867.     if (nextunit != NULL) {
  868.         unselect_all(map);
  869.         select_unit_on_map(map, nextunit);
  870.         scroll_to_unit(map, nextunit);
  871.         draw_selections(map);
  872.         if (map->autoselect) {
  873.             map->curunit = nextunit;
  874.         }
  875.     } else if (thisunit != NULL) {
  876.         scroll_to_unit(map, thisunit);
  877.         /* (should not be done this way, but how else?) */
  878.         if (map->autoselect
  879.             && (thisunit->act && thisunit->act->acp > 0)
  880.             && (thisunit->plan && !thisunit->plan->asleep && !thisunit->plan->reserve && !thisunit->plan->delayed)) {
  881.             map->curunit = thisunit;
  882.         }
  883.     }
  884. }
  885.  
  886. void
  887. scroll_best_map_to_unit(Unit *unit)
  888. {
  889.     Map *map, *bestmap;
  890.  
  891.     /* Find the "best" (highest power, unit already visible) map to scroll over
  892.        to the unit. */
  893.     bestmap = maplist;
  894.     for_all_maps(map) {
  895.         if (map->vp->power > bestmap->vp->power
  896.             || (map->vp->power == bestmap->vp->power
  897.                 && in_middle(map, unit->x, unit->y)
  898.                  && !in_middle(bestmap, unit->x, unit->y))) {
  899.             bestmap = map;
  900.         }
  901.     }
  902.     /* We have a map, now make it show the unit. */
  903.     if (!in_middle(bestmap, unit->x, unit->y)) {
  904.         scroll_to_unit(bestmap, unit);
  905.     }
  906.     SelectWindow(bestmap->window);
  907.     adjust_menus();
  908. }
  909.  
  910. /* Scroll the given map over to display the given unit. */
  911.  
  912. void
  913. scroll_to_unit(Map *map, Unit *unit)
  914. {
  915.     if (inside_area(unit->x, unit->y)) {
  916.         if (!in_middle(map, unit->x, unit->y)) {
  917.             set_view_focus(map->vp, unit->x, unit->y);
  918.             m_center_on_focus(map);
  919.             set_map_scrollbars(map);
  920.             force_map_update(map);
  921.         }
  922.     }
  923. }
  924.  
  925. /* This routine changes a map's viewport and magnification to fit the given rectangle. */
  926.  
  927. void
  928. magnify_to_fit(Map *map, int x1, int y1, int x2, int y2)
  929. {
  930.     int wid, hgt, wanted, power;
  931.  
  932.     DGprintf("Magnifying map to fit in area %d,%d - %d,%d\n", x1, y1, x2, y2);
  933.     /* (still need to do y/2 correction) */
  934.     wid = abs(x2 - x1) + 1;  hgt = abs(y2 - y1) + 1;
  935.     map->vp->vcx = min(x1, x2) + wid / 2;  map->vp->vcy = min(y1, y2) + hgt / 2;
  936.     /* Compute the "ideal" size of a displayed cell. */
  937.     wanted = min(map->vp->pxw / wid, map->vp->pxh / hgt);
  938.     /* Search for the best approximation. */
  939.     for (power = NUMPOWERS-1; power > 0; --power) {
  940.         if (hws[power] < wanted) break;
  941.     }
  942.     set_map_mag(map, power);
  943. }
  944.