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

macunit.c

/* Unit specific windows and dialogs for the Mac interface to Xconq.
   Copyright (C) 1998-2000 Hans Ronne and
  1992-1999  Stanley T. Shebs (closeup code).

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 contains all windows (closeups) and dialogs that refer to individual units.
Much of the stuff is from the old macadv.c file, some from macwins.c. global_advance_dialog 
has been renamed to side_research_dialog and is now in macwins.c. */ 

#include "conq.h"
#include "macconq.h"

/* Base numbers of resource cicns. */
#define LARGE_CICNS 2000
#define SMALL_CICNS 2100

/* Max number of facilities and occs displayed in city dialog. Should always agree with the DITL!  */
#define MAX_DISPLAYED_FACS 12
#define MAX_DISPLAYED_OCCS 24
#define CLEAR_AGENDA 99

int default_drawsizes = TRUE;       /* Draw unit size in maps, closeups and lists. */

extern void unit_research_dialog(Unit *unit);               

static UnitCloseup *create_unit_closeup(Unit *unit);
static void draw_simple_closeup(UnitCloseup *closeup);
static void set_simple_closeup_size(UnitCloseup *closeup);
static void draw_advanced_closeup(UnitCloseup *closeup);
static void copy_rect_from_gworld(WindowPtr win, Map *map, Rect cityrect, Rect itemrect);
static void draw_advanced_content(DialogPtr win, Unit *unit);
static void plot_resource_cicns(Map *map, Rect plotrect, int x, int y);
static void draw_landuse_near_unit(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int near);
static void toggle_landuse_one_cell(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int x, int y);
static void hit_closeup_dialog(WindowPtr win, int itemhit, Point mouse, int mods);

static void hit_unit_build_dialog(int ditem, Point mouse, int mods);

static MenuHandle build_construction_menu(Unit *unit);

/* Unit that is shown in the build window. */

Unit  *buildwin_unit = NULL;

/* Globals for unit closeup windows. */

int lastunitcloseuph = -1;
int lastunitcloseupv = -1;

static int m_per_row = 3;
static char *nummrows;

int closeup_spacing = 16;
int closeupwinwid = 240;

/* Unit closeups. */

/* This is the top-level access to bring up a unit closeup. */

void
show_unit_closeup(Unit *unit)
{
      UnitCloseup *unitcloseup;
      Map *map;

      if (frontmap) {
            /* Select this unit on the frontmap. */
            select_exactly_one_unit(frontmap, unit);
            /* Also make it the current unit if possible. */
            if (unit->plan
                  && !unit->plan->asleep
                  && !unit->plan->reserve
                  && !unit->plan->delayed
                  && unit->plan->waitingfortasks
                        ) {
                        frontmap->curunit = unit;
            }
            update_cell(frontmap, unit->x, unit->y);
      }

      /* Then find the closeup if it exists. */
      unitcloseup = find_unit_closeup(unit);
      /* Or create it. */
      if (!unitcloseup)
           unitcloseup = create_unit_closeup(unit);
      /* Check that we have one before proceeding. */
      if (unitcloseup && unitcloseup->window) {
            SelectTheWindow(unitcloseup->window);
            update_window(unitcloseup->window); 
      }
}

UnitCloseup *
find_unit_closeup(Unit *unit)
{
      UnitCloseup *unitcloseup;

      for_all_unit_closeups(unitcloseup) {
            if (unitcloseup->unit == unit && unitcloseup->window)
                return unitcloseup;
      }
      return NULL;
}

UnitCloseup *
unit_closeup_from_window(WindowPtr win)
{
      UnitCloseup *unitcloseup;
      
      for_all_unit_closeups(unitcloseup) {
            if (unitcloseup->window == win)
              return unitcloseup;
      }
      return NULL;
}

UnitCloseup *
create_unit_closeup(Unit *unit)
{
      UnitCloseup *unitcloseup;
      int               mainwidth, mainheight;
      Rect              winRect;          
      Point             winUL;
      Point             winDR;
      DialogPtr         win;
      int               u, w, h;
      GrafPtr           oldport;
            
      /* Stop if we lack a display. */
      if (!active_display(dside) || unit == NULL)
          return NULL;
      /* Stop if we may not examine this unit. */
      if (!side_sees_unit(dside, unit)
          && (!frontmap || !frontmap->see_all))
            return NULL;
      DGprintf("Creating a closeup of %s\n", unit_desig(unit));
      unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));
      unitcloseup->unit = unit;
      u = unit->type;

      if (u_advanced(unit->type))
            win = GetNewDialog(dCity, NULL, NULL);
      else  win = GetNewDialog(dCloseup, NULL, NULL);
      unitcloseup->window = win;
      
      /* Size and position code for non-advanced closeups. */
      if (u_advanced(unit->type) != TRUE) {
            /* Put it adjacent to clicked point if opened from within another closeup. */
            if (unit->transport && find_unit_closeup(unit->transport)) {
                  Point mouse;

                  GetMouse(&mouse);
                  LocalToGlobal(&mouse);
                  MoveWindow(win, mouse.h - 2 , mouse.v + 8, TRUE);
            /* else put it adjacent to the clicked or list-selected unit. */
            } else if (frontmap) {
                  Point upoint;
                  int sx, sy;
                                          
                  GetPort(&oldport);
                  SetPort(frontmap->window);
                  xform(frontmap, unit->x + 2, unit->y - 2, &sx, &sy);
                  upoint.h = sx; 
                  upoint.v = sy;
                  LocalToGlobal(&upoint);
                  MoveWindow(win, upoint.h, upoint.v, true);
                  SetPort(oldport);
            } else {
                  /* This only happens if no maps are open and a closeup 
                  is chosen from a list. */
                  stagger_window(unitcloseup->window, 
                                    &lastunitcloseuph, &lastunitcloseupv);
            }
            set_simple_closeup_size(unitcloseup);

            /* Find the closeups global coordinates. */
            GetPort(&oldport);
            SetPort(win);
            winRect = win->portRect;                  
            winUL.h = winRect.left;
            winUL.v = winRect.top;
            winDR.h = winRect.right;
            winDR.v = winRect.bottom;
            LocalToGlobal(&winUL);              
            LocalToGlobal(&winDR);              

            /* Make sure the closeup stays within the main screen. */
            get_main_screen_size(&mainwidth, &mainheight);
            if (winDR.h + 3 > mainwidth
                 || winUL.h < 3
                 || winDR.v + 3 > mainheight
                 || winUL.v < GetMBarHeight() + 3) {
                  MoveWindow(win, 
                        max(3, min(winUL.h, winUL.h + mainwidth - winDR.h - 3)),
                        max(GetMBarHeight() + 3, min(winUL.v, winUL.v + mainheight - winDR.v - 3)),
                        false);
            }
            SetPort(oldport);
      }

      /* Add window and menu titles. */
      sprintf(spbuf, "%s", medium_long_unit_handle(unit));
      capitalize(spbuf);
      add_window_menu_item(spbuf, win);
      /* Add it to the closeup list. */
      unitcloseup->next = unitcloseuplist;
      unitcloseuplist = unitcloseup;
      MakeDialogFloat(win);
      return unitcloseup;
}

/* Compute the right size for a simple closeup. */

void
set_simple_closeup_size(UnitCloseup *closeup)
{
      Unit        *unit = closeup->unit, *unit2;
      DialogPtr   win = closeup->window;
      int         hgt, m, u, count, occs = 0;
      Task        *task;
                  
      closeup_spacing = small_line_spacing + 1;
      /* This is the first line below the icon. */
      hgt = 60;
      /* Add row for location. */
      hgt += closeup_spacing;
      if (nummrows == NULL) {
            /* Compute and cache the space needed to display each unit's supply. */
            nummrows = xmalloc(numutypes);
            for_all_unit_types(u) {
                  nummrows[u] = count = 0;
                  for_all_material_types(m) {
                        if (um_storage_x(u, m) > 0)
                          ++count;
                  }
                  if (count > 0) {
                        nummrows[u] = count / m_per_row;
                        nummrows[u] += (count % m_per_row > 0 ? 1 : 0);
                  }
            }
      }
      hgt += nummrows[unit->type] * closeup_spacing;

      /* Compute space needed for plans and tasks. */
      if (unit->plan) {
            hgt += closeup_spacing;
            if (unit->plan->maingoal) {
                  hgt += closeup_spacing;
            }
            if (unit->plan->formation) {
                  hgt += closeup_spacing;
            }
            if (unit->plan->tasks) {
                  for_all_tasks(unit->plan, task) {
                        hgt += closeup_spacing;
                  }
            }
      }
      /* Add space for occupants. */
      if (unit->occupant) {
            for_all_occupants(unit, unit2) {
                  occs += 1;
            }
            hgt += (occs / 2 + occs % 2) * 22;
      }
      /* Resize the window if the size has changed. */
      if (win->portRect.bottom - win->portRect.top != hgt)
            SizeWindow(win, closeupwinwid, hgt, 1);
}

/* Draw all the fields and displays in a unit closeup. */

void
draw_unit_closeup(UnitCloseup *closeup)
{
      DialogPtr   win = closeup->window;
      Unit        *unit = closeup->unit;
      GrafPtr     oldport;

      /* Skip if we lack a display. */
      if (!active_display(dside))
            return;
      /* Blast the closeup if the unit is dead or has defected. */
      if (!in_play(unit)
            || (!side_sees_unit(dside, unit)
            && (!frontmap || !frontmap->see_all))) { 
            close_window(win);
            return;
      }

      /* This seemed like a good idea, but it interferes with the
      current unit display code. */
#if 0
      /* Make sure the unit's cell is updated before proceeding. */
      update_cell_display(dside, unit->x, unit->y, UPDATE_ALWAYS);
#endif

      GetPort(&oldport);
      SetPort(win);

      /* Draw the appropriate type of closeup. */
      if (u_advanced(unit->type))
            draw_advanced_closeup(closeup);
      else  draw_simple_closeup(closeup);

      SetPort(oldport);
}     

