Logo Search packages:      
Sourcecode: xconq version File versions  Download package

macmouse.c

/*    Mouse-driven user interaction for the Mac interface to Xconq.
      Copyright (C) 1992-1998 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

/* This file now ONLY contains user interaction code for the mouse
   such as clicking, dragging, selecting, scrolling and toggling items. */
     
#include "conq.h"
#include "macconq.h"

static int selrect;
static int downx, downy, downdir;

static void drag_for_distance(Map *map, int h0, int v0);

static void do_mouse_down_map_control_panel(Map *map, int h, int v, int mods);
static void do_mouse_down_map_content(Map *map, int h, int v, int mods);
static void select_all_dragged_over(Map *map, int h0, int v0, int mods);
static void select_area_and_zoom(Map *map, int h0, int v0, int mods);
static void move_on_drag(Map *map, Unit *unit, int mods);
static void select_all_units_in_rect(Map *map, Rect *rectptr);
static int move_the_selected_unit(Map *map, Unit *unit, int h, int v);
static void fire_the_selected_unit(Map *map, Unit *unit, int h, int v);
static void select_next_unit(Map *map);
static void select_next_actor(Map *map);
static void select_previous_actor(Map *map);
static void select_previous_mover(Map *map);
static void select_next_awake_mover(Map *map);
static void select_previous_awake_mover(Map *map);
static void select_another(Map *map, Unit *(*fn)(Side *side, Unit *unit));
static void magnify_to_fit(Map *map, int x1, int y1, int x2, int y2);

int topunithgt;

Map *curmap;

ControlActionUPP map_scroll_proc;

/* This scroll proc is shared by both the horizontal and vertical scrollbars. */

static pascal void
map_scroll_fn(ControlHandle control, short code)
{
      int pagesize, jump;

      /* The page jump should be most but not all of a screenful. */
      if (control == curmap->hscrollbar)
        pagesize = (3 * curmap->vp->pxw) / 4;
      else
        pagesize = (3 * curmap->vp->pxh) / 4;
      /* Adjust the pagesize to always be a multiple of 4. */
      pagesize = ((pagesize + 3) / 4) * 4;

      switch (code) {
            case inPageDown:
                  jump = pagesize;
                  break;
            case inDownButton:
                  jump = 4;
                  break;
            case inPageUp:
                  jump = 0 - pagesize;
                  break;
            case inUpButton:
                  jump = -4;
                  break;
            default:
                  jump = 0;
                  break;
      }
      if (control == curmap->hscrollbar) {
            scroll_map_window(curmap, jump, 0);
      } else if (control == curmap->vscrollbar){
            scroll_map_window(curmap, 0, jump);
      }
}

void
scroll_best_map_to_unit(Unit *unit, int bringtofront)
{
      Map *map, *bestmap;
      GrafPtr     oldport;
      
      /* Find the "best" (highest power, unit already visible) map to scroll over
         to the unit. */
      bestmap = maplist;
      for_all_maps(map) {
            if (map->vp->power > bestmap->vp->power
                || (map->vp->power == bestmap->vp->power
                    && in_middle(map, unit->x, unit->y)
                        && !in_middle(bestmap, unit->x, unit->y))) {
                  bestmap = map;
            }
      }
      /* We have a map, now make it show the unit. */
      if (!in_middle(bestmap, unit->x, unit->y)) {
            /* Save the current port. */
            GetPort(&oldport);
            /* Important or junk may appear in the list window. */
            SetPort(bestmap->window);
            scroll_to_unit(bestmap, unit);
            /* Force a gworld update of the whole map content to
               make sure the scrolled region is updated properly. */
            copy_from_gworld(bestmap, bestmap->contentrect);
            SetPort(oldport);
      }
      if (bringtofront) {
            SelectTheWindow(bestmap->window);
            update_window(bestmap->window);
            adjust_menus();
      }
}

/* Scroll the given map over to display the given unit. */

