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

side.c

/* Sides in Xconq.
   Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.

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

/* This file implements sides and functionality relating to sides in
   general. */

#include "conq.h"
#include "kernel.h"

#define checked_elev_at(x, y) (elevations_defined() ? elev_at(x, y) : 0)

static void init_visible_elevation(int x, int y);
static void init_visible_elevation_2(int x, int y);
static void calc_visible_elevation(int x, int y);
static void calc_visible_elevation_2(int x, int y);
static void cover_area_1(Side *side, struct a_unit *unit, int x0, int y0,
                   int x1, int y1);
static int see_materials(Side *side, int x, int y);
static int see_weather(Side *side, int x, int y);
static int mistaken_type(int u2);
static int remove_unit_view(Side *side, UnitView *olduview);

#define VIEW_HASH_SIZE 257

/* Head of the list of all sides. */

Side *sidelist;

/* Pointer to the side representing independent units. */

Side *indepside;

/* Pointer to the last side of the list. */

Side *lastside;

/* Temporary used in many places. */
     
Side *tmpside;

/* The actual number of sides in a game.  This number never decreases;
   sides no longer playing need to be around for recordskeeping purposes. */

int numsides;

/* This is the number of sides including indepside. We need it for the
   AI assignment in init.c. We want to keep numsides referring to
   normal sides only, since it is used a lot in the interface code,
   where we typically don't want to include indepside. */

int numtotsides;

/* Used to generate the id number of the side. */

int nextsideid;

/* Used for solid color emblems. These colors have been carefully
tested so that they stand out against most backgrounds and also can be
easily distinguished from each other. The indepside default emblem is
set to "none" since it usually goes without emblem. */

static char *default_emblems[16] = {      /* Current MAXSIDES + 1. */
      "none",
      "blue",
      "red",
      "yellow",
      "chartreuse",
      "dark-orange",
      "magenta",
      "cyan",
      "pink",
      "purple",
      "medium-sea-green",
      "rosy-brown",
      "cornflower-blue",
      "firebrick",
      "white",
      "goldenrod"
};

/* Cached values of global vision vars. */

short any_los = -1;

int any_material_views = -1;

char *any_material_views_by_m;

/* Pointer to buffer used for readable side description (for debugging). */

char *sidedesigbuf = NULL;

/* Pointer to the head of the list of players. */

Player *playerlist;

/* Pointer to the last player of the list, used to add players at end. */

Player *last_player;

/* The total number of players. */

int numplayers;

/* Use to generate the id number of each player. */

int nextplayerid;

char *playerdesigbuf = NULL;

/* The list of doctrine objects. */

Doctrine *doctrine_list;

Doctrine *last_doctrine;

int next_doctrine_id;

/* Init side machinery. Don't fill in indepside yet since numutypes
   etc. may not be defined.  */

void
init_sides(void)
{
    /* Set up the list of sides. */
    sidelist = lastside = (Side *) xmalloc(sizeof(Side));
    /* The independent units' side will be the first one in the list,
       is always created.  It cannot be filled in yet, because the
       numbers of the various types is not yet known. */
    indepside = sidelist;
    /* Note: indepside is included in numtotsides, but not numsides. */
    numtotsides = 0;
    numsides = 0;
    nextsideid = 0;
    /* Set up the list of players. */
    playerlist = last_player = NULL;
    numplayers = 0;
    /* Indepside also needs a player, so we now start with id 0. */
    nextplayerid = 0;
    /* Set up the player/side assignment array. */
    assignments = (Assign *) xmalloc(MAXSIDES * sizeof(Assign));
    /* Set up the list of doctrines. */
    doctrine_list = last_doctrine = NULL;
    /* Doctrine ids must start at 1. */
    next_doctrine_id = 1;
}

/* Create an object representing a side. */

Side *
create_side(void)
{
    int u, m;
    Side *newside;

    if (numsides >=  g_sides_max()) {
      run_error("Cannot have more than %d sides total!", g_sides_max());
    }
    /* Just fill in the names if dealing with indepside. */
    if (nextsideid == 0) {
      newside = indepside;
      newside->name = "Independents";
      newside->noun = "Independent";
      newside->pluralnoun = "Independents";
      newside->adjective = "independent";
      newside->colorscheme = "black,black,white";
      newside->emblemname = "none";
    } else {
      /* Else allocate a new side object and increase numsides. */
      newside = (Side *) xmalloc(sizeof(Side));
      /* numsides does not include indepside, thus increment it here. */
      ++numsides;
    }
    /* Fill in various side slots.  Only those with non-zero/non-NULL
       defaults need have anything done to them. */
    newside->id = nextsideid++;
    /* Always start sides IN the game. */
    newside->ingame = TRUE;
    /* Note that "everingame" is only set at the beginning of a turn. */
    /* Set up the relationships with other sides. */
    newside->knows_about = ALLSIDES; /* for now */
    newside->trusts = (short *) xmalloc((g_sides_max() + 1) * sizeof(short));
    newside->trades = (short *) xmalloc((g_sides_max() + 1) * sizeof(short));
    /* Set up per-unit-type slots. */
    newside->counts = (short *) xmalloc(numutypes * sizeof(short));
    newside->tech = (short *) xmalloc(numutypes * sizeof(short));
    newside->inittech = (short *) xmalloc(numutypes * sizeof(short));
    newside->numunits = (short *) xmalloc(numutypes * sizeof(short));
    newside->emblemname = default_emblems[newside->id];
    for_all_unit_types(u) {
      /* Start unit numbering at 1, not 0. */
      newside->counts[u] = 1;
    }
    newside->priority = -1;
    /* All sides should auto-finish by default. */
    newside->autofinish = TRUE;
    /* True by default, players should disable manually. */
    newside->willingtosave = TRUE;
    /* Put valid Lisp data into slots that need it. */
    newside->symbol = lispnil;
    newside->instructions = lispnil;
    newside->rawscores = lispnil;
    newside->aidata = lispnil;
    newside->uidata = lispnil;
    newside->possible_units = lispnil;
    newside->startx = newside->starty = -1;
    newside->init_center_x = newside->init_center_y = -1;
    newside->gaincounts = (short *) xmalloc(numutypes * num_gain_reasons * sizeof(short));
    newside->losscounts = (short *) xmalloc(numutypes * num_loss_reasons * sizeof(short));
    newside->atkstats = (long **) xmalloc(numutypes * sizeof(long *));
    newside->hitstats = (long **) xmalloc(numutypes * sizeof(long *));
    /* Necessary to enable AI control toggling of selected units! */
    newside->prefixarg = -1; 
    if (numatypes > 0) {
       newside->advance = (short *) xmalloc(numatypes * sizeof(short));
       newside->canresearch = (short *) xmalloc(numatypes * sizeof(short));
       update_canresearch_vector(newside);       
       newside->research_topic = NOADVANCE;
       newside->autoresearch = FALSE;
    }
    newside->canbuild = (short *) xmalloc(numutypes * sizeof(short));
    /* We can't update the canbuild vector yet, because its contents
       may depend on side class, which isn't set yet. */
    if (nummtypes > 0) {
      /* Set up the side's supply of materials. */
      newside->treasury = (short *) xmalloc(nummtypes * sizeof(short));
      for_all_material_types(m)
           if (side_has_treasury(newside, m))
        newside->treasury[m] = m_initial_treasury(m);
      /* Make space for default conversions and copy them over. */
      newside->c_rates = (short *) xmalloc(nummtypes * sizeof(short));
      {
          int m1, m2;
          
          /* First initilaize m. */
          m = NONMTYPE;
          /* For now, assume only one m. */
          for_all_material_types(m1) {
            for_all_material_types(m2) {
                if (mm_conversion(m1, m2) > 0) {
                  m = m1;
                  break;
                }
            }
          }
          if (m != NONMTYPE) {
            for_all_material_types(m2) {
                newside->c_rates[m2] = mm_conversion_default(m, m2);
            }
          }
      }
    }
    /* Link in at the end of the list of sides. */
    newside->next = NULL;
    /* Important to avoid the indepside->next = indepside loop! */
    if (newside != indepside) {
      lastside->next = newside;
      lastside = newside;
    }
    init_side_unithead(newside);
    /* numtotsides also includes indepside. */
    ++numtotsides;
    return newside;
}

/* To make the double links work, we have to have one pseudo-unit to
   serve as a head.  This unit should not be seen by any outside
   code. */

void
init_side_unithead(Side *side)
{
    if (side->unithead == NULL) {
      side->unithead = create_bare_unit(0);
      side->unithead->next = side->unithead;
      side->unithead->prev = side->unithead;
    }
}

/* Quick check to see if we have units. */

/* This should be improved to not be fooled by dead units? */

int
side_has_units(Side *side)
{
    return (side->unithead != NULL
          && (side->unithead->next != side->unithead));
}

/* Set up doctrine structures. */

void
init_doctrine(Side *side)
{
    int u;

    /* Every side must have a default doctrine to fall back on. */
    if (side->default_doctrine == NULL) {
      side->default_doctrine = new_doctrine(0);
    }
    /* Make each individual unit doctrine point to the generic
       doctrine by default. */
    if (side->udoctrine == NULL) {
      side->udoctrine =
        (Doctrine **) xmalloc(numutypes * sizeof(Doctrine *));
    }
    for_all_unit_types(u) {
      if (side->udoctrine[u] == NULL)
        side->udoctrine[u] = side->default_doctrine;
    }
}

void
init_self_unit(Side *side)
{
    Unit *unit;

    if ((g_self_required() /* || side prop? */)
      && side->self_unit == NULL) {
      for_all_side_units(side, unit) {
          if (u_can_be_self(unit->type)
            && in_play(unit)
            && completed(unit))
            side->self_unit = unit;
      }
    }
}

/* Initialize basic viewing structures for a side.  This happens when
   the side is created (during/after reading but before synthesis).
   At this time we're mostly allocating space; filling in the views
   happens once all the units are in position.  We don't know the
   final values of parameters like side->see_all, so we have to
   allocate some data that might never be used. */