void
draw_simple_closeup(UnitCloseup *closeup)
{
      int u, m, sx = 2, sy = 2, occs = 0, count = 0, mrow, sx2, sy2, w, h;
      char infobuf[BUFSIZE];
      Rect tmprect;
      DialogPtr win = closeup->window;
      Unit *unit = closeup->unit;
      Unit *unit2;

      /* First adjust closeup size to current need. */
      set_simple_closeup_size(closeup);
      /* Use the small font. */
      TextFont(small_font_id);
      TextSize(small_font_size);
      EraseRect(&win->portRect);
      u = unit->type;

      /* Draw the unit's image. */
      SetRect(&tmprect, sx, sy, sx + 44, sy + 44); 
      EraseRect(&tmprect);
      draw_unit_image(win, tmprect.left, tmprect.top, 
                        tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
                        unit->type, side_number(unit->side), 
                        (frontmap ? frontmap->sidecolors : default_sidecolors), 
                        0, !completed(unit),
                        (frontmap ? frontmap->draw_emblems : default_draw_emblems));
      /* Draw unit size on top of advanced unit. */
      if (u_advanced(unit->type) && default_drawsizes) 
            draw_unit_size(unit, win, tmprect.left, tmprect.top,
                                    tmprect.right - tmprect.left, tmprect.bottom - tmprect.top);

      /* Draw the unit's id next to the icon if debugging. */
      if (Debug || DebugG || DebugM) {
            sprintf(infobuf, "# %d", unit->id);
            MoveTo(52, sy + closeup_spacing);
            DrawText(infobuf, 0, strlen(infobuf));
      }
      /* Draw the unit's hit points and acp's next to the icon. */
      hp_desc(infobuf, unit, TRUE);
      MoveTo(52, sy + 2 * closeup_spacing);
      DrawText(infobuf, 0, strlen(infobuf));
      acp_desc(infobuf, unit, TRUE);
      MoveTo(52, sy + 3 * closeup_spacing);
      DrawText(infobuf, 0, strlen(infobuf));
      /* This is the first line below the icon. */
      sx = 6;
      sy += 60;
      /* Draw the transport if it exists. */
      if (unit->transport) {
            sprintf(infobuf, "Inside:");
            MoveTo(sx, sy);
            DrawText(infobuf, 0, strlen(infobuf));
            SetRect(&tmprect, 40, sy - 16, 62, sy + 6); 
            EraseRect(&tmprect);
            draw_unit_image(win, tmprect.left, tmprect.top,
                              tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
                              unit->transport->type, side_number(unit->transport->side), 
                              (frontmap ? frontmap->sidecolors : default_sidecolors), 
                              0, !completed(unit->transport),
                              (frontmap ? frontmap->draw_emblems : default_draw_emblems));
            sprintf(infobuf, "%s", short_unit_handle(unit->transport));
            tprintf(infobuf, " at %d,%d", unit->x, unit->y);
            MoveTo(66, sy);
            DrawText(infobuf, 0, strlen(infobuf));
      /* Else draw the unit's location. */
      } else {
            location_desc(infobuf, dside, unit, unit->type, unit->x, unit->y);
            MoveTo(sx, sy);
            DrawText(infobuf, 0, strlen(infobuf));
      }
      /* Draw the unit's supplies. */
      mrow = 0;
      while (supply_desc(infobuf, unit, mrow)) {
            sy += closeup_spacing;
            MoveTo(sx, sy);
            DrawText(infobuf, 0, strlen(infobuf));
            ++mrow;
      }
      /* Draw the unit's plan, if it has one. */
      if (unit->plan) {

            Task *task;
            Plan *plan = unit->plan;
            
            /* plan_desc now also includes goal_desc on the same line. To avoid 
            repetition only the first half of plan_desc is therefore included below. */ 
            strcpy(infobuf, "Plan: ");
            strcat(infobuf, plantypenames[plan->type]);
            if (plan->waitingfortasks)
              strcat(infobuf, " Waiting");
            if (plan->asleep)
              strcat(infobuf, " Asleep");
            if (plan->reserve)
              strcat(infobuf, " Reserve");
            if (plan->delayed)
              strcat(infobuf, " Delay");
            if (!plan->aicontrol)
              strcat(infobuf, " NoAI");
            if (plan->supply_is_low)
              strcat(infobuf, " SupplyLow");
            if (plan->tasks) {
                  int i = 0;
                  for_all_tasks(plan, task)
                        ++i;
                  tprintf(infobuf, " (%d task%s)", i, (i == 1 ? "" : "s"));
            } 
            sy += closeup_spacing;
            MoveTo(sx, sy);
            DrawText(infobuf, 0, strlen(infobuf));
            if (plan->maingoal) {
                        strcpy(infobuf, "Goal: ");
                        goal_desc(tmpbuf, plan->maingoal);
                        strncat(infobuf, tmpbuf, strlen(tmpbuf));
                        sy += closeup_spacing;              
                        MoveTo(sx, sy);
                        DrawText(infobuf, 0, strlen(infobuf));
            }
            if (plan->formation) {
                        strcpy(infobuf, "Formation: ");
                        goal_desc(infobuf+strlen(infobuf), plan->formation);
                        sy += closeup_spacing;              
                        MoveTo(sx, sy);
                        DrawText(infobuf, 0, strlen(infobuf));
            }
            if (plan->tasks) {
                  int i = 0;
                  for_all_tasks(plan, task) {
                        ++i;
                        sprintf(infobuf, "Task %d: ", i);
                        task_desc(tmpbuf, unit->side, unit, task);                  
                        strcat(infobuf, tmpbuf);
                        sy += closeup_spacing;              
                        MoveTo(sx, sy);
                        DrawText(infobuf, 0, strlen(infobuf));
                  }
            }
      }
      if (unit->occupant) {
            sy += 6;
            MoveTo(0, sy);
            LineTo(closeupwinwid, sy);
            sy += 4;
            /* Count the occupants. */
            for_all_occupants(unit, unit2) {
                  occs += 1;
            }
            /* Draw each occupant in the correct position. */
            for_all_occupants(unit, unit2) {
                  count += 1;
                  SetRect(&tmprect, sx, sy, sx + 22, sy + 22); 
                  EraseRect(&tmprect);
                  draw_unit_image(win, tmprect.left, tmprect.top,
                                    tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
                                    unit2->type, side_number(unit2->side), 
                                    (frontmap ? frontmap->sidecolors : default_sidecolors), 
                                    0, !completed(unit2),
                                    (frontmap ? frontmap->draw_emblems : default_draw_emblems));
                  sprintf(infobuf, "%s", short_unit_handle(unit2));
                  MoveTo(sx + 22, sy + 16);
                  DrawText(infobuf, 0, strlen(infobuf));
                  sy += 22;
                  /* Check if we should switch to second column. */
                  if (2 * count == occs || 2 * count == occs + 1) {
                        sx += closeupwinwid / 2;
                        sy -= count * 22; 
                  }
            }
      }                 
      SetCursor(&QD(arrow));
      draw_default_button(win, CancelButton);
}

/* First half of the old modal city_dialog. */

void
draw_advanced_closeup(UnitCloseup *closeup)
{
      int               run =0, width, height, downsx, downsy, downx, downy, sx, sy, m, d;
      short             ditem, mitem, done = FALSE, disabled = TRUE, i, u, u2, a, x, y;
      char              buf[32], cname[32], uname[32], sname[32];
      MenuHandle  buildMenu, advanceMenu, planMenu;
      Rect              itemrect, cityrect;
      Str255            pname, pnumber;
      Handle            itemhandle;  
      RGBColor          oldBack;
      Point             mouse;
      Unit              *unit;
      Side              *side;
      DialogPtr         win;
      Map               *map; 

      win = closeup->window;
      unit = closeup->unit;
      side = unit->side;
      map = frontmap;
      
      /* Give controlling side (including debuggers 
      and designers) full control. */
      if (side_controls_unit(dside, unit))
            disabled = FALSE;

      /* Make sure visibility is up to date. */
      if (side) {
            for_all_cells_within_reach(unit, x, y) {
                  if (terrain_visible(side, x, y)) {
                        see_exact(side, x, y);
                  }
            }
      }

      planMenu = GetMenu(mPlanTypes);
      buildMenu = build_construction_menu(unit);
      advanceMenu = build_research_menu(side);
      
      /* Get the type name of unit that is being constructed, if any. */
      if (unit->plan->tasks 
            && unit->plan->tasks->type == TASK_BUILD
            && is_unit_type(unit->plan->tasks->args[0])) {
            strcpy(uname, u_type_name(unit->plan->tasks->args[0]));
      } else if (unit->plan->asleep)
            strcpy(uname, "Asleep");
      else if (unit->plan->reserve)
            strcpy(uname, "Skip Turn");
      else  strcpy(uname, "Idle");

      /* Set build popup to uname. */
      m = CountMItems(buildMenu);
      GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
      SetCtlMax((ControlHandle) itemhandle, m);
      for (i = 1; i <= m; i++) {
            GetItem(buildMenu, i, pname);
            p2c(pname, cname);
            if (strcmp(uname, cname) != 0)
                  continue;
            SetCtlValue((ControlHandle) itemhandle, i);
      }

      /* Get the advance that is being researched by this unit, if any. */
      if (unit->curadvance != NOADVANCE) { 
             strcpy(sname, a_type_name(unit->curadvance));
      } else strcpy(sname, "Idle"); 

      /* Set advance popup to sname. */
      m = CountMItems(advanceMenu);
      GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
      SetCtlMax((ControlHandle) itemhandle, m);
      for (i = 1; i <= m; i++) {
            GetItem(advanceMenu, i, pname);
            p2c(pname, cname);
            if (strcmp(sname, cname) != 0)
                  continue;
            SetCtlValue((ControlHandle) itemhandle, i);
      }

      GetDItem(win, diCityMap, NULL, NULL, &itemrect);
      width = itemrect.right - itemrect.left;
      height = itemrect.bottom - itemrect.top;

      /* Save the Dialog background color. */
      oldBack = ((CGrafPtr) win)->rgbBkColor;
      BackColor(whiteColor);

      /* Draw background and frame. */
      InsetRect(&itemrect, -2, -2);
      RGBForeColor(&backcolor);
      PaintRect(&itemrect);
      PenSize(2, 2);
      ForeColor(blackColor);
      FrameRect(&itemrect);
      PenNormal();
      InsetRect(&itemrect, 2, 2);

      if (map) {
            /* Find the city position. */
            xform(map, unit->x, unit->y, &sx, &sy);

            /* Center cityrect around the city. */
            cityrect.left     = sx + map->vp->hw/2 - width/2;
            cityrect.right    = sx + map->vp->hw/2 + width/2;
            cityrect.top      = sy + map->vp->hh/2 - height/2;
            cityrect.bottom   = sy + map->vp->hh/2 + height/2;
            
            /* Find cityrect position in the offscreen gworld. */
            OffsetRect(&cityrect, map->offsetx + map->bufx - map->conw, 
                                      map->offsety + map->bufy - map->toph);

            /* Copy cityrect from the offscreen gworld. */
            copy_rect_from_gworld(win, map, cityrect, itemrect);

            /* Handle cityrect part that extends beyond left end of gworld. */
            if (area.xwrap && cityrect.left < map->gworldPortPtr->portRect.left) {
                  OffsetRect(&cityrect, map->vp->sxmax, 0);
                  copy_rect_from_gworld(win, map, cityrect, itemrect);
            }
            /* Handle cityrect part that extends beyond right end of gworld. */
            if (area.xwrap && cityrect.right > map->gworldPortPtr->portRect.right) {
                  OffsetRect(&cityrect, -map->vp->sxmax, 0);
                  copy_rect_from_gworld(win, map, cityrect, itemrect);
            }
                        
            /* Draw landuse within the cityrect. */
            d = max(width/map->vp->hw, height/map->vp->hh); /* d could be smaller? */
            draw_landuse_near_unit(map, win, itemrect, unit, d);
      }

      /* Restore dialog background. */
      RGBBackColor(&oldBack);

      /*Hide click map text from disabled sides. */
      if (disabled)
            HideDItem(win, diCityClickText);

      /* Set the AI control checkbox. */
      GetDItem(win, diCityAICheck, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->plan->aicontrol);
      /* Don't allow disabled sides to use the AI box. */
      if (disabled)
            HiliteControl((ControlHandle) itemhandle, 255); 

      /* Set the plan checkbox. */
      GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->autoplan);

      /* Set the advance checkbox. */
      GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->autoresearch);

      /* Set the autobuild radio button. */
      GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->autobuild);

      /* Set the manual build radio button. */
      GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, !unit->autobuild);

      /* Draw the run in its textfield. */
      GetDItem(win, diCityBuildEdit, NULL, &itemhandle, NULL);
      if (unit->plan->tasks
          && unit->plan->tasks->type == TASK_BUILD
          && unit->plan->tasks->args[3] > 0)
            NumToString(unit->plan->tasks->args[3], pname);
      else  NumToString(construction_run_doctrine(unit, 0), pname);
      SetIText(itemhandle, pname);
      SelIText(win, diCityBuildEdit, 0, 32767);

      /* Set plan type popup menu to current plan. */
      GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->plan->type + 1);

      /* Also update the checkmarks, since this menu is used elsewhere. */
      CheckItem(planMenu, miPlanTypeNone, (unit->plan->type == PLAN_NONE));
      CheckItem(planMenu, miPlanTypePassive, (unit->plan->type == PLAN_PASSIVE));
      CheckItem(planMenu, miPlanTypeDefensive, (unit->plan->type == PLAN_DEFENSIVE));
      CheckItem(planMenu, miPlanTypeExploratory, (unit->plan->type == PLAN_EXPLORATORY));
      CheckItem(planMenu, miPlanTypeOffensive, (unit->plan->type == PLAN_OFFENSIVE));
      CheckItem(planMenu, miPlanTypeColonizing, (unit->plan->type == PLAN_COLONIZING));
      CheckItem(planMenu, miPlanTypeImproving, (unit->plan->type == PLAN_IMPROVING));
      CheckItem(planMenu, miPlanTypeRandom, (unit->plan->type == PLAN_RANDOM));

      /* Unhilite controls for disabled sides and when under active AI control. */
      if (disabled || ai_controlled(unit)) {
            GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            /* Hide the runlength Edit field. */
            HideDItem(win, diCityBuildEdit);
            HideDItem(win, diCityBuildTimes);
            /* Update AI control info. */
            GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
            strcpy(buf, "Under ");
            strcat(buf, side->player->aitypename);
            strcat(buf, " control");
            c2p(buf, pname);
            SetIText(itemhandle, pname);
      } else {
            /* Hilite controls. */
            GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 0); 
            /* Show the runlength Edit field. */
            ShowDItem(win, diCityBuildEdit);
            ShowDItem(win, diCityBuildTimes);
            /* Update AI control info. */
            GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
            strcpy(buf, "Under manual control");
            c2p(buf, pname);
            SetIText(itemhandle, pname);
      }
      
      /* Always unhilite the autobuild radio button for normal units. */
      GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
      if (!acp_indep(unit)) {
            HiliteControl((ControlHandle) itemhandle, 255); 
            SetCtlValue((ControlHandle) itemhandle, FALSE);
      }     
      /* Always select the manual radio button for normal units. */
      GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
      if (!acp_indep(unit)) {
            SetCtlValue((ControlHandle) itemhandle, TRUE);
      }     

      /* Draw all the other stuff. */
      draw_advanced_content(win, unit);
      DrawDialog(win);

      SetCursor(&QD(arrow));
      draw_default_button(win, OkButton);
}