void
scroll_to_unit(Map *map, Unit *unit)
{
      int         oldsx = map->vp->sx, oldsy = map->vp->sy;
      int         usx, usy, newsx, newsy;
      Point       point;
      
      /* Return if unit is not at all on the map */
      if (!inside_area(unit->x, unit->y))
        return;

      xform(map, unit->x, unit->y, &usx, &usy);
      point.h = usx + map->vp->hw/2; 
      point.v = usy + map->vp->hh/2; ;
      /* Dont scroll if the unit already is in the window and is visible. */
      if (in_middle(map, unit->x, unit->y)
          && PtInRgn(point, map->window->visRgn))
        return;

      /* Find new vp */
      set_view_focus(map->vp, unit->x, unit->y);
      m_center_on_focus(map);
      newsx = map->vp->sx;
      newsy = map->vp->sy;

      /* Return if position is unchanged */
      if (oldsx == newsx && oldsy == newsy)
        return;
      
      /* Restore old vp and let scroll_map_window do the job */
      map->vp->sx = oldsx;
      map->vp->sy = oldsy;
      
      scroll_map_window(map, newsx - oldsx, newsy - oldsy);
}

/* Handle a mouse down in the map window. */

void
do_mouse_down_map(Map *map, Point mouse, int mods)
{
      short part;
      int newsx, newsy, dummy;
      ControlHandle control;

      if (map_scroll_proc == NULL) 
        map_scroll_proc = NewControlActionProc(map_scroll_fn);
      part = FindControl(mouse, map->window, &control);

      /* Horizontal scrollbar */
      if (control == map->hscrollbar) {
            switch (part) {
                  case inThumb:
                        part = TrackControl(control, mouse, NULL);
                        if (part == inThumb) {
                              /* Get apparent newsx from the new thumb position */
                              newsx = GetCtlValue(control);
                              /* Correct newsx for scrollbar scaling used to fix the 32K limit bug */                   
                              dummy = map->vp->sxmax;
                              while (dummy > 32000) { 
                                    dummy /= 2;
                                    newsx *= 2; /* Simulates the scaling but in reverse */
                              }           
                              /* Scroll the map window within its gworld */
                              scroll_map_window(map, newsx - map->vp->sx, 0);
                        }
                        break;
                  default:
                        curmap = map;
                        part = TrackControl(control, mouse, map_scroll_proc);
                        break;
            }

      /* Vertical scrollbar */
      } else if (control == map->vscrollbar) {
            switch (part) {
                  case inThumb:
                        part = TrackControl(control, mouse, NULL);
                        if (part == inThumb) {
                              /* Get apparent newsy from the new thumb position */
                              newsy = GetCtlValue(control);
                              /* Correct newsy for scrollbar scaling used to fix the 32K limit bug */                   
                              dummy = map->vp->symax;
                              while (dummy > 32000) { 
                                    dummy /= 2;
                                    newsy *= 2; /* Simulates the scaling but in reverse */
                              }           
                              /* Scroll the map window within its gworld */
                              scroll_map_window(map, 0, newsy - map->vp->sy);
                        }
                        break;
                  default:
                        curmap = map;
                        part = TrackControl(control, mouse, map_scroll_proc);
                        break;
            }
      } else if (mouse.h <= map->conw) {
            /* Interpret as a control panel hit. */
            do_mouse_down_map_control_panel(map, mouse.h, mouse.v, mods);
      } else {
            do_mouse_down_map_content(map, mouse.h, mouse.v, mods);
      }
}