int
init_view(Side *side)
{
    int terrainset = FALSE, t, u, m;

    /* Be sure that we're not trying to set this up too soon. */
    check_area_shape();
    /* Allocate terrain view layers. */
    if ((!g_terrain_seen() || !g_see_terrain_always())
      && side->terrview == NULL) {
      side->terrview = malloc_area_layer(char);
      if (!g_see_terrain_always()) {
          side->terrviewdate = malloc_area_layer(short);
          /* The terrview also holds the "have we seen it" flag, so
             it must always be allocated, but the aux terrain view
             need not be. */
          if (numcelltypes < numttypes) {
            side->auxterrview =
              (char **) xmalloc(numttypes * sizeof(short *));
            for_all_terrain_types(t) {
                if (!t_is_cell(t)) {
                  side->auxterrview[t] = malloc_area_layer(char);
                }
            }
            if (0 /* aux terrain seen separately from main terrain */) {
                side->auxterrviewdate =
                  (short **) xmalloc(numttypes * sizeof(short *));
                for_all_terrain_types(t) {
                  if (!t_is_cell(t)) {
                      side->auxterrviewdate[t] =
                        malloc_area_layer(short);
                  }
                }
            }
          }
      }
    } else {
      terrainset = TRUE;
    }
    /* Allocate material view layers. */
    if (any_material_views < 0) {
      any_material_views = FALSE;
      for_all_material_types(m) {
          for_all_terrain_types(t) {
            if (tm_storage_x(t, m) > 0 && tm_see_always(t, m) == 0) {
                any_material_views = TRUE;
                if (any_material_views_by_m == NULL)
                  any_material_views_by_m = xmalloc(nummtypes);
                any_material_views_by_m[m] = TRUE;
                break;
            }
          }
      }
    }
    if (any_material_views) {
      if (side->materialview == NULL)
        side->materialview = (short **) xmalloc(nummtypes * sizeof(short *));
      if (side->materialviewdate == NULL)
        side->materialviewdate =
          (short **) xmalloc(nummtypes * sizeof(short *));
      for_all_material_types(m) {
          if (any_material_views_by_m[m]) {
            if (side->materialview[m] == NULL) {
                side->materialview[m] = malloc_area_layer(short);
            }
            if (side->materialviewdate[m] == NULL) {
                side->materialviewdate[m] = malloc_area_layer(short);
            }
          }
      }
    }
    /* Allocate weather view layers. */
    if (any_temp_variation && !g_see_weather_always()) {
      if (side->tempview == NULL) {
          side->tempview = malloc_area_layer(short);
      }
      if (side->tempviewdate == NULL) {
          side->tempviewdate = malloc_area_layer(short);
      }
    }
    if (any_clouds && !g_see_weather_always()) {
      if (side->cloudview == NULL) {
          side->cloudview = malloc_area_layer(short);
      }
      if (side->cloudbottomview == NULL) {
          side->cloudbottomview = malloc_area_layer(short);
      }
      if (side->cloudheightview == NULL) {
          side->cloudheightview = malloc_area_layer(short);
      }
      if (side->cloudviewdate == NULL) {
          side->cloudviewdate = malloc_area_layer(short);
      }
    }
    if (any_wind_variation && !g_see_weather_always()) {
      if (side->windview == NULL) {
          side->windview = malloc_area_layer(short);
      }
      if (side->windviewdate == NULL) {
          side->windviewdate = malloc_area_layer(short);
      }
    }
    /* Allocate the vision coverage cache. */
    if (side->coverage == NULL) {
      side->coverage = malloc_area_layer(short);
    }
    /* Allocate vision altitude coverage if needed */
    if (any_los < 0) {
      any_los = FALSE;
      for_all_unit_types(u) {
          if (u_vision_bend(u) != 100) {
            any_los = TRUE;
            break;
          }
      }
    }
    if (side->alt_coverage == NULL && any_los) {
      side->alt_coverage = malloc_area_layer(short);
    }
    return terrainset;
}

/* Calculate the centroid of all the starting units. */

void
calc_start_xy(Side *side)
{
    int num = 0, sumx = 0, sumy = 0;
    Unit *unit;

    for_all_side_units(side, unit) {
      if (in_play(unit)) {
          sumx += unit->x;  sumy += unit->y;
          ++num;
      }
    }
    if (num > 0) {
      side->startx = wrapx(sumx / num);  side->starty = sumy / num;
    }
}

/* Given a side, get its "number", which is same as its "id". */

int
side_number(Side *side)
{
    return side->id;
}

/* The inverse function - given a number, figure out which side it is.
   Returns NULL for any failure. */
/* (should cache values in a table, do direct lookup) */

Side *
side_n(int n)
{
    Side *side;

    for_all_sides(side)
      if (side->id == n)
        return side;
    return NULL;
}

Side *
find_side_by_name(char *str)
{
    Side *side;

    if (empty_string(str))
      return NULL;
    for_all_sides(side) {
      if (!empty_string(side->name) && strcmp(side->name, str) == 0)
        return side;
      if (!empty_string(side->noun) && strcmp(side->noun, str) == 0)
        return side;
      if (!empty_string(side->pluralnoun) && strcmp(side->pluralnoun, str) == 0)
        return side;
      if (!empty_string(side->adjective) && strcmp(side->adjective, str) == 0)
        return side;
    }
    return NULL;
}

Side *
parse_side_spec(char *str)
{
    int s;
    char *reststr;

    if (isdigit(str[0])) {
      s = strtol(str, &reststr, 10);
      if (between(1, s, numsides)) {
          return side_n(s);
      }
    } else {
      return find_side_by_name(str);
    }
    return NULL;
}

/* This is true when one side controls another. */

int
side_controls_side(Side *side, Side *side2)
{
    return (side == side2 || side2->controlled_by == side);
}

short *max_control_ranges;

static int controller_here(int x, int y);

static int
controller_here(int x, int y)
{
    Unit *unit2;

    if (distance(x, y, tmpunit->x, tmpunit->y) < 2)
      return FALSE;
    for_all_stack(x, y, unit2) {
      if (side_controls_unit(tmpside, unit2)
          && probability(uu_control(unit2->type, tmpunit->type)))
        return TRUE;
    }
    return FALSE;
}

/* This is true if the given side may operate on the given unit. */

int
side_controls_unit(Side *side, Unit *unit)
{
    int dir, x1, y1;
    Unit *unit2;

    if (unit == NULL)
      return FALSE;
    if (is_designer(side))
      return TRUE;
    /* Also give debuggers full control. */
    if (Debug || DebugG || DebugM)
      return TRUE;
    if (side_controls_side(side, unit->side)) {
      /* The *unit* side must have the tech to use the unit, the controlling
         side would have to actually take over the unit if it wants to use
         its tech level to control the unit. */
      if (unit->side != NULL
          && unit->side->tech[unit->type] < u_tech_to_use(unit->type))
        return FALSE;
      if (u_direct_control(unit->type) || unit == side->self_unit)
        return TRUE;
      /* Unit is not under direct control of the side; look for a
         controlled unit that can control this unit. */
      if (max_control_ranges == NULL) {
          int u1, u2;

          max_control_ranges = (short *) xmalloc(numutypes * sizeof(short));
          for_all_unit_types(u2) {
            max_control_ranges[u2] = -1;
            for_all_unit_types(u1) {
                max_control_ranges[u2] =
                  max(max_control_ranges[u2], uu_control_range(u1, u2));
            }
          }
      }
      if (max_control_ranges[unit->type] >= 0) {
          for_all_stack(unit->x, unit->y, unit2) {
            if (unit != unit2
                && side_controls_unit(side, unit2)
                && probability(uu_control_at(unit2->type, unit->type)))
              return TRUE;
          }
          /* (what about occupants that could be controllers?) */
      }
      if (max_control_ranges[unit->type] >= 1) {
          for_all_directions(dir) {
            if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
                for_all_stack(x1, y1, unit2) {
                  if (side_controls_unit(side, unit2)
                      && probability(uu_control_adj(unit2->type, unit->type)))
                    return TRUE;
                }
            }
          }
      }
      if (max_control_ranges[unit->type] >= 2) {
          tmpside = side;
          tmpunit = unit;
          return search_around(unit->x, unit->y,
                         max_control_ranges[unit->type],
                         controller_here, &x1, &y1, 1);
      }
    }
    return FALSE;
}

/* This is true if the given side may examine the given unit. */

int
side_sees_unit(Side *side, Unit *unit)
{
    if (unit == NULL)
      return FALSE;
    if (side->see_all || side->show_all)
      return TRUE;
    if (side_controls_side(side, unit->side))
      return TRUE;
    /* We may also examine units that trust us, */
    if (trusted_side(unit->side, side))
      return TRUE;
    /* or if the game is over. */
    if (endofgame)
      return TRUE;      
    return FALSE;
}

/* This is true if the side can see the image of a unit. */

int
side_sees_image(Side *side, Unit *unit)
{
    UnitView *uview;

    if (side == NULL)
      run_error("NULL side to side_sees_image");
    if (unit == NULL)
      return FALSE;
    if (side->see_all || side->show_all)
      return TRUE;
    if (side_controls_side(side, unit->side))
      return TRUE;
#if 0 /* shouldn't be necessary */
    if (u_see_always(unit->type))
      return TRUE;
#endif
    for_all_view_stack(side, unit->x, unit->y, uview) {
      if (unit->type == view_type(uview) && unit->side == view_side(uview))
        return TRUE;
    }
    return FALSE;
}

int
num_units_in_play(Side *side, int u)
{
    int num = 0;
    Unit *unit;

    if (side != NULL && side->ingame) {
      for_all_side_units(side, unit) {
      if (unit->type == u
          && in_play(unit)
          && completed(unit))
        ++num;
      }
    }
    return num;
}

int
num_units_incomplete(Side *side, int u)
{
    int num = 0;
    Unit *unit;

    if (side != NULL && side->ingame) {
      for_all_side_units(side, unit) {
      if (unit->type == u && alive(unit) && !completed(unit)) ++num;
      }
    }
    return num;
}