static void
copy_rect_from_gworld(WindowPtr win, Map *map, Rect cityrect, Rect itemrect)
{
      Rect  portrect = map->gworldPortPtr->portRect;
      Rect  destrect = itemrect;
      Rect  sourcerect;
      
      /* Clip sourcerect to portrect. */
      SectRect(&cityrect, &portrect, &sourcerect);

      /* Clip destrect so that it will match sourcerect. */ 
      destrect.top      += max(0, portrect.top - cityrect.top);
      destrect.bottom   -= max(0, cityrect.bottom - portrect.bottom);
      destrect.left     += max(0, portrect.left - cityrect.left);
      destrect.right    -= max(0, cityrect.right - portrect.right);

      /* Copy from the map gworld. */
      LockPixels(GetGWorldPixMap(map->gworldPortPtr));
      CopyBits(  &((GrafPtr) map->gworldPortPtr)->portBits,
                  &((GrafPtr) win)->portBits,
                  &sourcerect, &destrect, srcCopy, NULL  );
      UnlockPixels(GetGWorldPixMap(map->gworldPortPtr));
}           

void
draw_advanced_content(DialogPtr win, Unit *unit)
{
      int               occupants[MAX_DISPLAYED_OCCS] = {0};
      char              cname[32], sname[32], buf[BUFSIZE];
      RGBColor          tmpcolor, oldBack;
      Str255            pname, pnumber;
      Handle            itemhandle;  
      CIconHandle       cicnhandle;
      Rect              itemrect, tmprect;
      Unit              *unit2;
      int               i, m; 

      /* Draw the unit icon. */
      GetDItem(win, diCityIcon, NULL, NULL, &itemrect);
      draw_unit_image(win, itemrect.left, itemrect.top, 
                        itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
                        unit->type, side_number(unit->side), 
                        (frontmap ? frontmap->sidecolors : default_sidecolors), 
                        0, !completed(unit),
                        (frontmap ? frontmap->draw_emblems : default_draw_emblems));
      /* Draw unit size on top of the icon. */
      if (default_drawsizes) 
            draw_unit_size(unit, win, itemrect.left, itemrect.top,
                        itemrect.right - itemrect.left, itemrect.bottom - itemrect.top);

      /* Draw the unit's name, type and side. */
      GetDItem(win, diCityName, NULL, &itemhandle, NULL);
      if (indep(unit))
            strcpy(buf, "Independent");
      else  strcpy(buf, side_adjective(unit->side));
      strcat(buf, " ");
      strcat(buf, u_type_name(unit->type));
      strcat(buf, " ");
      strcat(buf, unit->name);
      c2p(buf, pname);
      SetIText(itemhandle, pname);

      /* Draw the unit's size. */
      GetDItem(win, diCitySize, NULL, &itemhandle, NULL);
      strcpy(buf, "Size : ");
      NumToString(unit->size, pnumber);
      p2c(pnumber, cname);
      strcat(buf, cname);
      c2p(buf, pname);
      SetIText(itemhandle, pname);

      /* Draw the unit's transport if it exists (unlikely for an advanced unit but
      theoretically possible depending on game design). */
      if (unit->transport) {
            unit2 = unit->transport;

            /* Draw the transport icon. */
            GetDItem(win, diCityTransportIcon, NULL, NULL, &itemrect);
            draw_unit_image(win, itemrect.left, itemrect.top, 
                              itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
                              unit2->type, side_number(unit2->side), 
                              (frontmap ? frontmap->sidecolors : default_sidecolors), 
                              0, !completed(unit2),
                              (frontmap ? frontmap->draw_emblems : default_draw_emblems));
            /* Draw transport size on top of the icon. */
            if (default_drawsizes) 
                  draw_unit_size(unit2, win, itemrect.left, itemrect.top,
                              itemrect.right - itemrect.left, itemrect.bottom - itemrect.top);

            /* Draw the "Inside:" text. */
            GetDItem(win, diCityTransportText, NULL, &itemhandle, NULL);
            strcpy(buf, "Inside:");
            c2p(buf, pname);
            SetIText(itemhandle, pname);

            /* Draw the transport's name. */
            GetDItem(win, diCityTransportName, NULL, &itemhandle, NULL);
            strcpy(buf, short_unit_handle(unit2));
            c2p(buf, pname);
            SetIText(itemhandle, pname);
      }
      
      /* Draw AI control info. */
      GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
      strcpy(buf, "Under ");
      if (indep(unit) && unit->plan->aicontrol)
            strcat(buf, "brainless");
      else if (ai_controlled(unit))
            strcat(buf, unit->side->player->aitypename);
      else  strcat(buf, "manual"); 
      strcat(buf, " control");
      c2p(buf, pname);
      SetIText(itemhandle, pname);

      /* Draw cyan background for materials panel. */ 
      GetDItem(win, diCityMatPanel, NULL, NULL, &itemrect);
      tmpcolor.red = 0xBFFF;
      tmpcolor.green = 0xFFFF; 
      tmpcolor.blue = 0xFFFF;
      RGBForeColor(&tmpcolor);
      FillRect(&itemrect, QDPat(black));
      ForeColor(blackColor);
      FrameRect(&itemrect);
      
      /* Draw green background for facilities panel. */ 
      GetDItem(win, diCityFacPanel, NULL, NULL, &itemrect);
      tmpcolor.red = 0xBFFF;
      tmpcolor.green = 0xFFFF; 
      tmpcolor.blue = 0xBFFF;
      RGBForeColor(&tmpcolor);
      FillRect(&itemrect, QDPat(black));
      ForeColor(blackColor);
      FrameRect(&itemrect);

      /* Draw pink background for garrison panel. */ 
      GetDItem(win, diCityOccPanel, NULL, NULL, &itemrect);
      tmpcolor.red = 0xFFFF;
      tmpcolor.green = 0xBFFF; 
      tmpcolor.blue = 0xFFFF; 
      RGBForeColor(&tmpcolor);
      FillRect(&itemrect, QDPat(black));
      ForeColor(blackColor);
      FrameRect(&itemrect);

      /* Plot material statistics headings. */
      GetDItem(win, diCityMaterial, NULL, &itemhandle, NULL);
      SetIText(itemhandle, "\pMaterial");
      GetDItem(win, diCityProduction, NULL, &itemhandle, NULL);
      SetIText(itemhandle, "\pProduction");
      GetDItem(win, diCitySupply, NULL, &itemhandle, NULL);
      SetIText(itemhandle, "\pSupply");
      GetDItem(win, diCityTreasury, NULL, &itemhandle, NULL);
      SetIText(itemhandle, "\pTreasury");

      /* Draw statistics for material types 0 to 3. */
      for (m = 0; m < min(4, nummtypes); m++) {
            if (m_resource_icon(m)) {
                  /* Plot material icon. */
                  GetDItem(win, diCityMatBase + (5 * (m + 1)), NULL, NULL, &itemrect);
                  cicnhandle = GetCIcon(LARGE_CICNS + 10 * m_resource_icon(m) + 6);
                  PlotCIcon(&itemrect, cicnhandle);
                  DisposeCIcon(cicnhandle);
            }
            /* Plot material name. */
            GetDItem(win, diCityMatBase + (5 * (m + 1)) + 1, NULL, &itemhandle, NULL);
            c2p(mtypes[m].name, pname);
            SetIText(itemhandle, pname);
            /* Plot material production. */
            GetDItem(win, diCityMatBase + (5 * (m + 1)) + 2, NULL, &itemhandle, NULL);
            NumToString(unit->production[m], pnumber);
            SetIText(itemhandle, pnumber);
            /* Plot material supply. */
            GetDItem(win, diCityMatBase + (5 * (m + 1)) + 3, NULL, &itemhandle, NULL);
            NumToString(unit->supply[m], pnumber);
            SetIText(itemhandle, pnumber);
            /* Plot global treasury supply. */
            GetDItem(win, diCityMatBase + (5 * (m + 1)) + 4, NULL, &itemhandle, NULL);
            if (unit->side && m_treasury(m))
                  NumToString(unit->side->treasury[m], pnumber);
            else  NumToString(0, pnumber);
            SetIText(itemhandle, pnumber);
      }

      /* Draw construction status. */
      if (unit->plan && unit->plan->tasks && unit->plan->tasks->type == TASK_BUILD) {
            GetDItem(win, diCityBuildStatus, NULL, &itemhandle, NULL);

            /* Print cps of current unit if it exists, else print "0". */
            if (find_unit(unit->plan->tasks->args[1]) != NULL) {
                   NumToString((find_unit(unit->plan->tasks->args[1]))->cp, pnumber);
                   p2c(pnumber, cname);
            } else       strcpy(cname, "0");

            strcat(cname, " / ");
            NumToString(u_cp(unit->plan->tasks->args[0]), pnumber);
            p2c(pnumber, sname);
            strcat(cname, sname);
            
            /* Also print run progress. */
            if (unit->plan->tasks->args[3] > 1) {
                  strcat(cname, "   ( ");
                  NumToString(unit->plan->tasks->args[2] + 1, pnumber);
                  p2c(pnumber, sname);
                  strcat(cname, sname);
                  strcat(cname, " of ");
                  NumToString(unit->plan->tasks->args[3], pnumber);
                  p2c(pnumber, sname);
                  strcat(cname, sname);
                  strcat(cname, " )");                
            }                 
            c2p(cname, pnumber);
            SetIText(itemhandle, pnumber);
      }

      /* Draw research status. */
      if (unit->curadvance > 0 && unit->side) { 
            GetDItem(win, diCityResearchStatus, NULL, &itemhandle, NULL);
            NumToString(unit->side->advance[unit->curadvance], pnumber);
            p2c(pnumber, cname);
            strcat(cname, " / ");
            NumToString(a_rp(unit->curadvance), pnumber);
            p2c(pnumber, sname);
            strcat(cname, sname);
            c2p(cname, pnumber);
            SetIText(itemhandle, pnumber);
      }
      /* Sort the occupants. First load facilities. */
      i = 0;
      for_all_occupants(unit, unit2)
            if (alive(unit2) && u_facility(unit2->type))
                  occupants[i++] = unit2->id;

      /* Move to the end of the facilities list. */
      i = MAX_DISPLAYED_FACS;

      /* Then load other units. */
      for_all_occupants(unit, unit2)
            if (alive(unit2) &! u_facility(unit2->type))
                  occupants[i++] = unit2->id;

      /* Save the old Dialog background color. */
      oldBack = ((CGrafPtr) win)->rgbBkColor;
      BackColor(whiteColor);

      /* Draw all facilities and other occupants. */
      for (i = 0; i < MAX_DISPLAYED_OCCS; i++) {
            unit2 = find_unit(occupants[i]);
            if (unit2) {
                  /* First plot each unit icon. */
                  GetDItem(win, diCityOccBase + 2 * i, 0, 0, &itemrect);
                  draw_unit_image(win, itemrect.left, itemrect.top,
                                    itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
                                    unit2->type, side_number(unit2->side), 
                                    (frontmap ? frontmap->sidecolors : default_sidecolors), 
                                    0, !completed(unit2),
                                    (frontmap ? frontmap->draw_emblems : default_draw_emblems));
                  /* Then plot each unit name. */
                  GetDItem(win, diCityOccBase + 2 * i + 1, NULL, &itemhandle, NULL);
                  if (unit2->name) {
                        strcpy(buf, u_type_name(unit2->type));
                        strcat(buf, " ");
                        strcat(buf, unit2->name);
                  /* Skip the unit number for facilities. */
                  } else if (unit2->number > 0 &! u_facility(unit2->type)) {
                        NumToString(unit2->number, pnumber);
                        p2c(pnumber, cname);
                        strcpy(buf, cname);
                        strcat(buf, ordinal_suffix(unit2->number));
                        strcat(buf, " ");
                        strcat(buf, u_type_name(unit2->type));
                  } else      strcpy(buf, u_type_name(unit2->type));

                  /* Write out cps for each unit if debugging. */
                  if (Debug || DebugG || DebugM) {
                        strcat(buf, " ");
                        NumToString(unit2->cp, pnumber);
                        p2c(pnumber, cname);
                        strcat(buf, cname);
                        strcat(buf, "/");
                        NumToString(u_cp(unit2->type), pnumber);
                        p2c(pnumber, cname);
                        strcat(buf, cname);
                  }
                  c2p(buf, pname);
                  SetIText(itemhandle, pname);
            } else {
                  GetDItem(win, diCityOccBase + 2 * i + 1, NULL, &itemhandle, NULL);
                  c2p("", pname);
                  SetIText(itemhandle, pname);
            }
      }
      /* Restore dialog background. */
      RGBBackColor(&oldBack);
}