void
do_mouse_down_map_control_panel(Map *map, int h, int v, int mods)
{
      int winh = map->window->portRect.bottom - map->window->portRect.top;

      /* (should better organize tests here) */

      /* Use 15 instead of map->sbarwid here, since the latter may be zero. */
      if (between(winh - 2 * 15, v, winh)) {
            switch ((winh - v) / 15) {
                  case 0:
                        magnify_map(map, ((h < map->conw / 2) ? -1 : 1));
                        break;
#if 0
                  /* This was too confusing here, so it's now flushed.  However, the
                     capability still seems worthwhile, so it should reappear elsewhere. */
                  case 1:
                        map_modal = ZOOM_MODAL;
                        break;
#endif
            }
      } else if (v < 32) {
            toggle_survey(map);
      } else if ((v - 32) < 5 * 15) {
            switch ((v - 32) / 15) {
                  case 0:
                        if (h < map->conw / 2) {
                              select_previous_awake_mover(map);
                        } else {
                              select_next_awake_mover(map);
                        }
                        break;
                  case 1:
                        if (h < map->conw / 2) {
                              select_previous_mover(map);
                        } else {
                              select_next_mover(map);
                        }
                        break;
                  case 2:
                        if (h < map->conw / 2) {
                              select_previous_actor(map);
                        } else {
                              select_next_actor(map);
                        }
                        break;
                  case 3:
                        if (h < map->conw / 2) {
                              select_previous_unit(map);
                        } else {
                              select_next_unit(map);
                        }
                        break;
                  case 4:
                        beep();
                        break;
            }

      } else if (v - 32 - 5*15 - 2 - 7/*why?*/ < 9 * 11) {
            switch ((v - 32 - 5*15 - 2 - 7/*why?*/) / 11) {

                  case 0:
                        toggle_map_grid(map);
                        break;
                  case 1:
                        map->vp->draw_names = !map->vp->draw_names;
                        /* Also toggle featurenames if it had the same old setting as drawnames */
                        /* but let it alone if the setting already was different */
                        if (map->featurenames != map->vp->draw_names)
                              map->featurenames = !map->featurenames;
                        force_map_update(map);
                        break;
                  case 2:
                        toggle_map_people(map);
                        break;
                  case 3:
                        toggle_map_control(map);
                        break;
                  case 4:
                        toggle_map_plans(map);
                        break;
                  case 5:
                        toggle_map_ai(map);
                        break;
                  case 6:
                        if (dside->may_set_show_all) {
                              map->see_all = !map->see_all;
                              map->vp->show_all = map->see_all;
                              /* This is necessary in order to enable closeups that have no map 
                              or vp associated with them. Also needed to permit the designer to 
                              select an enemy unit. */
                              dside->see_all = map->see_all;
                              force_map_update(map);
                        }
                        break;
                  case 7:
                        toggle_map_sidecolors(map);
                        break;
                  case 8:
                        map->iconmasks = !map->iconmasks;
                        /* Also toggle boxmasks if it had the same old setting as iconmasks */
                        /* but let it alone if the setting already was different */
                        if (map->boxmasks != map->iconmasks)
                              map->boxmasks = !map->boxmasks;
                        /* Also toggle textmasks if it had the same old setting as iconmasks */
                        /* but let it alone if the setting already was different */
                        if (map->textmasks != map->iconmasks)
                              map->textmasks = !map->textmasks;
                        force_map_update(map);
                        break;
            }
      } else {
            /* Unused area, ignore */
      }
}

void
toggle_survey(Map *map)
{
      int i;
      Unit *unit;

      map->moveonclick = !map->moveonclick;
      map->autoselect = !map->autoselect;
      draw_control_panel(map);
      if (map->autoselect) {
            if (map->numselections > 0) {
                  for (i = 0; i < map->numselections; ++i) {
                        unit = map->selections[i];
                        if (unit != NULL) {
                              map->curunit = autonext_unit(dside, unit);
                              select_exactly_one_unit(map, map->curunit);
                        }
                  }
            }
      } else {
            /* Update appearance of selections. */
            draw_selections(map);
      }
}

void
magnify_map(Map *map, int inout)
{
      set_map_mag(map, map->vp->power + inout);
}

/* This sets the map's magnification directly and updates it. */

void
set_map_mag(Map *map, int newpower)
{
      newpower = clip_to_limits(0, newpower, NUMPOWERS-1);
      if (map->vp->power != newpower) {
      
            /* Erase other-map boxes in other windows */
            draw_related_maps(map);
            
            set_map_power(map, newpower);
            m_center_on_focus(map);
            set_map_scrollbars(map);

            /* Update the entire map */
            force_map_update(map);

            /* Redraw other-map boxes in other windows */
            draw_related_maps(map);
      }
}