Unit *
find_next_unit(Side *side, Unit *prevunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (prevunit == NULL)
        prevunit = side->unithead;
      for (unit = prevunit->next; unit != prevunit; unit = unit->next) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_prev_unit(Side *side, Unit *nextunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (nextunit == NULL) nextunit = side->unithead;
      for (unit = nextunit->prev; unit != nextunit; unit = unit->prev) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_next_actor(Side *side, Unit *prevunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (prevunit == NULL) prevunit = side->unithead;
      for (unit = prevunit->next; unit != prevunit; unit = unit->next) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && (unit->act && unit->act->initacp)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_prev_actor(Side *side, Unit *nextunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (nextunit == NULL)
        nextunit = side->unithead;
      for (unit = nextunit->prev; unit != nextunit; unit = unit->prev) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && (unit->act && unit->act->initacp)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_next_mover(Side *side, Unit *prevunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (prevunit == NULL)
        prevunit = side->unithead;
      for (unit = prevunit->next; unit != prevunit; unit = unit->next) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && has_acp_left(unit)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_prev_mover(Side *side, Unit *nextunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (nextunit == NULL)
        nextunit = side->unithead;
      for (unit = nextunit->prev; unit != nextunit; unit = unit->prev) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && has_acp_left(unit)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_next_awake_mover(Side *side, Unit *prevunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (prevunit == NULL)
        prevunit = side->unithead;
      for (unit = prevunit->next; unit != prevunit; unit = unit->next) {
          if (is_unit(unit)
            && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && has_acp_left(unit)
            && (unit->plan
                && !unit->plan->asleep
                && !unit->plan->reserve
                && !unit->plan->delayed)) {
            return unit;
          }
      }
    }
    return NULL;
}

Unit *
find_prev_awake_mover(Side *side, Unit *nextunit)
{
    Unit *unit = NULL;

    if (side != NULL) {
      if (nextunit == NULL) nextunit = side->unithead;
      for (unit = nextunit->prev; unit != nextunit; unit = unit->prev) {
          if (is_unit(unit) && unit->id > 0
            && alive(unit)
            && inside_area(unit->x, unit->y)
            && has_acp_left(unit)
            && (unit->plan
                && !unit->plan->asleep
                && !unit->plan->reserve
                && !unit->plan->delayed)) {
            return unit;
          }
      }
    }
    return NULL;
}

/* Compute the total number of action points available to the side at the
   beginning of the turn. */

int
side_initacp(Side *side)
{
    int totacp = 0;
    Unit *unit;

    for_all_side_units(side, unit) {
      if (alive(unit) && unit->act) {
          totacp += unit->act->initacp;
      }
    }
    return totacp;
}

/* Return the total of acp still unused by the side. */

int
side_acp(Side *side)
{
    int acpleft = 0;
    Unit *unit;

    for_all_side_units(side, unit) {
      if (alive(unit) && unit->act) {
          acpleft += unit->act->acp;
      }
    }
    return acpleft;
}

int
side_acp_reserved(Side *side)
{
    int acpleft = 0;
    Unit *unit;

    for_all_side_units(side, unit) {
      if (alive(unit) && unit->act) {
            if (unit->plan
                && (unit->plan->reserve || unit->plan->asleep)) {
            acpleft += unit->act->acp;
          }
      }
    }
    return acpleft;
}

#if 0  /* not presently needed, but possibly useful */
/* Return the total of acp still expected to be used by a human player. */

int
side_acp_human(Side *side)
{
    int acpleft = 0;
    Unit *unit;

    if (!side_has_display(side))
      return acpleft;

    for_all_side_units(side, unit) {
      if (unit != NULL
          && alive(unit)
          && inside_area(unit->x, unit->y)
          && has_acp_left(unit)
          && (unit->plan
            && !unit->plan->asleep
            && !unit->plan->reserve
            && unit->plan->tasks == NULL)) {
            acpleft += unit->act->acp;
      }
    }
    return acpleft;
}
#endif

/* (note that technology could be "factored out" of a game if all sides
   reach max tech at some point) */
/* (otherwise should compute once and cache) */
/* (note that once tech factors out, can never factor in again) */

int
using_tech_levels(void)
{
    int u;
    Side *side;

    for_all_sides(side) {
      for_all_unit_types(u) {
          if (side->tech[u] < u_tech_max(u))
            return TRUE;
      }
    }
    return FALSE;
}

/* Take the given side out of the game entirely.  This does not imply
   winning or losing, nor does it take down the side's display or AI. */

void
remove_side_from_game(Side *side)
{
    Side *side2;

    /* Officially flag this side as being no longer in the game. */
    side->ingame = FALSE;
    /* Update everybody on this. */
    for_all_sides(side2) {
      update_side_display(side2, side, TRUE);
    }
    /* Note that we no longer try to remove any images from other sides'
       views, because even with the side gone, the images may be useful
       information about where its units had gotten to.  For instance,
       if a unit had been captured shortly before the side lost, then its
       image might still correspond to an actual unit, with only its side
       changed, and other sides may want to investigate for themselves. */
}

int
num_displayed_sides(void)
{
    int n = 0;
    Side *side;

    for_all_sides(side) {
      if (side_has_display(side))
        ++n;
    }
    return n;
}

void
update_side_display_all_sides(Side *side, int rightnow)
{
    Side *side2;

    for_all_sides(side2) {
      if (side_has_display(side2)) {
          update_side_display(side2, side, rightnow);
      }
    }
}

void
set_side_name(Side *side, Side *side2, char *newname)
{
    side2->name = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_longname(Side *side, Side *side2, char *newname)
{
    side2->longname = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_shortname(Side *side, Side *side2, char *newname)
{
    side2->shortname = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_noun(Side *side, Side *side2, char *newname)
{
    side2->noun = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_pluralnoun(Side *side, Side *side2, char *newname)
{
    side2->pluralnoun = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_adjective(Side *side, Side *side2, char *newname)
{
    side2->adjective = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_emblemname(Side *side, Side *side2, char *newname)
{
    side2->emblemname = newname;
    update_side_display_all_sides(side2, TRUE);
}

void
set_side_colorscheme(Side *side, Side *side2, char *newname)
{
    side2->colorscheme = newname;
    update_side_display_all_sides(side2, TRUE);
}

#ifdef DESIGNERS

/* Implement the transition from regular side to a side that can
   alter the game's state directly (aka "designer"). */

void
become_designer(Side *side)
{
    Side *side2;

    if (side->designer)
      return;
    side->designer = TRUE;
    ++numdesigners;
    /* Designers have godlike power in the game, so mark it (permanently)
       as no longer a normal game. */
    compromised = TRUE;
    /* Designers get to see everything if they want, but it's useful
       to see a player view sometimes, so give designers the ability
       to toggle the accurate display.  Note that we must leave the
       side's see_all at its original value, so that unit views
       continue to be constructed and manipulated properly. */
    if (!side->see_all)
      side->may_set_show_all = TRUE;
    side->show_all = TRUE;
    update_everything();
    /* Let everybody know of the change in status. */
    notify_all("%s IS NOW A DESIGNER.", short_side_title(side));
    for_all_sides(side2) {
      update_side_display(side2, side, TRUE);
    }
}

/* Give up the godlike powers. */

void
become_nondesigner(Side *side)
{
    Side *side2;

    if (!side->designer)
      return;
    side->designer = FALSE;
    --numdesigners;
    /* Go back to the original viewing capabilities. */
    side->may_set_show_all = FALSE;
    side->show_all = side->see_all;
    update_everything();
    notify_all("%s is no longer a designer.", short_side_title(side));
    for_all_sides(side2) {
      update_side_display(side2, side, TRUE);
    }
}

#endif /* DESIGNERS */

/* Return TRUE if side1 trusts side2.  Note that the nature of trust
   is such that we don't want to check that the trust is mutual
   (although in practice it would be a foolish player who would trust
   a side that doesn't reciprocate!) */

int
trusted_side(Side *side1, Side *side2)
{
    if (side1 == side2)
      return TRUE;
    if (side1 == NULL || side2 == NULL || side1->trusts == NULL)
      return FALSE;
    return (side1->trusts[side2->id]);
}

void
set_trust(Side *side, Side *side2, int val)
{
    int oldval;
    Side *side3;

    if (side == side2)
      return;
    if (side->trusts == NULL)
      return;
    oldval = side->trusts[side2->id];
    if (val == oldval)
      return;
    side->trusts[side2->id] = val;
    /* This is a major change that all other sides will know about. */
    for_all_sides(side3) {
      if (active_display(side3)) {
          /* (should be handled by nlang.c) */
          notify(side3, "%s %s %s",
               side_name(side),
               (val ? "now trusts" : "no longer trusts"),
               side_name(side2));
          update_side_display(side3, side, FALSE);
          update_side_display(side3, side2, TRUE);
      }
    }
    /* (should update views, list of units known about, etc) */
    /* (if cell goes from exact to only recorded, update display anyhow) */
}

void
set_controlled_by(Side *side, Side *side2, int val)
{
    char tmpbuf[BUFSIZE];
    int changed = FALSE;

    if (side == NULL || side2 == NULL || side == side2)
      return;
    if (val) {
      /* Make side be controlled. */
      if (side->controlled_by == NULL) {
          side->controlled_by = side2;
          /* (should rework unit movement vectors now?) */
          changed = TRUE;
      } else {
          /* Can't happen. */
      }
    } else {
      /* Make side be uncontrolled. */
      if (side->controlled_by == side2) {
          side->controlled_by = NULL;
          /* (should rework unit movement vectors now?) */
          changed = TRUE;
      } else {
          /* Can't happen. */
      }
    }
    if (changed) {
      strcpy(tmpbuf, short_side_title(side2));
      /* (should be handled by nlang.c) */
      notify_all("%s%s control%s %s now.",
               tmpbuf,
               (val ? "" : " no longer"),
               (short_side_title_plural_p(side2) ? "" : "s"),
               short_side_title(side));
    }
}

/* What interfaces should use to tweak the autofinish flag. */

void
set_autofinish(Side *side, int value)
{
    side->autofinish = value;
}

/* What interfaces should use to tweak the autoresearch flag. */

void
set_autoresearch(Side *side, int value)
{
    side->autoresearch = value;
}

/* Being at war requires only ones of the sides to consider itself so. */

/* (Should the other side's relationships be tweaked also?) */

int
enemy_side(Side *side1, Side *side2)
{
    if (trusted_side(side1, side2))
      return FALSE;
    return TRUE;
}

/* A formal alliance requires the agreement of both sides. */

int
allied_side(Side *side1, Side *side2)
{
    if (trusted_side(side1, side2))
      return TRUE;
    return FALSE;
}

/* Neutralness is basically anything else. */

int
neutral_side(Side *side1, Side *side2)
{
    return (!enemy_side(side1, side2) && !allied_side(side1, side2));
}

void
set_willing_to_save(Side *side, int flag)
{
    int oldflag = side->willingtosave;
    Side *side2;

    if (flag != oldflag) {
      side->willingtosave = flag;
      /* Inform everybody of our willingness to save. */
      for_all_sides(side2) {
          if (active_display(side2)) {
            notify(side2, "%s is%s willing to save the game.",
                        side_name(side), (flag ? "" : " not"));
            update_side_display(side2, side, TRUE);
          }
      }
    }
}

void
set_willing_to_draw(Side *side, int flag)
{
    int oldflag = side->willingtodraw;
    Side *side2;

    if (flag != oldflag) {
      side->willingtodraw = flag;
      /* Inform everybody of our willingness to draw. */
      for_all_sides(side2) {
          if (active_display(side2)) {
            notify(side2, "%s is%s willing to declare the game a draw.",
                        side_name(side), (flag ? "" : " not"));
            update_side_display(side2, side, TRUE);
          }
      }
    }
}

/* Set the self-unit of the given side.  This is only called when done at
   the direction of the side, and may fail if the side can't change its
   self-unit voluntarily. */

void
set_side_self_unit(Side *side, Unit *unit)
{
    if (!in_play(unit))
      return;
    if (side->self_unit
        && in_play(side->self_unit)
        && !u_self_changeable(side->self_unit->type))
      return;
    side->self_unit = unit;
    /* (should update some part of display?) */
}

/* Message-forwarding function. */

void
send_message(side, sidemask, str)
Side *side;
SideMask sidemask;
char *str;
{
    char *sidedesc, buf[BUFSIZE];
    SideMask testmask;
    Side *side2, *sender;

    sender = side;
    testmask = add_side_to_set(side, NOSIDES);
    if (sidemask == NOSIDES || empty_string(str)) {
      notify(side, "You say nothing.");
      return;
    } else if (sidemask == testmask) {
      notify(side, "You mumble to yourself.");
      return;
    } else if (sidemask == ALLSIDES) {
      notify(side, "You broadcast \"%s\" to all.", str);
    } else {
      sidedesc = sidemask_desc(buf, sidemask);
      notify(side, "You send \"%s\" to \"%s\".", str, sidedesc);
    }
    /* Handle messages that are to have anonymous senders. */
    if (strlen(str) > 6  && strncmp(str, "(anon)", 6) == 0) {
      str += 6;
      sender = NULL;
    }
    for_all_sides(side2) {
      if (side2 != side && side_in_set(side2, sidemask)) {
          receive_message(side2, sender, str);
      }
    }
}

/* Handle the receipt of a message.  Some messages may result in specific
   actions, but the default is just to forward to AIs and displays. */

void
receive_message(side, sender, str)
Side *side, *sender;
char *str;
{
    /* Look for specially-recognized messages. */
    if (strcmp("%reveal", str) == 0) {
      reveal_side(sender, side, NULL);
    } else {
      /* Give the message to interface if present. */
      if (side_has_display(side)) {
          update_message_display(side, sender, str, TRUE);
      }
      /* Also give the message to any local AI. */
      if (side_has_ai(side)) {
          ai_receive_message(side, sender, str);
      }
    }
}

/* General method for passing along info about one side to another. */

void
reveal_side(Side *sender, Side *recipient, int *types)
{
    int x, y;
    Unit *unit;

    if (sender == NULL)
      return;
    if (g_see_all())
      return;
    if (!g_terrain_seen()) {
      for_all_cells(x, y) {
          if (terrain_view(sender, x, y) != UNSEEN
              && terrain_view(recipient, x, y) == UNSEEN) {
              set_terrain_view(recipient, x, y, terrain_view(sender, x, y));
              /* (should update unit views also) */
          }
      }
    }
    for_all_side_units(sender, unit) {
      if (in_play(unit) && (types == NULL || types[unit->type])) {
          see_exact(recipient, unit->x, unit->y);
      }
    }
    /* Whether the amount of data is small or large, there is little value
       in being selective about updates - just redo entire maps. */
    update_area_display(recipient);
}

/* Modify doctrine according to a specification. */

void
set_doctrine(Side *side, char *spec)
{
    int u;
    char *arg, *arg2, *rest, substr[BUFSIZE];
    Doctrine *doctrine;

    rest = get_next_arg(spec, substr, &arg);
    if ((doctrine = find_doctrine_by_name(arg)) != NULL) {
      /* Found a specific named doctrine. */
    } else if ((u = utype_from_name(arg)) != NONUTYPE) {
      doctrine = side->udoctrine[u];
    } else if (strcmp(arg, "default") == 0) {
      doctrine = side->default_doctrine;
    }
    if (doctrine->locked) {
      /* (should mention name of doctrine) */
      notify(side, "This doctrine cannot be changed!");
      return;
    }
    rest = get_next_arg(rest, substr, &arg);
    if (strcmp(arg, "resupply") == 0) {
      rest = get_next_arg(rest, substr, &arg2);
      doctrine->resupply_percent = atoi(arg2);
    } else if (strcmp(arg, "rearm") == 0) {
      rest = get_next_arg(rest, substr, &arg2);
      doctrine->rearm_percent = atoi(arg2);
    } else if (strcmp(arg, "repair") == 0) {
      rest = get_next_arg(rest, substr, &arg2);
      doctrine->repair_percent = atoi(arg2);
    } else if (strcmp(arg, "run") == 0) {
      notify(side, "Can't modify construction runs yet");
    } else {
      notify(side, "\"%s\" not a known doctrine property", arg);
      notify(side, "Known ones are: ask, resupply, rearm, repair, run");
    }
}

void
set_side_research(Side *side, int a)
{
    side->research_topic = a;
}

/* Vision. */

static UnitView tmp_unit_view;

UnitView *
unit_view_at(Side *side, int x, int y)
{
    int hash;
    Unit *unit;
    UnitView *uv;

    if (side->see_all) {
      unit = unit_at(x, y);
      if (unit == NULL)
        return NULL;
      tmp_unit_view.type = unit->type;
      tmp_unit_view.side_id = unit->side->id;
      tmp_unit_view.size = unit->size;
      tmp_unit_view.x = unit->x;  tmp_unit_view.y = unit->y;
      tmp_unit_view.date = g_turn();
      tmp_unit_view.id = unit->id;
      tmp_unit_view.unit = unit;
      tmp_unit_view.nexthere = NULL;
      return &tmp_unit_view;
    }
    /* This might be called during synthesis, before sides are set up. */
    if (side->unit_views == NULL)
      return NULL;
    hash = (x ^ y) % VIEW_HASH_SIZE;
    for (uv = side->unit_views[hash]; uv != NULL; uv = uv->nexthere) {
      if (x == uv->x && y == uv->y)
        return uv;
    }
    return NULL;
}

UnitView *
unit_view_next(Side *side, int x, int y, UnitView *uview)
{
    Unit *unit;
    UnitView *uv2;

    if (side->see_all) {
      unit = uview->unit->nexthere;
      if (unit == NULL)
        return NULL;
      tmp_unit_view.type = unit->type;
      tmp_unit_view.side_id = unit->side->id;
      tmp_unit_view.size = unit->size;
      tmp_unit_view.x = unit->x;  tmp_unit_view.y = unit->y;
      tmp_unit_view.date = g_turn();
      tmp_unit_view.id = unit->id;
      tmp_unit_view.unit = unit;
      tmp_unit_view.nexthere = NULL;
      return &tmp_unit_view;
    }
    for (uv2 = uview->nexthere; uv2 != NULL; uv2 = uv2->nexthere) {
      if (x == uv2->x && y == uv2->y)
        return uv2;
    }
    return NULL;
}

UnitView *
add_unit_view(Side *side, Unit *unit)
{
    int i, changed = FALSE, rehash = FALSE;
    UnitView *uv, *uview = NULL;

    /* Look for an existing unit view that is tied to the given unit. */
    if (side->unit_views != NULL) {
      for (i = 0; i < VIEW_HASH_SIZE; ++i) {
          for (uv = side->unit_views[i]; uv != NULL; uv = uv->nexthere) {
            if (uv->id == unit->id) {
                uview = uv;
                break;
            }
          }
          if (uview != NULL)
            break;
      }
    }
    if (uview == NULL) {
      uview = (UnitView *) xmalloc(sizeof(UnitView));
      uview->id = unit->id;
      add_unit_view_raw(side, uview, unit->x, unit->y);
      changed = TRUE;
    }
    if (uview->type != unit->type) {
      uview->type = unit->type;
      changed = TRUE;
    }
    if (uview->side_id != unit->side->id) {
      uview->side_id = unit->side->id;
      changed = TRUE;
    }
    if (uview->size != unit->size) {
      uview->size = unit->size;
      changed = TRUE;
    }
    if (side == unit->side)
      uview->unit = unit;
    else
      uview->unit = NULL;
    if (uview->x != unit->x) {
      remove_unit_view(side, uview);
      rehash = TRUE;
      uview->x = unit->x;
      changed = TRUE;
    }
    if (uview->y != unit->y) {
      remove_unit_view(side, uview);
      uview->y = unit->y;
      rehash = TRUE;
      changed = TRUE;
    }
    if (rehash) {
      add_unit_view_raw(side, uview, uview->x, uview->y);
    }
    /* Irrespective of whether any view content changed, we now know
       that the view of this unit is current, so date it. */
    uview->date = g_turn();
    return (changed ? uview : NULL);
}

void
add_unit_view_raw(Side *side, UnitView *uview, int x, int y)
{
    int hash;
    UnitView *uv2, *prevview;

    if (side->unit_views == NULL)
      side->unit_views =
      (UnitView **) xmalloc(VIEW_HASH_SIZE * sizeof(UnitView *));
    prevview = NULL;
    hash = (x ^ y) % VIEW_HASH_SIZE;
    for (uv2 = side->unit_views[hash]; uv2 != NULL; uv2 = uv2->nexthere) {
      if (uview->id < uv2->id
          && (prevview == NULL || uview->id > prevview->id)) {
          break;
      }
      prevview = uv2;
    }
    uview->nexthere = uv2;
    if (prevview == NULL) {
      side->unit_views[hash] = uview;
    } else {
      prevview->nexthere = uview;
    }
    uview->x = x;  uview->y = y;
}

int
remove_unit_view(Side *side, UnitView *olduview)
{
    int hash;
    UnitView *uv, *prevview = NULL;

    if (side->unit_views == NULL)
      return FALSE;
    hash = (olduview->x ^ olduview->y) % VIEW_HASH_SIZE;
    for (uv = side->unit_views[hash]; uv != NULL; uv = uv->nexthere) {
      if (olduview == uv) {
          if (prevview == NULL) {
            side->unit_views[hash] = uv->nexthere;
          } else {
            prevview->nexthere = uv->nexthere;
          }
          return TRUE;
      }
      prevview = uv;
    }
    return FALSE;
}

/* What happens when a unit appears on a given cell. */

/* An always-seen unit has builtin spies/tracers to inform everybody
   else of all its movements.  When such a unit occupies a cell,
   coverage is turned on and remains on until the unit leaves that
   cell. */

/* If this unit with onboard spies wanders into unknown territory,
   shouldn't that territory become known as well?  I think the unseen
   test should only apply during initialization. */
/* But if always-seen unit concealed during init, will magically appear
   when it first moves! */

void
all_see_occupy(Unit *unit, int x, int y, int inopen)
{
    Side *side;
    int always = u_see_always(unit->type);
    
    for_all_sides(side) {
      if (side->see_all) {
          /* No work to do if the side sees everything. */
          update_cell_display(side, x, y, UPDATE_ALWAYS);
      } else if (side_sees_unit(side, unit)) {
          see_cell(side, x, y);
      } else if (side_tracking_unit(side, unit)) {
          see_cell(side, x, y);
      } else {
          if (always && terrain_view(side, x, y) != UNSEEN) {
            add_cover(side, x, y, 1);
            set_alt_cover(side, x, y, 0);
          }
          if (inopen || always) {
            see_cell(side, x, y);
          }
      }
    }
    /* If the occupation happens during the game, nearby units that
       are on unfriendly sides should wake up.  (should also be:
       "unless they were directed to sleep in presence of enemy") */
    if (gameinited) {
      int dir, x1, y1;
      Unit *unit2, *unit3;
            
      for_all_directions(dir) {
          if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
            for_all_stack(x1, y1, unit2) {
                if (unit2->side != NULL
                  && unit2->side != unit->side
                  && units_visible(unit2->side, x, y)
                  && !unit_trusts_unit(unit2, unit)) {
                  wake_unit(unit2->side, unit2, FALSE);
                }
                for_all_occupants(unit2, unit3) {
                  if (unit3->side != NULL
                      && unit3->side != unit->side
                      && units_visible(unit3->side, x, y)
                      && !unit_trusts_unit(unit3, unit)) {
                      wake_unit(unit3->side, unit3, FALSE);
                  }
                }
            }
          }
      }
    }
}

/* Some highly visible unit types cannot leave a cell without everybody
   knowing about the event.  The visibility is attached to the unit, not
   the cell, so first the newly-empty cell is viewed, then view coverage
   is decremented. */

void
all_see_leave(Unit *unit, int x, int y, int inopen)
{
    Side *side;
    int always = u_see_always(unit->type), domore, update;
    UnitView *uview;

    for_all_sides(side) {
      if (side->see_all) {
          /* No work to do if the side sees everything. */
          update_cell_display(side, x, y, UPDATE_ALWAYS);
      } else if (side_sees_unit(side, unit)
               && in_area(unit->x, unit->y)) {
          see_cell(side, x, y);
      } else if (side_tracking_unit(side, unit)) {
          see_cell(side, x, y);
      } else {
          if (always && terrain_view(side, x, y) != UNSEEN) {
            see_cell(side, x, y);
            if (cover(side, x, y) > 0)
              add_cover(side, x, y, -1);
            if (side->alt_coverage)
              /* (should recalc alt coverage, since was 0) */;
          }
          /* Won't be called twice, because view coverage is 0 now. */
          if (inopen) {
            see_cell(side, x, y);
          }
          /* special hack to flush images we *know* are garbage */
          if (cover(side, x, y) < 1) {
            domore = TRUE;
            update = FALSE;
            while (domore) {
                domore = FALSE;
                for_all_view_stack(side, x, y, uview) {
                  if (side == view_side(uview)) {
                      remove_unit_view(side, uview);
                      domore = TRUE;
                      update = TRUE;
                      break;
                  }
                }
            }
            if (update)
              update_cell_display(side, x, y, UPDATE_ALWAYS);
          }
      }
    }
}

static int tmpx0, tmpy0, tmpz0;
static int tmpnx, tmpny, tmpnz;

static void
init_visible_elevation(int x, int y)
{
    int elev = checked_elev_at(x, y);

    set_tmp2_at(x, y, elev - tmpz0);
}

static void
init_visible_elevation_2(int x, int y)
{
    int elev = checked_elev_at(x, y);

    set_tmp3_at(x, y, elev - tmpnz);
}


static void
calc_visible_elevation(int x, int y)
{
    int dir, x1, y1, elev, tmp, tmpa, tmpb;
    int adjelev = 9999, viselev, dist, dist1, cellwid = area.cellwidth;

    elev = checked_elev_at(x, y);
    dist = distance(x, y, tmpx0, tmpy0);
    if (cellwid <= 0)
      cellwid = 1;
    for_all_directions(dir) {
      if (point_in_dir(x, y, dir, &x1, &y1)) {
          dist1 = distance(x1, y1, tmpx0, tmpy0);
          if (dist1 < dist) {
            tmpa = tmp2_at(x1, y1);
            /* Account for the screening effect of the elevation
                   difference. */
            /* (dist1 will never be zero) */
            tmpa = (tmpa * dist * cellwid) / (dist1 * cellwid);
            tmpb = checked_elev_at(x1, y1)
              + t_thickness(terrain_at(x1, y1))
              - tmpz0;
            tmpb = (tmpb * dist * cellwid) / (dist1 * cellwid);
            tmp = max(tmpa, tmpb);
            adjelev = min(adjelev, tmp + tmpz0);
          }
      }
    }
    viselev = max(adjelev, elev);
    set_tmp2_at(x, y, viselev - tmpz0);
}

static void
calc_visible_elevation_2(int x, int y)
{
    int dir, x1, y1, elev, tmp, tmpa, tmpb;
    int adjelev = 9999, viselev, dist, dist1, cellwid = area.cellwidth;

    elev = checked_elev_at(x, y);
    dist = distance(x, y, tmpnx, tmpny);
    if (cellwid <= 0)
      cellwid = 1;
    for_all_directions(dir) {
      if (point_in_dir(x, y, dir, &x1, &y1)) {
          dist1 = distance(x1, y1, tmpnx, tmpny);
          if (dist1 < dist) {
            tmpa = tmp3_at(x1, y1);
            /* Account for the screening effect of the elevation
                   difference. */
            /* (dist1 will never be zero) */
            tmpa = (tmpa * dist * cellwid) / (dist1 * cellwid);
            tmpb = checked_elev_at(x1, y1)
              + t_thickness(terrain_at(x1, y1))
              - tmpnz;
            tmpb = (tmpb * dist * cellwid) / (dist1 * cellwid);
            tmp = max(tmpa, tmpb);
            adjelev = min(adjelev, tmp + tmpnz);
          }
      }
    }
    viselev = max(adjelev, elev);
    set_tmp3_at(x, y, viselev - tmpnz);
}

/* Unit's beady eyes are shifting from one location to another.  Since
   new things may be coming into view, we have to check and maybe draw
   lots of cells (but only need the one output flush, fortunately). */

/* (LOS comes in here, to make irregular coverage areas) */

void
cover_area(Side *side, Unit *unit, Unit *oldtransport, int x0, int y0,
         int nx, int ny)
{
    Side *side2;

    if (side != NULL
      && !side->see_all
      && completed(unit)
        && (oldtransport == NULL
         || uu_occ_vision(unit->type, oldtransport->type) > 0)) {
      if (side->ingame) {
          /* Active sides keep their allies informed.  Note that
             this will effectively leak information to allies of
             allies, even if the unit's side is not an ally
             directly. */
          for_all_sides(side2) {
            if (trusted_side(side, side2)) {
                cover_area_1(side2, unit, x0, y0, nx, ny);
            }
          }
      } else {
          /* Inactive sides just have their own data maintained
             (not clear if this is critical, but seems sensible) */
          cover_area_1(side, unit, x0, y0, nx, ny);
      }
    }
}

/* Set this flag to redo coverage without doing all the side effects
   that come with see_cell. */

static int suppress_see_cell;

/* Set this flag to initialize views without waking any units.  This
   is necessary when restoring a game, since we construct the view
   using this code, but don't want to modify the units' plans from
   what they were when the game was saved. */

int suppress_see_wakeup;

static void
cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
{
    int u = unit->type, range0, nrange, range, x, y, x1, y1, x2, y2;
    int y1c, y2c, xw, cov, los, r;
    int xmin, ymin, xmax, ymax, oldcov, newcov, anychanges;

    if (side->coverage == NULL)
      return;
    range0 = nrange = u_vision_range(u);
    /* Adjust for the effects of nighttime on vision range. */
    if (in_area(x0, y0)) {
      if (night_at(x0, y0)) {
          range0 =
            (range0 * ut_vision_night_effect(u, terrain_at(x0, y0))) / 100;
      }
    } else {
      range0 = 0;
    }
    if (in_area(nx, ny)) {
      if (night_at(nx, ny)) {
          nrange =
            (nrange * ut_vision_night_effect(u, terrain_at(nx, ny))) / 100;
      }
    } else {
      nrange = 0;
    }
    range = max(range0, nrange);
    allocate_area_scratch(1);
    anychanges = FALSE;
    /* First, set the union of the from and to areas to the existing
       coverage. */
    /* These may be outside the area - necessary since units may be able
       to see farther in x than the height of the area. */
    /* Compute the maximum bounds that may be affected. */
    if (y0 >= 0) {
      if (ny >= 0) {
          ymin = min(y0, ny);
          ymax = max(y0, ny);
      } else {
          ymin = ymax = y0;
      }
    } else if (ny >= 0) {
      ymin = ymax = ny;
    }
    if (x0 >= 0) {
      if (nx >= 0) {
          xmin = min(x0, nx);
          xmax = max(x0, nx);
      } else {
          xmin = xmax = x0;
      }
    } else if (nx >= 0) {
      xmin = xmax = nx;
    }
    if (any_los) {
      /* Need extra scratch layers, will be used for visible
           elevation cache. */
      allocate_area_scratch(3);
    }
    los = FALSE;
    /* (should also adjust for effect of clouds here) */
    if (u_vision_bend(u) != 100) {
      los = TRUE;
      /* Compute the minimum elevation for visibility at each cell. */
      if (in_area(x0, y0)) {
          tmpx0 = x0;  tmpy0 = y0;
          tmpz0 = checked_elev_at(x0, y0)
            + unit_alt(unit)
            + ut_eye_height(u, terrain_at(x0, y0));
          apply_to_area(x0, y0, range0, init_visible_elevation);
          /* Leave own and adj cells alone, they will always be visible. */
          for (r = 2; r <= range0; ++r) {
            apply_to_ring(x0, y0, r, r, calc_visible_elevation);
          }
          /* We now have a layer indicating how high things must be
               to be visible. */
      }
      if (in_area(nx, ny)) {
          tmpnx = nx;  tmpny = ny;
          tmpnz = checked_elev_at(nx, ny)
            + unit_alt(unit)
            + ut_eye_height(u, terrain_at(nx, ny));
          apply_to_area(nx, ny, nrange, init_visible_elevation_2);
          /* Leave own and adj cells alone, they will always be visible. */
          for (r = 2; r <= nrange; ++r) {
            apply_to_ring(nx, ny, r, r, calc_visible_elevation_2);
          }
          /* We now have another layer, indicating how high things must be
               to be visible from the new location. */
      }
    }
    /* Copy the current coverage into the tmp layer. */
    y1 = y1c = ymin - range;
    y2 = y2c = ymax + range;
    /* Clip the iteration bounds. */
    if (y1c < 0)
      y1c = 0;
    if (y2c > area.height - 1)
      y2c = area.height - 1;
    for (y = y1c; y <= y2c; ++y) {
      x1 = xmin - range;
      x2 = xmax + range;
      for (x = x1; x <= x2; ++x) {
          if (in_area(x, y)) {
            xw = wrapx(x);
            set_tmp1_at(xw, y, cover(side, xw, y));
          }
      }
    }
    /* Decrement coverage around the old location. */
    if (in_area(x0, y0)) {
      y1 = y1c = y0 - range0;
      y2 = y2c = y0 + range0;
      /* Clip the iteration bounds. */
      if (y1c < 0)
        y1c = 0;
      if (y2c > area.height - 1)
        y2c = area.height - 1;
      for (y = y1c; y <= y2c; ++y) {
          x1 = x0 - (y < y0 ? (y - y1) : range0);
          x2 = x0 + (y > y0 ? (y2 - y) : range0);
          for (x = x1; x <= x2; ++x) {
            if (in_area(x, y)) {
                xw = wrapx(x);
                if (!los
                  || ((tmp2_at(xw, y) + tmpz0)
                      <= (checked_elev_at(xw, y)
                        + t_thickness(terrain_at(xw, y))))) {
                  cov = tmp1_at(xw, y) - 1;
                  /* Should never go negative, detect if so. */
                  if (cov < 0) {
                      Dprintf("Negative coverage for %s at %d,%d\n",
                            side_desig(side), xw, y);
                  }
                  set_tmp1_at(xw, y, cov);
                }
                if (los && (alt_cover(side, xw, y) == (tmp2_at(xw, y) + tmpz0)))
                  /* this unit set the min, should recalc alt
                         coverage now */;
            }
          }
      }
    }
    /* Increment coverage around the new location. */
    if (in_area(nx, ny)) {
      y1 = y1c = ny - nrange;
      y2 = y2c = ny + nrange;
      /* Clip the iteration bounds. */
      if (y1c < 0)
        y1c = 0;
      if (y2c > area.height - 1)
        y2c = area.height - 1;
      for (y = y1c; y <= y2c; ++y) {
          x1 = nx - (y < ny ? (y - y1) : nrange);
          x2 = nx + (y > ny ? (y2 - y) : nrange);
          for (x = x1; x <= x2; ++x) {
            if (in_area(x, y)) {
                xw = wrapx(x);
                if (!los
                  || ((tmp3_at(xw, y) + tmpnz)
                      <= (checked_elev_at(xw, y)
                        + t_thickness(terrain_at(xw, y))))) {
                  cov = tmp1_at(xw, y) + 1;
                  set_tmp1_at(xw, y, cov);
                }
                if (los)
                  set_alt_cover(side, xw, y,
                            min(alt_cover(side, xw, y),
                              (tmp3_at(xw, y) + tmpnz)));
            }
          }
      }
    }
    /* Now update the actual coverage.  Do this over an area that includes
       both the decrement and increment changes. */
    y1 = y1c = ymin - range;
    y2 = y2c = ymax + range;
    /* Clip the iteration bounds. */
    if (y1c < 0)
      y1c = 0;
    if (y2c > area.height - 1)
      y2c = area.height - 1;
    for (y = y1c; y <= y2c; ++y) {
      x1 = xmin - range;
      x2 = xmax + range;
      for (x = x1; x <= x2; ++x) {
          if (in_area(x, y)) {
            xw = wrapx(x);
            oldcov = cover(side, xw, y);
            newcov = tmp1_at(xw, y);
            if (newcov != oldcov) {
                set_cover(side, xw, y, newcov);
                /* Skip over unit view updating if we're just repairing
                   the coverage layer. */
                if (suppress_see_cell)
                  continue;
                if (newcov > oldcov
                  && see_cell(side, xw, y)
                  && !suppress_see_wakeup)
                  react_to_seen_unit(side, unit, xw, y);
                if ((newcov > 0 && oldcov == 0)
                  || (newcov == 0 && oldcov > 0)
                  || (DebugG && newcov != oldcov))
                  update_cell_display(side, xw, y, UPDATE_COVER);
                anychanges = TRUE;
            }
          }
      }
    }
    /* If we're seeing new things, make sure they're on the display. */
    if (anychanges)
      flush_display_buffers(side);
}

/* Use this to clear out garbled view coverage.  Note however that this
   may not touch any unit views, otherwise players could collect info
   about hard-to-see units by refreshing/recomputing over and over. */

void
reset_coverage(void)
{
    Side *side;

    if (g_see_all())
      return;
    suppress_see_cell = TRUE;
    for_all_sides(side)
      calc_coverage(side);
    suppress_see_cell = FALSE;
}

/* Calculate/recalculate the view coverage layers of a side. */

void
calc_coverage(Side *side)
{
    int x, y, pop, visible[MAXSIDES];
    Unit *unit;
    Side *side2;

    if (side->coverage == NULL)
      return;
    Dprintf("Calculating all view coverage for %s\n", side_desig(side));
    /* Either init all cells to 0, or use populations to decide. */
    if (people_sides_defined()) {
      /* First figure out which sides' people will tell us stuff and
         cache it, so as to speed up the init. */
      for_all_sides(side2) {
          visible[side2->id] = FALSE;
          if (trusted_side(side2, side) || side2->controlled_by == side)
            visible[side2->id] = TRUE;
      }
      for_all_cells(x, y) {
          pop = people_side_at(x, y);
          set_cover(side, x, y, ((pop != NOBODY && visible[pop]) ? 1 : 0));
      }
    } else {
      for_all_cells(x, y) {
          set_cover(side, x, y, 0);
      }
    }
    /* Add coverage by, and of, the units already in place. */
    for_all_units(unit) {
      if (in_play(unit)) {
          x = unit->x;  y = unit->y;
          if (trusted_side(unit->side, side)) {
            /* Units that trust us tell us stuff. */
            cover_area(side, unit, unit->transport, -1, -1, x, y);
          } else if (u_see_always(unit->type)
                   && terrain_view(side, x, y) != UNSEEN) {
            /* Always-seen units effectively monitor their current
               location for us, even if they don't like us. */
            add_cover(side, x, y, 1);
          }
      }
    }
}

/* Look for and clear out any bogus view data. */

void
reset_all_views(void)
{
    Side *side;

    for_all_sides(side) {
      if (!side->see_all) {
          reset_view(side);
      }
    }
}

void
reset_view(Side *side)
{
    int x, y, domore;
    UnitView *uview;

    for_all_cells(x, y) {
      /* Remove any leftover images of our own units. */
      if (cover(side, x, y) < 1) {
          domore = TRUE;
          while (domore) {
            domore = FALSE;
            for_all_view_stack(side, x, y, uview) {
                if (side == view_side(uview)) {
                  remove_unit_view(side, uview);
                  domore = TRUE;
                  break;
                }
            }
          }
      }
    }
}

void
react_to_seen_unit(Side *side, Unit *unit, int x, int y)
{
    int eu;
    Unit *eunit;
    UnitView *uview;
    Side *es;

    if (g_see_all() /* see real unit */) {
      /* (should look at all of stack if can be mixed) */
      if ((eunit = unit_at(x, y)) != NULL) {
          if (unit->plan && !allied_side(eunit->side, side)) {
            /* should do a more general alarm */
              wake_unit(unit->side, unit, TRUE);
          }
      }
    } else if (side->coverage != NULL) {
      uview = unit_view_at(side, x, y);
      if (uview != NULL) {
          eu = view_type(uview);  es = view_side(uview);
          /* react only to certain utypes? */
          if (unit->plan && !allied_side(es, side)) {
            /* should do a more general alarm */
            wake_unit(unit->side, unit, TRUE);
          }
      }
    } else {
      /* ??? */
    }
}

/* Decide whether any side acquires tracking on a unit. */

int any_tracking = -1;

void
maybe_track(Unit *unit)
{
    int x0, y0, dir, x1, y1, chance;
    Unit *unit2;

    if (any_tracking < 0) {
      int u1, u2;

      any_tracking = FALSE;
      for_all_unit_types(u1) {
          for_all_unit_types(u2) {
            if (uu_track(u1, u2) > 0) {
                any_tracking = TRUE;
                break;
            }
          }
          if (any_tracking)
            break;
      }
    }
    if (!any_tracking)
      return;
    x0 = unit->x;  y0 = unit->y;
    for_all_stack(x0, y0, unit2) {
      if (in_play(unit2)
          && unit2 != unit
          && unit2->side != unit->side
          && unit2->side != NULL) {
          chance = uu_track(unit2->type, unit->type);
          if (xrandom(10000) < chance) {
            add_side_to_set(unit2->side, unit->tracking);
          }
      }
    }
    for_all_directions(dir) {
      if (interior_point_in_dir(x0, y0, dir, &x1, &y1)) {
          for_all_stack(x1, y1, unit2) {
            if (in_play(unit2)
                && unit2 != unit
                && unit2->side != unit->side
                && unit2->side != NULL) {
                chance = uu_track(unit2->type, unit->type);
                if (xrandom(10000) < chance) {
                  add_side_to_set(unit2->side, unit->tracking);
                }
            }
          }
      }
    }
}

/* Decide whether any side tracking a unit lost it. */

void
maybe_lose_track(Unit *unit, int nx, int ny)
{
    int t = terrain_at(nx, ny), chance;
    Side *side;

    chance = ut_lose_track(unit->type, t);
    if (chance > 0) {
      for_all_sides(side) {
          if (side_tracking_unit(side, unit)
            && xrandom(10000) < chance) {
            remove_side_from_set(side, unit->tracking);
            /* (should notify?) */
          }
      }
    }
}

extern void compute_see_chances(void);

int any_see_chances = -1;

int any_people_see_chances = -1;

int people_always_see;

int max_see_chance_range;

int any_see_mistake_chances = -1;

int max_see_mistake_range;

/* Determine whether there is any possibility of an uncertain sighting,
   and cache the conclusion. */

void
compute_see_chances(void)
{
    int u1, u2, u3, m1;

    any_see_chances = FALSE;
    any_people_see_chances = FALSE;
    people_always_see = TRUE;
    max_see_chance_range = -1;
    any_see_mistake_chances = FALSE;
    max_see_mistake_range = -1;
    for_all_unit_types(u2) {
      for_all_unit_types(u1) {
          if (uu_see_at(u1, u2) != 100) {
            any_see_chances = TRUE;
            max_see_chance_range = max(max_see_chance_range, 0);
          }
          if (uu_see_adj(u1, u2) != 100) {
            any_see_chances = TRUE;
            max_see_chance_range = max(max_see_chance_range, 1);
          }
          if (uu_see(u1, u2) != 100) {
            any_see_chances = TRUE;
            max_see_chance_range = max(max_see_chance_range, u_vision_range(u1));
          }
          if (uu_see_mistake(u1, u2) > 0) {
            for_all_unit_types(u3) {
                if (uu_looks_like(u2, u3) > 0) {
                  any_see_mistake_chances = TRUE;
                  break;
                }
            }
            max_see_mistake_range = max(max_see_mistake_range, u_vision_range(u1));
          }
      }
      for_all_material_types(m1) {
          if (m_people(m1) > 0 && um_people_see(u2, m1) > 0) {
            any_people_see_chances = TRUE;
            if (um_people_see(u2, m1) < 100)
              people_always_see = FALSE;
          }
      }
      if (people_sides_defined())
        any_people_see_chances = TRUE;
    }
}

/* Update the view of this cell for everybody's benefit.  May have to write
   to many displays. */

void
all_see_cell(int x, int y)
{
    Side *side;

    for_all_sides(side) {
      see_cell(side, x, y);
    }
}

static int test_for_successful_viewer(int x, int y);
static int test_for_possible_viewer(int x, int y);

static Unit *tmpunittosee, *tmpseer;

static void mistake_view(Side *side, Unit *seer, UnitView *uview);

/* Look at the given position, possibly not seeing anything.  Return
   true if a unit was spotted. */

int
see_cell(Side *side, int x, int y)
{
    int update, updatet, chance, curview, x1, y1;
    int m, mupdate, wupdate, flags, domore, rslt, sawthis;
    Unit *unit, *unit2;
    UnitView *uview, *newuview;

    if (!in_area(x, y))
      return FALSE;
    update = updatet = rslt = FALSE;
    /* If we see everything, just pass through to updating the display. */
    /* (avoid using views so that designer mode won't affect view state?) */
    if (side->see_all) {
      update = updatet = TRUE;
      rslt = (unit_at(x, y) != NULL);
    } else if (cover(side, x, y) > 0) {
      /* Always update our knowledge of the terrain. */
      curview = terrain_view(side, x, y);
      if (curview == UNSEEN || !g_see_terrain_always()) {
          set_terrain_view(side, x, y, buildtview(terrain_at(x, y)));
          if (!g_see_terrain_always())
            set_terrain_view_date(side, x, y, g_turn());
          update = updatet = TRUE;
      }
      if (any_material_views) {
          mupdate = see_materials(side, x, y);
          if (mupdate)
            update = TRUE;
      }
      if (temperatures_defined() || clouds_defined() || winds_defined()) {
          wupdate = see_weather(side, x, y);
          if (wupdate)
            update = TRUE;
      }
      if (any_see_chances < 0)
        compute_see_chances();
      /* Get rid of any unit views that are no longer valid here. */
      domore = TRUE;
      while (domore) {
          domore = FALSE;
          for_all_view_stack(side, x, y, uview) {
            if ((unit = view_unit(uview)) == NULL
                || uview->x != unit->x
                || uview->y != unit->y) {
                if (remove_unit_view(side, uview))
                  update = TRUE;
                domore = TRUE;
                break;
            }
          }
      }
      for_all_stack(x, y, unit) {
          /* Reset the flag that says whether we've spotted this
             particular unit in the stack. */
          sawthis = FALSE;
          /* First test for always-accurate views. */
          if (side_sees_unit(side, unit)
            || u_see_always(unit->type)
            || (people_always_see
                && people_sides_defined()
                && people_side_at(x, y) == side->id)) {
            newuview = add_unit_view(side, unit);
            if (newuview)
              update = TRUE;
            rslt = sawthis = TRUE;
          } else if (any_see_chances) {
            /* If there are any unit-vs-unit see chances, use this
               code to scan around the given location for units that
               might be viewing this cell. */
            /* First consider the possibility that we might be
               stacked in the same cell as someone who could see
               us. */
            for_all_stack(x, y, unit2) {
                if (unit2->side == side) {
                  chance = uu_see_at(unit2->type, unit->type);
                  if (chance >= 100 || probability(chance)) {
                      newuview = add_unit_view(side, unit);
                      if (newuview)
                        update = TRUE;
                      rslt = sawthis = TRUE;
                      /* (should check for hallucination?) */
                      break;
                  }
                }
            }
            /* If nothing in the stack to see us, scan the area. */
            if (max_see_chance_range > 0 && !sawthis) {
                tmpside = side;
                tmpunittosee = unit;
                tmpseer = NULL;
                if (search_around(x, y, max_see_chance_range,
                              test_for_successful_viewer,
                              &x1, &y1, 1)
                  || test_for_successful_viewer(x, y)) {
                  newuview = add_unit_view(side, unit);
                  if (newuview)
                    update = TRUE;
                  rslt = sawthis = TRUE;
                  /* Check for hallucination.  Since we know the
                     viewing unit from our search just above, we
                     can test it directly. */
                  if (any_see_mistake_chances && cover(side, x, y) == 1)
                    mistake_view(side, tmpseer, newuview);
                }
            }
          } else {
            /* Default case tests terrain visibility effect only. */
            chance = ut_visibility(unit->type, terrain_at(x, y));
            if (chance >= 100 || probability(chance)) {
                newuview = add_unit_view(side, unit);
                if (newuview)
                  update = TRUE;
                rslt = sawthis = TRUE;
                /* If only one unit is viewing the cell, it might
                   make a mistake.  Since we don't know the
                   specific viewing unit, we have to search for
                   it. */
                if (any_see_mistake_chances && cover(side, x, y) == 1) {
                  tmpside = side;
                  tmpunittosee = unit;
                  tmpseer = NULL;
                  if (search_around(x, y, max_see_mistake_range,
                                test_for_possible_viewer,
                                &x1, &y1, 1))
                    mistake_view(side, tmpseer, newuview);
                }
            }
          }
      }
      /* Check if any populations in the cell see something. */
      if (any_people_see_chances
          && people_sides_defined()
          && people_side_at(x, y) == side->id
          && any_cell_materials_defined()) {
          for_all_stack(x, y, unit) {
            for_all_material_types(m) {
                if (cell_material_defined(m)
                  && material_at(x, y, m) > 0) {
                  chance = um_people_see(unit->type, m);
                  if (probability(chance)) {
                      newuview = add_unit_view(side, unit);
                      if (newuview)
                        update = TRUE;
                      /* Proceed to consider the next unit. */
                      break;
                  }
                }
            }
          }
      }
    }
    /* If there was any change in what was visible, tell the display. */
    if (update) {
      flags = UPDATE_ALWAYS;
      /* If the view of the terrain changed, adjacent cells probably
         need to be redrawn as well. */
      if (updatet)
        flags |= UPDATE_ADJ;
      update_cell_display(side, x, y, flags);
    }
    /* Indicate whether any units were seen at this location, even if
       no display updates were needed. */
    return rslt;
}

/* Test the given location to see if there is a unit that will spot
   the given tmpunittosee.  If the roll of the dice is successful,
   then write the seeing unit into tmpseer. */

static int
test_for_successful_viewer(int x, int y)
{
    int u, u2 = tmpunittosee->type, x2 = tmpunittosee->x, y2 = tmpunittosee->y;
    int dist, chance;
    Unit *unit;

    dist = distance(x, y, x2, y2);
    for_all_stack_with_occs(x, y, unit) {
      u = unit->type;
      if (unit->side == tmpside
          && u_vision_range(u) >= dist
          /* (should test LOS) */) {
          if (dist == 1)
            chance = uu_see_adj(u, u2);
          else
            /* (should interpolate according to distance?) */
            chance = uu_see(u, u2);
          chance = (chance * ut_visibility(u2, terrain_at(x2, y2))) / 100;
          if (unit->transport)
            chance =
            (chance * uu_occ_vision(u, unit->transport->type)) / 100;
          if (chance >= 100 || probability(chance)) {
            tmpseer = unit;
            return TRUE;
          }
      }
    }
    return FALSE;
}

/* Return TRUE if there is a unit at the given location that could be
   the one seeing the tmpunittosee.  This is not that accurate, since
   it doesn't test whether the unit found is the actual one that would
   have seen the given unit. */

static int
test_for_possible_viewer(int x, int y)
{
    int x2 = tmpunittosee->x, y2 = tmpunittosee->y;
    int dist;
    Unit *unit;

    dist = distance(x, y, x2, y2);
    for_all_stack(x, y, unit) {
      if (unit->side == tmpside
          && u_vision_range(unit->type) >= dist
          /* (should test LOS) */) {
          tmpseer = unit;
          return TRUE;
      }
    }
    return FALSE;
}

/* If the viewing unit might possibly misidentify the type of unit it
   saw, compute the chance and replace the view's type with one of the
   possible types that it could be mistaken for. */

static void
mistake_view(Side *side, Unit *seer, UnitView *uview)
{
    int mistakechance;

    if (seer == NULL)
      return;
    /* Decide if the unit appears to be one of some other type. */
    mistakechance = uu_see_mistake(seer->type, uview->type);
    if (mistakechance > 0 && xrandom(10000) < mistakechance)
      uview->type = mistaken_type(uview->type);
}

/* Given a unit type, return a similar-looking type that it might be
   confused with. */

static int
mistaken_type(int u2)
{
    int u3, totalweight, randval;

    totalweight = 0;
    for_all_unit_types(u3) {
      totalweight += uu_looks_like(u2, u3);
    }
    /* If nothing that it looks like, return original type. */
    if (totalweight == 0)
      return u2;
    randval = xrandom(totalweight);
    totalweight = 0;
    for_all_unit_types(u3) {
      totalweight += uu_looks_like(u2, u3);
      if (randval < totalweight)
        return u3;      
    }
    return u2;
}

/* "Bare-bones" viewing, for whenever you know exactly what's there.
   This is the lowest level of all viewing routines, and executed a lot. */

void
see_exact(Side *side, int x, int y)
{
    int oldtview, newtview, update, mupdate, wupdate;
    Unit *unit;
    UnitView *newuview;

    if (!in_area(x, y))
      return;
    if (side->see_all) {
      /* It may not really be necessary to do anything to the
         display, but the kernel doesn't know if the interface is
         drawing all the units that are visible, or is only drawing
         "interesting" ones, or whatever.  It would be up to the
         interface to decide that, say, its magnification power for
         a map is such that only one unit is being displayed, and
         that the update from here doesn't result in any visible
         changes to what's already been drawn on the screen. */
      update = TRUE;
    } else {
      update = FALSE;
      oldtview = terrain_view(side, x, y);
      newtview = buildtview(terrain_at(x, y));
      set_terrain_view(side, x, y, newtview);
      set_terrain_view_date(side, x, y, g_turn());
      if (oldtview != newtview)
        update = TRUE;
      mupdate = see_materials(side, x, y);
      if (mupdate)
        update = TRUE;
      wupdate = see_weather(side, x, y);
      if (wupdate)
        update = TRUE;
      for_all_stack(x, y, unit) {
          newuview = add_unit_view(side, unit);
          if (newuview)
            update = TRUE;
      }
    }
    /* If there was any change in what was visible, tell the display. */
    if (update) {
      update_cell_display(side, x, y, UPDATE_ALWAYS | UPDATE_ADJ);
    }
}

static int
see_materials(Side *side, int x, int y)
{
    int m, curview, update;

    update = FALSE;
    if (!any_material_views)
      return update;
    for_all_material_types(m) {
      if (any_material_views_by_m[m]) {
          curview = material_view(side, x, y, m);
          if (curview != material_at(x, y, m)) {
            set_material_view(side, x, y, m, material_at(x, y, m));
            update = TRUE;
          }
          /* Even if the data didn't change, our information is
             now up-to-date. */
          if (1)
            set_material_view_date(side, x, y, m, g_turn());
      }
    }
    return update;
}

static int
see_weather(Side *side, int x, int y)
{
    int curview, update;

    update = FALSE;
    if (temperatures_defined()) {
      curview = temperature_view(side, x, y);
      if (curview != temperature_at(x, y)) {
          set_temperature_view(side, x, y, temperature_at(x, y));
          update = TRUE;
      }
      /* Even if the data didn't change, our information is
         now up-to-date. */
      if (!g_see_weather_always())
        set_temperature_view_date(side, x, y, g_turn());
    }
    if (clouds_defined()) {
      curview = cloud_view(side, x, y);
      if (curview != raw_cloud_at(x, y)) {
          set_cloud_view(side, x, y, raw_cloud_at(x, y));
          update = TRUE;
      }
      curview = cloud_bottom_view(side, x, y);
      if (curview != raw_cloud_bottom_at(x, y)) {
          set_cloud_bottom_view(side, x, y, raw_cloud_bottom_at(x, y));
          update = TRUE;
      }
      curview = cloud_height_view(side, x, y);
      if (curview != raw_cloud_height_at(x, y)) {
          set_cloud_height_view(side, x, y, raw_cloud_height_at(x, y));
          update = TRUE;
      }
      /* Even if the data didn't change, our information is
         now up-to-date. */
      if (!g_see_weather_always())
        set_cloud_view_date(side, x, y, g_turn());
      /* Only need one date for the three layers of view data. */
    }
    if (winds_defined()) {
      curview = wind_view(side, x, y);
      if (curview != raw_wind_at(x, y)) {
          set_wind_view(side, x, y, raw_wind_at(x, y));
          update = TRUE;
      }
      /* Even if the data didn't change, our information is
         now up-to-date. */
      if (!g_see_weather_always())
        set_wind_view_date(side, x, y, g_turn());
    }
    return update;
}

/* A border has been seen if the cells on both sides has been seen. */

int
seen_border(Side *side, int x, int y, int dir)
{
    int x1, y1;

    if (side->see_all)
      return TRUE;
    if (terrain_view(side, x, y) == UNSEEN)
      return FALSE;
    if (point_in_dir(x, y, dir, &x1, &y1))
      if (terrain_view(side, x1, y1) == UNSEEN)
      return FALSE;
    return TRUE;
}

/* Make a printable identification of the given side.  This should be
   used for debugging and designing, not regular play. */

char *
side_desig(Side *side)
{
    if (sidedesigbuf == NULL)
      sidedesigbuf = xmalloc(BUFSIZE);
    if (side != NULL) {
      sprintf(sidedesigbuf, "s%d (%s)", side_number(side), side_name(side));
      if (side->self_unit) {
          tprintf(sidedesigbuf, "(self is #%d)", side->self_unit->id);
      }
    } else {
      sprintf(sidedesigbuf, "nullside");
    }
    return sidedesigbuf;
}

/* Add a player into the list of players.  All values are defaults here. */

Player *
add_player(void)
{
    Player *player = (Player *) xmalloc(sizeof(Player));

    player->id = nextplayerid++;
    /* Note that all names and suchlike slots are NULL. */
    ++numplayers;
    /* Add this one to the end of the player list. */
    if (last_player == NULL) {
      playerlist = last_player = player;
    } else {
      last_player->next = player;
      last_player = player;
    }
    Dprintf("Added player #%d\n", player->id);
    return player;
}

Player *
find_player(int n)
{
    Player *player;

    for_all_players(player) {
       if (player->id == n)
      return player;
    }
    return NULL;
}

/* Transform a player object into a regularized form. */

void
canonicalize_player(Player *player)
{
    if (player == NULL)
      return;
    if (empty_string(player->displayname))
      player->displayname = NULL;
    if (empty_string(player->aitypename))
      player->aitypename = NULL;
    /* This seems like a logical place to canonicalize an AI type
       of "ai" into a specific type, but we don't necessarily know
       the best default until the game is closer to starting. */
}

/* Make a printable identification of the given player.  The output
   here must be correctly parseable according to player spec rules,
   although it doesn't have to be pretty or minimal. */

char *
player_desig(Player *player)
{
    if (playerdesigbuf == NULL)
      playerdesigbuf = xmalloc(BUFSIZE);
    if (player != NULL) {
      sprintf(playerdesigbuf, "%s,%s/%s@%s+%d",
            (player->name ? player->name : ""),
            (player->aitypename ? player->aitypename : ""),
            (player->configname ? player->configname : ""),
            (player->displayname ? player->displayname : ""),
            player->advantage);
    } else {
      sprintf(playerdesigbuf, "nullplayer");
    }
    return playerdesigbuf;
}

/* Return the net advantage of the side, accounting for both the
   side's predetermined advantage and the player's choice. */

int
actual_advantage(Side *side)
{
    int advantage = max(1, side->advantage);

    if (side->player)
      advantage = side->player->advantage;
    return advantage;
}

/* Doctrine handling. */

Doctrine *
new_doctrine(int id)
{
    Doctrine *doctrine = (Doctrine *) xmalloc(sizeof(Doctrine));

    /* Doctrine ids start at 1 and go up. */
    if (id <= 0)
      id = next_doctrine_id++;
    else
      next_doctrine_id = id + 1;
    doctrine->id = id;
    /* Fill in all the doctrine slots with their default values. */
    doctrine->resupply_percent = 50;
    doctrine->rearm_percent = 20;
    /* 35% is basically 1/3, rounded up. */
    doctrine->repair_percent = 35;
    doctrine->construction_run = (short *) xmalloc (numutypes * sizeof(short));
    /* We just committed on the number of unit types. */
    canaddutype = FALSE;
    /* Add the new doctrine to the end of the list of doctrines. */
    if (last_doctrine != NULL) {
      last_doctrine->next = doctrine;
      last_doctrine = doctrine;
    } else {
      doctrine_list = last_doctrine = doctrine;
    }
    return doctrine;
}

Doctrine *
find_doctrine_by_name(char *name)
{
    Doctrine *doctrine;

    if (name == NULL)
      return NULL;
    for_all_doctrines(doctrine) {
      if (doctrine->name != NULL && strcmp(name, doctrine->name) == 0)
        return doctrine;
    }
    return NULL;
}

Doctrine *
find_doctrine(int id)
{
    Doctrine *doctrine;

    for_all_doctrines(doctrine) {
      if (doctrine->id == id)
        return doctrine;
    }
    return NULL;
}

#if 0 /* seems useful, but never actually used */
Doctrine *
clone_doctrine(Doctrine *olddoc)
{
    int tmpid;
    Doctrine *newdoc, *tmpnext;

    newdoc = new_doctrine(0);
    tmpid = newdoc->id;
    tmpnext = newdoc->next;
    memcpy(newdoc, olddoc, sizeof(Doctrine));
    newdoc->id = tmpid;
    /* Always allocate and copy subarrays. */
    newdoc->construction_run = (short *) xmalloc (numutypes * sizeof(short));
    memcpy(newdoc->construction_run, olddoc->construction_run, numutypes * sizeof(short));
    newdoc->next = tmpnext;
    return newdoc;
}
#endif

/* Standing order handling. */

StandingOrder *
new_standing_order(void)
{
    StandingOrder *sorder;

    sorder = (StandingOrder *) xmalloc(sizeof(StandingOrder));
    sorder->types = xmalloc(numutypes);
    return sorder;
}

/* Add a new standing order for the side.  This function can add to front
   or back of existing list of orders. */

int order_conds_match(StandingOrder *sorder, StandingOrder *sorder2);

void
add_standing_order(Side *side, StandingOrder *sorder, int pos)
{
    StandingOrder *sorder2, *saved;

    if (sorder->task == NULL) {
      /* Cancelling an order. */
      saved = NULL;
      if (side->orders == NULL) {
          /* No orders, so nothing to do. */
          notify(side, "No orders to cancel");
      } else if (order_conds_match(sorder, side->orders)) {
          /* Delete the first order in the list. */
          saved = side->orders;
          if (side->last_order == side->orders)
            side->last_order = side->orders->next;
          side->orders = side->orders->next;
      } else {
          for (sorder2 = side->orders; sorder2->next != NULL; sorder2 = sorder2->next) {
            if (order_conds_match(sorder, sorder2->next)) {
                saved = sorder2->next;
                if (side->last_order == sorder2->next)
                  side->last_order = sorder2->next->next;
                sorder2->next = sorder2->next->next;
                break;
            }
          }
          /* If we're here, no match; might be user error, but can't be sure,
             so don't say anything. */
      }
      if (saved != NULL) {
          notify(side, "Cancelled order `%s'", standing_order_desc(saved, spbuf));
      }
    } else if (pos == 0) {
      /* Add order to front of list. */
      sorder->next = side->orders;
      side->orders = sorder;
      if (side->last_order == NULL)
        side->last_order = sorder;
    } else if (side->last_order != NULL) {
      /* Add order to end of list. */
      side->last_order->next = sorder;
      side->last_order = sorder;
    } else {
      /* First standing order for the side. */
      side->orders = side->last_order = sorder;
    }
}

int
order_conds_match(StandingOrder *sorder, StandingOrder *sorder2)
{
    return (sorder->condtype == sorder2->condtype
          && sorder->a1 == sorder2->a1
          && sorder->a2 == sorder2->a2
          && sorder->a3 == sorder2->a3);
}

int
parse_standing_order(Side *side, char *cmdstr)
{
    StandingOrder *sorder, *sorder2;

    if (cmdstr[0] == '?') {
      if (side->orders != NULL) {
          notify(side, "Current standing orders:");
          for (sorder2 = side->orders; sorder2 != NULL; sorder2 = sorder2->next) {
            notify(side, "  %s", standing_order_desc(sorder2, spbuf));
          }
      } else {
          notify(side, "No standing orders in effect.");
      }
      return 0;
    }
    sorder = new_standing_order();
    cmdstr = parse_unit_types(side, cmdstr, sorder->types);
    if (cmdstr == NULL)
      return (-1);
    cmdstr = parse_order_cond(side, cmdstr, sorder);
    if (cmdstr == NULL)
      return (-2);
    cmdstr = parse_task(side, cmdstr, &(sorder->task));
    if (cmdstr == NULL)
      return (-3);
    add_standing_order(side, sorder, 0);
    if (sorder->task != NULL) {
      notify(side, "New standing order: %s", standing_order_desc(sorder, spbuf));
    }
    return 0;
}

/* (should all go to nlang.c?) */

char *
parse_unit_types(Side *side, char *str, char *utypevec)
{
    char *arg, substr[BUFSIZE], *rest;
    int u;

    rest = get_next_arg(str, substr, &arg);
    u = utype_from_name(arg);
    if (u != NONUTYPE) {
      utypevec[u] = 1;
    } else if (strcmp(arg, "all") == 0) {
      for_all_unit_types(u)
        utypevec[u] = 1;
    } else {
      notify(side, "Unit type \"%s\" not recognized", arg);
      return NULL;
    }
    return rest;
}

char *
parse_order_cond(Side *side, char *str, StandingOrder *sorder)
{
    int x = 0, y = 0, dist = 0;
    char *arg, *arg2, substr[BUFSIZE], *rest;
    Unit *unit;

    rest = get_next_arg(str, substr, &arg);
    if (strcmp(arg, "at") == 0 || strcmp(arg, "@") == 0) {
      sorder->condtype = sorder_at;
    } else if (strcmp(arg, "in") == 0) {
      sorder->condtype = sorder_in;
    } else if (strcmp(arg, "within") == 0 || strcmp(arg, "near") == 0) {
      sorder->condtype = sorder_near;
    } else {
      notify(side, "Condition type \"%s\" not recognized", arg);
      return NULL;
    }
    if (sorder->condtype == sorder_near) {
      rest = get_next_arg(rest, substr, &arg);
      dist = strtol(arg, &arg2, 10);
      sorder->a3 = dist;
    }
    rest = get_next_arg(rest, substr, &arg);
    x = strtol(arg, &arg2, 10);
    if (arg != arg2 && *arg2 == ',') {
      y = strtol(arg2 + 1, &arg, 10);
      sorder->a1 = x;  sorder->a2 = y;
      return rest;
    } else if ((unit = find_unit_by_name(arg)) != NULL) {
      if (sorder->condtype == sorder_at || sorder->condtype == sorder_near) {
          sorder->a1 = x;  sorder->a2 = y;
      } else if (sorder->condtype == sorder_in) {
          sorder->a1 = unit->id;
      } else {
          return NULL;
      }
      return rest;
    } else {
      notify(side, "Condition argument \"%s\" not recognized", arg);
      return NULL;
    }
}

char *
standing_order_desc(StandingOrder *sorder, char *buf)
{
    int u, v = -1, i = 1;
    int *args;

    for_all_unit_types(u) {
      if (sorder->types[u]) {
          if (v < 0)
            v = u;
      } else {
          i = 0;
      }
    }
    if (v < 0 || sorder->task == NULL) {
      strcpy(buf, "invalid");
      return buf;
    }
    sprintf(buf, "if %s ", (i ? "all" : u_type_name(v)));

    switch (sorder->condtype) {
    case sorder_at:
      tprintf(buf, "at %d,%d ", sorder->a1, sorder->a2);
      break;
    case sorder_in:
      tprintf(buf, "in %s ", short_unit_handle(find_unit(sorder->a1)));
      break;
    case sorder_near:
      tprintf(buf, "within %d %d,%d ", sorder->a3,  sorder->a1, sorder->a2);
      break;
    default:
      strcat(buf, "unknown");
      return buf;
    }

    i = sorder->task->type;
    args = sorder->task->args;
    switch (i) {
    case TASK_MOVE_TO:
      tprintf(buf, "%s %d,%d", taskdefns[i].display_name, args[0], args[1]);
      break;
    case TASK_SENTRY:
      tprintf(buf, "%s %d", taskdefns[i].display_name, args[0]);
      break;
    default:
      task_desc(buf+strlen(buf), NULL, NULL, sorder->task);
      break;
    }
    return buf;
}

/* Collect the next whitespace-separated argument. */
/* (should move to util.c or nlang.c) */

char *
get_next_arg(char *str, char *buf, char **rsltp)
{
    char *p;

    strcpy(buf, str);
    p = buf;
    /* Skip past any leading whitespace. */
    while (isspace(*p))
      ++p;
    if (*p == '"') {
      ++p;
      *rsltp = p;
      while (*p != '"' && *p != '\0')
        ++p;
      *p = '\0';
      /* Increment so later scanning looks past the closing quote. */ 
      ++p;
    } else {
      *rsltp = p;
      while (!isspace(*p) && *p != '\0')
        ++p;
      *p = '\0';
    }
    return str + (p - buf);
}

/* Agreement handling here for now. */

Agreement *agreement_list = NULL;

Agreement *last_agreement;

int num_agreements = 0;

int next_agreement_id = 1;

char *agreement_desig_buf;

void
init_agreements(void)
{
    agreement_list = last_agreement = NULL;
    num_agreements = 0;
}

Agreement *
create_agreement(int id)
{
    Agreement *ag = (Agreement *) xmalloc(sizeof(Agreement));

    if (id == 0)
      id = next_agreement_id++;
    ag->id = id;
    ag->terms = lispnil;
    ag->next = NULL;
    if (agreement_list != NULL) {
      last_agreement->next = ag;
    } else {
      agreement_list = ag;
    }
    last_agreement = ag;
    ++num_agreements;
    return ag;
}

Agreement *
find_agreement(int id)
{
    Agreement *ag;

    for_all_agreements(ag) {
      if (ag->id == id)
        return ag;
    }
    return NULL;
}

char *
agreement_desig(Agreement *ag)
{
    if (agreement_desig_buf == NULL)
      agreement_desig_buf = xmalloc(BUFSIZE);
    sprintf(agreement_desig_buf, "<ag %s %s %s>",
          (ag->typename ? ag->typename : "(null)"),
          (ag->name ? ag->name : "(null)"),
          (ag->terms == lispnil ? "(no terms)" : "...terms..."));
    return agreement_desig_buf;
}

/* Helper functions to init view layers from rle encoding. */

void
fn_set_terrain_view(int x, int y, int val)
{
    int rawval;

    if (1)
      rawval = val;
    else
      /* This is a more efficient encoding, but only usable if can guarantee
       see-terrain-always upon rereading. */
      rawval = (val ? buildtview(terrain_at(x, y)) : 0);
    set_terrain_view(tmpside, x, y, rawval);
}

void
fn_set_terrain_view_date(int x, int y, int val)
{
    set_terrain_view_date(tmpside, x, y, val);
}

void
fn_set_aux_terrain_view(int x, int y, int val)
{
    /* Filter anything but the basic six bits. */
    val &= 0x3f;
    set_aux_terrain_view(tmpside, x, y, tmpttype, val);
}

void
fn_set_aux_terrain_view_date(int x, int y, int val)
{
    set_aux_terrain_view_date(tmpside, x, y, tmpttype, val);
}

void
fn_set_material_view(int x, int y, int val)
{
    set_material_view(tmpside, x, y, tmpmtype, val);
}

void
fn_set_material_view_date(int x, int y, int val)
{
    set_material_view_date(tmpside, x, y, tmpmtype, val);
}

void
fn_set_temp_view(int x, int y, int val)
{
    set_temperature_view(tmpside, x, y, val);
}

void
fn_set_temp_view_date(int x, int y, int val)
{
    set_temperature_view_date(tmpside, x, y, val);
}

void
fn_set_cloud_view(int x, int y, int val)
{
    set_cloud_view(tmpside, x, y, val);
}

void
fn_set_cloud_bottom_view(int x, int y, int val)
{
    set_cloud_bottom_view(tmpside, x, y, val);
}

void
fn_set_cloud_height_view(int x, int y, int val)
{
    set_cloud_height_view(tmpside, x, y, val);
}

void
fn_set_cloud_view_date(int x, int y, int val)
{
    set_cloud_view_date(tmpside, x, y, val);
}

void
fn_set_wind_view(int x, int y, int val)
{
    set_wind_view(tmpside, x, y, val);
}

void
fn_set_wind_view_date(int x, int y, int val)
{
    set_wind_view_date(tmpside, x, y, val);
}

#ifdef DESIGNERS

static void paint_view_1(int x, int y);

static int tmptview;
static int tmpuviewflag;

/* Paint the side's view with given values. */

void
paint_view(Side *side, int x, int y, int r, int tview, int uviewflag)
{
    tmpside = side;
    tmptview = tview;
    tmpuviewflag = uviewflag;
    apply_to_area_plus_edge(x, y, r, paint_view_1);
}

static void
paint_view_1(int x, int y)
{
    int tview, oldtview = terrain_view(tmpside, x, y);

    if (oldtview != tmptview) {
      tview = tmptview;
      if (tview == 1234567) {
          tview = buildtview(terrain_at(x, y));
      }
      set_terrain_view(tmpside, x, y, tview);
      /* If the flag says so, see all units in the cell, otherwise
         leave all units unseen. */
      /* (should we do a see_cell in any case?) */
      if (tmpuviewflag && tview != UNSEEN) {
          see_exact(tmpside, x, y);
      }
      update_cell_display(tmpside, x, y, UPDATE_ALWAYS | UPDATE_ADJ);
    }
}

#endif /* DESIGNERS */

Generated by  Doxygen 1.6.0   Back to index