]> granicus.if.org Git - xconq/blobdiff - kernel/side.c
commits for 7.5.0 pre-release tarball
[xconq] / kernel / side.c
index e7d85bac57318a7c611fdb8edf0bd7f13d51a72c..f6c3689bd5840f0e9dabcf5ed4895ec3f62b694c 100644 (file)
@@ -1,5 +1,6 @@
 /* Sides in Xconq.
    Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.
+   Copyright (C) 2004-2005 Eric A. McDonald.
 
 Xconq is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -14,6 +15,28 @@ any later version.  See the file COPYING.  */
 
 #define checked_elev_at(x, y) (elevations_defined() ? elev_at(x, y) : 0)
 
+namespace Xconq {
+    int people_always_see;
+    int any_people_see_chances = -1;
+    int any_see_chances = -1;
+    int max_see_chance_range;
+    int any_see_mistake_chances = -1;
+    int max_see_mistake_range;
+    int any_overwatch_cache = -1;
+    PackedBoolTable *any_overwatch_chances = NULL;
+    int *overwatch_against_range_max = NULL;
+    int *overwatch_against_range_min = NULL;
+}
+
+/* From 'ui.h'. */
+extern void set_unit_image(Unit *unit);
+extern void set_unit_view_image(UnitView *uview);
+
+void return_default_colorname(Side *side);
+
+static char *pick_default_colorname(Side *side);
+static void allocate_unit_view_block(void);
+static UnitView *create_bare_unit_view(void);
 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);
@@ -23,7 +46,19 @@ static void cover_area_1(Side *side, struct a_unit *unit, int x0, int y0,
 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 void update_master_uview(Unit *unit, int lookabove);
 static int remove_unit_view(Side *side, UnitView *olduview);
+static void flush_one_view(UnitView *uview);
+
+static int see_unit(Unit *seer, Unit *tosee, SeerNode *seers, int *numseers);
+static int side_occs_see_unit(int x, int y, int *numseers, ParamBox *parambox);
+static int side_ustack_see_unit(int x, int y, int *numseers, 
+                               ParamBox *parambox);
+static void compute_overwatch_cache(void);
+
+#ifndef INITMAXVIEWS
+#define INITMAXVIEWS 200
+#endif
 
 #define VIEW_HASH_SIZE 257
 
@@ -59,37 +94,134 @@ int numtotsides;
 
 int nextsideid;
 
+/* Test if a given side's sideclass matches a given sideclass. */
+
+int
+fn_test_side_in_sideclass(Obj *osclass, ParamBox *pbox)
+{
+    char *sclass = NULL;
+    int sid = -1;
+    ParamBoxSide *paramboxs = NULL;
+    Obj *rest = lispnil;
+
+    assert_error(osclass, "Attempted to access a NULL GDL object");
+    assert_error(pbox, "Attempted to access a NULL parameter box");
+    assert_error(pbox->get_type() == PBOX_TYPE_SIDE,
+                "Wrong type of paramter box passed to test function");
+    paramboxs = (ParamBoxSide *)pbox;
+    /* If object is NIL, then return immediately. */
+    if (osclass == lispnil)
+      return FALSE;
+    /* If the object is a string, then assume it is a sideclass. */
+    if (stringp(osclass)) {
+        sclass = c_string(osclass);
+        if (paramboxs->side && (paramboxs->side != indepside)) {
+            if (empty_string(paramboxs->side->sideclass))
+              return FALSE;
+            return (!strcmp(sclass, paramboxs->side->sideclass));
+        }
+       else 
+          return (!strcmp(sclass, "independent"));
+    }
+    /* If the object is a number, then assume it is a side ID. */
+    else if (numberp(osclass)) {
+       sid = c_number(osclass);
+       if (paramboxs->side)
+         return (sid == paramboxs->side->id);
+       else
+         return (!sid);
+    }
+    /* If the object is a cons, then assume it is a list of side ID's 
+       inclusive-or side classes, and iterate through list until a 
+       match is found or end of list is encountered. */
+    else if (consp(osclass)) {
+       for_all_list(osclass, rest) {
+           if (fn_test_side_in_sideclass(car(rest), pbox))
+             return TRUE;
+       }
+       return FALSE;
+    }
+    /* Else, we can't use the object. */
+    else {
+        init_warning("Testing side against garbled sideclass expression");
+        /* Be permissive if continued. */
+        return TRUE;
+    }
+    return FALSE;
+}
+
 /* 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. */
+   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",
+char *default_colornames[MAXSIDES + 1] = {
+       "lemon-chiffon",
        "blue",
        "red",
        "yellow",
-       "chartreuse",
+       "green",
        "dark-orange",
-       "magenta",
+       "deep-pink",
        "cyan",
-       "pink",
        "purple",
-       "medium-sea-green",
+       "tomato",
+       "violet",
+       "dodger-blue",
+       "light-sea-green",
        "rosy-brown",
-       "cornflower-blue",
-       "firebrick",
-       "white",
-       "goldenrod"
+       "slate-blue",
+       "firebrick"
 };
 
+/* Default colors used so far by any non-removed side. Stores the 
+   number of the side using the color, or else zero. */
+
+static short default_colornames_used[MAXSIDES + 1];
+
+static char
+*pick_default_colorname(Side *side) 
+{
+       int i;
+       
+       if (side == indepside) {
+               return default_colornames[0];
+       } 
+       /* We start at 1 since default_colors[0] is reserved for 
+       indepside, whether or not it is present in the game. */
+       for (i = 1; i <= MAXSIDES +1; i++) {
+               if (default_colornames_used[i] == FALSE) {
+                       default_colornames_used[i] = TRUE;
+                       return default_colornames[i];
+               }
+       }
+       /* Should never happen, but humor the compiler. */
+       return "black";
+}
+
+void
+return_default_colorname(Side *side) 
+{
+       int i;
+       
+       /* We start at 1 since default_colors[0] is reserved for 
+       indepside, whether or not it is present in the game. */
+       for (i = 1; i <= MAXSIDES + 1; i++) {
+           if (default_colornames_used[i] == TRUE
+               && strcmp(side->default_color, default_colornames[i]) == 0) {
+               default_colornames_used[i] = FALSE;
+               return;
+           }
+       }
+}
+
 /* Cached values of global vision vars. */
 
 short any_los = -1;
 
 int any_material_views = -1;
 
-char *any_material_views_by_m;
+char *any_material_views_by_m = NULL;
 
 /* Pointer to buffer used for readable side description (for debugging). */
 
@@ -121,6 +253,68 @@ Doctrine *last_doctrine;
 
 int next_doctrine_id;
 
+/* The list of available unit views. */
+
+UnitView *freeviews;
+
+/* The global linked list of all unit views. */
+
+UnitView *viewlist = NULL;
+
+/* The number of views in viewlist. */
+
+static int numunitviews = 0;
+
+/* Grab a block of unit view objects to work with. */
+
+static void
+allocate_unit_view_block(void)
+{
+    int i;
+    UnitView *viewblock = (UnitView *) xmalloc(INITMAXVIEWS * sizeof(UnitView));
+
+    for (i = 0; i < INITMAXVIEWS; ++i) {
+        viewblock[i].id = -1;
+        viewblock[i].nexthere = &viewblock[i+1];
+    }
+    viewblock[INITMAXVIEWS-1].nexthere = NULL;
+    freeviews = viewblock;
+    Dprintf("Allocated space for %d unit views.\n", INITMAXVIEWS);
+}
+
+/* The primitive unit view creator. */
+
+UnitView *
+create_bare_unit_view(void)
+{
+    UnitView *newview;
+
+    /* If our free list is empty, go and get some more units. */
+    if (freeviews == NULL) {
+       allocate_unit_view_block();
+    }
+    /* Take the first unit off the free list. */
+    newview = freeviews;
+    freeviews = freeviews->nexthere;
+    /* ...but an invalid id. */
+    newview->id = -1;
+    return newview;
+}
+
+/* Count how many units total are on a side. */
+
+int
+n_units_on_side(Side *side)
+{
+    int n = 0;
+    Unit *unit = NULL;
+    /* (NOTE: Probably faster to sum up the side totals for each type. 
+       The question is whether those tallies are accurate.) */
+    for_all_side_units(side, unit) 
+      ++n;
+    return n;
+}
+
 /* Init side machinery. Don't fill in indepside yet since numutypes
    etc. may not be defined.  */
 
@@ -143,7 +337,7 @@ init_sides(void)
     /* 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));
+    assignments = (Assign *) xmalloc((MAXSIDES + 1) * sizeof(Assign));
     /* Set up the list of doctrines. */
     doctrine_list = last_doctrine = NULL;
     /* Doctrine ids must start at 1. */
@@ -158,9 +352,16 @@ create_side(void)
     int u, m;
     Side *newside;
 
+    /* Too many sides. */
     if (numsides >=  g_sides_max()) {
        run_error("Cannot have more than %d sides total!", g_sides_max());
     }
+    /* Prevent many crashes and much hair-pulling. */
+    if (numutypes <= 0) {
+       /* (Should probably be regarded as an init_error; however, this 
+           segment of code may be executed "late".) */
+       run_error("Unit types must be defined before defining sides!");
+    }
     /* Just fill in the names if dealing with indepside. */
     if (nextsideid == 0) {
        newside = indepside;
@@ -191,7 +392,10 @@ create_side(void)
     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];
+    newside->default_color = pick_default_colorname(newside);
+    if (newside != indepside) {
+       newside->emblemname = newside->default_color;
+    }
     for_all_unit_types(u) {
        /* Start unit numbering at 1, not 0. */
        newside->counts[u] = 1;
@@ -217,21 +421,27 @@ create_side(void)
     /* 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->advance = (short *) xmalloc(numatypes * sizeof(short));
+       newside->canresearch = (short *) xmalloc(numatypes * sizeof(short));
+       newside->research_precluded = 
+           (short *)xmalloc(numatypes * sizeof(short));
+       newside->research_topic = NOADVANCE;
+       newside->autoresearch = FALSE;
+       newside->research_goal = NONATYPE;
     }
     newside->canbuild = (short *) xmalloc(numutypes * sizeof(short));
+    newside->cancarry = (short *) xmalloc(numutypes * sizeof(short));
+    newside->candevelop = (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);
+       newside->treasury = (long *) xmalloc(nummtypes * sizeof(long));
+       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));
        {
@@ -255,6 +465,8 @@ create_side(void)
            }
        }
     }
+    newside->controlled_by_id = -1;
+    newside->playerid = -1;
     /* Link in at the end of the list of sides. */
     newside->next = NULL;
     /* Important to avoid the indepside->next = indepside loop! */
@@ -322,12 +534,15 @@ init_self_unit(Side *side)
     Unit *unit;
 
     if ((g_self_required() /* || side prop? */)
+       && side != indepside
        && 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;
+               && completed(unit)) {
+               side->self_unit = unit;
+               break;
+           }   
        }
     }
 }