void
do_mouse_down_map_content(Map *map, int h, int v, int mods)
{
      int i, rslt, anysuccess;
      Unit *unit;
      
      /* Remember this cell. */
      m_nearest_cell(map, h, v, &downx, &downy);
      /* Assume that last place clicked is a reasonable focus. */
      if (inside_area(downx, downy)) {
            map->vp->vcx = downx;  map->vp->vcy = downy;
      }
      /* First handle refocus clicks in world map. */
      if (map == worldmap) {
            WindowPtr    win;
            Map *nextmap;

            /* Find the next frontmost map. */
            for_all_windows(win) {
                  if (map_from_window(win) != NULL
                       && win != FrontWindow()) {
                              nextmap = map_from_window(win);
                        /* Scroll next map to the new focus. */
                        set_map_focus(nextmap, downx, downy);           
                        break;
                  }
            }
            return;
      }           
      if (map_modal != NO_MODAL) {
            switch (map_modal) {
                  case ATTACK_MODAL:
                        set_position_modally();
                        do_attack(dside);
                        break;
                  case FIRE_MODAL:
                        set_position_modally();
                        do_fire(dside);
                        break;
                  case FIRE_INTO_MODAL:
                        set_position_modally();
                        do_fire_into(dside);
                        break;
                  case SET_FORMATION_MODAL:
                        set_position_modally();
                        do_set_formation(dside);
                        break;
                  case MOVE_TO_MODAL:
                        set_position_modally();
                        do_move_to_command();
                        break;
                  case ADD_TERRAIN_MODAL:
                        set_position_modally();
                        do_add_terrain(dside);
                        break;
                  case REMOVE_TERRAIN_MODAL:
                        set_position_modally();
                        do_remove_terrain(dside);
                        break;
                  case DISTANCE_MODAL:
                        drag_for_distance(map, h, v);
                        break;
                  case ZOOM_MODAL:
                        select_area_and_zoom(map, h, v, mods);
                        break;
                  case GENERIC_MODAL:
                        set_position_modally();
                        break;
                  default:
                        run_error("unknown modal tool %d", map_modal);
                        break;
            }
            /* Reset modality whether or not the command succeeded, otherwise
               the player can get caught here if no commands can succeed. */
            map_modal = NO_MODAL;
      } else if (mods & cmdKey) {
            if (map->moveonclick && map->autoselect) {
                  unselect_all(map);
                  m_nearest_unit(map, h, v, &unit);
                  /* Always select the unit if it exists and we may examine it. */
                  if (side_sees_unit(dside, unit)) {
                        map->curunit = unit;
                        select_unit_on_map(map, unit);
                        draw_selections(map);
                        /* But only move on drag if we control it. */
                        if (side_controls_unit(dside, unit))
                              move_on_drag(map, unit, mods);
                  } else {
                        select_all_dragged_over(map, h, v, mods);
                        /* Pick the first of the multiple selection as the "current unit". */
                        if (map->numselections > 0) {
                              map->curunit = map->selections[0];
                        }
                  }
            } else {
                  anysuccess = FALSE;
                  for (i = 0; i < map->numselections; ++i) {
                        /* Only move those selected units that we control. */ 
                        unit = map->selections[i];
                        if (side_controls_unit(dside, unit)) {
                              rslt = move_the_selected_unit(map, unit, h, v);
                              if (rslt)
                                anysuccess = TRUE;
                        }
                  }
                  if (!anysuccess)
                    beep();
            }
      } else if (mods & optionKey) {
            for (i = 0; i < map->numselections; ++i) {
                  /* Only fire those selected units that we control. */ 
                  unit = map->selections[i];
                  if (side_controls_unit(dside, unit)) {
                        fire_the_selected_unit(map, unit, h, v);
                  }
            }
      } else if (mods & shiftKey) {
            m_nearest_unit(map, h, v, &unit);
            if (unit && side_sees_unit(dside, unit)) {
                  /* Invert the selection status of the unit. */
                  if (unit_is_selected(map, unit)) {
                        unselect_unit_on_map(map, unit);
                        update_cell(map, unit->x, unit->y);
                  } else {
                        select_unit_on_map(map, unit);
                        draw_selections_at(map, unit->x, unit->y);
                  }
                  draw_unit_info(map);
            } else {
                  select_all_dragged_over(map, h, v, mods);
            }
      } else if (mods & controlKey) {
            m_nearest_unit(map, h, v, &unit);
            /* Open closeup if clicking on a unit that we may examine. */
            if (map->see_all || side_sees_unit(dside, unit)) {
                  show_unit_closeup(unit);
            /* Else focus on clicked spot and select closest own unit. */
            } else {
                  long  unitdist, mindist = 32000;

                  /* Focus on the clicked point. */
                  set_map_focus(map, downx, downy);
                  for_all_side_units(dside, unit) {
                        if (side_controls_unit(dside, unit)) {
                              /* Use least square method. */
                              unitdist = (downx - unit->x) * (downx - unit->x) + 
                                                  (downy - unit->y) * (downy - unit->y);
                              if (unitdist < mindist) {
                                    mindist = unitdist;
                                    map->curunit = unit;
                              }
                        }
                  }
                  if (map->curunit) {
                        unselect_all(map);
                        select_unit_on_map(map, map->curunit);
                        draw_selections(map);
                  }
            }
      } else {
            /* Interpret an unmodified mouse down. */
#ifdef DESIGNERS
            if (is_designer(dside) && tooltype != notool) {
                  apply_designer_tool(map, h, v, mods);
            } else
#endif /* DESIGNERS */
            if (map->moveonclick && !is_designer(dside)) {
                  /* Usually will only be one to move, but be general anyway. */
                  anysuccess = FALSE;
                  for (i = 0; i < map->numselections; ++i) {
                        /* Only move those selected units that we control. */ 
                        unit = map->selections[i];
                        if (side_controls_unit(dside, unit)) {
                              rslt = move_the_selected_unit(map, unit, h, v);
                              if (rslt)
                                anysuccess = TRUE;
                        }
                  }
                  if (!anysuccess)
                    beep();
                  map->scrolltocurunit = TRUE;
            } else {
                  unselect_all(map);
                  m_nearest_unit(map, h, v, &unit);
                  /* Always select the unit if it exists and we may examine it. */
                  if (side_sees_unit(dside, unit)) {
                        map->curunit = unit;
                        select_unit_on_map(map, unit);
                        draw_selections(map);
                        /* But only move on drag if we control it. */
                        if (side_controls_unit(dside, unit))
                              move_on_drag(map, unit, mods);
                  } else {
                        select_all_dragged_over(map, h, v, mods);
                        /* Pick the first of the selection as the current unit. */
                        if (map->numselections > 0)
                              map->curunit = map->selections[0];
                  }
            }
      }
}