/* 
      Draw the size number of a city on top of its unit icon. 
*/

void
draw_unit_size(Unit *unit, WindowPtr win, int sx, int sy, int sw, int sh)
{
      UnitCloseup *closeup;
      Map               *map;
      List              *list;
      Str255            pnumber;
      short             curfont, cursize, curstyle;
      int               e;

      /* Filter out very small images. */
      if (sw < 16)
            return; 
      /* NULL pointer check. */
      if (!unit || !unit->size)
            return;
             
      /* Find current closeup, map or list. */
      closeup = unit_closeup_from_window(win);
      map = map_from_window(win);
      list = list_from_window(win);
      e = side_number(unit->side);
            
      /* Save the current font. */
      curfont = QD(thePort)->txFont;
      cursize = QD(thePort)->txSize;
      curstyle = QD(thePort)->txFace;

      /* Default colors used in the absence of defined side colors. */
      RGBForeColor(&forecolor);
      RGBBackColor(&maskcolor);

            /* Load city size into pascal string. */
            NumToString(unit->size, pnumber);

            /* Use Chicago. */
            TextFont(0);

            /* Use fixed optimized fonts if asked to do so. */
      if ((map && map->optimize_fonts)
          || (list && default_optimize_fonts)
          || (closeup && default_optimize_fonts)) {

                  if (sh < 32)
                        TextSize(9);
                  else if (sh < 64)
                        TextSize(12);
                  else if (sh < 128)
                        TextSize(24);
                  else  TextSize(48);

            /* Else scale text sizes according to formula. */
            } else TextSize(min(max(9, sh/2), 48));

            /* First use the mask color to plot the core. */
      if (((map && map->sidecolors) 
          || (list && list->sidecolors)
          || (closeup && default_sidecolors)) 
                && icon_mask_color[e]) {
                  RGBForeColor(&(get_sideColor(e, icon_mask_color[e])));
            } else      RGBForeColor(&maskcolor);
            TextFace(bold + condense);

            /* Handtuned setting for 16 x 16. */
            if (sh < 32) MoveTo(sx + 1, sy + 10);
            else MoveTo(sx + sw/5, sy + 3 * sh/5);
            DrawString(pnumber);

            /* Then use the main color to plot the outline. */
      if (((map && map->sidecolors) 
          || (list && list->sidecolors) 
          || (closeup && default_sidecolors)) 
                && main_icon_color[e]) {
                  RGBForeColor(&(get_sideColor(e, main_icon_color[e])));
            } else      RGBForeColor(&forecolor);
            TextFace(shadow + condense);

            /* Handtuned setting for 16 x 16. */
            if (sh < 32) MoveTo(sx + 1, sy + 10);
            else MoveTo(sx + sw/5, sy + 3 * sh/5);
            DrawString(pnumber);

      /* Restore the current font. */
      TextFont(curfont);
      TextSize(cursize);
      TextFace(curstyle);

      /* Restore colors. */
      ForeColor(blackColor);
      BackColor(whiteColor);
}

/* 
      Draw landuse in all cells near unit. Assumes the port is already set. 
*/

static void
draw_landuse_near_unit(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int near)
{
      int               mapsx, mapsy, winsx, winsy, sx, sy, x, y;
      Rect              imagerect;
      Str255            pnumber;
      RgnHandle         tmprgn;

      /* Return if landuse is not defined. */
      if (!user_defined())
            return;

      /* Clip to itemrect. */
      tmprgn = NewRgn();
      GetClip(tmprgn);
      ClipRect(&itemrect);

      /* Find win coordinates of unit (city) cell. */ 
      winsx = (itemrect.right + itemrect.left) / 2 - map->vp->hw/2;
      winsy = (itemrect.bottom + itemrect.top) / 2 - map->vp->hh/2;
      
      /* Find map coordinates of unit (city) cell. */
      xform(map, unit->x, unit->y, &mapsx, &mapsy);
      
      /* Zero the number of used cells. */
      unit->usedcells = 0;
      /* Go through all cells within "near" steps from unit. */
      for_all_cells_within_range(unit->x, unit->y, near, x, y) {
            /* Skip if cell is unused. */
            if (user_at(x, y) == NOUSER)
                  continue;
            /* Skip if cell is outside area. */
            if (!inside_area(x, y))
                  continue;
            /* Skip if the cell is not visible to the unit's side. */
            if (!terrain_visible(unit->side, x, y))
                  continue;
            /* Recalculate the number of used cells. */
            if (user_at(x, y) == unit->id)
                  unit->usedcells += 1;
            /* Find map coordinates of cell. */
            xform(map, x, y, &sx, &sy);
            /* Add win-map offset. */
            sx += winsx - mapsx;
            sy += winsy - mapsy;
            /* Adjust to unit part of cell. */
            sx += (map->vp->hw - map->vp->uw) / 2;  
            sy += (map->vp->hh - map->vp->uh) / 2;
            /* Set imagerect. */
            SetRect(&imagerect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);
            /* Draw a colored square if cell is used by another unit. */
            if (find_unit(user_at(x, y)) && user_at(x, y) != unit->id) {
                  int e = side_number(find_unit(user_at(x, y))->side);
                  PenSize(2, 2);
                  RGBForeColor(&(get_sideColor(e, main_icon_color[e])));
                  FrameRect(&imagerect);
                  PenNormal();
                  ForeColor(blackColor);
            /* Else plot the resource cicns for that cell. */
            } else plot_resource_cicns(map, imagerect, x, y);
      }
      /* Restore old clip. */
      SetClip(tmprgn);
      DisposeRgn(tmprgn);
}

/* 
      Plot resource cicns in single cell. Assumes that the port already is set.
      Max 4 material types can be displayed at the same time. The icon used for
      each material type is determined by the mtype property m_resource_icon. 
*/