@@ -386,7 +601,7 @@ init_view(Side *side)
                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 = (char *)xmalloc(nummtypes);
                    any_material_views_by_m[m] = TRUE;
                    break;
                }
@@ -449,7 +664,7 @@ init_view(Side *side)
     if (any_los < 0) {
        any_los = FALSE;
        for_all_unit_types(u) {
-           if (u_vision_bend(u) != 100) {
+           if (!u_can_see_behind(u)) {
                any_los = TRUE;
                break;
            }
@@ -641,18 +856,32 @@ side_controls_unit(Side *side, Unit *unit)
 int
 side_sees_unit(Side *side, Unit *unit)
 {
+    int x = -1, y = -1;
+
     if (unit == NULL)
       return FALSE;
+    /* If the side is omniscient. */
     if (side->see_all || side->show_all)
       return TRUE;
+    /* If the unit is 'see-always' and on known terrain in the arena. */
+    x = unit->x;  y = unit->y;
+    if (u_see_always(unit->type) && inside_area(x, y)
+       && (UNSEEN != terrain_view(side, x, y)))
+      return TRUE;
+    /* If the side controls the unit's side. */
     if (side_controls_side(side, unit->side))
       return TRUE;
-    /* We may also examine units that trust us, */
+    /* If the unit's side trusts the side. */
     if (trusted_side(unit->side, side))
       return TRUE;
-    /* or if the game is over. */
+    /* If the game is over. */
     if (endofgame)
       return TRUE;      
+    /* If the side's people can see the unit. */
+    if (Xconq::people_always_see && people_sides_defined() 
+       && (people_side_at(unit->x, unit->y) == side->id))
+      return TRUE;
+    /* Else, side cannot see. */
     return FALSE;
 }
 
@@ -662,6 +891,7 @@ int
 side_sees_image(Side *side, Unit *unit)
 {
     UnitView *uview;
+    int x = -1, y = -1;
 
     if (side == NULL)
       run_error("NULL side to side_sees_image");
@@ -669,19 +899,48 @@ side_sees_image(Side *side, Unit *unit)
       return FALSE;
     if (side->see_all || side->show_all)
       return TRUE;
+    /* If the unit is 'see-always' and on known terrain in the arena. */
+    x = unit->x;  y = unit->y;
+    if (u_see_always(unit->type) && inside_area(x, y)
+       && (UNSEEN != terrain_view(side, x, y)))
+      return TRUE;
     if (side_controls_side(side, unit->side))
       return TRUE;
-#if 0 /* shouldn't be necessary */
+#if (0)
     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;
+    for_all_view_stack_with_occs(side, unit->x, unit->y, uview) {
+       if (unit->type == uview->type 
+           && unit->side == side_n(uview->siden)
+           && unit->id == uview->id) {
+               return TRUE;
+       }
     }
     return FALSE;
 }
 
+/* Test whether the occupants of the given unit are visible to the
+   given side. */
+
+int
+occupants_visible(Side *side, Unit *unit)
+{
+    if (unit->occupant == NULL)
+      return FALSE;
+    /* If the side is omniscient. */
+    if (side->see_all)
+      return TRUE;
+    /* If the side sees the transport and the occupants are to be revealed. */
+    if (side_sees_unit(side, unit) && u_see_occupants(unit->type))
+      return TRUE;
+    /* If the side owns a seeing unit in the transport. */
+    /* (TODO: Handle the case of a trusted unit providing info.) */
+    if (side_owns_viewer_in_unit(side, unit))
+      return TRUE;
+    return FALSE;
+}
+
 int
 num_units_in_play(Side *side, int u)
 {
@@ -707,7 +966,11 @@ num_units_incomplete(Side *side, int u)
 
     if (side != NULL && side->ingame) {
       for_all_side_units(side, unit) {
-       if (unit->type == u && alive(unit) && !completed(unit)) ++num;
+       if (unit->type == u 
+           && in_play(unit) 
+           && !completed(unit)) {
+           ++num;
+       }
       }
     }
     return num;
@@ -1103,6 +1366,7 @@ become_designer(Side *side)
     if (!side->see_all)
       side->may_set_show_all = TRUE;
     side->show_all = TRUE;
+    place_legends(side);
     update_everything();
     /* Let everybody know of the change in status. */
     notify_all("%s IS NOW A DESIGNER.", short_side_title(side));
@@ -1125,6 +1389,7 @@ become_nondesigner(Side *side)
     /* Go back to the original viewing capabilities. */
     side->may_set_show_all = FALSE;
     side->show_all = side->see_all;
+    place_legends(side);
     update_everything();
     notify_all("%s is no longer a designer.", short_side_title(side));
     for_all_sides(side2) {
@@ -1321,10 +1586,7 @@ set_side_self_unit(Side *side, Unit *unit)
 /* Message-forwarding function. */
 
 void
-send_message(side, sidemask, str)
-Side *side;
-SideMask sidemask;
-char *str;
+send_message(Side *side, SideMask sidemask, char *str)
 {
     char *sidedesc, buf[BUFSIZE];
     SideMask testmask;
@@ -1360,9 +1622,7 @@ char *str;
    actions, but the default is just to forward to AIs and displays. */
 
 void
-receive_message(side, sender, str)
-Side *side, *sender;
-char *str;
+receive_message(Side *side, Side *sender, char *str)
 {
     /* Look for specially-recognized messages. */
     if (strcmp("%reveal", str) == 0) {
@@ -1451,43 +1711,47 @@ set_doctrine(Side *side, char *spec)
 }
 
 void
-set_side_research(Side *side, int a)
+set_side_research_topic(Side *side, int a)
 {
     side->research_topic = a;
 }
 
-/* Vision. */
+void
+set_side_research_goal(Side *side, int a)
+{
+    side->research_goal = a;
+}
 
-static UnitView tmp_unit_view;
+void
+set_side_startx(Side *side, int x)
+{
+    side->startx = x;
+}
+
+void
+set_side_starty(Side *side, int y)
+{
+    side->starty = y;
+}
+
+/* Vision. */
 
 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;
+    hash = (wrapx(x) ^ y) % VIEW_HASH_SIZE;
+    for (uv = side->unit_views[hash]; uv != NULL; uv = uv->nextinhash) {
+       if (wrapx(x) == uv->x 
+           && y == uv->y
+           && uv->transport == NULL) {
+               return uv;
+       }
     }
     return NULL;
 }
@@ -1495,86 +1759,371 @@ unit_view_at(Side *side, int x, int y)
 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;
+    /* If we are looking among the side views. */
+    if (side) {
+       for (uv2 = uview->nextinhash; uv2 != NULL; uv2 = uv2->nextinhash) {
+           if (wrapx(x) == uv2->x && y == uv2->y && uv2->transport == NULL) 
+             return uv2;
+       }
     }
+    /* If we are looking among master views. */
+    else
+      return uview->nexthere;
     return NULL;
 }
 
+/* Look for an existing unit view that is tied to the given unit. */
+
 UnitView *
-add_unit_view(Side *side, Unit *unit)
+find_unit_view(Side *side, Unit *unit)
 {
-    int i, changed = FALSE, rehash = FALSE;
-    UnitView *uv, *uview = NULL;
+    UnitView *uv, *ov;
+    int i;
 
-    /* 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 (!side) {
+       update_master_uview(unit, FALSE);
+       return unit->uview;
+    }
+    if (side->unit_views == NULL) {
+       return NULL;
+    }
+    for (i = 0; i < VIEW_HASH_SIZE; ++i) {
+       for (uv = side->unit_views[i]; uv != NULL; uv = uv->nextinhash) {
+           if (uv->id == unit->id) {
+               return uv;
+           }
+           /* Also check all the occupant views. */
+           for_all_occupant_views_with_occs(uv, ov) {
+               if (ov->id == unit->id) {
+                   return ov;
                }
            }
-           if (uview != NULL)
-             break;
        }
     }
+    return NULL;
+}
+
+/*! \todo This function will need to be modified to behave differently for 
+           clients and servers once we are about to do the client-server 
+           separation. For clients, it will actually send a network query 
+           to the server asking if we can get back a shadow unit for the 
+           unit handle stored with the uview. NULL will be returned by the 
+           function if the network response is no unit, else a pointer to 
+           the shadow unit will be returned. For the server, it will simply 
+           return a pointer to the actual unit associated with the uview.
+           For now, we default to the server behavior.
+*/
+
+Unit *
+query_unit_from_uview(UnitView *uview)
+{
+    return view_unit(uview);
+}
+
+/* Get the updated master uview for a given unit. */
+/*! \note This function (as well as a number of others) will have to be 
+         changed to take a unit handle of some sort (perhaps unit ID) when 
+         true client-server separation is done. There will also be two 
+         separate functions: one that does the client query and gets back 
+         the mashalled uview, and one that handles the query on the server 
+         side, and marshals an appropriate response back to the client. */
+/* {Client and Arbiter Function} */
+
+UnitView *
+query_uvstack_from_unit(Unit *unit)
+{
+    /* If an out-of-play unit was given, then return a NULL uview. */
+    if (!in_play(unit))
+      return NULL;
+    /* Update the master uview first. */
+    update_master_uview(unit, FALSE);
+    /* Return the master uview. */
+    return unit->uview;
+}
+
+/* Get the uvstack for a given location from an omniscient perspective. */
+/*! \note See note for 'query_uvstack_from_unit'. */
+/* {Client and Arbiter Function} */
+
+UnitView *
+query_uvstack_at(int x, int y)
+{
+    Unit *unit = NULL;
+
+    /* If x and y are out-of-area, then return a NULL uview. */
+    if (!inside_area(x, y))
+      return NULL;
+    /* Get the unit at given location, if any. */
+    unit = unit_at(x, y);
+    /* If no unit is there, then return NULL. */
+    if (!unit)
+      return NULL;
+    /* Else, get uview stack from unit. */
+    return query_uvstack_from_unit(unit);
+}
+
+/* Find out if an uview is in the correct position in a stack (cell or 
+   transport), relative to another uview. */
+
+int
+is_at_correct_uvstack_position(UnitView *uview, UnitView *uview2)
+{
+    int u = NONUTYPE, u2 = NONUTYPE;
+    int uso = -1, u2so = -1;
+
+    assert_error(uview, "Attempted to place a NULL uview in a uvstack");
+    assert_error(uview2, "Attempted to compare an uview in a NULL uvstack");
+    u = uview->type;
+    u2 = uview2->type;
+    uso = u_stack_order(u);
+    u2so = u_stack_order(u2);
+    /* Enforce side ordering 1st. */
+    /*! \todo Add support for holding units belonging to the dside first in 
+             the stack, last in the stack, or in the natural side order. 
+             Currently, only the natural side order is supported. */
+    if (uview->siden > uview2->siden)
+      return FALSE;
+    if (uview->siden < uview2->siden)
+      return TRUE;
+    /* Enforce stack ordering 2nd. */
+    if (uso < u2so)
+      return FALSE;
+    if (uso > u2so)
+      return TRUE;
+    /* Enforce utype ordering 3rd. */
+    if (u > u2)
+      return FALSE;
+    if (u < u2)
+      return TRUE;
+    /* Enforce uview ID ordering 4th. */
+    if (uview->id < uview2->id)
+      return TRUE;
+    return FALSE;
+}
+
+/* Add uview to the given transport's uvstack. */
+
+void
+add_uview_to_uvstack(UnitView *uview, UnitView *tsptview)
+{
+    UnitView *topview = NULL, *occview = NULL, *nextview = NULL, 
+            *prevview = NULL;
+
+    assert_error(uview, "Attempted to add a NULL uview to a uvstack");
+    assert_error(tsptview, 
+                "Attempted to add an uview to a NULL transport uvstack");
+    topview = tsptview->occupant;
+    if (topview) {
+       /* Insert uview into the occupant list at its correct position. */
+       for_all_occupant_views(tsptview, occview) {
+           if (is_at_correct_uvstack_position(uview, occview)) {
+               nextview = occview;
+#if (0)
+               if (uview == nextview->nexthere)
+                 nextview->nexthere = NULL;
+#endif
+               if (occview == topview) 
+                 topview = uview;
+               break;
+           }
+           prevview = occview;
+       }
+       if (prevview != NULL) {
+           prevview->nexthere = uview;
+       }
+    } else {
+       topview = uview;
+    }
+    uview->nexthere = nextview;
+    tsptview->occupant = topview;
+}
+
+/* Initialize and fill out an uview from unit data. */
+
+void
+fill_out_uview(Unit *unit, Side *side, UnitView *uview)
+{
+    /* Sanity checks. */
+    assert_error(unit, "Attempted to fill out an unit view with a NULL unit");
+    assert_error(uview, "Attempted to fill out a NULL unit view");
+    /* Fill out uview. */
+    uview->observer = (side ? side->id : -1);
+    uview->unit = unit;
+    uview->x = unit->x;
+    uview->y = unit->y;
+    uview->siden = unit->side->id;
+    uview->type = unit->type;
+    uview->size = unit->size;
+    uview->id = unit->id;
+    uview->imf = unit->imf;
+    uview->image_name = unit->image_name;
+    uview->complete = completed(unit);
+    uview->occupant = NULL;
+    uview->transport = NULL;
+    uview->nextinhash = NULL;
+    uview->nexthere = NULL;
+    uview->vnext = NULL;
+}
+
+/* Update the master uview of an unit. */
+
+void
+update_master_uview(Unit *unit, int lookabove)
+{
+    UnitView *uview = NULL;
+
+    /* Return immediately if no unit to update. */
+    if (!unit)
+      return;
+    /* If we are to look above and the unit has a transport, 
+       then update the transport's view first. Once the top of the transport 
+       chain is reached, then occs (including this unit) will be filled out, 
+       and so there is no need to duplicate work. Thus, return afterword. */
+    if (lookabove && unit->transport) {
+       update_master_uview(unit->transport, TRUE);
+       return;
+    }
+    /* Allocate the uview, if it does already exist. */
+    uview = unit->uview;
+    if (!uview) {
+       uview = (UnitView *)xmalloc(sizeof(UnitView));
+       unit->uview = uview;
+    }
+    /* Fill out the uview from unit. */
+    fill_out_uview(unit, NULL, uview);
+    /* Link with transport, if any. */
+    uview->transport = (unit->transport ? unit->transport->uview : NULL);
+    /* Recurse into and link with first occ, if any. */
+    /* This will update all of the occ's occs and next neighbors. */
+    if (unit->occupant)
+      update_master_uview(unit->occupant, FALSE);
+    uview->occupant = (unit->occupant ? unit->occupant->uview : NULL);
+    /* Recurse into and link with next neighbor, if any. */
+    /* This will update all of the neighbor's occs and next neighbors. */
+    /* The 'nextinhash' field is also updated with the value of the next 
+       neighbor so that 'unit_view_next' will not be broken. */
+    if (unit->nexthere)
+      update_master_uview(unit->nexthere, FALSE);
+    uview->nexthere = (unit->nexthere ? unit->nexthere->uview : NULL);
+    uview->nextinhash = uview->nexthere;
+}
+
+/* Add a new unit view to the side's unit views. */
+
+UnitView *
+add_unit_view(Side *side, Unit *unit)
+{
+    int changed = FALSE, rehash = FALSE, x, y;
+    UnitView *tranview = NULL, *uview = NULL, *occview = NULL;
+    Unit *occ;
+
+    /* We need to create any transport view first to avoid an infinite
+       loop between transport and occupant. */
+    if (unit->transport) {
+       tranview = find_unit_view(side, unit->transport);
+       if (tranview == NULL) {
+           /* Recursive. Will climb up transport tree if necessary. */
+           tranview = add_unit_view(side, unit->transport);
+           /* Since occupant views also get generated,
+              we can return after this. */
+           return find_unit_view(side, unit);
+       }
+    }
+    /* Now we check if our unit has a view. */
+    uview = find_unit_view(side, unit);
+    if (uview) {
+       x = uview->x;
+       y = uview->y;
+       /* Blast the view if it is in the wrong location, 
+          or if it has the wrong transport. */
+       if (x != unit->x 
+           || y != unit->y
+           || (uview->transport && !unit->transport)
+           || (!uview->transport && unit->transport)
+           || (uview->transport && unit->transport 
+               && uview->transport->id != unit->transport->id)) {
+               remove_unit_view(side, uview);
+               uview = NULL;
+               /* Update the old cell display. */
+               update_cell_display(side, x, y, UPDATE_ALWAYS);
+       }
+    }
+    /* We need a new unit view. */
     if (uview == NULL) {
-       uview = (UnitView *) xmalloc(sizeof(UnitView));
+       ++numunitviews;
+       uview = create_bare_unit_view();
+       fill_out_uview(unit, side, uview);
+#if (0)
+       uview->observer = side->id;
+       uview->unit = unit;
+       uview->type = unit->type;
+       uview->size = unit->size;
        uview->id = unit->id;
-       add_unit_view_raw(side, uview, unit->x, unit->y);
+       uview->imf = unit->imf;
+       uview->image_name = unit->image_name;
+       uview->complete = completed(unit);
+       uview->occupant = NULL;
+       uview->transport = tranview;
+       uview->nextinhash = NULL;
+       uview->nexthere = NULL;
+#endif
+       uview->transport = tranview;
+       /* Insert it into the global list. */
+       uview->vnext = viewlist;
+       viewlist = uview;
+       /* Splice uview into the hash table, but only if it is the image of a 
+          top unit. */
+       if (unit->transport == NULL) {
+           add_unit_view_raw(side, uview, unit->x, unit->y);
+       /* Else splice uview into the occupant list of its transport view. */
+       } else {
+           add_uview_to_uvstack(uview, tranview);
+       }
        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;
+    if (uview->siden != unit->side->id) {
+       uview->siden = unit->side->id;
        changed = TRUE;
     }
-    if (uview->size != unit->size) {
-       uview->size = unit->size;
+    if (uview->name != unit->name) {
+       uview->name = unit->name;
        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;
+    if (uview->imf != unit->imf) {
+       uview->imf = unit->imf;
        changed = TRUE;
     }
-    if (uview->y != unit->y) {
-       remove_unit_view(side, uview);
-       uview->y = unit->y;
-       rehash = TRUE;
+    if (uview->image_name != unit->image_name) {
+       uview->image_name = unit->image_name;
        changed = TRUE;
     }
-    if (rehash) {
-       add_unit_view_raw(side, uview, uview->x, uview->y);
+    if (uview->size != unit->size) {
+       uview->size = unit->size;
+       changed = TRUE;
     }
+    if (uview->complete != completed(unit)) {
+       uview->complete = completed(unit);
+       changed = TRUE;
+    }
+    /* Clean out any stale occupant views. */
+    for_all_occupant_views(uview, occview) {
+       remove_unit_view(side, occview);
+       changed = TRUE;
+    }
+    /* Add any occupant views that should be added. */
+    if (unit->occupant && occupants_visible(side, unit)) {
+       uview->occupant = NULL;
+       for_all_occupants(unit, occ) 
+         add_unit_view(side, occ);
+       if (uview->occupant)
+         changed = TRUE;
+    }    
     /* 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();
@@ -1591,46 +2140,253 @@ add_unit_view_raw(Side *side, UnitView *uview, int x, int y)
       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)) {
+    hash = (wrapx(x) ^ y) % VIEW_HASH_SIZE;
+    for (uv2 = side->unit_views[hash]; uv2 != NULL; uv2 = uv2->nextinhash) {
+       if (is_at_correct_uvstack_position(uview, uv2)) {
            break;
        }
        prevview = uv2;
     }
-    uview->nexthere = uv2;
+    uview->nextinhash = uv2;
     if (prevview == NULL) {
        side->unit_views[hash] = uview;
     } else {
-       prevview->nexthere = uview;
+       prevview->nextinhash = uview;
     }
-    uview->x = x;  uview->y = y;
+    uview->x = x;  
+    uview->y = y;
 }
 
 int
 remove_unit_view(Side *side, UnitView *olduview)
 {
     int hash;
-    UnitView *uv, *prevview = NULL;
+    UnitView *uv = NULL, *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;
+    prevview = uv = NULL;
+    /* Splice the unit out of any occupant list. */
+    if (olduview->transport) {
+       for_all_occupant_views(olduview->transport, uv) {
+           if (olduview == uv) {
+               if (prevview == NULL) {
+                   olduview->transport->occupant = uv->nexthere;
+               } else {
+                   prevview->nexthere = uv->nexthere;
+               }
+               break;
+           }
+           prevview = uv;
+       }
+    /* Else splice out the view from the hash table. */     
+    } else {
+       hash = (wrapx(olduview->x) ^ olduview->y) % VIEW_HASH_SIZE;
+       for (uv = side->unit_views[hash]; uv != NULL; uv = uv->nextinhash) {
+           if (uv && uv == olduview) {
+               if (prevview == NULL) {
+                   side->unit_views[hash] = uv->nextinhash;
+               } else {
+                   prevview->nextinhash = uv->nextinhash;
+               }
+               break;
            }
-           return TRUE;
+           prevview = uv;
+       }
+    }
+    /* Then clean out the occupant views. */
+    for_all_occupant_views(olduview, uv) {
+       remove_unit_view(side, uv);
+    }
+    /* Finally mark the view as garbage. But don't hit its links yet, 
+       since we might be traversing an occupant list here. */
+    olduview->id = -1;
+    return TRUE;
+}
+
+/* Get rid of all stale unit views at once. */
+
+void
+flush_stale_views(void)
+{
+    UnitView *uview, *prevview, *nextuv;
+    int n = 0;
+
+    if (viewlist == NULL)
+      return;
+    uview = viewlist;
+    while (uview->id == -1) {
+       nextuv = uview->vnext;
+       flush_one_view(uview);
+       uview = nextuv;
+       if (uview == NULL)
+         break;
+    }
+    viewlist = uview;
+    prevview = NULL;
+    for_all_unit_views(uview) {
+       if (uview->id == -1) {
+           nextuv = uview->vnext;
+           prevview->vnext = uview->vnext;
+           flush_one_view(uview);
+           uview = prevview;
+           ++n;
+       } else {
+           prevview = uview;
+       }
+    }
+    Dprintf("%d stale unit views flushed.\n", n);
+    Dprintf("%d unit views left.\n", numunitviews);
+}
+
+/* Keep it clean - hit all links to other places.  Some might not be
+   strictly necessary, but this is not an area to take chances with. */
+
+static void
+flush_one_view(UnitView *uview)
+{
+    uview->occupant = NULL;
+    uview->transport = NULL;
+    uview->nextinhash = NULL;
+    uview->vnext = NULL;
+    /* Add it on the front of the list of available views. */
+    uview->nexthere = freeviews;
+    freeviews = uview;
+    --numunitviews;
+}
+
+extern int do_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m);
+
+/* Check if one unit can perform overwatch fire upon another? */
+
+int
+can_overwatch(Unit *ounit, Unit *unit)
+{
+    int u = NONUTYPE, ou = NONUTYPE;
+    int range = -1;
+    int rslt = A_ANY_OK;
+
+    assert_error(ounit, "Attempted to access a NULL overwatcher");
+    assert_error(unit, "Attempted to access a NULL unit");
+    ou = ounit->type;
+    u = unit->type;
+    range = distance(ounit->x, ounit->y, unit->x, unit->y);
+    if (!between(u_range_min(ou), range, u_range(ou)))
+      return FALSE;
+    if (!between(uu_zoo_range_min(ou, u), range, uu_zoo_range(ou, u)))
+      return FALSE;
+    if (0 >= fire_hit_chance(ou, u))
+      return FALSE;
+    /*! \todo Handle any other probablility checks. */
+    /* Check if overwatcher can perform a valid fire. */
+    rslt = check_fire_at_action(ounit, ounit, unit, -1);
+    /* If so, then go for it. */
+    if (valid(rslt))
+      return TRUE;
+    /* If not, it may simply be because of ACP. We need to check this case. */
+    if ((A_ANY_NO_ACP == rslt) && unit->act) {
+       if (u_acp_to_fire(ou) <= (unit->act->acp + uu_acp_overwatch(ou, u)))
+         return TRUE;
+    }
+    /* Else, assume that we can't. */
+    return FALSE;
+}
+
+/* Check if any overwatchers in a particular cell. */
+/* (Search Predicate) */
+/* {Arbiter Function} */
+
+int
+try_overwatch_from(int x, int y, int *counter, ParamBox *parambox)
+{
+    Unit *unit = NULL, *unit2 = NULL;
+    int u = NONUTYPE;
+    Side *oside = NULL;
+    ParamBoxUnitSide *paramboxus = NULL;
+
+    assert_warning_return(inside_area(x, y), 
+                         "Tried to overwatch from outside playing area", 
+                         FALSE);
+    assert_error(parambox, "Attempted to use a NULL parambox");
+    assert_error(PBOX_TYPE_UNIT_SIDE == parambox->get_type(), 
+                "Attempted to use wrong type of parambox in a search");
+    paramboxus = (ParamBoxUnitSide *)parambox;
+    assert_error(paramboxus->unit, "Attempted to access a NULL unit");
+    assert_error(paramboxus->side, 
+                "Attempted to access a NULL overwatcher side");
+    unit = paramboxus->unit;
+    oside = paramboxus->side;
+    u = unit->type;
+    for_all_stack_with_occs(x, y, unit2) {
+       /* Skip any units that are not on the overwatching side. */
+       if (unit2->side != oside)
+         continue;
+       /* Can the enemy unit perform overwatch fire against the 
+          given unit? */
+       if (can_overwatch(unit2, unit)) {
+           if (unit2->act)
+             unit2->act->acp += uu_acp_overwatch(unit2->type, u);
+           do_fire_at_action(unit2, unit2, unit, -1);
+           if (counter)
+             ++(*counter);
+           /*! \todo Limit by max num of overwatch firings for victim type. */
        }
-       prevview = uv;
     }
     return FALSE;
 }
 
+/* Check if there any overwatch fire attempts against a given unit. 
+   If so, check if the fire action is valid by the overwatcher against the 
+   given unit. */
+/* {Arbiter Function} */
+
+void
+try_overwatch_against(Unit *unit)
+{
+    Side *oside = NULL;
+    UnitView *uview = NULL;
+    int u = NONUTYPE;
+    ParamBoxUnitSide paramboxus;
+    int counter = 0;
+
+    /* Sanity checks. */
+    assert_error(unit, "Attempted to overwatch a NULL unit");
+    u = unit->type;
+    /* Compute cache, if necessary. */
+    if (Xconq::any_overwatch_cache == -1)
+      compute_overwatch_cache();
+    /* Look through each side's view of the unit, if any. */
+    for_all_sides(oside) {
+       /* Skip unit's side. */
+       if (unit->side == oside)
+         continue;
+       /* If the side is not an enemy then skip its reaction. */
+       if (!enemy_side(unit->side, oside))
+         continue;
+       /* Check if the side can see the given unit. */
+       uview = find_unit_view(oside, unit);
+       /* If not, then skip side's overwatch reaction. */
+       if (!uview)
+         continue;
+       /* Setup the parambox for searching. */
+       paramboxus.unit = unit;
+       paramboxus.side = oside;
+       /* If side can see given unit, then start searching for overwatchers. */
+       if (0 >= Xconq::overwatch_against_range_min[u])
+         try_overwatch_from(unit->x, unit->y, &counter, &paramboxus);
+       /*! \todo Search in ring starting from min range. */
+       /*! \todo Limit by max num of overwatch firings for victim type. */
+       limited_search_around(unit->x, unit->y, 
+                             Xconq::overwatch_against_range_max[u], 
+                             try_overwatch_from, 1, &counter, 
+                             cover(oside, unit->x, unit->y), 
+                             (ParamBox *)&paramboxus);
+    }
+    if (counter)
+      Dprintf("%s received overwatch fire from %d units.\n", 
+             unit_desig(unit), counter);
+}
+
 /* What happens when a unit appears on a given cell. */
 
 /* An always-seen unit has builtin spies/tracers to inform everybody
@@ -1648,16 +2404,21 @@ void
 all_see_occupy(Unit *unit, int x, int y, int inopen)
 {
     Side *side;
-    int always = u_see_always(unit->type);
+    int dir, x1, y1;
+    Unit *unit2, *unit3;
+    int u = NONUTYPE;
+    int always = FALSE;
     
+    assert_error(unit, "Attempted to create views of a NULL unit");
+    u = unit->type;
+    always = u_see_always(u);
     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)) {
+       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 (cover(side, x, y) > 0) {
+           see_cell(side, x, y);
        } else {
            if (always && terrain_view(side, x, y) != UNSEEN) {
                add_cover(side, x, y, 1);
@@ -1672,9 +2433,6 @@ all_see_occupy(Unit *unit, int x, int y, int inopen)
        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) {
@@ -1695,6 +2453,13 @@ all_see_occupy(Unit *unit, int x, int y, int inopen)
                }
            }
        }
+       /* Check for any overwatch against the occupying unit. */
+       if (Xconq::any_overwatch_cache == -1)
+         compute_overwatch_cache();
+       if (Xconq::any_overwatch_cache 
+           && get_packed_bool(Xconq::any_overwatch_chances, 0, u)
+           && !Xconq::suppress_reactions)
+         try_overwatch_against(unit);
     }
 }
 
@@ -1711,14 +2476,13 @@ all_see_leave(Unit *unit, int x, int y, int inopen)
     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)
+       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 (cover(side, x, y) > 0) {
+           see_cell(side, x, y);
        } else {
            if (always && terrain_view(side, x, y) != UNSEEN) {
                see_cell(side, x, y);
@@ -1737,8 +2501,8 @@ all_see_leave(Unit *unit, int x, int y, int inopen)
                update = FALSE;
                while (domore) {
                    domore = FALSE;
-                   for_all_view_stack(side, x, y, uview) {
-                       if (side == view_side(uview)) {
+                   for_all_view_stack_with_occs(side, x, y, uview) {
+                       if (side->id == uview->siden) {
                            remove_unit_view(side, uview);
                            domore = TRUE;
                            update = TRUE;
@@ -1850,9 +2614,8 @@ cover_area(Side *side, Unit *unit, Unit *oldtransport, int x0, int y0,
     Side *side2;
 
     if (side != NULL
-       && !side->see_all
        && completed(unit)
-        && (oldtransport == NULL
+       && (oldtransport == NULL
           || uu_occ_vision(unit->type, oldtransport->type) > 0)) {
        if (side->ingame) {
            /* Active sides keep their allies informed.  Note that
@@ -1860,7 +2623,8 @@ cover_area(Side *side, Unit *unit, Unit *oldtransport, int x0, int y0,
               allies, even if the unit's side is not an ally
               directly. */
            for_all_sides(side2) {
-               if (trusted_side(side, side2)) {
+               if (trusted_side(side, side2)
+                   || side2->see_all) {
                    cover_area_1(side2, unit, x0, y0, nx, ny);
                }
            }
@@ -1888,7 +2652,7 @@ 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 y1c, y2c, cov, los, r;
     int xmin, ymin, xmax, ymax, oldcov, newcov, anychanges;
 
     if (side->coverage == NULL)
@@ -1946,7 +2710,7 @@ cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
     }
     los = FALSE;
     /* (should also adjust for effect of clouds here) */
-    if (u_vision_bend(u) != 100) {
+    if (!u_can_see_behind(u)) {
        los = TRUE;
        /* Compute the minimum elevation for visibility at each cell. */
        if (in_area(x0, y0)) {
@@ -1989,8 +2753,7 @@ cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
        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));
+               set_tmp1_at(x, y, cover(side, x, y));
            }
        }
     }
@@ -2008,20 +2771,19 @@ cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
            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;
+                       || ((tmp2_at(x, y) + tmpz0)
+                           <= (checked_elev_at(x, y)
+                               + t_thickness(terrain_at(x, y))))) {
+                       cov = tmp1_at(x, 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);
+                                   side_desig(side), wrapx(x), y);
                        }
-                       set_tmp1_at(xw, y, cov);
+                       set_tmp1_at(x, y, cov);
                    }
-                   if (los && (alt_cover(side, xw, y) == (tmp2_at(xw, y) + tmpz0)))
+                   if (los && (alt_cover(side, x, y) == (tmp2_at(x, y) + tmpz0)))
                      /* this unit set the min, should recalc alt
                          coverage now */;
                }
@@ -2042,18 +2804,17 @@ cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
            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);
+                       || ((tmp3_at(x, y) + tmpnz)
+                           <= (checked_elev_at(x, y)
+                               + t_thickness(terrain_at(x, y))))) {
+                       cov = tmp1_at(x, y) + 1;
+                       set_tmp1_at(x, y, cov);
                    }
                    if (los)
-                     set_alt_cover(side, xw, y,
-                                   min(alt_cover(side, xw, y),
-                                       (tmp3_at(xw, y) + tmpnz)));
+                     set_alt_cover(side, x, y,
+                                   min(alt_cover(side, x, y),
+                                       (tmp3_at(x, y) + tmpnz)));
                }
            }
        }
@@ -2072,31 +2833,35 @@ cover_area_1(Side *side, Unit *unit, int x0, int y0, int nx, int ny)
        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);
+               oldcov = cover(side, x, y);
+               newcov = tmp1_at(x, y);
                if (newcov != oldcov) {
-                   set_cover(side, xw, y, newcov);
+                   set_cover(side, x, 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);
+                       && see_cell(side, x, y)
+                       && !suppress_see_wakeup) {
+                               react_to_seen_unit(side, unit, x, y);
+                   }
                    if ((newcov > 0 && oldcov == 0)
                        || (newcov == 0 && oldcov > 0)
-                       || (DebugG && newcov != oldcov))
-                     update_cell_display(side, xw, y, UPDATE_COVER);
+                       || (DebugG && newcov != oldcov)) {
+                               update_cell_display(side, x, y, UPDATE_COVER);
+                   }
                    anychanges = TRUE;
                }
            }
        }
     }
     /* If we're seeing new things, make sure they're on the display. */