void
select_all_dragged_over(Map *map, int h0, int v0, int mods)
{
      Point pt0, pt1, newmouse;
      int drawn = FALSE;
      Rect tmprect;

      SetPt(&pt0, h0, v0);
      SetPt(&pt1, h0, v0);
      SetRect(&tmprect, h0, v0, h0, v0);
      /* (should be a generic subr?) */
      PenMode(patXor);
      PenPat(QDPat(gray));
      while (WaitMouseUp()) {
            GetMouse(&newmouse);
            if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
                  if (drawn) {
                        tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
                        tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
                        FrameRect(&tmprect);
                  }
                  pt1 = newmouse;
                  tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
                  tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
                  FrameRect(&tmprect);
                  drawn = TRUE;
            }
      }
      if (drawn) {
            tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
            tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
            FrameRect(&tmprect);
      }
      PenNormal();
      select_all_units_in_rect(map, &tmprect);
}

void
select_area_and_zoom(Map *map, int h0, int v0, int mods)
{
      Point pt0, pt1, newmouse;
      int drawn = FALSE, x, y;
      Rect tmprect;

      SetPt(&pt0, h0, v0);
      SetPt(&pt1, h0, v0);
      /* (should be a generic subr) */
      PenMode(patXor);
      PenPat(QDPat(gray));
      while (WaitMouseUp()) {
            GetMouse(&newmouse);
            if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
                  if (drawn) {
                        tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
                        tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
                        FrameRect(&tmprect);
                  }
                  pt1 = newmouse;
                  tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
                  tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
                  FrameRect(&tmprect);
                  drawn = TRUE;
            }
      }
      if (drawn) {
            tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
            tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
            FrameRect(&tmprect);
      }
      PenNormal();
      m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
      if (x != downx && y != downy) {
            magnify_to_fit(map, downx, downy, x, y);
      }
}

/* This routine changes a map's viewport and magnification to fit the given rectangle. */