void
plot_resource_cicns(Map *map, Rect plotrect, int x, int y)
{
      CIconHandle cicnhandle;
      int m, n, i;

      n = (map->vp->uh > 16 ? LARGE_CICNS : SMALL_CICNS);
      for_all_material_types(m) {
            i = m_resource_icon(m);
            if (i) {
                  cicnhandle = GetCIcon(n + 10 * i + production_at(x, y, m));
                  PlotCIcon(&plotrect, cicnhandle);
                  DisposeCIcon(cicnhandle);
            }
      }
}

int
do_mouse_down_unit_closeup(UnitCloseup *unitcloseup, Point mouse, int mods)
{
      int   count = 0, occs = 0, occx = 0, occy = 0;
      DialogPtr win = unitcloseup->window;
      Unit  *unit, *unit2;
      Rect  tmprect;
      Map   *map;
      Plan  *plan;
      Task  *task;
      short ditem;

      /* Check if a dialog item was hit and branch to dialog handling code in that case.
      This makes it possible to avoid using the Dialog Manager. */
      ditem = FindDItem(win, mouse) + 1;  /* FindDItem returns (ditem -1)! */
      if (ditem > 0) {
            /* First undo previous conversion to locals by do_mouse_down. */
            LocalToGlobal(&mouse);
            hit_closeup_dialog(win, ditem, mouse, mods);
            return;
      }
      /* Handle clicks on transport or occupants in simple closeups. */
      unit = unitcloseup->unit;
      if (u_advanced(unit->type) != TRUE) {
            occy = 60;
            if (unit->transport) {
                  SetRect(&tmprect, 0, occy - 16, closeupwinwid, occy + 4);
                  if (PtInRect(mouse, &tmprect)) {                
                        /* Close the current closeup unless ctrl-clicking. */
                        if ((mods & controlKey) == 0) {
                              close_window(win);
                        }
                        /* Show the transport's closeup. */
                        show_unit_closeup(unit->transport);             
                        return;
                  }
            }
            if (unit->occupant) {
                  occy +=  closeup_spacing * (nummrows[unit->type] + 1);
                  if (unit->plan) {
                        occy += closeup_spacing;
                        if (unit->plan->maingoal) {
                              occy += closeup_spacing;
                        }
                        if (unit->plan->formation) {
                              occy += closeup_spacing;
                        }
                        if (unit->plan->tasks) {
                              for_all_tasks(unit->plan, task) {
                                    occy += closeup_spacing;
                              }
                        }
                  }
                  /* Count the occupants. */
                  for_all_occupants(unit, unit2) {    
                        occs += 1;
                  }
                  for_all_occupants(unit, unit2) {
                        count += 1; 
                        SetRect(&tmprect, occx, occy, occx + closeupwinwid / 2, occy +22);
                        if (PtInRect(mouse, &tmprect)) {                
                              /* Close the current closeup unless ctrl-clicking. */
                              if ((mods & controlKey) == 0) {
                                    close_window(win);
                              }
                              /* Show closeup of occupant that was clicked on. */
                              show_unit_closeup(unit2);                 
                              return;
                        }
                        occy += 22;
                        /* Check if we should switch to second column. */
                        if (2 * count == occs || 2 * count == occs + 1) {
                              occx += closeupwinwid / 2;
                              occy -= count * 22;     
                        }
                  }
            }
      }
      /* The click was in an empty part of the closeup window. We call
      show_unit_closeup in order to select the unit on the frontmap and 
      make it the current unit. */
      show_unit_closeup(unit);
}

TEHandle runlength_text = nil;

/* This function examines keystrokes destined for closeup dialogs. 
It returns TRUE for keys that got handled, and FALSE for all other keys. */ 

int 
do_key_down_closeup(UnitCloseup *closeup, char key, char code, int mods)
{
      Point mouse = {0, 0};         /* Dummy variable. */
      int   advanced;
      
      advanced = u_advanced(closeup->unit->type);

      /* We hit the Enter or Return key. */
      if (key == 0x4C || key == 0x24) {
            if (advanced)
                  hit_closeup_dialog(closeup->window, OkButton, mouse, mods);
            else  close_window(closeup->window);
            return TRUE;
      /* We hit the Escape key. */
      } else if (key == 0x35) {
            close_window(closeup->window);
            return TRUE;
      /* 0-9 should be forwarded to the dialog's Edit text field instead of 
      being interpreted as command prefixes. Also forward backspace. */
      } else if (advanced 
                && (between(0x12, key, 0x17)    /* Keyboard 1-6 */ 
                  || key == 0x19                      /* Keyboard 9 */ 
                  || key == 0x1A                      /* Keyboard 7 */ 
                  || key == 0x1C                      /* Keyboard 8 */ 
                  || key == 0x1D                      /* Keyboard 0 */ 
                  || key == 0x33)) {                  /* Backspace   */ 

      /* The Dialog Manager no longer handles Edit Text fields for us. */
            TEKey(code, ((DialogPeek) closeup->window)->textH);
            return TRUE;
      }
      return FALSE;
}

/* Second half of the old modal city_dialog. */