-    if (anychanges)
-      flush_display_buffers(side);
+    if (anychanges) {
+       flush_display_buffers(side);
+       /* Also flush the unit view list. */
+       flush_stale_views();
+    }
 }
 
 /* Use this to clear out garbled view coverage.  Note however that this
@@ -2116,17 +2881,41 @@ reset_coverage(void)
     suppress_see_cell = FALSE;
 }
 
+/* Same as above but does not suppress see-cell. Needs to be called on
+sunrise etc. when unit views should be updated. */
+
+void
+really_reset_coverage(void)
+{
+    Side *side;
+
+    if (g_see_all())
+      return;
+    for_all_sides(side)
+      calc_coverage(side);
+}
+
 /* Calculate/recalculate the view coverage layers of a side. */
 
+/* Note: this code is very inefficient in that each unit view is added
+and then removed about 50 times. Should fix this. */
+
 void
 calc_coverage(Side *side)
 {
-    int x, y, pop, visible[MAXSIDES];
+    int x, y, pop, visible[MAXSIDES + 1];
     Unit *unit;
     Side *side2;
 
     if (side->coverage == NULL)
       return;
+    /* Should figure out why this does not work. */
+    if (0/*side->see_all*/) {
+       for_all_cells(x, y) {
+               set_cover(side, x, y, 1);
+       }
+       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()) {
@@ -2150,7 +2939,8 @@ calc_coverage(Side *side)
     for_all_units(unit) {
        if (in_play(unit)) {
            x = unit->x;  y = unit->y;
-           if (trusted_side(unit->side, side)) {
+           if (trusted_side(unit->side, side)
+               || side->see_all) {
                /* Units that trust us tell us stuff. */
                cover_area(side, unit, unit->transport, -1, -1, x, y);
            } else if (u_see_always(unit->type)
@@ -2189,8 +2979,8 @@ reset_view(Side *side)
            domore = TRUE;
            while (domore) {
                domore = FALSE;
-               for_all_view_stack(side, x, y, uview) {
-                   if (side == view_side(uview)) {
+               for_all_view_stack_with_occs(side, x, y, uview) {
+                   if (side->id == uview->siden) {
                        remove_unit_view(side, uview);
                        domore = TRUE;
                        break;
@@ -2209,22 +2999,25 @@ react_to_seen_unit(Side *side, Unit *unit, int x, int y)
     UnitView *uview;
     Side *es;
 
+    /* (The g_see_all() branch appears to be unused, because 
+       'calc_coverage' is only called when g_see_all() is not true.) */
     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);
+               selectively_wake_unit(unit->side, unit, TRUE, FALSE);
            }
        }
     } else if (side->coverage != NULL) {
        uview = unit_view_at(side, x, y);
        if (uview != NULL) {
-           eu = view_type(uview);  es = view_side(uview);
+           eu = uview->type;  
+           es = side_n(uview->siden);
            /* react only to certain utypes? */
            if (unit->plan && !allied_side(es, side)) {
                /* should do a more general alarm */
-               wake_unit(unit->side, unit, TRUE);
+               selectively_wake_unit(unit->side, unit, TRUE, FALSE);
            }
        }
     } else {
@@ -2310,24 +3103,16 @@ maybe_lose_track(Unit *unit, int nx, int ny)
 
 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. */
+/*! \note 'any_see_chances' will be only be set if a see chance is not 100%. 
+         Thus, if all the see chances are 0, it will be set. The variable 
+         name is somewhat misleading. */
 
 void
 compute_see_chances(void)
 {
+    using namespace Xconq;
     int u1, u2, u3, m1;
 
     any_see_chances = FALSE;
@@ -2348,8 +3133,9 @@ compute_see_chances(void)
            }
            if (uu_see(u1, u2) != 100) {
                any_see_chances = TRUE;
-               max_see_chance_range = max(max_see_chance_range, u_vision_range(u1));
            }
+           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) {
@@ -2357,7 +3143,8 @@ compute_see_chances(void)
                        break;
                    }
                }
-               max_see_mistake_range = max(max_see_mistake_range, u_vision_range(u1));
+               max_see_mistake_range = max(max_see_mistake_range, 
+                                           u_vision_range(u1));
            }
        }
        for_all_material_types(m1) {
@@ -2372,6 +3159,46 @@ compute_see_chances(void)
     }
 }
 
+/* Compute the overwatch cache. Useful for optimizing searches for 
+   overwatchers. */
+
+void
+compute_overwatch_cache(void)
+{
+    using namespace Xconq;
+    int u1 = NONUTYPE, u2 = NONUTYPE;
+    int range = -1;
+
+    /* Allocate the overwatch-against range arrays. */
+    any_overwatch_chances = create_packed_bool_table(1, numutypes);
+    overwatch_against_range_max = (int *)xmalloc(numutypes * sizeof(int));
+    overwatch_against_range_min = (int *)xmalloc(numutypes * sizeof(int));
+    init_packed_bool_table(any_overwatch_chances);
+    memset(overwatch_against_range_max, -1, numutypes * sizeof(int));
+    memset(overwatch_against_range_min, INT_MAX, numutypes * sizeof(int));
+    /* Find out if any overwatch is being done. */
+    any_overwatch_cache = FALSE;
+    for_all_unit_types(u1) {
+       /* If an unit cannot fire, then it cannot overwatch by definition. */
+       if (0 >= u_range(u1))
+         continue;
+       /* Check if the firing unit can engage in any overwatch. */
+       for_all_unit_types(u2) {
+           if ((-1 < uu_zoo_range(u1, u2)) && (0 < fire_hit_chance(u1, u2))) {
+               /*! \todo Also consider an actual probability. */
+               any_overwatch_cache = TRUE;
+               set_packed_bool(any_overwatch_chances, 0, u2, TRUE);
+               range = min(uu_zoo_range(u1, u2), u_range(u1));
+               overwatch_against_range_max[u2] = 
+                   max(overwatch_against_range_max[u2], range);
+               range = max(uu_zoo_range_min(u1, u2), u_range_min(u1));
+               overwatch_against_range_min[u2] =
+                   min(overwatch_against_range_min[u2], range);
+           }
+       }
+    }
+}
+
 /* Update the view of this cell for everybody's benefit.  May have to write
    to many displays. */
 
@@ -2385,12 +3212,198 @@ all_see_cell(int x, int y)
     }
 }
 
-static int test_for_successful_viewer(int x, int y);
-static int test_for_possible_viewer(int x, int y);
+static void mistake_view(Side *side, Unit *seer, UnitView *uview);
 
-static Unit *tmpunittosee, *tmpseer;
+/* Check if one unit sees another, and flag whether that vision is clear 
+   or not. */
+/* {Arbiter Function} */
 
-static void mistake_view(Side *side, Unit *seer, UnitView *uview);
+int
+see_unit(Unit *seer, Unit *tosee, SeerNode *seers, int *numseers)
+{
+    int dist = -1;
+    int u = NONUTYPE, us = NONUTYPE;
+
+    assert_error(seers, "Attempted to access a NULL array in seeing code");
+    assert_error(numseers && (*numseers >= 0), 
+                "Attempted to access a bad counter in seeing code");
+    assert_warning_return(in_play(seer), 
+                         "Attempted to access an out-of-play unit", FALSE);
+    assert_warning_return(in_play(tosee), 
+                         "Attempted to access an out-of-play unit", FALSE);
+    if (seer == tosee)
+      return FALSE;
+    if (seer->side == tosee->side)
+      return FALSE;
+    u = tosee->type;
+    us = seer->type;
+    dist = distance(tosee->x, tosee->y, seer->x, seer->y);
+    if (probability(see_chance(seer, tosee))) {
+       seers[*numseers].seer = seer;
+       if ((uu_see_mistake_range_min(us, u) <= dist)
+           && uu_see_mistake(us, u)) {
+           if (xrandom(10000) < uu_see_mistake(us, u))
+             seers[*numseers].mistakes = TRUE;
+       }
+       ++(*numseers);
+       return TRUE;
+    }
+    return FALSE;
+}
+
+/* Iterate 'see_unit' over all the occs of a transport. */
+/* (Can be used as a search predicate.) */
+/* {Arbiter Function} */
+
+int
+side_occs_see_unit(int x, int y, int *numseers, ParamBox *parambox)
+{
+    ParamBoxUnitUnitSeers *paramboxuu = NULL;
+    ParamBoxUnitUnitSeers *paramboxuu2 = NULL;
+    int foundseer = FALSE;
+    Unit *seer = NULL;
+
+    assert_warning_return(in_area(x, y), "Attempted to use illegal coordinates",
+                         FALSE);
+    assert_warning_return(parambox, "Attempted to access a NULL parambox",
+                         FALSE);
+#if (0)
+    assert_warning_return(PBOX_TYPE_UNIT_UNIT_SEERS == parambox->get_type(), 
+                         "Attempted to use wrong type of parambox",
+                         FALSE);
+#endif
+    paramboxuu = (ParamBoxUnitUnitSeers *)parambox;
+    assert_warning_return(paramboxuu->unit1, 
+                         "Could not check visibility of an unit", FALSE);
+    assert_warning_return(paramboxuu->unit2, 
+                         "Attempted to access a NULL unit", FALSE);
+    assert_warning_return(numseers && (*numseers >= 0), 
+                         "Invalid parambox data encountered", FALSE);
+    assert_warning_return(paramboxuu->seers,
+                         "Attempted to access a NULL array", FALSE);
+    /* If no possible seers, then return now. */
+    if (!paramboxuu->unit2->occupant)
+      return FALSE;
+    /* Iterate over all occupants. */
+    for_all_occupants(paramboxuu->unit2, seer) {
+       /* Paranoia. Skip any out-of-play unit. */
+       if (!in_play(seer))
+         continue;
+       /* HACK: We need to pass more information into the recursive routine. 
+          The seer may not be of the same side as the side that is supposed 
+          to be seeing. For now we must assume that an occ of the correct 
+          side is not lurking in an unit of the wrong side. */
+       if (seer->side != paramboxuu->unit2->side) {
+           continue;
+       }
+       /* Descend depth-first before checking anything else. A seer of the 
+          correct side may be lurking in a transport of an incorrect side. */
+       if (seer->occupant) {
+           paramboxuu2 = 
+               (ParamBoxUnitUnitSeers *)xmalloc(sizeof(ParamBoxUnitUnitSeers));
+           paramboxuu2->unit1 = paramboxuu->unit1;
+           paramboxuu2->unit2 = seer;
+           paramboxuu2->seers = paramboxuu->seers;
+           if (side_occs_see_unit(x, y, numseers, (ParamBox *)paramboxuu2))
+             foundseer = TRUE;
+           if (paramboxuu2)
+             free(paramboxuu2);
+       }
+       /*! \todo Put the side filter back in the correct order once the 
+                 proper side info is being passed to 'side_occs_see_unit'. */
+#if (0)
+       /* Skip any units not on given side. */
+       /* (TODO: Also account for trusted sides.) */
+       if (seer->side != paramboxuu->unit2->side) {
+           continue;
+       }
+#endif
+       /* Try seeing with the current seer. */
+       if (see_unit(seer, paramboxuu->unit1, paramboxuu->seers, numseers))
+         foundseer = TRUE;
+       /* Check if we should conitnue evaluating. */
+       if (*numseers >= cover(paramboxuu->unit2->side, x, y))
+         break;
+    }
+    /* return foundseer; */
+    return FALSE; 
+}
+
+/* Iterate 'see_unit' over all the units of a stack. */
+/* (Can be used as a search predicate.) */
+/* {Arbiter Function} */
+
+int
+side_ustack_see_unit(int x, int y, int *numseers, ParamBox *parambox)
+{
+    ParamBoxUnitSideSeers *paramboxus = NULL;
+    ParamBoxUnitUnitSeers *paramboxuu = NULL;
+    int foundseer = FALSE;
+    Unit *seer = NULL;
+
+    assert_warning_return(in_area(x, y), "Attempted to use illegal coordinates",
+                         FALSE);
+    assert_warning_return(parambox, "Attempted to access a NULL parambox",
+                         FALSE);
+    assert_warning_return(PBOX_TYPE_UNIT_SIDE_SEERS == parambox->get_type(), 
+                         "Attempted to use wrong type of parambox",
+                         FALSE);
+    paramboxus = (ParamBoxUnitSideSeers *)parambox;
+    assert_warning_return(paramboxus->unit, 
+                         "Could not check visibility of an unit", FALSE);
+    assert_warning_return(paramboxus->side, 
+                         "Attempted to access a NULL side", FALSE);
+    assert_warning_return(numseers && (*numseers >= 0), 
+                         "Invalid parambox data encountered", FALSE);
+    assert_warning_return(paramboxus->seers,
+                         "Attempted to access a NULL array", FALSE);
+    /* If no possible seers, then return now. */
+    if (!unit_at(x, y))
+      return FALSE;
+    /* Iterate over all stack members. */
+    for_all_stack(x, y, seer) {
+       /* Paranoia. Skip any out-of-play units in stack. */
+       if (!in_play(seer))
+         continue;
+       /* HACK: We need to pass more information into the recursive routine. 
+          The seer may not be of the same side as the side that is supposed 
+          to be seeing. For now we must assume that an occ of the correct 
+          side is not lurking in an unit of the wrong side. */
+       if (seer->side != paramboxus->side) {
+           continue;
+       }
+       /* Descend depth-first before checking anything else. A seer of the 
+          correct side may be lurking in a transport of an incorrect side. */
+       if (seer->occupant) {
+           paramboxuu = 
+               (ParamBoxUnitUnitSeers *)xmalloc(sizeof(ParamBoxUnitUnitSeers));
+           paramboxuu->unit1 = paramboxus->unit;
+           paramboxuu->unit2 = seer;
+           paramboxuu->seers = paramboxus->seers;
+           if (side_occs_see_unit(x, y, numseers, (ParamBox *)paramboxuu))
+             foundseer = TRUE;
+           if (paramboxuu)
+             free(paramboxuu);
+       }
+       /*! \todo Put the side filter back in the correct order once the 
+                 proper side info is being passed to 'side_occs_see_unit'. */
+#if (0)
+       /* Skip any units not on given side. */
+       /* (TODO: Also account for trusted sides.) */
+       if (seer->side != paramboxus->side) {
+           continue;
+       }
+#endif
+       /* Try seeing with the current seer. */
+       if (see_unit(seer, paramboxus->unit, paramboxus->seers, numseers))
+         foundseer = TRUE;
+       /* Check if we should conitnue evaluating. */
+       if (*numseers >= cover(paramboxus->side, x, y))
+         break;
+    }
+    /* return foundseer; */
+    return FALSE;
+}
 
 /* Look at the given position, possibly not seeing anything.  Return
    true if a unit was spotted. */
@@ -2398,27 +3411,36 @@ static void mistake_view(Side *side, Unit *seer, UnitView *uview);
 int
 see_cell(Side *side, int x, int y)
 {
-    int update, updatet, chance, curview, x1, y1;
+    int update, updatet, chance, curview;
     int m, mupdate, wupdate, flags, domore, rslt, sawthis;
-    Unit *unit, *unit2;
+    Unit *unit;
     UnitView *uview, *newuview;
+    SeerNode seers [BUFSIZE];
+    int numseers = 0, i = 0, seenclearly = FALSE, cellcover = 0;
+    ParamBoxUnitSideSeers paramboxus;
 
     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) {
+    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());
+           if (!g_see_terrain_always()) {
+               set_terrain_view_date(side, x, y, g_turn());
+           }
            update = updatet = TRUE;
+           if (side->see_all) {
+               add_cover(side, x, y, 1);
+           } else {
+               for_all_stack(x, y, unit) {
+                   if (u_see_always(unit->type)) {
+                       add_cover(side, x, y, 1);
+                       break;
+                   }
+               }
+           }
        }
        if (any_material_views) {
            mupdate = see_materials(side, x, y);
@@ -2430,103 +3452,156 @@ see_cell(Side *side, int x, int y)
            if (wupdate)
              update = TRUE;
        }