void
magnify_to_fit(Map *map, int x1, int y1, int x2, int y2)
{
      int wid, hgt, wanted, power;

      DGprintf("Magnifying map to fit in area %d,%d - %d,%d\n", x1, y1, x2, y2);
      /* (still need to do y/2 correction) */
      wid = abs(x2 - x1) + 1;  hgt = abs(y2 - y1) + 1;
      map->vp->vcx = min(x1, x2) + wid / 2;  map->vp->vcy = min(y1, y2) + hgt / 2;
      /* Compute the "ideal" size of a displayed cell. */
      wanted = min(map->vp->pxw / wid, map->vp->pxh / hgt);
      /* Search for the best approximation. */
      for (power = NUMPOWERS-1; power > 0; --power) {
            if (hws[power] < wanted) break;
      }
      set_map_mag(map, power);
}

void
move_on_drag(Map *map, Unit *unit, int mods)
{
      int sx, sy, sw, sh, h0, v0, drawn = FALSE, x, y;
      Point pt0, pt1, newmouse;

      m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
      h0 = sx + sw / 2;  v0 = sy + sh / 2;
      SetPt(&pt0, h0, v0);
      SetPt(&pt1, h0, v0);
      /* (should be a generic subr?) */
      PenMode(patXor);
      while (WaitMouseUp()) {
            GetMouse(&newmouse);
            /* should scroll, then abort if we drag outside the window */
            if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
            }
            if (!EqualPt(pt1, newmouse)) {
                  if (drawn) {
                        MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
                  }
                  pt1 = newmouse;
                  MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
                  drawn = TRUE;
            }
      }
      /* Erase the last drawn line. */
      if (drawn) {
            MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
      }
      PenNormal();
      m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
      if (x != downx || y != downy) {
            if (!move_the_selected_unit(map, unit, pt1.h, pt1.v))
              beep();
      } else {
            /* (should try to enter another unit in this cell) */
      }
}

void
drag_for_distance(Map *map, int h0, int v0)
{
      Point pt0, pt1, newmouse;
      int drawn = FALSE, x, y;

      SetPt(&pt0, h0, v0);
      SetPt(&pt1, h0, v0);
      /* (should be a generic subr) */
      PenMode(patXor);
      PenPat(QDPat(gray));
      while (WaitMouseUp()) {
            GetMouse(&newmouse);
            /* should scroll, then abort if we drag outside the window */
            if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
            }
            if (!EqualPt(pt1, newmouse)) {
                  if (drawn) {
                        MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
                  }
                  pt1 = newmouse;
                  MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
                  drawn = TRUE;
            }
      }
      /* Erase the last drawn line. */
      if (drawn) {
            MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
      }
      PenNormal();
      m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
      notify(dside, "Distance from %d,%d to %d,%d is %d",
               downx, downy, x, y, distance(downx, downy, x, y));
}

void
unselect_all(Map *map)
{
      Unit *unit;

      while (map->numselections > 0) {
            /* Start from top of selection list. */
            unit = map->selections[map->numselections - 1];       
            /* Remove unit from selection list before updating its cell. */
            --map->numselections;
            update_cell(map, unit->x, unit->y);
      }
      draw_unit_info(map);
}

/* Add the given unit to the array of units selected in the given map.  If we need
   more space, then grow the array by 50%. */

void
select_unit_on_map(Map *map, Unit *unit)
{
      if (map->numselections >= map->maxselections) {
            int newsize = map->maxselections + map->maxselections / 2;
            Unit **newarray = (Unit **) realloc((char *) map->selections, newsize * sizeof(Unit *));

            if (newarray == NULL) {
                  run_warning("couldn't realloc map selection array");
                  return;
            }
            map->maxselections = newsize;
            map->selections = newarray;
      }
      map->selections[map->numselections++] = unit;
}

int
unit_is_selected(Map *map, Unit *unit)
{
      int i;

      for (i = 0; i < map->numselections; ++i) {
            if (map->selections[i] == unit)
              return TRUE;
      }
      return FALSE;
}

void
unselect_unit_on_map(Map *map, Unit *unit)
{
      int i, j;

      for (i = 0; i < map->numselections; ++i) {
            if (map->selections[i] == unit) {
                  /* Keep selection list contiguous, move other units down. */
                  for (j = i + 1; j < map->numselections; ++j) {
                        map->selections[j - 1] = map->selections[j];
                  }
                  --map->numselections;
                  draw_unit_info(map);
                  return;
            }
      }
}

/* Given a map and a rectangle in it, select all the units whose images touch on
   that rectangle. */