void
hit_closeup_dialog(WindowPtr win, int ditem, Point mouse, int mods)
{
      int               run =0, width, height, downsx, downsy, downx, downy, sx, sy, m;
      short             aicontrol, autoplan, autoresearch, autobuild, plantype, advance, build;
      short             mitem, done = FALSE, disabled = TRUE, i, u, u2, a;
      char              buf[32], cname[32], uname[32], sname[32];
      int               occupants[MAX_DISPLAYED_OCCS] = {0};
      MenuHandle  buildMenu, advanceMenu;
      Str255            pname, pnumber;
      UnitCloseup *unitcloseup;
      Unit              *unit, *unit2;
      HelpNode          *helpnode;
      Handle            itemhandle;  
      Rect              itemrect;
      GrafPtr           oldport;
      Map               *map;
      Side              *side;

      unitcloseup = unit_closeup_from_window(win);
      unit = unitcloseup->unit;
      side = unit->side;
      map = frontmap;
      buildMenu = build_construction_menu(unit);
      advanceMenu = build_research_menu(side);

      /* Give controlling side (including debuggers 
      and designers) full control. */
      if (side_controls_unit(dside, unit))
            disabled = FALSE;

      /* Sort the occupants. First load facilities. */
      i = 0;
      for_all_occupants(unit, unit2)
            if (alive(unit2) && u_facility(unit2->type))
                  occupants[i++] = unit2->id;

      /* Move to the end of the facilities list. */
      i = MAX_DISPLAYED_FACS;

      /* Then load other units. */
      for_all_occupants(unit, unit2)
            if (alive(unit2) &! u_facility(unit2->type))
                  occupants[i++] = unit2->id;

      GetPort(&oldport);      
      SetPort(win);
      DrawDialog(win);

      switch (ditem) {

      case  diCityMap:        /* Find the cell that was clicked. */
            if (disabled      /* Don't allow other sides to change things. */ 
                || ai_controlled(unit))
                  break;
            GetDItem(win, diCityMap, NULL, NULL, &itemrect);
            width = itemrect.right - itemrect.left;
            height = itemrect.bottom - itemrect.top;
            /* Find the city position. */
            xform(map, unit->x, unit->y, &sx, &sy);
            GlobalToLocal(&mouse);
            downsx = mouse.h - itemrect.left + sx + map->vp->hw/2 - width/2;
            downsy = mouse.v - itemrect.top + sy + map->vp->hh/2 - height/2;
            m_nearest_cell(map, downsx, downsy, &downx, &downy);
            if (!inside_area(downx, downy)) 
                  break;
            if (!cell_is_within_reach(unit, downx, downy))
                  break;
            /* Toggle landuse on or off for the cell. */
            toggle_landuse_one_cell(map, win, itemrect, unit, downx, downy);
      break;

      case diCityAICheck:     /* Toggle AI control checkbox. */
            if (disabled)     /* Don't allow other sides to change things. */ 
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
            /* This is the only control we want to set as soon as it is clicked. */
            net_set_unit_ai_control(side, unit, GetCtlValue((ControlHandle) itemhandle), FALSE);
            /* Update the dialog to reflect altered control hiliting. */
            draw_unit_closeup(unitcloseup);
      break;

      case diCityPlanPopup:         /* Handle Popup menus. */
      case diCityAdvancePopup:
      case diCityBuildPopup: 
            if (disabled || ai_controlled(unit))
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            /* This is no longer automatically done by the dialog manager. */
            TrackControl((ControlHandle) itemhandle, mouse, (void *) -1);
            /* Make sure the new menu selection is drawn. */
            DrawControls(win);
      break;

      case diCityPlanCheck:         /* Toggle autoplan checkbox. */
            if (disabled || ai_controlled(unit))
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
      break;

      case diCityAdvanceCheck:      /* Toggle autoresearch checkbox. */
            if (disabled || ai_controlled(unit))
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
      break;

      case diCityBuildCheck:  /* Switch autobuild button. */
            if (disabled || ai_controlled(unit))
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
            GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
      break;

      case diCityBuildRepeat:       /* Switch manual build button. */
            if (disabled || ai_controlled(unit))
                  break;
            GetDItem(win, ditem, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
            GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
            SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
      break;

      case  OkButton:
            /* Don't allow others to save any changes. */
            if (disabled || ai_controlled(unit)) {
                  done = TRUE;
                  break;
            }
            /* Get the AI control state. */
            GetDItem(win, diCityAICheck, NULL, &itemhandle, NULL);
            aicontrol = GetCtlValue((ControlHandle) itemhandle);

            /* Get the autoplan state. */
            GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
            autoplan = GetCtlValue((ControlHandle) itemhandle);

            /* Get the autoresearch state. */
            GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
            autoresearch = GetCtlValue((ControlHandle) itemhandle);

            /* Get the autobuild state. */
            GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
            autobuild = GetCtlValue((ControlHandle) itemhandle);

            /* Get the selected plan type. */
            GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
            plantype = GetCtlValue((ControlHandle) itemhandle) - 1;

            /* Get the selected research task. */
            GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
            mitem = GetCtlValue((ControlHandle) itemhandle);
            GetItem(advanceMenu, mitem, pname);
            p2c(pname, sname);
            advance = NOADVANCE;
            for_all_advance_types(a) {
                  if (strcmp(a_type_name(a), sname) == NULL) {
                        advance = a;
                        break;
                  }
            }

            /* Always set runlength to doctrine if on autobuild. */
            if (unit->autobuild || ai_controlled(unit)) {
                  run = construction_run_doctrine(unit, 0);
            } else {                            
                  /* Else get the runlength from its text field. */
                  GetDItem(win, diCityBuildEdit, NULL, &itemhandle, NULL);
                        GetIText(itemhandle, pname);
                  StringToNum(pname, (long *) &run);
                  run = min(max(run, 0), CLEAR_AGENDA);
            }

            /* Get the selected build task. */
            GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
            mitem = GetCtlValue((ControlHandle) itemhandle);
            GetItem(buildMenu, mitem, pname);
            p2c(pname, uname);
            build = NONUTYPE;
            for_all_unit_types(u) {
                  /* Find the utype that was selected in the menu. */
                  if (strcmp(u_type_name(u), uname) == NULL) {
                        build = u;
                        break;                        
                  }
            }

            /* We need to do this last of all since the net_set functions call
            update_unit_display which redraws the closeup itself. The first
            call to a net_set function therefore resets all popup menus to the
            initial values, thus destroying the settings we want to save. */

            net_set_unit_ai_control(side, unit, aicontrol, FALSE);
            net_set_unit_autoplan(side, unit, autoplan);
            net_set_unit_autoresearch(side, unit, autoresearch);
            net_set_unit_autobuild(side, unit, autobuild);
            net_set_unit_plan_type(side, unit, plantype);
            net_set_unit_curadvance(side, unit, advance);
            if (build == NONUTYPE) {
                  net_clear_task_agenda(side, unit);
                   if (strcmp(uname, "Asleep") == NULL) {
                        net_set_unit_asleep(side, unit, TRUE, FALSE);
                  } else if (strcmp(uname, "Skip Turn") == NULL) {
                        net_set_unit_reserve(side, unit, TRUE, FALSE);
                  }
            } else      net_set_build_task(unit, build, run, 0, 0);

            done = TRUE;
      break;
      
      case CancelButton:
            done = TRUE;
      break;

      case HelpButton:        
            helpnode = find_help_node(first_help_node, u_type_name(unit->type));
            if (helpnode)
                  show_help_window(helpnode);
            else  beep();
      break;
            
      default:
            /* Open new closeup if click was on occupant icon or text . */
            if (between(diCityOccBase, ditem, diCityOccBase + 2 * MAX_DISPLAYED_OCCS - 1)) {
                  unit2 = find_unit(occupants[(ditem - diCityOccBase) / 2]);
                  if (unit2) {
                        /* Close the old closeup unless ctrl-clicking. */
                        if ((mods & controlKey) == 0) {
                              close_window(win);
                        }
                        show_unit_closeup(unit2);
                  }
            }
            /* Open new closeup if click was on the transport's icon or text. */
            if (unit->transport
                && (ditem == diCityTransportText
                    || ditem == diCityTransportIcon
                    || ditem == diCityTransportName)) {
                  /* Close the old closeup unless ctrl-clicking. */
                  if ((mods & controlKey) == 0) {
                        close_window(win);
                  }
                  show_unit_closeup(unit->transport);
            }
      break;
      
      }

      if (done)
          close_window(win);

      /* Restore old port. */
      SetPort(oldport);
}

/* 
      Toggle landuse for a single cell. Assumes the port already is set. 
*/

void
toggle_landuse_one_cell(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int x, int y)
{
      int               mapsx, mapsy, winsx, winsy, sx, sy, nearx, neary;
      Rect              maprect, winrect;
      RgnHandle         tmprgn;

      /* Return if landuse is undefined. */
      if (!user_defined())
            return;
      /* Return if the cell is not visible to this side. */
      if (!terrain_visible(unit->side, x, y))
            return;
      /* Return if the cell is used by another unit. */
      if (user_at(x, y) != NOUSER && user_at(x, y) != unit->id)
            return;
      /* Return if using maxcells and we are trying to add one more. */
      if (unit->usedcells >= unit->maxcells && user_at(x, y) != unit->id)
            return;
      /* Return if independents or untrusted side has a unit in the cell. */
      if (unit_at(x, y) != NULL) {
            Unit *unit2;
            for_all_stack(x, y, unit2)
                  if (!trusted_side(unit->side, unit2->side))
                        return;
      }
      /* Toggle landuse by unit either on or off for the cell. */
      if (user_at(x, y) == NOUSER) {
            set_user_at(x, y, unit->id);
            unit->usedcells += 1;
      } else if (user_at(x, y) == unit->id) {
            set_user_at(x, y, NOUSER);
            unit->usedcells -= 1;
      } else return;
      
      /* Clip to itemrect. */
      tmprgn = NewRgn();
      GetClip(tmprgn);
      ClipRect(&itemrect);

      /* Find the cell position. */
      xform(map, x, y, &sx, &sy);

      /* Adjust to unit part of cell. */
      sx += (map->vp->hw - map->vp->uw) / 2;  
      sy += (map->vp->hh - map->vp->uh) / 2;

      /* Find cell rect position in the map. */
      SetRect(&maprect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);

      /* Find cell rect position in the offscreen gworld. */
      OffsetRect(&maprect, map->offsetx + map->bufx - map->conw, 
                                map->offsety + map->bufy - map->toph);

      /* Find win coordinates of unit (city) cell. */ 
      winsx = (itemrect.right + itemrect.left) / 2 - map->vp->hw/2;
      winsy = (itemrect.bottom + itemrect.top) / 2 - map->vp->hh/2;
      
      /* Find map coordinates of unit (city) cell. */
      xform(map, unit->x, unit->y, &mapsx, &mapsy);
      
      /* Add win-map offset. */
      sx += winsx - mapsx;
      sy += winsy - mapsy;

      /* Find cell rect position in the dialog window. */
      SetRect(&winrect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);

      /* Copy cellrect from gworld to dialog window. */
      LockPixels(GetGWorldPixMap(map->gworldPortPtr));
      CopyBits(  &((GrafPtr) map->gworldPortPtr)->portBits,
                  &((GrafPtr) win)->portBits,
                  &maprect, &winrect, srcCopy, NULL  );
      UnlockPixels(GetGWorldPixMap(map->gworldPortPtr));

      /* Plot resource cicns on top if cell is used by city. */
      if (user_at(x, y) == unit->id) 
            plot_resource_cicns(map, winrect, x, y);

      /* Restore old clip. */
      SetClip(tmprgn);
      DisposeRgn(tmprgn);
}

void
destroy_unit_closeup(UnitCloseup *unitcloseup)
{
      UnitCloseup *unitcloseup2;
      Unit  *unit;
      Map   *map;

      unit = unitcloseup->unit;
      update_cell_display(dside, unit->x, unit->y, UPDATE_ALWAYS);
      if (unitcloseuplist == unitcloseup) {
            unitcloseuplist = unitcloseup->next;
      } else {
            for_all_unit_closeups(unitcloseup2) {
                  if (unitcloseup2->next == unitcloseup) {
                        unitcloseup2->next = unitcloseup->next;
                  }
            }
      }
      /* (should destroy substructs) */
      free(unitcloseup);
}

DialogPtr buildwin = nil;

/* This is the top-level access to bring up the scores window, can be called
   anywhere, anytime. */

void
show_unit_build_dialog(Unit *unit)
{
      int         mainwidth, mainheight;
      Rect        winRect;          
      Point       winUL;
      Point       winDR;
      GrafPtr     oldport;
      Point       upoint;
      int         sx, sy;

      /* Create the dialog if necessary. */
      if (buildwin == nil) {
            buildwin = GetNewDialog(dBuild, NULL, NULL);
            /* Don't show it until we have moved it. */
            HideTheWindow(buildwin);
      }
      if (frontmap) {
            /* Position the dialog next to the unit. */
            GetPort(&oldport);
            SetPort(frontmap->window);
            xform(frontmap, unit->x + 1, unit->y - 1, &sx, &sy);
            upoint.h = sx; 
            upoint.v = sy + (frontmap->vp->power < 5 ? frontmap->vp->hh : frontmap->vp->hh/2);
            LocalToGlobal(&upoint);
            MoveWindow(buildwin, upoint.h, upoint.v, true);

            /* Find the dialog's global coordinates. */
            SetPort(buildwin);
            winRect = buildwin->portRect;             
            winUL.h = winRect.left;
            winUL.v = winRect.top;
            winDR.h = winRect.right;
            winDR.v = winRect.bottom;
            LocalToGlobal(&winUL);              
            LocalToGlobal(&winDR);        

            /* Make sure it stays within the main screen. */
            get_main_screen_size(&mainwidth, &mainheight);
            if (winDR.h + 3 > mainwidth
                 || winUL.h < 3
                 || winDR.v + 3 > mainheight
                 || winUL.v < GetMBarHeight() + 3) {
                  MoveWindow(buildwin, 
                        max(3, min(winUL.h, winUL.h + mainwidth - winDR.h - 3)),
                        max(GetMBarHeight() + 3, min(winUL.v, winUL.v + mainheight - winDR.v - 3)),
                        false);
            }
            SetPort(oldport);
      }
      /* Mark the unit as buildwin_unit. */
      buildwin_unit = unit;
      MakeDialogFloat(buildwin);
      SelectTheWindow(buildwin);
      update_window(buildwin);
}

void
draw_unit_build_dialog(int force)
{
      char              cname[32], uname[32], sname[32], buf[BUFSIZE], buf3[BUFSIZE];
      short             ditem, mitem, done = FALSE, i, m, d, u, u2;
      Unit              *unit = buildwin_unit;
      Task              *lasttask = &(unit->plan->last_task);
      Task              *task = unit->plan->tasks;
      Str255            pname, pnumber;
      WindowPtr         win = buildwin;
      Handle            itemhandle; 
      MenuHandle  buildMenu;
      GrafPtr           oldport;
      int               run;

      /* Skip if we lack a display. */
      if (!active_display(dside))
            return;
      /* Blast the dialog if the unit is dead or has defected. */
      if (!in_play(unit)
            || !side_controls_unit(dside, unit)) { 
            close_window(win);
            return;
      }
      GetPort(&oldport);
      SetPort(win);

      buildMenu = build_construction_menu(unit);

      /* Get the type name of unit that is being constructed, if any. */
      if (task 
            && task->type == TASK_BUILD
            && task->args[3] > 0
            && unit->plan->tasks->args[2] < task->args[3]
            && is_unit_type(task->args[0])) {
            strcpy(uname, u_type_name(task->args[0]));
      } else if (unit->plan->asleep)
            strcpy(uname, "Asleep");
      else if (unit->plan->reserve)
            strcpy(uname, "Skip Turn");
      else  strcpy(uname, u_type_name(favored_type(unit)));

      /* Set build popup to uname. */
      m = CountMItems(buildMenu);
      GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
      SetCtlMax((ControlHandle) itemhandle, m);
      for (i = 1; i <= m; i++) {
            GetItem(buildMenu, i, pname);
            p2c(pname, cname);
            if (strcmp(uname, cname) != 0)
                  continue;
            SetCtlValue((ControlHandle) itemhandle, i);
      }

      /* Check if a build task just was completed. */
      if (lasttask
           && lasttask->type == TASK_BUILD
           && unit->plan->last_task_outcome == TASK_IS_COMPLETE
           && lasttask->args[3] > 0) {
            if (lasttask->args[3] > 1) {
                  NumToString(lasttask->args[3], pnumber);
                  p2c(pnumber, cname);
                  strcpy(uname, cname);
                  strcat(uname, " ");
                  strcat(uname, plural_form(u_type_name(lasttask->args[0])));
            } else strcpy(uname, u_type_name(lasttask->args[0]));
            strcpy(buf, unit->name ? unit->name : u_type_name(unit->type));
            strcat(buf, " completes ");
            strcat(buf, uname);
            strcat(buf, ". Pick a new task.");
      /* We are building something. */
      } else if (task
           && task->type == TASK_BUILD
           && task->args[3] > 0
           && unit->plan->tasks->args[2] < task->args[3]) {
            if (lasttask->args[3] > 1) {
                  NumToString(task->args[2] + 1, pnumber);
                  p2c(pnumber, uname);
                  strcat(uname, ordinal_suffix(task->args[2] + 1));
                  strcat(uname, " of ");
                  NumToString(task->args[3], pnumber);
                  p2c(pnumber, cname);
                  strcat(uname, cname);
                  strcat(uname, " ");
                  strcat(uname, plural_form(u_type_name(task->args[0])));
            } else strcpy(uname, u_type_name(lasttask->args[0]));
            strcpy(buf, unit->name ? unit->name : u_type_name(unit->type));
            strcat(buf, " is building ");
            strcat(buf, uname);
            strcat(buf, ".\r");
            /* Print cps of current unit if it exists, else print "0". */
            if (find_unit(task->args[1]) != NULL) {
                  NumToString((find_unit(task->args[1]))->cp, pnumber);
                  p2c(pnumber, cname);
            } else       strcpy(cname, "0");
            strcat(cname, " of ");
            NumToString(u_cp(task->args[0]), pnumber);
            p2c(pnumber, sname);
            strcat(cname, sname);
            strcat(cname, " points completed.");
            strcat(buf, cname);           
      /* We are not building anything at all. */
      } else {
            strcpy(buf, "The workers in ");
            strcat(buf, unit->name ? unit->name : u_type_name(unit->type));
            strcat(buf, " are idle. Pick a new task.");
      }
      /* Check if the text field needs an update. */
      GetDItem(win, diBuildText, NULL, &itemhandle, NULL);
      GetIText(itemhandle, pname);
      p2c(pname, buf3);
      if (strcmp(buf, buf3) != 0) {
            c2p(buf, pname);
            SetIText(itemhandle, pname);
            force = TRUE;
      }
      /* Set the autobuild radio button. */
      GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
      if (acp_indep(unit)) {
            HiliteControl((ControlHandle) itemhandle, 0); 
            SetCtlValue((ControlHandle) itemhandle, unit->autobuild);
      } else {
            HiliteControl((ControlHandle) itemhandle, 255); 
            SetCtlValue((ControlHandle) itemhandle, FALSE);
      }     
      /* Set the manual build radio button. */
      GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
      if (acp_indep(unit)) {
            SetCtlValue((ControlHandle) itemhandle, !unit->autobuild);
      } else {
            SetCtlValue((ControlHandle) itemhandle, TRUE);
      }     

      /* Draw the run in its textfield. */
      GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
      if (task
          && task->type == TASK_BUILD
          && task->args[3] > 0)
            NumToString(task->args[3], pname);
      else  NumToString(construction_run_doctrine(unit, 0), pname);
      SetIText(itemhandle, pname);
      SelIText(win, diBuildEdit, 0, 32767);

      /* Unhilite all controls when under active AI control. */
      if (ai_controlled(unit)) {
            GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
            HiliteControl((ControlHandle) itemhandle, 255); 
            /* Hide the runlength Edit field. */
            HideDItem(win, diBuildEdit);
            HideDItem(win, diBuildTimes);
      }
      
      if (force) {
            DrawDialog(win);
            draw_default_button(win, OkButton);
            MoveTo(10, 132);
            TextFont(small_font_id);
            TextSize(small_font_size);
            DrawString("\pHit Return to confirm and Escape to close.");
            TextFont(0);
            TextSize(0);
      }
      SetPort(oldport);
}

/* This function examines keystrokes destined for closeup dialogs. 
It returns TRUE for keys that got handled, and FALSE for all other keys. */ 

int 
do_key_down_build(char key, char code, int mods)
{
      Point mouse = {0, 0};         /* Dummy variable. */

      /* We hit the Enter or Return key. */
      if (key == 0x4C || key == 0x24) {
            hit_unit_build_dialog(OkButton, mouse, mods);
            return TRUE;
      /* We hit the Escape key. */
      } else if (key == 0x35) {
            close_window(buildwin);
            return TRUE;
      /* 0-9 should be forwarded to the dialog's Edit text field instead of 
      being interpreted as command prefixes. Also forward backspace. */
      } else if (between(0x12, key, 0x17)       /* Keyboard 1-6 */ 
                  || key == 0x19                      /* Keyboard 9 */ 
                  || key == 0x1A                      /* Keyboard 7 */ 
                  || key == 0x1C                      /* Keyboard 8 */ 
                  || key == 0x1D                      /* Keyboard 0 */ 
                  || key == 0x33) {             /* Backspace   */ 

      /* The Dialog Manager no longer handles Edit Text fields for us. */
            TEKey(code, ((DialogPeek) buildwin)->textH);
            return TRUE;
      }
      return FALSE;
}

int
do_mouse_down_build(Point mouse, int mods)
{
      int   count = 0, occs = 0, occx = 0, occy = 0;
      DialogPtr win = buildwin;
      Unit  *unit2;
      Rect  tmprect;
      Map   *map;
      Plan  *plan;
      Task  *task;
      short ditem;

      /* Check if a dialog item was hit and branch to dialog handling code in that case.
      This makes it possible to avoid using the Dialog Manager. */
      ditem = FindDItem(win, mouse) + 1;  /* FindDItem returns (ditem -1)! */
      if (ditem > 0) {
            /* First undo previous conversion to locals by do_mouse_down. */
            LocalToGlobal(&mouse);
            hit_unit_build_dialog(ditem, mouse, mods);
            return;
      }
}

void
hit_unit_build_dialog(int ditem, Point mouse, int mods)
{
      int               run =0, width, height, downsx, downsy, downx, downy, sx, sy, m;
      short             autobuild, plantype, build;
      short             mitem, done = FALSE, disabled = TRUE, i, u, u2, a;
      char              buf[32], cname[32], uname[32], sname[32];
      MenuHandle  buildMenu;
      Str255            pname, pnumber;
      UnitCloseup *unitcloseup;
      Unit              *unit, *unit2;
      HelpNode          *helpnode;
      Handle            itemhandle;  
      Rect              itemrect;
      GrafPtr           oldport;
      Map               *map;
      Side              *side;
      WindowPtr         win;
      
      win = buildwin;
      map = frontmap;
      unit = frontmap->curunit;
      side = unit->side;
      buildMenu = build_construction_menu(unit);

      GetPort(&oldport);      
      SetPort(win);
      DrawDialog(win);

      switch (ditem) {

            case diBuildAuto:       /* Toggle autobuild and manual build buttons. */
              if (!acp_indep(unit) || ai_controlled(unit))
                      break;
                  GetDItem(win, ditem, NULL, &itemhandle, NULL);
                  SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                  GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
                  SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                  break;

            case diBuildRepeat:     /* Toggle autobuild and manual build buttons. */
              if (!acp_indep(unit) || ai_controlled(unit))
                      break;
                  GetDItem(win, ditem, NULL, &itemhandle, NULL);
                  SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                  GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
                  SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                  break;

            case  diBuildPopup:     /* Find selected unit type. */
                  if (ai_controlled(unit))
                      break;
                  GetDItem(win, ditem, NULL, &itemhandle, NULL);
                  /* This is no longer automatically done by the dialog manager. */
                  TrackControl((ControlHandle) itemhandle, mouse, (void *) -1);
                  /* Make sure the new menu selection is drawn. */
                  DrawControls(win);
                  mitem = GetCtlValue((ControlHandle) itemhandle);
                  GetItem(buildMenu, mitem, pname);
                  p2c(pname, uname);
                  /* If a real choice was made, we exit the dialog through this 
                  recursive call. */
                  if (strcmp(uname, "Idle") != NULL) {
                        hit_unit_build_dialog(OkButton, mouse, mods);
                        return;
                  }
                  break;

            /* The OK and Cancel buttons are now hidden from view, but can be activated
            by passing the corresponding item from do_key_down_build in response to
            the Return and Cancel keys. The OkButton case is also called recursively above
            in order to execute the menu choice. */

            case  OkButton:
                  if (ai_controlled(unit)) {
                        done = TRUE;
                        break;
                  }
                  /* Get the autobuild state. */
                  GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
                  autobuild = GetCtlValue((ControlHandle) itemhandle);

                  /* Always set runlength to default if on autobuild. */
                  if (unit->autobuild || ai_controlled(unit))
                        run = construction_run_doctrine(unit, 0);
                  else {                              
                        /* Else get the runlength from its text field. */
                        GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
                              GetIText(itemhandle, pname);
                        StringToNum(pname, (long *) &run);
                        run = min(max(run, 0), CLEAR_AGENDA);
                  }

                  /* Get the selected build task. */
                  GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
                  mitem = GetCtlValue((ControlHandle) itemhandle);
                  GetItem(buildMenu, mitem, pname);
                  p2c(pname, uname);
                  build = NONUTYPE;
                  for_all_unit_types(u) {
                        /* Find the utype that was selected in the menu. */
                        if (strcmp(u_type_name(u), uname) == NULL) {
                              build = u;
                              break;                        
                        }
                  }

                  /* We need to do this last of all since the net_set functions call
                  update_unit_display which redraws the closeup itself. The first
                  call to a net_set function therefore resets all popup menus to the
                  initial values, thus destroying the settings we want to save. */

                  net_set_unit_autobuild(side, unit, autobuild);
                  /* No matching utype means "Idle", "Asleep" or "Wait til next turn"
                   was selected. */
                  if (build == NONUTYPE) {
                        net_clear_task_agenda(side, unit);
                         if (strcmp(uname, "Asleep") == NULL) {
                              net_set_unit_asleep(side, unit, TRUE, FALSE);
                        } else if (strcmp(uname, "Skip Turn") == NULL) {
                              net_set_unit_reserve(side, unit, TRUE, FALSE);
                        }
                  } else      net_set_build_task(unit, build, run, 0, 0);
                  done = TRUE;
                  break;

            case  CancelButton:
                  done = TRUE;
                  break;

            default:
                  break;
      }
      if (done)
          close_window(win);

      /* Restore old port. */
      SetPort(oldport);
}





/*
      Research dialog for a single advanced unit. 
*/

void
unit_research_dialog(Unit *unit)
{
      short             ditem, mitem, done = FALSE, i, m, d, s;
      char              cname[32], sname[32], buf[BUFSIZE];
      Str255            pname, pnumber;
      Handle            itemhandle; 
      MenuHandle  popMenu;
      GrafPtr           oldport;
      DialogPtr         win;
      
      /* Open the dialog. */
      win = GetNewDialog(dAdvance, NULL, (DialogPtr) -1);
      GetPort(&oldport);      
      SetPort(win);

      /* Hide all scientists. */
      HideDItem(win, diAdvanceMadScientist);
      HideDItem(win, diAdvanceBusyScientist);
      HideDItem(win, diAdvanceIdleScientist);
      popMenu = GetMenu(mResearchPopup);

      s = unit->curadvance;
      /* Research is going on. */
      if (s >= 0) { 
            strcpy(sname, a_type_name(s));
            /* Check if current advance has been completed. */
            if (has_advance(unit->side, s)) {
                  /* Do bells and whistles. */
                  strcpy(buf, "Scientists in ");
                  strcat(buf, unit->name);
                  strcat(buf, " discover ");
                  strcat(buf, sname);
                  strcat(buf, "!\r\r");
                  strcat(buf, "Pick new research:");
                  /* Show mad scientist. */
                  ShowDItem(win, diAdvanceMadScientist);
                  /* Only do it once. */
                        unit->curadvance = NOADVANCE;
            } else {
                  /* Report ongoing research. */
                  strcpy(buf, "The scientists in ");
                  strcat( buf, unit->name);
                  strcat( buf, " are researching ");
                  strcat(buf, sname);
                  strcat(buf, ".");             
                  /* Show busy scientist. */
                  ShowDItem(win, diAdvanceBusyScientist);
            }
      /* No current research. */
      } else {
            strcpy(buf, "The scientists in ");
            strcat( buf, unit->name);
            strcat( buf, " are idle.\r");
            strcat( buf, "You should put them to work!\r\r");
            strcat( buf, "Pick new research:");
            /* Show idle scientist. */
            ShowDItem(win, diAdvanceIdleScientist);
      }
      c2p(buf, pname);
      GetDItem(win, diAdvanceMainText, NULL, &itemhandle, NULL);
      SetIText(itemhandle, pname);
      /* Set the advance auto checkbox. */
      GetDItem(win, diAdvanceCheck, NULL, &itemhandle, NULL);
      SetCtlValue((ControlHandle) itemhandle, unit->autoresearch);
      /* Load researchable advance types into popup menu. */
      for_all_advance_types(s) {
            if (side_can_research(unit->side, s)) {
                  c2p(a_type_name(s), pname);
                  AppendMenu(popMenu, pname);
            }
      }

      ShowWindow(win);
      while (!done) {
            SetCursor(&QD(arrow));
            draw_default_button(win, OkButton);

            /* Set popup to sname. */
            m = CountMItems(popMenu);
            GetDItem(win, diAdvancePopup, NULL, &itemhandle, NULL);
            SetCtlMax((ControlHandle) itemhandle, m); /* Important! */
            for (i = 1; i <= m; i++) {
                  GetItem(popMenu, i, pname);
                  p2c(pname, cname);
                  if (strcmp(sname, cname) != 0)
                        continue;
                  SetCtlValue((ControlHandle) itemhandle, i);
            }

            ModalDialog(NULL, &ditem);
            switch (ditem) {
                  case diAdvanceCheck: /* Toggle advance auto checkbox. */
                        GetDItem(win, ditem, NULL, &itemhandle, NULL);
                        SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                        break;

                  case  diAdvancePopup: /* Current research popup menu. */
                        GetDItem(win, ditem, NULL, &itemhandle, NULL);
                        mitem = GetCtlValue((ControlHandle) itemhandle);
                        GetItem(popMenu, mitem, pname);
                        p2c(pname, sname);
                        break;

                  case  OkButton:
                        /* Get the advance auto checkbox state. */
                        GetDItem(win, diAdvanceCheck, NULL, &itemhandle, NULL);
                        unit->autoresearch = GetCtlValue((ControlHandle) itemhandle);
                        /* Stop any research if "Idle" was selected. */
                        if (strcmp("Idle", sname) == 0) {
                              unit->curadvance = NOADVANCE;
                        /* Else select new advance to research. */
                        } else for_all_advance_types(s) {
                              /* Skip until we find atype that is selected in popMenu. */
                              if (strcmp(a_type_name(s), sname) != 0)
                                    continue;
                              unit->curadvance = s;
                              }
                        done = TRUE;
                        break;

                  case  CancelButton:
                        done = TRUE;
                        break;

                  default:
                        break;
            }
      }

      /* Close the dialog. */
      DisposeDialog(win);
      /* Restore old port. */
      SetPort(oldport);
}

#if 0

/*
      Planning dialog for a single advanced unit. 
*/

void
unit_plan_dialog(Unit *unit)
{
      short             ditem, mitem, done = FALSE, i, m, d, s;
      char              cname[32], sname[32], buf[BUFSIZE];
      Str255            pname, pnumber;
      Handle            itemhandle; 
      MenuHandle  planMenu;
      GrafPtr           oldport;
      DialogPtr         win;
      
      /* Open the dialog. */
      win = GetNewDialog(dPlan, NULL, (DialogPtr) -1);
      GetPort(&oldport);      
      SetPort(win);

      strcpy( buf, unit->name);
      strcat( buf, " needs a plan.\r\rPick one:\r");
      c2p(buf, pname);
      GetDItem(win, diPlanMainText, NULL, &itemhandle, NULL);
      SetIText(itemhandle, pname);

      /* Set plan type popup menu to current plan. */
      GetDItem(win, diPlanPopup, NULL, &itemhandle, NULL);
      if (unit->plan->type == PLAN_NONE)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeNone);
      else if (unit->plan->type == PLAN_PASSIVE)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypePassive);
      else if (unit->plan->type == PLAN_DEFENSIVE)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeDefensive);
      else if (unit->plan->type == PLAN_EXPLORATORY)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeExploratory);
      else if (unit->plan->type == PLAN_OFFENSIVE)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeOffensive);
      else if (unit->plan->type == PLAN_COLONIZING)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeColonizing);
      else if (unit->plan->type == PLAN_IMPROVING)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeImproving);
      else if (unit->plan->type == PLAN_RANDOM)
            SetCtlValue((ControlHandle) itemhandle, miPlanTypeRandom);
      /* Also update the checkmarks, since this menu is used elsewhere. */
      planMenu = GetMenu(mPlanTypes);
      CheckItem(planMenu, miPlanTypeNone, (unit->plan->type == PLAN_NONE));
      CheckItem(planMenu, miPlanTypePassive, (unit->plan->type == PLAN_PASSIVE));
      CheckItem(planMenu, miPlanTypeDefensive, (unit->plan->type == PLAN_DEFENSIVE));
      CheckItem(planMenu, miPlanTypeExploratory, (unit->plan->type == PLAN_EXPLORATORY));
      CheckItem(planMenu, miPlanTypeOffensive, (unit->plan->type == PLAN_OFFENSIVE));
      CheckItem(planMenu, miPlanTypeColonizing, (unit->plan->type == PLAN_COLONIZING));
      CheckItem(planMenu, miPlanTypeImproving, (unit->plan->type == PLAN_IMPROVING));
      CheckItem(planMenu, miPlanTypeRandom, (unit->plan->type == PLAN_RANDOM));

      ShowWindow(win);
      while (!done) {
            SetCursor(&QD(arrow));
            draw_default_button(win, OkButton);

            ModalDialog(NULL, &ditem);
            switch (ditem) {
                  case diPlanCheck: /* Toggle plan auto checkbox. */
                        GetDItem(win, ditem, NULL, &itemhandle, NULL);
                        SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
                        break;

                  case  OkButton:
                        /* Get the advance auto checkbox state. */
                        GetDItem(win, diPlanCheck, NULL, &itemhandle, NULL);
                        unit->autoplan = GetCtlValue((ControlHandle) itemhandle);
                        /* Get selected plan type. */
                        GetDItem(win, diPlanPopup, NULL, &itemhandle, NULL);
                        mitem = GetCtlValue((ControlHandle) itemhandle);
                        switch (mitem)  {
                              case miPlanTypeNone:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_NONE);
                                    break;
                              case miPlanTypePassive:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_PASSIVE);
                                    break;
                              case miPlanTypeDefensive:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_DEFENSIVE);
                                    break;
                              case miPlanTypeExploratory:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_EXPLORATORY);
                                    break;
                              case miPlanTypeOffensive:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_OFFENSIVE);
                                    break;
                              case miPlanTypeColonizing:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_COLONIZING);
                                    break;
                              case miPlanTypeImproving:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_IMPROVING);
                                    break;
                              case miPlanTypeRandom:
                                    net_set_unit_plan_type(unit->side, unit, PLAN_RANDOM);
                                    break;
                        }
                        done = TRUE;
                        break;

                  case  CancelButton:
                        done = TRUE;
                        break;

                  default:
                        break;
            }
      }

      /* Close the dialog. */
      DisposeDialog(win);
      /* Restore old port. */
      SetPort(oldport);
}