-       if (any_see_chances < 0)
+       if (Xconq::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;
+               /* Conditionally flush unit views. */
+               /*! \note Improve this code to consider view flushing 
+                         granularity. */
+               unit = view_unit(uview);
+               if (!unit || (uview->x != unit->x) || (uview->y != unit->y)
+                   || (uview->transport 
+                       && (view_unit(uview->transport) != unit->transport))
+                   || (!u_see_always(uview->type) 
+                       && (uview->date < g_turn()))) {
+                   if (remove_unit_view(side, uview)) {
+                       update = TRUE;
+                   }
                    domore = TRUE;
                    break;
                }
            }
        }
+       /* Iterate over all units-to-see in a given location. */
        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)) {
+           /* If any definite sightings. */
+           if (side_sees_unit(side, unit)) {
                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 any possible sightings. */
+           /* All see chances may be perfect by default ('any_see_chances' 
+              will be FALSE), but terrain, night, etc... modifications still 
+              take place, and mistaken viewings can still occur. */
+           }
+           else {
+               numseers = 0;
+               cellcover = cover(side, x, y);
+#if (0)
+               /* Allocate array of max possible seers. */
+               if (cellcover > 0)
+                 seers = (SeerNode *)xmalloc(cellcover * sizeof(SeerNode));
+#endif
+               /* Reset the seers array. */
+               memset(seers, 0, BUFSIZE*sizeof(SeerNode));
+               /* Setup parambox to iterate through ustacks. */
+               paramboxus.unit = unit;
+               paramboxus.side = side;
+               paramboxus.seers = seers;
+               /* Look in the same cell. */
+               if (cellcover)
+                 side_ustack_see_unit(x, y, &numseers, 
+                                      (ParamBox *)(&paramboxus));
+               /* Look in other cells out to the maximum vision range. */
+               /* (NOTE: There should probably be a performance choice 
+                   here. If the max vision range is sufficiently large, then 
+                   it may be cheaper to iterate through the side units 
+                   instead of searching the area.) */
+               if ((numseers < cellcover) 
+                   && (0 < Xconq::max_see_chance_range)) {
+                   limited_search_around(x, y, Xconq::max_see_chance_range, 
+                                         side_ustack_see_unit, 1, &numseers, 
+                                         cellcover, (ParamBox *)&paramboxus);
                }
-               /* 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)) {
+               /* Let the caller know that something is seen. */
+               if (numseers)
+                 rslt = TRUE;
+               seenclearly = FALSE;
+               /* Now that we have gathered up all the seers, start 
+                  building unmistaken unit views. */
+               for (i = 0; i < numseers; ++i) {
+                   /* Skip over mistaken views. */
+                   if (seers[i].mistakes)
+                     continue;
+                   /* Construct an unit view. */
+                   /* If coordinated vision, then we build the new uview 
+                      without hesitation. */
+                   /* (NOTE: Currently, uncoordinated vision is not supported 
+                       by the Xconq uview code.) */
+                   if (1 /* (TODO: Replace with appropriate flag.) */) {
                        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);
+                       seenclearly = TRUE;
                    }
-               }
-           } 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 this is a new unit view, then a display update is 
+                      needed. */
+                   /* (NOTE: This machinery will probably need to be 
+                       changed if uncoordinated vision or advanced fog-of-war 
+                       make it into Xconq.) */
                    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);