void
select_all_units_in_rect(Map *map, Rect *rectptr)
{
      int rectissmall = FALSE;
      int sx, sy, sw, sh;
      Unit *unit;
      Rect unitrect, tmprect;
      
      /* First see if we're selecting over a large area or within a single cell. */
      if (rectptr->right - rectptr->left < map->vp->hw
            && rectptr->bottom - rectptr->top < map->vp->hh) rectissmall = TRUE;
      /* Now look at all the plausible units and see if any's image intersects the rect. */
      for_all_units(unit) {
            if (in_play(unit)
                 && side_sees_unit(dside, unit)
                 && (rectissmall || unit->transport == NULL)) {
                  m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
                  SetRect(&unitrect, sx, sy, sx + sw, sy + sh);
                  if (SectRect(&unitrect, rectptr, &tmprect)) {
                        select_unit_on_map(map, unit);
                  }
            }
      }
      draw_selections(map);
}

/* This translates the user's "go to here" into appropriate tasks and/or actions. */

int
move_the_selected_unit(Map *map, Unit *unit, int h, int v)
{
      int x, y, rslt;
      Unit *other = NULL;

      m_nearest_cell(map, h, v, &x, &y);
      if (unit_at(x, y) != NULL) {
            m_nearest_unit(map, h, v, &other);
      }
      rslt = advance_into_cell(dside, unit, x, y, other, NULL);
      return rslt;
}

void
fire_the_selected_unit(Map *map, Unit *unit, int h, int v)
{
      int x, y;
      Unit *other;

      m_nearest_cell(map, h, v, &x, &y);
      if (x != unit->x || y != unit->y) {
            if (unit->act && unit->plan) { /* (should be more sophisticated test?) */
                  if ((other = unit_at(x, y)) != NULL) {
                        /* There's a unit to fire at. */
                        if (other->side == unit->side) {
                              beep();
                        } else {
                              net_prep_fire_at_action(unit, unit, other, -1);
                        }
                  } else {
                        beep();
                  }
            }
      }
}

void
select_exactly_one_unit(Map *map, Unit *unit)
{
    Unit *thisunit;

#if 0       /* This prevents unselection of other selected units, which is
            what we want to do when selecting exactly one unit. */
      if (map->numselections > 0) {       
            thisunit = map->selections[0];
            if (thisunit == unit) return;
      }
#endif

      unselect_all(map);
      select_unit_on_map(map, unit);
      scroll_to_unit(map, unit);
      draw_selections(map);
}

void
select_next_unit(Map *map)
{
      select_another(map, find_next_unit);
}

void
select_previous_unit(Map *map)
{
      select_another(map, find_prev_unit);
}

void
select_next_actor(Map *map)
{
      select_another(map, find_next_actor);
}

void
select_previous_actor(Map *map)
{
      select_another(map, find_prev_actor);
}

void
select_next_mover(Map *map)
{
      select_another(map, find_next_mover);
}

void
select_previous_mover(Map *map)
{
      select_another(map, find_prev_mover);
}

void
select_next_awake_mover(Map *map)
{
      select_another(map, find_next_awake_mover);
}

void
select_previous_awake_mover(Map *map)
{
      select_another(map, find_prev_awake_mover);
}

/* Given a map and a searching function, go find the "next" matching unit and select it. */

void
select_another(Map *map, Unit *(*fn)(Side *side, Unit *unit))
{
    Unit *thisunit, *nextunit;

      if (fn == NULL) {
            beep();
            return;
      }
      if (map->numselections > 0) {
            thisunit = map->selections[0];
      } else {
            thisunit = NULL;
      }
      nextunit = (*fn)(dside, thisunit);
      if (nextunit != NULL) {
            unselect_all(map);
            select_unit_on_map(map, nextunit);
            scroll_to_unit(map, nextunit);
            draw_selections(map);
            if (map->autoselect) {
                  map->curunit = nextunit;
            }
      } else if (thisunit != NULL) {
            scroll_to_unit(map, thisunit);
            /* (should not be done this way, but how else?) */
            if (map->autoselect
                  && has_acp_left(thisunit)
                  && (thisunit->plan && !thisunit->plan->asleep && !thisunit->plan->reserve && !thisunit->plan->delayed)) {
                  map->curunit = thisunit;
            }
      }
}


Generated by  Doxygen 1.6.0   Back to index