#endif

MenuHandle
build_construction_menu(Unit *unit)
{
      MenuHandle menu;
      Str255 pname;
      int i, u, items;

      menu = GetMenu(mBuildPopup);
      /* First clean out all old items. */
      items = CountMenuItems(menu);
      for (i = 0; i < items; i++)
            DelMenuItem(menu, 1);
      items = 0;
      /* Load buildable unit type names into popup menu. */
      for_all_unit_types(u) {
            if (side_can_build(unit->side, u)
                  && could_create(unit->type, u) 
                      /* There may be room inside the creator. */
                  && (type_can_occupy(u, unit)
                     /* We are already building it anyway. */
                     || u == unit->plan->tasks->args[0]
                     /* There may be room in an adjacent cell. */
                     || uu_create_range(unit->type, u) > 0
                     /* There may be room in the same cell. */
                     || type_can_occupy_cell(u, unit->x, unit->y)
                     /* Special case for settlers that build a city and disappear. */
                     || (type_can_occupy_cell_without(u, unit->x, unit->y, unit)
                        && uu_hp_to_garrison(unit->type, u) >= u_hp(unit->type)))) {
                  c2p(u_type_name(u), pname);
                  AppendMenu(menu, pname);
                  ++items;
            }
      }
      /* Add a separator if we have buildable units. */
      if (items)
          InsMenuItem(menu, "\p(-", 0);
      InsMenuItem(menu, "\pIdle", 0);
      InsMenuItem(menu, "\pSkip Turn", 0);
      InsMenuItem(menu, "\pAsleep", 0);
      return menu;
 }

MenuHandle
build_research_menu(Side *side)
{
      MenuHandle menu;
      Str255 pname;
      int i, a, items;

      menu = GetMenu(mResearchPopup);
      /* First clean out all old items. */
      items = CountMenuItems(menu);
      for (i = 0; i < items; i++)
            DelMenuItem(menu, 1);
      items = 0;
      /* Load scientific advances that can be researched by side. */
      for_all_advance_types(a) {
            if (side_can_research(side, a)) {
                  c2p(a_type_name(a), pname);
                  AppendMenu(menu, pname);
                  ++items;
            }
      }
      /* Add a separator if we have any advances. */
      if (items)
          InsMenuItem(menu, "\p(-", 0);
      InsMenuItem(menu, "\pIdle", 0);
      InsMenuItem(menu, "\pStop Research", 0);
      return menu;
 }


Generated by  Doxygen 1.6.0   Back to index