-                   }
+                   /* If coordinated vision, then any single accurate 
+                      sighting will be sufficient, and we don't need to 
+                      build additional unit views. */
+                   if (1 /* (TODO: Replace with appropriate flag.) */)
+                     break;
+               } /* for all unmistaken views */
+               /* If no one saw anything clearly, then investigate the 
+                  possibility of mistaken views. */
+               for (i = 0; i < numseers; ++i) {
+                   /* If coordinated vision and something else already 
+                      clearly seen, then what are we doing here? Get out. */
+                   if (1 /* (TODO: Replace with appropriate flag.) */
+                       && seenclearly)
+                     break;
+                   /* If coordinated vision and we are still in this loop, 
+                      then all seers must be mistaken about what they see. */
+                   if (1 /* (TODO: Replace with appropriate flag.) */) {
+                       newuview = add_unit_view(side, unit);
+                       /* If this is a new unit view, then a display update is 
+                          needed. */
+                       /* (NOTE: This machinery will probably need to be 
+                           changed if uncoordinated vision or advanced 
+                           fog-of-war make it into Xconq.) */
+                       if (newuview)
+                         update = TRUE;
+                       /* If no advanced fog-of-war, then we pick one of the 
+                          seers at random and agree upon its view of things, 
+                          and don't bother with other possible views. */
+                       if (newuview
+                           && 1 /* (TODO: Replace with appropriate flag.) */) {
+                           /* Mistake the view. */
+                           mistake_view(side, seers[xrandom(numseers)].seer, 
+                                        newuview);
+                           break;
+                       }
+                       /* (TODO: Handle advanced fog-of-war.) */
+                   } /* Coordinated vision. */
+                   /* Skip over unmistaken views. */
+                   if (!(seers[i].mistakes))
+                     continue;
+                   /* (TODO: Handle uncoordinated vision.) */
+               } /* for all mistaken views */
+#if (0)
+               /* Deallocate array of seers if necessary. */
+               if (seers) {
+                   free(seers);
+                   seers = NULL;
                }
+#endif
            }
-       }
+       } /* for all stack units */
        /* Check if any populations in the cell see something. */
-       if (any_people_see_chances
+       /* 'side_sees_unit' will detect an unit if the people of a side 
+           always see and are present in a cell. */
+       if (!Xconq::people_always_see && Xconq::any_people_see_chances
            && people_sides_defined()
            && people_side_at(x, y) == side->id
            && any_cell_materials_defined()) {
@@ -2561,65 +3636,6 @@ see_cell(Side *side, int x, int y)
     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. */
@@ -2627,14 +3643,13 @@ test_for_possible_viewer(int x, int y)
 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);
+    assert_warning_return(seer, "Attempted to see with a NULL unit",);
+    assert_warning_return(side, "Attempted to access a NULL side",);
+    assert_warning_return(uview, "Attempted to see a NULL unit view",);
+    uview->type = mistaken_type(uview->type);
+    uview->image_name = NULL;
+    uview->imf = NULL;
+    set_unit_view_image(uview);
 }
 
 /* Given a unit type, return a similar-looking type that it might be
@@ -2668,9 +3683,9 @@ mistaken_type(int u2)
 void
 see_exact(Side *side, int x, int y)
 {
-    int oldtview, newtview, update, mupdate, wupdate;
+    int oldtview, newtview, update, mupdate, wupdate, domore;
     Unit *unit;
-    UnitView *newuview;
+    UnitView *uview;
 
     if (!in_area(x, y))
       return;
@@ -2698,9 +3713,21 @@ see_exact(Side *side, int x, int y)
        wupdate = see_weather(side, x, y);
        if (wupdate)
          update = TRUE;
+       /* Flush all unit views. */
+       domore = TRUE;
+       while (domore) {
+           domore = FALSE;
+           for_all_view_stack(side, x, y, uview) {
+               if (remove_unit_view(side, uview)) {
+                   update = TRUE;
+               }
+               domore = TRUE;
+               break;
+           }
+       }
        for_all_stack(x, y, unit) {
-           newuview = add_unit_view(side, unit);
-           if (newuview)
+           uview = add_unit_view(side, unit);
+           if (uview)
              update = TRUE;
        }
     }
@@ -2758,12 +3785,14 @@ see_weather(Side *side, int x, int y)
            update = TRUE;
        }
        curview = cloud_bottom_view(side, x, y);
-       if (curview != raw_cloud_bottom_at(x, y)) {
+       if (cloud_bottoms_defined() 
+           && (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)) {
+       if (cloud_heights_defined()
+           && (curview != raw_cloud_height_at(x, y))) {
            set_cloud_height_view(side, x, y, raw_cloud_height_at(x, y));
            update = TRUE;
        }
@@ -2810,12 +3839,26 @@ seen_border(Side *side, int x, int y, int dir)
 char *
 side_desig(Side *side)
 {
+    Unit *unit;
+
     if (sidedesigbuf == NULL)
-      sidedesigbuf = xmalloc(BUFSIZE);
+      sidedesigbuf = (char *)xmalloc(BUFSIZE);
     if (side != NULL) {
-       sprintf(sidedesigbuf, "s%d (%s)", side_number(side), side_name(side));
+       /* The side name is all capitalized in the output. Why? */
+       sprintf(sidedesigbuf, "s%d %s", side_number(side), side_name(side));
        if (side->self_unit) {
-           tprintf(sidedesigbuf, "(self is #%d)", side->self_unit->id);
+               unit = find_unit_dead_or_alive(side->self_unit->id);
+               if (in_play(unit)) {
+                       if (!mobile(unit->type) || u_advanced(unit->type)) {
+                               tprintf(sidedesigbuf, " (capital %s %s)", 
+                                               (side->ingame ? "is" : "was"),
+                                               short_unit_handle(unit));
+                       } else {
+                               tprintf(sidedesigbuf, " (leader %s %s)", 
+                                               (side->ingame ? "is" : "was"),
+                                               short_unit_handle(unit));
+                       }
+               }
        }
     } else {
        sprintf(sidedesigbuf, "nullside");
@@ -2880,9 +3923,9 @@ char *
 player_desig(Player *player)
 {
     if (playerdesigbuf == NULL)
-      playerdesigbuf = xmalloc(BUFSIZE);
+      playerdesigbuf = (char *)xmalloc(BUFSIZE);
     if (player != NULL) {
-       sprintf(playerdesigbuf, "%s,%s/%s@%s+%d",
+       snprintf(playerdesigbuf, BUFSIZE, "%s,%s/%s@%s+%d",
                (player->name ? player->name : ""),
                (player->aitypename ? player->aitypename : ""),
                (player->configname ? player->configname : ""),
@@ -2925,9 +3968,14 @@ new_doctrine(int id)
     doctrine->rearm_percent = 20;
     /* 35% is basically 1/3, rounded up. */
     doctrine->repair_percent = 35;
+    doctrine->resupply_complete = 100;
+    doctrine->rearm_complete = 100;
+    doctrine->repair_complete = 100;
+    doctrine->min_turns_food = 5;
+    doctrine->min_distance_fuel = 20;
     doctrine->construction_run = (short *) xmalloc (numutypes * sizeof(short));
     /* We just committed on the number of unit types. */
-    canaddutype = FALSE;
+    disallow_more_unit_types();
     /* Add the new doctrine to the end of the list of doctrines. */
     if (last_doctrine != NULL) {
        last_doctrine->next = doctrine;
@@ -2992,7 +4040,7 @@ new_standing_order(void)
     StandingOrder *sorder;
 
     sorder = (StandingOrder *) xmalloc(sizeof(StandingOrder));
-    sorder->types = xmalloc(numutypes);
+    sorder->types = (char *)xmalloc(numutypes);
     return sorder;
 }
 
@@ -3239,6 +4287,8 @@ get_next_arg(char *str, char *buf, char **rsltp)
     return str + (p - buf);
 }
 
+#if 0
+
 /* Agreement handling here for now. */
 
 Agreement *agreement_list = NULL;
@@ -3294,14 +4344,16 @@ char *
 agreement_desig(Agreement *ag)
 {
     if (agreement_desig_buf == NULL)
-      agreement_desig_buf = xmalloc(BUFSIZE);
+      agreement_desig_buf = (char *)xmalloc(BUFSIZE);
     sprintf(agreement_desig_buf, "<ag %s %s %s>",
-           (ag->typename ? ag->typename : "(null)"),
+           (ag->agtype ? ag->agtype : "(null)"),
            (ag->name ? ag->name : "(null)"),
            (ag->terms == lispnil ? "(no terms)" : "...terms..."));
     return agreement_desig_buf;
 }
 
+#endif
+
 /* Helper functions to init view layers from rle encoding. */
 
 void
@@ -3438,3 +4490,149 @@ paint_view_1(int x, int y)
 }
 
 #endif /* DESIGNERS */
+
+/* Find a side's total supply of a given material.  */
+
+int 
+side_material_supply(Side * side, int m)
+{
+       Unit    * unit;
+       int     supply = 0;
+
+       if (!is_material_type(m)) {
+               return -1;
+       }
+       /* Add up unit supplies. */
+       for_all_side_units(side, unit) {
+               /* This also counts units under construction, which
+               can store materials. */
+               if (in_play(unit)) {
+                       supply += unit->supply[m];
+               }
+       }
+       /* Add stuff in the side's treasury. */
+       if (side_has_treasury(side, m)) {
+               supply += side->treasury[m];
+       }
+       return supply;
+}
+
+/* Find a side's total production of a given material. */
+
+int side_material_production(Side * side, int m)
+{
+       Unit    *unit;
+       int     x, y;
+       int     production = 0;
+
+       for_all_side_units(side, unit) {
+               if(!is_active(unit)) {
+                       continue;
+               }
+               /* Only advanced units collect materials from cells. */
+               if (u_advanced(unit->type)) {
+                       for_all_cells_within_reach(unit, x, y) {
+                               if (!inside_area(x, y)) {
+                                       continue;
+                               }
+                               if (user_at(x, y) == unit->id) {
+                                       production += production_at(x, y, m);
+                               }
+                       }
+               }
+               /* Both advanced and non-advanced units may do this. */
+               production += base_production(unit, m);
+       }
+       return production;
+}
+/* Find a side's total storage capacity for a given material. */
+
+int side_material_storage(Side * side, int m)
+{
+       Unit    *unit;
+       int     storage = 0;
+
+       for_all_side_units(side, unit) {
+               /* This also counts units under construction, which
+               can store materials. */
+               if(!in_play(unit)) {
+                       continue;
+               }
+               storage += um_storage_x(unit->type, m);
+       }
+       /* Add room in the side's treasury if it exists. */
+       if (side_has_treasury(side, m)) {
+               /* Clip to VARHI to prevent numeric overflow. */
+               storage += min(g_treasury_size(), VARHI - storage);
+       }
+       return storage;
+}
+
+/* Selectively choose an advance from a weighted list accounting for what 
+   advances the side already has and could possibly research. */
+
+Obj *
+choose_side_research_goal_from_weighted_list(Obj *lis, int *totalweightp, 
+                                            Side *side)
+{
+    int n, sofar, weight, a = NONATYPE;
+    char buf[BUFSIZE];
+    Obj *rest, *head, *tail, *rslt, *adv;
+
+    assert_error(lis, "Attempted to access a NULL list");
+    assert_error(lis != lispnil, "Attempted to use a nil list");
+    assert_error(side, "Attempted to access a NULL side");
+    if (*totalweightp <= 0) {
+       for_all_list(lis, rest) {
+           head = car(rest);
+           if (symbolp(head))
+             adv = eval_symbol(head);
+           else if (consp(head))
+             adv = cadr(head);
+           else
+             adv = head;
+           if (!atypep(adv) || !is_advance_type(c_number(adv))) {
+               Dprintf(
+"Illegal list element encountered in a side research goal list.");
+               return lispnil;
+           }
+           else
+             a = c_number(adv);
+           if (has_advance(side, a) || side->research_precluded[a])
+             continue;
+           weight = ((consp(head) && numberp(car(head))) ? c_number(car(head)) 
+                                                         : 1);
+           *totalweightp += weight;
+       }
+    }
+    /* If total weight is 0, then return nil. */
+    if (*totalweightp == 0) 
+      return lispnil;
+    n = xrandom(*totalweightp);
+    sofar = 0;
+    rslt = lispnil;
+    for_all_list(lis, rest) {
+       head = car(rest);
+       if (symbolp(head))
+         adv = eval_symbol(head);
+       else if (consp(head))
+         adv = cadr(head);
+       else
+         adv = head;
+       a = c_number(adv);
+       if (has_advance(side, a) || side->research_precluded[a])
+         continue;
+       if (consp(head) && numberp(car(head))) {
+           sofar += c_number(car(head));
+           tail = cadr(head);
+       } else {
+           sofar += 1;
+           tail = head;
+       }
+       if (sofar > n) {
+           rslt = tail;
+           break;
+       }
+    }
+    return rslt;
+}