]> granicus.if.org Git - xconq/blobdiff - kernel/task.c
commits for 7.5.0 pre-release tarball
[xconq] / kernel / task.c
index b9c2e3b407fea462379d8c25c46092fd2122de06..eb4326f8549422eee9f68dad34d2c45c2b65542c 100644 (file)
@@ -1,5 +1,6 @@
 /* Unit task execution and general task functions.
    Copyright (C) 1992-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
@@ -9,6 +10,13 @@ any later version.  See the file COPYING.  */
 #include "conq.h"
 #include "kernel.h"
 
+/* (The following inclusion is only temporary if we end up proceeding
+    with the extraction of all AI code from this file.) */
+#include "aiutil.h"
+
+#include "aiunit.h"
+#include "aiunit2.h"
+
 /* This is the number of tasks to allocate initially.  More will always be
    allocated as needed, so this should be a "reasonable" value. */
 
@@ -16,8 +24,6 @@ any later version.  See the file COPYING.  */
 #define INITMAXTASKS 100
 #endif
 
-#define CLEAR_AGENDA 99
-
 enum choicestate {
     eitherway,
     leftthenright,
@@ -30,16 +36,17 @@ static int compare_directions(const void *a0, const void *a1);
 static int test_for_buildable(int x, int y);
 static int collect_test(int x, int y);
 static int extractable_test(int x, int y);
-static int repair_test(int x, int y);
-static int resupply_test(int x, int y);
 static TaskOutcome execute_task_aux(Unit *unit, Task *task);
+
+//! Perform a toolup subtask on behalf of a construct/build task.
+static TaskOutcome do_toolup_subtask(Unit *constructor, int uc);
+
 static TaskOutcome do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep);
 static void allocate_task_block(void);
-static int fire_can_damage(Unit *unit, Unit *unit2);
+#if (0)
 static Unit *repair_here(int x, int y);
-static Unit *aux_resupply_here(Unit *unit);
+#endif
 static Unit *resupply_here(int x, int y);
-static int can_auto_resupply_self(Unit *unit, int *materials, int numtypes);
 
 
 /* Declare all the task functions. */
@@ -75,6 +82,16 @@ static int tmpbuildutype;
 
 static Unit *tmpbuilder, *tmpbuildunit;
 
+/* Used by the resupply code. */
+
+int *lowm = NULL;
+
+int numlow = 0; 
+
+static int tmpx, tmpy;
+
+static int *foundm;
+
 /* Allocate an initial collection of task objects. */
 
 void
@@ -167,22 +184,52 @@ test_for_buildable(int x, int y)
    completion for a given number of units of a given type. */
 
 static TaskOutcome
-do_build_task(Unit *unit, Task *task)
-{
-    int u = unit->type, dir, tmpdir, nx, ny, tp, rslt;
-    int x = unit->x, y = unit->y, x2, y2;
-    int u2 = task->args[0], run = task->args[3];
-    Unit *unit2 = NULL;
-    Side *us = unit->side;
-
-    if (task->args[2] >= run) {
-        return TASK_IS_COMPLETE;
+do_build_task(Unit *builder, Task *task)
+{
+    int rslt = A_ANY_OK;
+    int idc = -1;
+    int uc = NONUTYPE;
+    Unit *buildee = NULL;
+    Side *side = NULL;
+
+    assert_error(is_active(builder),
+                "AI: Attempted to build with an inactive unit");
+    assert_warning_return(task, "AI: Attempted to run invalid construct task",
+                          TASK_IS_COMPLETE);
+    side = builder->side;
+    // Attempt to retrieve buildee unit from ID.
+    idc = task->args[0];
+    buildee = find_unit(idc);
+    if (!in_play(buildee))
+       return TASK_FAILED;
+    if (fullsized(buildee))
+       return TASK_IS_COMPLETE;
+    uc = buildee->type;
+    builder->creation_id = -1;
+    // If research or development is needed, then we should not be in this code.
+    if (!side_can_build(side, uc))
+        return TASK_FAILED;
+    // We must handle tooling, if necessary.
+    if (!has_enough_tooling(builder, uc))
+       return do_toolup_subtask(builder, uc);
+    // Try performing build action.
+    if (valid(rslt = can_build(builder, builder, buildee))) {
+       prep_build_action(builder, builder, buildee);
+       return TASK_PREPPED_ACTION;
+    }
+    // Wait for more materials or ACP, if necessary.
+    // TODO: Proactively accumulate materials, if possible.
+    if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
+        set_unit_reserve(builder->side, builder, TRUE, FALSE);
+        return TASK_IS_INCOMPLETE;
     }
+    return TASK_FAILED;
+#if (0) // HACKING NOTE: Old devel logic. Keep around for now.
     /* See if our technology needs improvement in order to build this
        type. */
     if (is_unit_type(u2)
        && u_tech_to_build(u2) > 0
-        && us->tech[u2] < u_tech_to_build(u2)) {
+       && us->tech[u2] < u_tech_to_build(u2)) {
         if (uu_acp_to_develop(u, u2) > 0) {
            push_develop_task(unit, u2, u_tech_to_build(u2));
            return TASK_IS_INCOMPLETE;
@@ -198,92 +245,7 @@ do_build_task(Unit *unit, Task *task)
            return TASK_FAILED;
         }
     }
-    /* See if we need to toolup to work on this type. */
-    if (is_unit_type(u2)) {
-       tp = (unit->tooling ? unit->tooling[u2] : 0);
-       if (tp < uu_tp_to_build(u, u2)) {
-           if (uu_acp_to_toolup(u, u2) > 0) {
-               if (valid(check_toolup_action(unit, unit, u2))) {
-                   prep_toolup_action(unit, unit, u2);
-                   return TASK_PREPPED_ACTION;
-               } else {
-                   /* We get three trys to toolup before giving up. */
-                   return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
-               }
-           } else {
-               /* Can't do the necessary toolup. */
-               return TASK_FAILED;
-           }
-       }
-    }
-    /* Check out the unit supposedly in progress. */
-    if (task->args[1] != 0) {
-       unit2 = find_unit(task->args[1]);
-       if (in_play(unit2) && unit2->type == u2) {
-           if (fullsized(unit2)) {
-               /* Clear the reference to the unit. */
-               task->args[1] = 0;
-               ++(task->args[2]);
-               if (task->args[2] >= run) {
-                   return TASK_IS_COMPLETE;
-               }
-           }
-       }
-    }
-    /* Find something to work on. */
-    unit2 = find_unit_to_complete(unit, task);
-    /* Try to pick up an optional location. */
-    x2 = task->args[4];  y2 = task->args[5];
-    if (!inside_area(x2, y2)) {
-       x2 = x;  y2 = y;
-    }
-    /* No incomplete unit found, so try to create one. */
-    if (unit2 == NULL) {
-       /* First try to create as occupant, but not if an explicit
-          location has been supplied. */
-       if ((x2 == x && y2 == y)
-           && valid((rslt = check_create_in_action(unit, unit, u2, unit)))) {
-           prep_create_in_action(unit, unit, u2, unit);
-           return TASK_PREPPED_ACTION;
-       } else if (rslt == A_ANY_NO_MATERIAL) {
-           /* Assume that the builder is accumulating necessary materials,
-              and just wait. */
-           set_unit_reserve(unit->side, unit, TRUE, FALSE);
-           return TASK_IS_INCOMPLETE;
-       } else if (valid((rslt = check_create_at_action(unit, unit, u2,
-                                                       x2, y2, 0)))) {
-           prep_create_at_action(unit, unit, u2, x2, y2, 0);
-           return TASK_PREPPED_ACTION;
-       } else if (rslt == A_ANY_NO_MATERIAL) {
-           /* Assume that the builder is accumulating necessary materials,
-              and just wait. */
-           set_unit_reserve(unit->side, unit, TRUE, FALSE);
-           return TASK_IS_INCOMPLETE;
-       } else {
-           /* As a fallback, try creating in an adjacent cell. */
-           /* (should try any cell within create range) */
-           for_all_directions_randomly(dir,tmpdir) {
-               if (interior_point_in_dir(x, y, dir, &nx, &ny)
-                   && valid(check_create_at_action(unit, unit, u2,
-                                                   nx, ny, 0))) {
-                   prep_create_at_action(unit, unit, u2, nx, ny, 0);
-                   return TASK_PREPPED_ACTION;
-               }
-           }
-           return TASK_FAILED;
-       }
-    } else {
-       /* Record the incomplete unit's id for use the next time around. */
-       task->args[1] = unit2->id;
-    }
-    /* We have an incomplete unit to work on, try to do a build action. */
-    if (valid(check_build_action(unit, unit, unit2))) {
-       prep_build_action(unit, unit, unit2);
-       return TASK_PREPPED_ACTION;
-    } else {
-       /* Try three times before giving up. */
-       return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
-    }
+#endif
 }
 
 Unit *
@@ -434,11 +396,28 @@ static TaskOutcome
 do_hit_position_task(Unit *unit, Task *task)
 {
     int u = unit->type, tx, ty, dist;
-
+    UnitView *uview = NULL;
+
+    /* (Temporary hack. Ask the planner to re-evaluate continuation of 
+       this task. If it wants to, then it will issue a new one. If not, 
+       then move on and do something more productive.) */
+    /* (A better solution would be to add a new arg to the task that would 
+       tell it the number of times to attempt execution before returning to 
+       the planner for guidance.) */
+    if (task->execnum > 3)
+      return TASK_IS_COMPLETE;
     /* This is to hit a given place. */
     /* (ask for a number of hits?) */
     tx = task->args[0];  ty = task->args[1];
     dist = distance(tx, ty, unit->x, unit->y);
+    /* Make sure that we are not going to hit a friendly unit. */
+    /* (What if we have unit with vision-range -1 in cell and they aren't 
+       seen by any of our own units?) */
+    for_all_view_stack_with_occs(unit->side, tx, ty, uview) {
+        if (!enemy_side(unit->side, side_n(uview->siden)))
+          return TASK_FAILED;
+    } 
+    /* Try performing a fire-into action. */
     if (valid(check_fire_into_action(unit, unit, tx, ty, 0, -1))) {
        prep_fire_into_action(unit, unit, tx, ty, 0, -1);
        return TASK_PREPPED_ACTION;
@@ -458,21 +437,27 @@ do_hit_unit_task(Unit *unit, Task *task)
     UnitView *uview;
     Side *us = unit->side;
 
+    /* Temporary hack. Ask the planner to re-evaluate continuation of 
+       this task. If it wants to, then it will issue a new one. If not, 
+       then move on and do something more productive. */
+    /* A better solution would be to add a new arg to the task that would 
+       tell it the number of times to attempt execution before returning to 
+       the planner for guidance. */
+    if (task->execnum > 3)
+      return TASK_IS_COMPLETE;
     /* This is to hit a (given type/side of) unit at a given place. */
-    /* (should add spec for number of hits to attempt?) */
-    tx = task->args[0];  ty = task->args[1];
-    tu = task->args[2];  ts = task->args[3];
+    tx = task->args[0];  
+    ty = task->args[1];
+    tu = task->args[2];  
+    ts = task->args[3];
     dist = distance(tx, ty, unit->x, unit->y);
-    if (dist <= 1 /* direct attack range */) {
-       if (can_attack(unit)) {
+    if (dist <= 1) {
+       if (can_attack_any(unit, unit)) {
            for_all_view_stack(us, tx, ty, uview) {
-               if (!trusted_side(us, view_side(uview))
-                   && (tu == NONUTYPE || tu == view_type(uview))) {
-                   /* (should check designated side to hit, if defined) */
+               if (ts == uview->siden && tu == uview->type) {
                    unit2 = view_unit(uview);
-                   if (unit2 != NULL
-                       && valid(check_attack_action(unit, unit, unit2, 100))
-                       && attack_can_damage_or_capture(unit, unit2)) {
+                   if (unit2 
+                       && valid(check_attack_action(unit, unit, unit2, 100))) {
                        prep_attack_action(unit, unit, unit2, 100);
                        return TASK_PREPPED_ACTION;
                    }
@@ -481,25 +466,19 @@ do_hit_unit_task(Unit *unit, Task *task)
            /* Maybe we can overrun, but not if our cell is known to
               be clear of enemies. */
            if (dist > 0
-               && valid(check_overrun_action(unit, unit, tx, ty, unit->z,
-                                             100))) {
+               && valid(
+                   check_overrun_action(unit, unit, tx, ty, unit->z, 100))) {
                prep_overrun_action(unit, unit, tx, ty, unit->z, 100);
                return TASK_PREPPED_ACTION;
            }
        }
        /* Might be able to fire at pointblank range. */
-       if (can_fire(unit) && dist >= u_range_min(u)) {
+       if (can_fire_at_any(unit, unit) && dist >= u_range_min(u)) {
            for_all_view_stack(us, tx, ty, uview) {
-               if (!trusted_side(us, view_side(uview))
-                   && (tu == NONUTYPE || tu == view_type(uview))
-                   /* (should check designated side to hit, if defined) */
-                   ) {
+               if (ts == uview->siden && tu == uview->type) {
                    unit2 = view_unit(uview);
-                   if (unit2 != NULL
-                       && valid(check_fire_at_action(unit, unit, unit2, -1))
-                       /* Also count with capture when firing at
-                          pointblank range. */
-                       && fire_can_damage_or_capture(unit, unit2)) {
+                   if (unit2 
+                       && valid(check_fire_at_action(unit, unit, unit2, -1))) {
                        prep_fire_at_action(unit, unit, unit2, -1);
                        return TASK_PREPPED_ACTION;
                    }
@@ -515,50 +494,11 @@ do_hit_unit_task(Unit *unit, Task *task)
     /* If we're within firing range, attempt to fire. */
     if (dist <= u_range(u)) {
        for_all_view_stack(us, tx, ty, uview) {
-           int danger = FALSE;
-
-           unit2 = view_unit(uview);
-           if (unit2 == NULL)
-             continue;
-           /* Check if we may safely move in and attempt to capture a unit. */
-
-           for_all_stack_with_occs(tx, ty, unit2) {
-               int u2 = unit2->type;
-               
-               if (trusted_side(us, unit2->side))
-                 continue;
-               if (!is_active(unit2))
-                 continue;
-               if (u_acp(u2) + u_acp_to_fire(u2) <= 0)
-                 continue;
-               if (uu_hit(u2, u) <= 0)
-                 continue;
-               if (uu_damage(u2, u) <= 0)
-                 continue;
-               /* Someone over there can still hit and damage us. */
-               danger = TRUE;
-               break;
-           }
-           if (!danger) {
-               for_all_stack(tx, ty, unit2) {                          
-                   if (!trusted_side(us, unit2->side)
-                       && (tu == NONUTYPE || unit2->type == tu)
-                       && real_capture_chance(unit, unit2) > 0) {
-                       push_move_to_task(unit, tx, ty, dist);
-                       return TASK_IS_INCOMPLETE;
-                   }
-               }
-           }
-           for_all_stack(tx, ty, unit2) {
-               if (!trusted_side(us, unit2->side)
-                   && (tu == NONUTYPE || unit2->type == tu)
-                   /* (should check designated side to hit, if defined) */
-                   ) {
-                   if (valid(check_fire_at_action(unit, unit, unit2, -1))
-                       && fire_can_damage(unit, unit2)) {
-                       prep_fire_at_action(unit, unit, unit2, -1);
-                       return TASK_PREPPED_ACTION;
-                   }
+           if (ts == uview->siden && tu == uview->type) {
+               unit2 = view_unit(uview);
+               if (unit2 && valid(check_fire_at_action(unit, unit, unit2, -1))) {
+                   prep_fire_at_action(unit, unit, unit2, -1);
+                   return TASK_PREPPED_ACTION;
                }
            }
        }
@@ -585,205 +525,6 @@ do_hit_unit_task(Unit *unit, Task *task)
     return TASK_FAILED;
 }
 
-/* Return true if the unit can actually damage the other unit. */
-
-int
-attack_can_damage_or_capture(Unit *unit, Unit *unit2)
-{
-    Unit *occ;
-
-    if (!alive(unit) || !alive(unit2))
-      return FALSE;
-    if (capture_chance(unit->type, unit2->type, unit2->side) > 0)
-      return TRUE;
-    /* (should check for right kind of ammo) */
-    if (uu_hit(unit->type, unit2->type) <= 0)
-      return FALSE;
-    /* Also take into account if we can damage any occs. Differs from
-       attack code in that only visible occs are considered. */
-    /* Return TRUE if we can damage the unit itself. */
-    if (uu_damage(unit->type, unit2->type) > 0)
-      return TRUE;
-    /* Else check if we can hit and damage any of its occs. */
-    for_all_occs_with_occs(unit2, occ) {
-       int immune = FALSE;
-       Unit *transport = occ->transport;
-
-       /* Skip occs that we can't hit or damage. */
-       if (uu_hit(unit->type, occ->type <= 0)
-           || uu_damage(unit->type, occ->type <= 0))
-         continue;
-       /* Skip occs that we cannot see (no cheating - not even for the AI!) */
-       if (!side_sees_image(unit->side, occ))
-         continue;                     
-       /* For vulnerable occs, check recursively that we also can hit
-          all transports in the chain leading down to the occ. */
-       while (transport) {
-           if (uu_hit(unit->type, transport->type) <= 0) {
-               immune = TRUE;
-               break;
-           }
-           transport = transport->transport;
-       }
-       /* If we found an occ that is not immune we are done. */
-       if (!immune)
-         return TRUE;
-    }          
-    /* We can't capture or hit anything. */
-    return FALSE;
-}
-
-/* Return true if the unit can actually damage the other unit by
-   firing at it. */
-
-int
-fire_can_damage(Unit *unit, Unit *unit2)
-{
-    Unit *occ;
-
-    if (!alive(unit) || !alive(unit2))
-      return FALSE;
-    /* (should check for right kind of ammo) */
-    if (uu_hit(unit->type, unit2->type) <= 0)
-      return FALSE;
-    /* Also take into account if we can damage any occs. Differs from fire code
-    in that only visible occs are considered. */
-    /* Return TRUE if we can damage the unit itself. */
-    if (uu_damage(unit->type, unit2->type) > 0)
-       return TRUE;
-    /* Else check if we can hit and damage any of its occs. */
-    for_all_occs_with_occs(unit2, occ) {
-
-       int     immune = FALSE;
-       Unit    *transport = occ->transport;
-
-       /* Skip occs that we can't hit or damage. */
-       if (uu_hit(unit->type, occ->type <= 0)
-           || uu_damage(unit->type, occ->type <= 0))
-               continue;
-       /* Skip occs that we cannot see (no cheating - not even for the AI!) */
-       if (!side_sees_image(unit->side, occ))
-               continue;                       
-       /* For vulnerable occs, check recursively that we also can 
-       hit all transports in the chain leading down to the occ. */
-       while (transport) {
-               if (uu_hit(unit->type, transport->type) <= 0) {
-                       immune = TRUE;
-                       break;
-               }
-               transport = transport->transport;
-       }
-       /* If we found an occ that is not immune we are done. */
-       if (!immune)
-               return TRUE;
-    }          
-    /* We can't damage anything. */
-    return FALSE;
-}
-
-int
-fire_can_damage_or_capture(Unit *unit, Unit *unit2)
-{
-    Unit *occ;
-
-    if (!alive(unit) || !alive(unit2))
-       return FALSE;
-    /* Always return TRUE if we can capture the unit. */
-    if (capture_chance(unit->type, unit2->type, unit2->side) > 0)
-       return TRUE;
-    /* If we can't hit the unit we can't hit any of its occs. */
-    if (uu_hit(unit->type, unit2->type) <= 0)
-       return FALSE;
-    /* Return TRUE if we can damage the unit itself. */
-    if (uu_damage(unit->type, unit2->type) > 0)
-      return TRUE;
-    /* Else check if we can hit and damage any of its occs. */
-    for_all_occs_with_occs(unit2, occ) {
-       int immune = FALSE;
-       Unit *transport = occ->transport;
-
-       /* Skip occs that we can't hit or damage. */
-       if (uu_hit(unit->type, occ->type <= 0)
-           || uu_damage(unit->type, occ->type <= 0))
-         continue;
-       /* Skip occs that we cannot see (no cheating - not even for the AI!) */
-       if (!side_sees_image(unit->side, occ))
-         continue;                     
-       /* For vulnerable occs, check recursively that we also can 
-       hit all transports in the chain leading down to the occ. */
-       while (transport) {
-           if (uu_hit(unit->type, transport->type) <= 0) {
-               immune = TRUE;
-               break;
-           }
-           transport = transport->transport;
-       }
-       /* If we found an occ that is not immune we are done. */
-       if (!immune)
-         return TRUE;
-    }          
-    /* We can't capture or hit anything. */
-    return FALSE;
-}
-
-/* Probability that one real unit will capture another real unit with
-   protection taken into account.  Differs from real capture code in
-   that only visible occupants are considered (no cheating!) */
-
-int
-real_capture_chance(Unit *unit, Unit *unit2)
-{
-    int chance, prot;
-    int u2 = unit2->type;
-    int u = unit->type;
-    Unit *unit3;
-
-    chance = capture_chance(unit->type, unit2->type, unit2->side);
-
-    /* Occupants can protect the unit. */
-    for_all_occupants(unit2, unit3) {
-       if (is_active(unit3)
-           && side_sees_image(unit->side, unit3)) {
-           prot = uu_protection(unit3->type, u2);
-           if (prot != 100)
-             chance = (chance * prot) / 100;
-       }
-    }
-    /* And so can its neighbours. */
-    for_all_stack(unit2->x, unit2->y, unit3) {
-       if (unit3 != unit2
-           && is_active(unit3)
-           && side_sees_image(unit->side, unit3)
-           && unit3->side == unit2->side) {
-           prot = uu_stack_protection(unit3->type, u2);
-           if (prot != 100)
-             chance = (chance * prot) / 100;
-       }
-    }
-    for_all_stack_with_occs(unit2->x, unit2->y, unit3) {
-       if (is_active(unit3)
-           && side_sees_image(unit->side, unit3)
-           /* We also extend protection to our buddies! */
-           && trusted_side(unit3->side, unit2->side)) {
-           /* This is when a unit, such as triple-A, provides unique
-              protection against a specific attacker, such as
-              bombers, to all other units in the cell. */
-           prot = uu_cellwide_protection_against(unit3->type, u);
-           if (prot != 100)
-             chance = (chance * prot) / 100;
-           /* This is when a unit (such as a garrison) specifically
-              protects a second unit, such as a fort (but not other
-              nearby units), against all forms of attack. It thus
-              works the same way as uu_protection and
-              uu_stack_protection. */
-           prot = uu_cellwide_protection_for(unit3->type, u2);     
-           if (prot != 100)
-             chance = (chance * prot) / 100;
-       }
-    }
-    return chance;
-}
-
 int
 target_visible(Unit *unit, Task *task)
 {
@@ -794,11 +535,11 @@ target_visible(Unit *unit, Task *task)
     tx = task->args[0];  ty = task->args[1];
     tu = task->args[2];  ts = task->args[3];
 
-    for_all_view_stack(us, tx, ty, uview) {
-       if (!trusted_side(us, view_side(uview))
-           && (tu == NONUTYPE || tu == view_type(uview)))
-         /* (should test task's target side also?) */
-         return TRUE;
+    for_all_view_stack_with_occs(us, tx, ty, uview) {
+       if (ts == uview->siden
+           && (tu == NONUTYPE || tu == uview->type)) {
+           return TRUE;
+       }
     }
     return FALSE;
 }
@@ -810,33 +551,118 @@ static TaskOutcome
 do_move_dir_task(Unit *unit, Task *task)
 {
     int dir, tx, ty;
-    Unit *unit2;
-
+    Unit *unit2 = NULL;
+    UnitView *uview = NULL;
+    Side *side = NULL;
+    int u = NONUTYPE;
+    int rslt = A_ANY_CANNOT_DO;
+    int curmp = 0, fullmp = 0;
+
+    side = unit->side;
+    u = unit->type;
     if ((task->args[1])-- > 0) {
        dir = task->args[0];
+       /* Is the next cell even valid? */
        if (!point_in_dir(unit->x, unit->y, dir, &tx, &ty)) {
            return TASK_FAILED;
        }
-       if (unit_at(tx, ty)) {
-           for_all_stack(tx, ty, unit2) {
-               if (unit_trusts_unit(unit, unit2) /* but a spy could enter untrusted unit... */
-                   && valid(check_enter_action(unit, unit, unit2))) {
+       /* Can we squeeze into the cell in any way, shape, or form? */
+       if (!side_thinks_it_can_put_type_at(side, u, tx, ty))
+         return TASK_FAILED;
+       /* Can the unit move into the cell? */
+       if (valid(check_move_action(unit, unit, tx, ty, 0))) {
+           /* (Probably need to set reserve here for low ACP and MP cases, 
+               like we do for the enter action below.) */
+           prep_move_action(unit, unit, tx, ty, 0);
+           return TASK_PREPPED_ACTION;
+       } 
+       /* Can the unit occupy another unit in the cell? */
+       else if (unit_view_at(side, tx, ty)) {
+           for_all_view_stack(side, tx, ty, uview) {
+               /* (A spy could enter untrusted unit...) */
+               if (!trusted_side(side, side_n(uview->siden)))
+                 continue;
+               unit2 = view_unit(uview);
+               rslt = check_enter_action(unit, unit, unit2);
+               /* If not enough ACP left this turn, then wait. */
+               if ((A_ANY_NO_ACP == rslt) 
+                   && (can_have_enough_acp(unit, 
+                                           uu_acp_to_enter(u, uview->type)))) {
+                   set_unit_reserve(side, unit, TRUE, FALSE);
+                   return TASK_IS_INCOMPLETE;
+               }
+               /* If not enough MP left this turn, then wait. */
+               else if (A_MOVE_NO_MP == rslt) {
+                   if (can_be_actor(unit)) {
+                       curmp = (unit->act->acp * unit_speed(unit, tx, ty)) 
+                               / 100 + u_free_mp(u);
+                       fullmp = ((total_acp_for_turn(unit) - u_acp_min(u)) *
+                                  unit_speed(unit, tx, ty)) / 100 + 
+                                 u_free_mp(u);
+                   }
+                   else {
+                     curmp = 0; fullmp = 0;
+                   }
+                   if (fullmp > curmp) {
+                       set_unit_reserve(side, unit, TRUE, FALSE);
+                       return TASK_IS_INCOMPLETE;
+                   }
+               }
+               /* Else, we can enter now, then do it. */
+               else if (valid(rslt)) {
                    prep_enter_action(unit, unit, unit2);
                    return TASK_PREPPED_ACTION;
                }
-           }
+           } /* for_all_view_stack */
            return TASK_FAILED;
-       } else if (valid(check_move_action(unit, unit, tx, ty, 0))) {
-           prep_move_action(unit, unit, tx, ty, 0);
-           return TASK_PREPPED_ACTION;
-       } else {
+       } 
+       else {
            return TASK_FAILED;
        }
-    } else {
+    } /* if ((task->args[1])-- > 0) */
+    /* Specified number of moves has already been executed. */
+    else {
        return TASK_IS_COMPLETE;
     }
 }
 
+#if (0)
+/* If we are sitting in the same cell or transport as a resupply source, 
+   resupply is "free" for both us and the resupply source, and the 
+   resupply source has some supplies that we could stock up on, then 
+   swipe the supplies before moving on. */
+int
+stock_up_materials_for_free(Unit *unit)
+{
+    Unit *supplier = NULL;
+    int u = NONUTYPE, u2 = NONUTYPE;
+    int m = NONMTYPE;
+
+    u = unit->type;
+    if (!has_full_amount_of_all_materials(unit)) {
+        for_all_stack(unit->x, unit->y, supplier) {
+            if (enemy_side(unit->side, supplier->side))
+              continue;
+            if (!(supplier->supply))
+              continue;
+            u2 = supplier->type;
+            for_all_material_types(m) {
+    int dir;
+                /* TODO: Finish implementing. */
+            }
+        }
+    }
+    return FALSE;
+}
+#endif
+
+static int
+could_directly_board_ferry(Unit * unit, Unit * transport)
+{
+    return (can_occupy(unit, transport) && 
+               valid(check_enter_action(unit, unit, transport)));
+}
+
 /* The move-to task is the main way for units to get from point A to
    point B.  In addition to the destination, the task has a required
    distance, so it will succeed if the unit is within that distance to
@@ -847,17 +673,78 @@ do_move_to_task(Unit *unit, Task *task)
 {
     int dist, tx, ty, check;
     Unit *unit2, *occ;
+    int canceltask = FALSE;
+    Task *tmptask = NULL;
 
     /* This task is to get to a designated location somehow. */
     tx = task->args[0];  ty = task->args[1];
     dist = distance(tx, ty, unit->x, unit->y);
     if (dist <= task->args[3]) {
+#if (0)
+       /* If unit is a mobile transport, then don't get jittery. */
+       if (unit->occupant && mobile(unit->type)) {
+           for_all_occupants(unit, occ) {
+               if (has_acp_left(occ) && unit->plan 
+                   && !unit->plan->reserve) {
+                   /* delay_unit(unit, TRUE); */
+                   return TASK_IS_INCOMPLETE;
+               }
+           }
+       }
+#endif
+       return TASK_IS_COMPLETE;
+    }
+#if (0)
+    /* Try to top off supplies if supplier around and supply transfer 
+       does not cost any ACP. */
+    if (stock_up_materials_for_free(unit))
+      return TASK_IS_INCOMPLETE;
+#endif
+    /* Abort the move-to, if it is associated with a resupply task that is 
+       no longer necessary. Some games will run supply lines while a unit is 
+       en route to resupply, and thereby negate the need to explicitly 
+       move to a resupply point and resupply. */
+    if (task->next && (TASK_RESUPPLY == task->next->type)) {
+        if (NONMTYPE == task->next->args[0]) {
+           if (has_full_amount_of_all_materials(unit))
+             canceltask = TRUE;
+       }
+        else if (has_full_amount_of_material(unit, task->next->args[0]))
+          canceltask = TRUE;
+        if (canceltask) {
+            tmptask = task->next;
+            task->next = task->next->next;
+            free_task(tmptask);
+            /* Lie, so that we can hopefully replan or retask. */
+            /* (Should have new TaskResult, TASK_CANCELLED.) */
+            return TASK_IS_COMPLETE;
+        }
+    } 
+#if (0)
+    /* Abort the move-to, if it is not associated with a resupply 
+       or entry task, and the unit is AI-controlled, 
+       and the distance to the destination 
+       is > the real operating range of the unit, 
+       or unit has less fuel than its doctrine threshold, 
+       and the unit is not in a transport. */
+    if ((!task->next 
+        || (task->next
+            && ((task->next->type != TASK_RESUPPLY) 
+                && (task->next->type != TASK_OCCUPY))))
+       && ai_controlled(unit)
+       && (((dist - task->args[3]) > real_operating_range_best(unit))
+           || past_halfway_point(unit))
+       && !unit->transport) {
+       clear_task_agenda(unit);
+       /* Lie, so that we can hopefully replan or retask. */
+       /* (Should have new TaskResult, TASK_CANCELLED.) */
        return TASK_IS_COMPLETE;
     }
+#endif
     switch (dist) {
       case 0:
-       /* We're there already, nothing more to do. */
-       return TASK_IS_COMPLETE;
+      /* We're there already, nothing more to do. */
+      return TASK_IS_COMPLETE;
       case 1:
        /* Adjacent cell, do a single move. */
        /* But first, if there are units here already, prefer to
@@ -903,7 +790,7 @@ do_move_to_task(Unit *unit, Task *task)
                try to move. */
            if (check == A_MOVE_NO_MP) {
                notify(unit->side,
-                      "%s has insufficient ACP to move this turn; waiting until next",
+                      "%s is resting until next turn.",
                       unit_handle(unit->side, unit));
                set_unit_reserve(unit->side, unit, TRUE, FALSE);
                return TASK_IS_INCOMPLETE;
@@ -940,20 +827,26 @@ do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep)
     /* If on mobile transport, let it handle things. */
     if (unit->transport != NULL
        && mobile(unit->transport->type)
-       /* and the transport is not blocked */
-       && flip_coin()) {
+       /* and the transport is not stuck */
+       && probability(95)) {
        set_unit_reserve(unit->side, unit, TRUE, FALSE);
        return TASK_IS_INCOMPLETE;
     }
-    numdirs = choose_move_dirs(unit, tx, ty, TRUE,
-                              plausible_move_dir, sort_directions, dirs);
+    numdirs = 
+       choose_move_dirs(unit, tx, ty, TRUE, plausible_move_dir, 
+                        sort_directions, dirs);
+    if (!numdirs)
+      numdirs = 
+       choose_move_dirs(unit, tx, ty, FALSE, plausible_move_dir, 
+                        sort_directions, dirs);
     for (i = 0; i < numdirs; ++i) {
        point_in_dir(unit->x, unit->y, dirs[i], &nx, &ny);
        for_all_stack(nx, ny, unit2) {
            if (can_occupy(unit, unit2)) {
                if (valid(check_enter_action(unit, unit, unit2))) {
                    prep_enter_action(unit, unit, unit2);
-                   /* We (probably) made forward progress, so reopen choice of dirs. */
+                   /* We (probably) made forward progress, 
+                       so reopen choice of dirs. */
                    *statep = eitherway;
                    return TASK_PREPPED_ACTION;
                } else {
@@ -1100,7 +993,7 @@ do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep)
        /* If we're just short on mp, wait until the next turn to try to move. */
        if (check == A_MOVE_NO_MP) {
            if (unit->side)
-             notify(unit->side, "%s has insufficient MP to move this turn; waiting until next",
+             notify(unit->side, "%s is resting until next turn.",
                                 unit_handle(unit->side, unit));
            set_unit_reserve(unit->side, unit, TRUE, FALSE);
            return TASK_IS_INCOMPLETE;
@@ -1113,10 +1006,27 @@ static TaskOutcome
 do_occupy_task(Unit *unit, Task *task)
 {
     int dist;
-    Unit *transport = find_unit(task->args[0]);
+    Unit *transport = NULL;
+    int tx = -1;
+    int ty = -1;
+
+    transport = find_unit_dead_or_alive(task->args[0]);
+    /* Transport may have left play in the interim between attempted task 
+       executions.
+    */
+    if (!transport || !in_play(transport)) {
+        if (transport) {
+          DMprintf("%s attempted to enter out-of-play transport %s.\n", 
+                    unit_desig(unit), unit_desig(transport));
+        } else {
+          DMprintf("%s attempted to enter a bogus transport with id %d.\n",
+                   unit_desig(unit), task->args[0]);
+        }
+        return TASK_FAILED;
+    }
 
-    if (!in_play(transport))
-      return TASK_FAILED;
+    tx = transport->x;
+    ty = transport->y;
     /* (should also fail if we don't know where transport is anymore) */
     if (unit->transport == transport) {
        return TASK_IS_COMPLETE;
@@ -1143,10 +1053,21 @@ do_occupy_task(Unit *unit, Task *task)
 static TaskOutcome
 do_pickup_task(Unit *unit, Task *task)
 {
-    Unit *occupant = find_unit(task->args[0]);
+    Unit *occupant = NULL;
 
-    if (!in_play(occupant))
-      return TASK_FAILED;
+    occupant = find_unit_dead_or_alive(task->args[0]);
+    /* Potential occupant may have left play in the interim between 
+       attempted task executions. */
+    if (!occupant || !in_play(occupant)) {
+        if (occupant) {
+          DMprintf("%s attempted to pickup out-of-play unit %s.\n",
+                    unit_desig(unit), unit_desig(occupant));
+        } else {
+          DMprintf("%s attempted to pickup a bogus unit with id %d.\n",
+                   unit_desig(unit), task->args[0]);
+        }
+        return TASK_FAILED;
+    }
     wake_unit(occupant->side, occupant, FALSE);
     if (occupant->transport == unit)
       return TASK_IS_COMPLETE;
@@ -1230,9 +1151,8 @@ do_collect_task(Unit *unit, Task *task)
                }
            }
        }
-       /* Compute how far out to look for a delivery point; be a little
-          optimistic. */
-       range = operating_range_best(unit->type);
+       /* Compute how far out to look for a delivery point. */
+       range = real_operating_range_best(unit);
        if (search_around(unit->x, unit->y, range, collect_test,
                          &x2, &y2, 1)) {
            /* (should find actual unit and chase it directly) */
@@ -1257,7 +1177,7 @@ do_collect_task(Unit *unit, Task *task)
     for_all_stack(x, y, unit3) {
        if (in_play(unit3)
            && unit3->side == unit->side
-           && u_acp(unit3->type) == 0) {
+           && type_max_acp(unit3->type) == 0) {
            if (valid(check_transfer_action(unit, unit3, m, 1, unit))) {
                prep_transfer_action(unit, unit3, m, 1, unit);
                return TASK_PREPPED_ACTION;
@@ -1338,120 +1258,487 @@ extractable_test(int x, int y)
     return FALSE;
 }
 
-static TaskOutcome
-do_repair_task(Unit *unit, Task *task)
-{
-    int x, y, u = unit->type, m, range = area.maxdim;
-    int ux = unit->x, uy = unit->y;
-    Unit *unit2;
+/* This function uses a global approach in that it checks if the unit
+and its destination are located in or are adjacent to the same region 
+(sea or continent). Since the region area layer is precomputed during 
+startup, this check is fast. There are certain simplistic assumptions 
+built into the code, however. First, it is assumed that resupply and
+repair points for naval units can be on land (i.e. in ports) but not vice 
+versa. Second, it is assumed that naval units always can enter such
+land-based resupply points, if they are adjacent to a liquid cell. */
+
+/* This code does not handle units on board of transports. A ground
+unit at sea will therefore fail to set a resupply or repair task since
+it cannot find a land path to a supply point. An air unit on board of a
+carrier will, however, be able to set a resupply task on land, since
+it can get there by itself. Ultimately, we should implement a way for
+occupants to set repair tasks and signal their transport where they
+need to go. In some games, occupants will leech supplies off their 
+transport, so the latter will evetually run out of supplies and set
+its own resupply task. */
+
+/* The code does not check if a path to the destination really exists
+that our unit may follow. Posible problems: enemy units and hostile
+terrain. The fact that u_ground_mobile is set for a unit does not
+mean that it may traverse all possible ground terrain. Ultimately,
+the code should test for paths as well. */
 
-    for_all_material_types(m) {
-       if (um_consumption_per_move(u, m) > 0) {
-           range = min(range, unit->supply[m] / um_consumption_per_move(u, m));
+int
+direct_access_to(int x, int y)
+{
+       int x1, y1, x2, y2;  
+       int u = tmpunit->type;
+       int ux = tmpunit->x;
+       int uy = tmpunit->y;
+       
+       /* Airborne units always have access everywhere. */
+       if (u_air_mobile(u)) {
+               return TRUE;
        }
-    }
-    tmpunit = unit;
-    /* (should use doctrine to decide when repairs sufficient) */
-    if (unit->hp >= (u_hp(u) * 80) / 100) {  /* what if unit is multi-part? */
-       return TASK_IS_COMPLETE;
-    } else if (u_hp_recovery(u) > 0) {
-       if (0 /* too close to enemies */) {
-           /* (should move away from danger) */
-       } else {
-           /* Just hang out and wait to get better. */
-           set_unit_reserve(unit->side, unit, TRUE, FALSE);
+       /* Landborne units have access if both cells belong
+       to the same non-liquid region. */
+       if (u_ground_mobile(u)) {
+               if (!t_liquid(terrain_at(ux, uy))
+                   && !t_liquid(terrain_at(x, y))) {
+                       if (aref(area.landsea_regions, ux, uy) 
+                               == aref(area.landsea_regions, x, y)) {
+                               return TRUE;
+                       }       
+               }
        }
-       return TASK_IS_INCOMPLETE;
-    } else if (unit->transport != NULL /* and transport repairs */) {
-       set_unit_reserve(unit->side, unit, TRUE, FALSE);
-       return TASK_IS_INCOMPLETE;
-    } else if ((unit2 = repair_here(ux, uy)) != NULL
-              && unit2 != unit->transport) {   
-       prep_enter_action(unit, unit, unit2);
-       return TASK_PREPPED_ACTION;
-    } else if (search_around(ux, uy, range, repair_test, &x, &y, 1)) {
-       /* (should collect actual unit and chase it directly) */
-       push_move_to_task(unit, x, y, 0);
+       /* Seaborne units have access if both cells are adjacent
+       to the same liquid region. */
+       if (u_naval_mobile(u)) {
+               /* We are starting from a liquid cell. */
+               if (t_liquid(terrain_at(ux, uy))) {
+                       /* Test if the destination belongs to the same region. */
+                       if (aref(area.landsea_regions, ux, uy) 
+                               == aref(area.landsea_regions, x, y)) {
+                               return TRUE;
+                       }
+                       /* Next test cells adjacent to the destination. */
+                       for_all_cells_within_range(x, y, 1, x2, y2) {
+                               if (aref(area.landsea_regions, ux, uy) 
+                                       == aref(area.landsea_regions, x2, y2)) {
+                                       return TRUE;
+                               }
+                       }
+               /* We are starting from a land cell. */
+               } else {
+                       /* Scan adjacent liquid cells. */
+                       for_all_cells_within_range(ux, uy, 1, x1, y1) {
+                               if (t_liquid(terrain_at(x1, y1))) {
+                                       /* Test if the destination belongs to the same region. */
+                                       if (aref(area.landsea_regions, x1, y1) 
+                                               == aref(area.landsea_regions, x, y)) {
+                                               return TRUE;
+                                       }
+                                       /* Next test cells adjacent to the destination. */
+                                       for_all_cells_within_range(x, y, 1, x2, y2) {
+                                               if (aref(area.landsea_regions, x1, y1) 
+                                                       == aref(area.landsea_regions, x2, y2)) {
+                                                       return TRUE;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }       
+       return FALSE;
+}
+
+static TaskOutcome
+do_toolup_subtask(Unit *constructor, int uc)
+{
+    int rslt = A_ANY_OK;
+
+    assert_error(is_active(constructor), 
+                "AI: Attempted to toolup an inactive unit");
+    assert_error(is_unit_type(uc),
+                "AI: Encountered and invalid unit type to toolup for");
+    // If by some strange coincidence we are inside this function, 
+    // and constructor already has enough tooling, 
+    // then get out.
+    if (has_enough_tooling(constructor, uc))
        return TASK_IS_INCOMPLETE;
-    } else {
-       /* (should be able to signal interface usefully somehow) */
+    // If we cannot perform the toolup action, then the task must fail.
+    if (!valid(rslt = 
+              can_toolup_for(constructor, constructor, uc))) {
+       // TODO: Should notify side.
        return TASK_FAILED;
     }
+    // Try to prep a toolup action.
+    if (prep_toolup_action(constructor, constructor, uc))
+       return TASK_PREPPED_ACTION;
+    return TASK_FAILED;
 }
 
-Unit *
-repair_here(int x, int y)
-{
-    Unit *unit;
-
-    for_all_stack(x, y, unit) {
-       if (unit != tmpunit
-           && unit_trusts_unit(tmpunit, unit)
-           && (uu_auto_repair(unit->type, tmpunit->type) > 0
-               || 0 /* (should allow for explicit repair actions) */)) {
-           return unit;
+static TaskOutcome
+do_construct_task(Unit *constructor, Task *task)
+{
+    int rslt = A_ANY_OK;
+    int uc = NONUTYPE;
+    int run = 0, x = -1, y = -1;
+    int transid = -1;
+    Unit *creation = NULL, *transport = NULL;
+    Side *side = NULL;
+    Task *tasks = NULL;
+
+    assert_error(is_active(constructor), 
+                "AI: Attempted to construct with an inactive unit");
+    assert_warning_return(task, "AI: Attempted to run invalid construct task",
+                         TASK_IS_COMPLETE);
+    side = constructor->side;
+    // Unpack task args.
+    uc = task->args[0];
+    run = task->args[1];
+    // If run length <= 0, then we're done.
+    if (0 >= run)
+       return TASK_IS_COMPLETE;
+    transid = task->args[2];
+    // If no transport or out-of-play transport, then try getting coords.
+    transport = find_unit(transid);
+    if (!in_play(transport)) {
+       x = task->args[3];  y = task->args[4];
+       // If coords are also invalid, then task fails.
+       if (!inside_area(x, y))
+           return TASK_FAILED;
+    }
+    // If constructor has its creation ID set, 
+    // then see if it matches an incomplete unit.
+    //  If so, then decrement the construction run length,
+    //  clear the creation ID, and push a build task, if necessary.
+    creation = find_unit(constructor->creation_id);
+    if (in_play(creation)) {
+       constructor->creation_id = -1;
+       --task->args[1];
+       // Push or set build task, if creation is incomplete.
+       // Push, if we have more construction planned.
+       // Set, if this is last of construction run.
+       if (task->args[1])
+           push_build_task(
+               constructor, creation->id, u_cp(creation->type));
+       else
+           set_build_task(
+               constructor, creation->id, u_cp(creation->type));
+       tasks = (constructor->plan ? constructor->plan->tasks : NULL);
+       if (!tasks || (TASK_BUILD != tasks->type))
+           return TASK_FAILED;
+       tasks->args[1] = creation->id;
+       return TASK_IS_INCOMPLETE;
+    }
+    // Else, we try to proceed with creating an unit 
+    // in the desired cell or transport.
+    // If research or development is needed, then we should not be in this code.
+    if (!side_can_build(side, uc))
+       return TASK_FAILED;
+    // We must handle tooling, if necessary.
+    if (!has_enough_tooling(constructor, uc))
+       return do_toolup_subtask(constructor, uc);
+    // If transport is valid, then try creating in it.
+    if (in_play(transport)) {
+       if (valid(rslt = 
+                 can_create_in(constructor, constructor, uc, transport))) {
+           prep_create_in_action(constructor, constructor, uc, transport);
+           return TASK_PREPPED_ACTION;
        }
-       /* should look at occupants in stack too */
     }
-    return NULL;
+    // Else, try creating in designated cell.
+    else {
+       if (valid(rslt = 
+                 can_create_at(constructor, constructor, uc, x, y))) {
+           prep_create_at_action(constructor, constructor, uc, x, y, 0);
+           return TASK_PREPPED_ACTION;
+       }
+    }
+    // Wait for more materials or ACP, if necessary.
+    // TODO: Proactively accumulate materials, if possible.
+    if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
+       set_unit_reserve(constructor->side, constructor, TRUE, FALSE);
+       return TASK_IS_INCOMPLETE;
+    }
+    return TASK_FAILED;
 }
 
-static int
-repair_test(int x, int y)
-{
-    return (repair_here(x, y) != NULL);
+static TaskOutcome
+do_repair_self_subtask(Unit *repairee, Task *task)
+{
+    Unit *unit2 = NULL, *unitbest = NULL;
+    Side *side = NULL;
+    Task *tasks2 = NULL;
+    int u = NONUTYPE, u2 = NONUTYPE;
+    int tx = -1, ty = -1, dist = -1;
+    int score = -1, scorebest = -1;
+    
+    assert_error(is_active(repairee), 
+                "AI: Attempted to repair with an inactive unit");
+    assert_warning_return(task, "AI: Attempted to run invalid repair task",
+                         TASK_IS_COMPLETE);
+    // Useful info.
+    side = repairee->side;
+    u = repairee->type;
+    tx = repairee->x;  ty = repairee->y;
+    // Try hp-recovery mechanism.
+    if (0 < u_hp_recovery(u)) {
+       set_unit_reserve(side, repairee, TRUE, FALSE);
+       return TASK_IS_INCOMPLETE;
+    }
+    // Check if anyone else is tasked with repairing us.
+    for_all_side_units(side, unit2) {
+       if (unit2 == repairee)
+           continue;
+       // Skip inactive units.
+       if (!is_active(unit2))
+           continue;
+       // Skip taskless units.
+       tasks2 = (unit2->plan ? unit2->plan->tasks : NULL);
+       if (!tasks2)
+           continue;
+       // Useful info.
+       u2 = unit2->type;
+       // If 'move-to' task, then inspect closer.
+       if ((TASK_MOVE_TO == tasks2->type)
+           && (uu_auto_repair_range(u2, u)
+               >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
+           tasks2 = tasks2->next;
+       if ((TASK_MOVE_TO == tasks2->type)
+           && (uu_repair_range(u2, u)
+               >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
+           tasks2 = tasks2->next;
+       // If 'occupy' task, then inspect closer.
+       if (TASK_OCCUPY == tasks2->type)
+           tasks2 = tasks2->next;
+       // If not 'repair' task, then skip.
+       if (TASK_REPAIR != tasks2->type)
+           continue;
+       // Skip, if repairee is not the object of the repair.
+       if (repairee->id != tasks2->args[0])
+           continue;
+       // If we've made it this far, then we know enough.
+       // Now we just sit back and let the other unit do the work.
+       set_unit_reserve(side, repairee, TRUE, FALSE);
+       return TASK_IS_INCOMPLETE;
+    } // for all side units
+    // If not auto-repair, then we report that the task failed.
+    // TODO: Find an unit to explicitly repair us, and schedule the repair.
+    if (!Xconq::any_auto_repair)
+       return TASK_FAILED;
+    // Try to find someone to auto-repair us.
+    for_all_side_units(side, unit2) {
+       if (unit2 == repairee)
+           continue;
+       // Skip inactive units.
+       if (!is_active(unit2))
+           continue;
+       // Probably not worth chasing mobile units. Let them come to us.
+       if (mobile(unit2->type))
+           continue;
+       // Useful info.
+       u2 = unit2->type;
+       dist = distance(unit2->x, unit2->y, tx, ty);
+       // Subtract auto-repair range from distance.
+       // Note: For repairs that must occur inside a transport, 
+       //  a penalty of 1 is effectively added.
+       dist = max(0, dist - uu_auto_repair_range(u2, u));
+       // If repairee is immobile,
+       //   and potential repairer is out of range, then skip it.
+       if (!mobile(u) && (0 < dist))
+           continue;
+       // Score potential repairer.
+       score = hp_per_turn_est(unit2, u) / isqrt(dist + 1);
+       // Remember unit if it is best potential repairer.
+       if (score > scorebest) {
+           scorebest = score;
+           unitbest = unit2;
+       }
+    } // for all side units
+    if (0 < scorebest) {
+       u2 = unitbest->type;
+       // Calculate distance to be within repair range of best 
+       //  potential repairer.
+       dist = distance(unitbest->x, unitbest->y, repairee->x, repairee->y);
+       dist = max(0, dist - uu_auto_repair_range(u2, u));
+       // If distance to repairer > 0, then move there.
+       // Note: A potential repairer with distance > 0 should never 
+       //  have been selected, so this is not a concern.
+       if (dist > 0) {
+           // Move within range of potential repairer.
+           if (0 <= uu_auto_repair_range(u2, u))
+               // Move within auto-repair range of potential repairer.
+               push_move_to_task(
+                   repairee, unitbest->x, unitbest->y, 
+                   uu_auto_repair_range(u2, u));
+           else {
+               // Prepare to occupy potential repairer.
+               // TODO: Technically, we need to search not only 
+               //  the potential repairer but all occs under it to 
+               //  find an opening that we can occupy.
+               push_occupy_task(repairee, unitbest);
+               // Move-to potential repairer before occupation of it.
+               // NOTE: Move-to may not be necessary in all cases,
+               //  but it cannot hurt.
+               push_move_to_task(repairee, unitbest->x, unitbest->y, 1);
+           }
+       }
+       // Else, wait around.
+       else 
+           set_unit_reserve(side, repairee, TRUE, FALSE);
+       // Return the task as being incomplete.
+       // If tasks were pushed onto the stack they will execute next.
+       return TASK_IS_INCOMPLETE;
+    } // 0 < scorebest
+    return TASK_FAILED;
 }
 
-static int *lowm = NULL, numlow; 
-
-/* Test the given unit to see if it or one of its occupants can help
-   the unit looking for supplies. */
+static TaskOutcome
+do_repair_task(Unit *repairer, Task *task)
+{
+    TaskOutcome rslt = TASK_IS_INCOMPLETE;
+    Unit *repairee = NULL;
+    Side *side = NULL;
+    int id = -1;
+    int u = NONUTYPE, u2 = NONUTYPE;
+    int hpgoal = -1;
+
+    assert_error(is_active(repairer), 
+                "AI: Attempted to repair with an inactive unit");
+    assert_warning_return(task, "AI: Attempted to run invalid repair task",
+                         TASK_IS_COMPLETE);
+    // Get repairee.
+    id = task->args[0];
+    repairee = find_unit(id);
+    // Is repairee in play?
+    if (!in_play(repairee))
+       return TASK_FAILED;
+    // Useful info.
+    u = repairer->type;
+    u2 = repairee->type;
+    side = repairer->side;
+    hpgoal = task->args[1];
+    // If repair is finished, then indicate that the task is complete.
+    if (repairee->hp >= hpgoal)
+       return TASK_IS_COMPLETE;
+    // Set up explicit repair action, if possible.
+    if (valid(can_repair(repairer, repairer, repairee))) {
+       prep_repair_action(repairer, repairer, repairee);
+       return TASK_PREPPED_ACTION;
+    }
+    // Wait around for auto-repair, if possible.
+    if (valid(can_auto_repair(repairer, repairee))) {
+       // If repairer is repairee, then try to get repaired various ways.
+       if ((repairer == repairee) 
+           && (TASK_FAILED != (rslt = do_repair_self_subtask(repairee, task))))
+           return rslt;
+       else {
+           set_unit_reserve(side, repairer, TRUE, FALSE);
+           return TASK_IS_INCOMPLETE;
+       }
+    }
+    // None of the above worked. Something is wrong.
+    Dprintf("%s repair task failed!\n", unit_desig(repairer));
+    return TASK_FAILED;
+}
 
-Unit *
-aux_resupply_here(Unit *unit)
+#if (0)
+int
+can_repair_from_here(int x, int y)
 {
-    int i, enough = TRUE;
-    Unit *occ;
+       int x1, y1, u = tmpunit->type, u2, range = 0, space = FALSE;
+       Unit *unit;
 
-    if (unit_trusts_unit(unit, tmpunit)
-       && can_carry(unit, tmpunit)) {
-       for (i = 0; i < numlow; ++i) {
-           if (unit->supply[lowm[i]] == 0)
-             enough = FALSE;
-       }
-       /* this should be controlled by doctrine? */
-       /* shouldn't wake up, should get a new task to "wait up"
-          or even approach if possible */
-       /*    wake_unit(unit->side, unit, FALSE);  */
-       if (enough)
-         return unit;
-    }
-    for_all_occupants(unit, occ) {
-       if (aux_resupply_here(occ)) {
-           return occ;
+       /* First test if auto-repair of this unit type is at all possible 
+           (should never get this far if not, but check anyway). */
+       if (!will_be_auto_repaired
+           || !will_be_auto_repaired[u]) {
+           return FALSE;    
        }
-    }
-    return NULL;
+       /* Then test if we can get there. */
+       if (!direct_access_to(x, y)) {
+           return FALSE;
+       }
+       /* Also check if we can sit there. */
+       if (!type_survives_in_cell(u, x, y)
+           || !type_can_occupy_cell(u, x, y)) {
+           /* If not, check if we can sit inside a unit at (x, y). */
+           for_all_stack_with_occs(x, y, unit) {
+               /* We are testing for empty type on the optimistic assumption
+                   that there will be some space when we get there. */
+               if (type_can_occupy_empty_type(u, unit->type)) {
+                   space = TRUE;
+                   break;
+               }
+           }
+           if (!space) {
+                   return FALSE;
+           }
+       }
+       tmpx = x;
+       tmpy = y;
+       /* First check this cell. */
+       if (repair_here(x, y)) {
+           return TRUE;
+       }
+       /* Compute how far out we need to search. */
+       for_all_unit_types(u2) {
+           range = max(range, uu_auto_repair_range(u2, u));
+       }
+       /* Search adjacent cells. */
+       if (search_around(
+               x, y, range, (int (*)(int, int)) repair_here, &x1, &y1, 1)) {
+           return TRUE;
+       }               
+       return FALSE;
 }
 
 Unit *
-resupply_here(int x, int y)
+repair_here(int x, int y)
 {
-    Unit *unit, *resupplier;
+    int u = tmpunit->type, range = 0;
+    Unit *unit2;
 
-    for_all_stack(x, y, unit) {
-       resupplier = aux_resupply_here(unit);
-       if (resupplier)
-         return resupplier;
+    for_all_stack_with_occs(x, y, unit2) {
+       /* Skip over inactive units. */
+       if (!is_active(unit2)) {
+               continue;
+       }
+       /* Skip over untrusting units. */
+       if (!unit_trusts_unit(unit2, tmpunit)) {
+               continue;
+       }
+       /* Skip over units that cannot repair us. */
+       if (uu_auto_repair(unit2->type, u) <= 0) {
+               continue;
+       }
+       range = uu_auto_repair_range(unit2->type, u);
+       /* Test if unit2 can repair unit and is within auto repair range of 
+           (x, y). Force the auto repair range to 0 if it is set to -1 to 
+           signal that only occupants should be repaired. */
+       if (distance(x, y, tmpx, tmpy) <= max(range, 0)) {
+           /* Check that tmpunit can occupy unit2 if this is required. 
+               Note: we optimistically test for room in empty transport 
+               since things may change once we get there. 
+               Should eventually add code that kicks out other occs to 
+               make room for damaged units that need repair. */
+           if (range < 0
+               && !type_can_occupy_empty_type(u, unit2->type)) {
+                   continue;
+           }
+           /* If the auto-repair range is zero and we cannot enter either
+               the cell or unit2, we also have a problem. */
+           if (range == 0
+               && (!type_survives_in_cell(u, x, y)
+                   || !type_can_occupy_cell(u, x, y))
+               && !type_can_occupy_empty_type(u, unit2->type)) {
+                   continue;
+           }
+           /* We assume that the calling code already has checked that 
+               tmpunit can sit at (tmpx, tmpy). */
+           return unit2;
+       }
     }
     return NULL;
 }
-
-static int
-resupply_test(int x, int y)
-{
-    return (resupply_here(x, y) != NULL);
-}
+#endif
 
 /* Replenish our supplies, using one of several strategies, which as
    usual depends on the game, unit, terrain, etc.  Strategies include
@@ -1467,10 +1754,14 @@ do_resupply_task(Unit *unit, Task *task)
     int x, y, u = unit->type, m, range;
     int ux = unit->x, uy = unit->y;
     Unit *unit2;
+#if (0)
+    int amtavail = 0, amtwanted = 0;
+#endif
+    int i = 0;
 
-    /* A unit acting randomly might have gotten this task, even if the game
-       has no materials or the unit no supply.  Pretend that it was OK and
-       get out of here. */
+    /* A unit acting randomly might have gotten this task, even if the 
+    game has no materials or the unit no supply. Pretend that it was OK 
+    and get out of here. */
     if (nummtypes == 0 || unit->supply == NULL)
       return TASK_IS_COMPLETE;
     tmpside = unit->side;
@@ -1490,41 +1781,147 @@ do_resupply_task(Unit *unit, Task *task)
            lowm[numlow++] = m;
        }
     }
-    /* We're all full up, must be OK. */
+    /* We're all filled up, must be OK. */
     if (numlow == 0) {
        return TASK_IS_COMPLETE;
+    /* Set up an explicit extraction action if possible. We will still 
+       benefit from any auto-resupply that is available.*/
+    } else if (valid(check_extract_action(unit, unit, 
+                                         unit->x, unit->y, m, 1))) {
+       prep_extract_action(unit, unit, unit->x, unit->y, m, 1);
+       return TASK_PREPPED_ACTION;
+    /* Should not just sit around when there may be more expedient ways to 
+       get supplies that could let us get back into action the same turn. */
     } else if (can_auto_resupply_self(unit, lowm, numlow)) {
        set_unit_reserve(unit->side, unit, TRUE, FALSE);
-       return (probability(10) ? TASK_FAILED : TASK_IS_INCOMPLETE);
-    } else if (unit->transport != NULL) {
-       /* (should fix this test, transport is not necessarily of any help) */
-       /* (could attempt to resupply via direct action) */
+       return TASK_IS_INCOMPLETE;
+    /* Check if we are already at a valid resupply point. */
+    } else if (can_resupply_from_here(ux, uy)) {
+       /* Try to enter any provider in the same cell. Note: this
+           is to ensure that aircraft lands on a carrier. If we already
+           have a transport, we stay put. */
+       if (!unit->transport) {
+           for (i = 0; i < numlow; ++i) {
+               tmpmtype = lowm[i];
+               tmpx = ux;
+               tmpy = uy;
+               unit2 = resupply_here(ux, uy);
+               if (unit2
+                   && can_occupy(unit, unit2)) {
+                   prep_enter_action(unit, unit, unit2);
+                   return TASK_PREPPED_ACTION;
+               }
+           }
+       }
+       /* Otherwise, just wait for the economy code to to its job. */
        set_unit_reserve(unit->side, unit, TRUE, FALSE);
-       return (probability(10) ? TASK_FAILED : TASK_IS_INCOMPLETE);
-    } else if ((unit2 = resupply_here(ux, uy)) != NULL
-       && unit2 != unit->transport) {  
-       prep_enter_action(unit, unit, unit2);
-       return TASK_PREPPED_ACTION;
+       return TASK_IS_INCOMPLETE;
     } else {
-       /* Compute how far out to look for a resupply point; be a little
-          optimistic. */
-       range = operating_range_best(u);
-       /* (should reduce range if materials are really low) */
-       if (search_around(ux, uy, range, resupply_test, &x, &y, 1)) {
-           /* (should collect actual unit and chase it directly) */
-           /* (should only need to get within outlength of needed supplies) */
+       /* Compute how far out to look for a resupply point. */
+       range = real_operating_range_best(unit);
+       if (search_around(ux, uy, range, can_resupply_from_here, &x, &y, 1)) {
            push_move_to_task(unit, x, y, 0);
+           DMprintf("Resupply task: %s is moving to (%d, %d).", 
+                    unit_desig(unit), x, y);
            return TASK_IS_INCOMPLETE;
        } else {
-           /* Failure - sometimes just sit, but usually try something else. */
-           if (probability(10))
-             set_unit_reserve(unit->side, unit, TRUE, FALSE);
-           /* (should be able to signal interface usefully somehow) */
+           /* None of the above worked. Something is wrong. */
+           Dprintf("%s resupply task failed!\n", unit_desig(unit));
            return TASK_FAILED;
        }
     }
 }
 
+int
+can_resupply_from_here(int x, int y)
+{
+    int i, x1, y1, u = tmpunit->type, space = FALSE;
+    Unit *unit;
+
+    /* First test if we can get there. */
+    if (!direct_access_to(x, y)) {
+       return FALSE;
+    }
+    /* Also check if we can sit there. */
+    if (!type_survives_in_cell(u, x, y)
+       || !type_can_occupy_cell(u, x, y)) {
+       /* If not, check if we can sit inside a unit at (x, y). */
+       for_all_stack_with_occs(x, y, unit) {
+           /* We are testing for empty type on the optimistic assumption
+               that there will be some space when we get there. */
+           if (type_can_occupy_empty_type(u, unit->type)) {
+               space = TRUE;
+               break;
+           }
+       }
+       if (!space) {
+           return FALSE;
+       }
+    }
+    if (foundm == NULL) {
+       foundm = (int *) xmalloc(nummtypes * sizeof(int));
+    }
+    /* Search adjacent cells within our inlength for needed supplies. */
+    for (i = 0; i < numlow; ++i) {
+       foundm[i] = FALSE;
+       tmpmtype = lowm[i];
+       tmpx = x;
+       tmpy = y;
+       /* First search this cell. */
+       if (resupply_here(x, y)) {
+           foundm[i] = TRUE;
+           continue;           
+       }
+       if (search_around(x, y, um_inlength(u, lowm[i]), 
+           (int (*)(int, int)) resupply_here, &x1, &y1, 1)){
+           foundm[i] = TRUE;
+           continue;
+       }               
+    }
+    /* If we cannot resupply a needed material from here, we are done. */
+    for (i = 0; i < numlow; ++i) {
+       if (foundm[i] == FALSE) {
+           return FALSE; 
+       }
+    }
+    return TRUE;
+}
+
+Unit *
+resupply_here(int x, int y)
+{
+    int u, tu = tmpunit->type, range = 0;
+    Unit *unit;
+
+    for_all_stack_with_occs(x, y, unit) {
+       u = unit->type;
+       /* Can't resupply from ourselves. */
+       if (unit == tmpunit) 
+         continue;
+       /* Can't resupply from enemies. */
+       if (!unit_trusts_unit(unit, tmpunit)) 
+         continue;
+       /* The unit lacks our needed supply. */
+       if (unit->supply[tmpmtype] == 0) 
+         continue;
+       /* Compute the maximal possible supply range. */   
+       range = min(um_outlength(u, tmpmtype), um_inlength(tu, tmpmtype));
+       /* Resupply is impossible. */
+       if (range < 0)
+         continue;
+       /* We are out of range. */
+       if (range < distance(unit->x, unit->y, tmpx, tmpy)) 
+         continue;
+       /* Range zero and there is no room for us. We assume 
+           that it is necessary to enter the provider. */
+       if (range == 0 && !can_occupy(tmpunit, unit)) 
+         continue;
+       /* If range > 0 we assume that there is always room for 
+           us in a cell within range. */
+       return unit;
+    }
+    return NULL;
+}
 
 /* Return true if our own automatic material production is *greater*
    than our consumption, for the given list of materials. */
@@ -1549,6 +1946,21 @@ can_auto_resupply_self(Unit *unit, int *materials, int numtypes)
 static TaskOutcome
 do_sentry_task(Unit *unit, Task *task)
 {
+    Task *nexttask = NULL;
+    Unit *transport = NULL;
+
+    if (task->next)
+      nexttask = task->next;
+    /* If we were waiting for a transport, and are now in it, 
+       then don't wait. */
+    if (nexttask->type == TASK_OCCUPY) {
+       transport = find_unit_dead_or_alive(nexttask->args[0]);
+       if (in_play(transport) && (transport == unit->transport)) {
+           task->next = nexttask->next;
+           free_task(nexttask);
+       }
+       return TASK_IS_COMPLETE;
+    }
     if (task->args[0] > 0) {
        set_unit_reserve(unit->side, unit, TRUE, FALSE);
        --(task->args[0]);
@@ -1582,14 +1994,13 @@ execute_task(Unit *unit)
     /* This should never happen. */
     if (unit->plan == NULL)
       run_error("???");
-    /* If the unit is AI-run, don't do more than task execution with
+    /* If the unit is AI-run, don't do more than one task execution with
        it during each run step. */
-    if (unit->plan->last_task_outcome != TASK_UNKNOWN
-       && unit->plan->last_task_outcome != TASK_PREPPED_ACTION
-       && side_has_ai(unit->side)
+    if (ai_controlled(unit)
+         && plan->last_task_outcome != TASK_UNKNOWN
+       && plan->last_task_outcome != TASK_PREPPED_ACTION
        /* unless it is acp-independent ... */
-       && !acp_indep(unit)
-       && !unit->plan->aicontrol)
+       && !acp_indep(unit))
       return unit->plan->last_task_outcome;
     task = plan->tasks;
     if (task == NULL)
@@ -1620,8 +2031,7 @@ execute_task(Unit *unit)
            pop_task(plan);
            DMprintf("removed it");
            /* We might be buzzing, so maybe go into reserve. */
-           if (probability(g_ai_badtask_reserve_chance())
-                && g_units_may_go_into_reserve()) {
+           if (probability(g_ai_badtask_reserve_chance())) {
                plan->reserve = TRUE;
                DMprintf(" and went into reserve");
            }
@@ -1670,11 +2080,55 @@ execute_task_aux(Unit *unit, Task *task)
     return (*fn)(unit, task);
 }
 
+/* Wrapper to 'choose_move_dirs' function for providing "backward" 
+   compatibility to the 'choose_move_direction' function that some 
+   functions scattered around the kernel came to depend upon. */
+/* (The 'range' parameter is dead weight. Should we/can we do anything 
+    meaningful with it in the new context?) */
+int
+choose_move_direction(Unit *unit, int x, int y, int range)
+{
+    int rslt = NODIR;
+    int numdirs = 0;
+    int dirs[NUMDIRS];
+    int i = 0, ix, iy;
+
+    /* Try to find a shortest path first. */
+    numdirs = choose_move_dirs(unit, x, y, TRUE, plausible_move_dir, 
+                              sort_directions, dirs);
+#if (1)
+    /* If that fails then try to find any path. */
+    /* (Unfortunately, this is not compatible with 'do_approach_subtask'. 
+       If this piece of code is enabled and called from an UI, 
+       it may say a move is OK, but a movement task that is prepped may, 
+       in fact, fail in do_approach_subtask. So, for the time being, we 
+       must accept that certain legitimate paths will be rejected.) */
+    if (!numdirs)
+      numdirs = choose_move_dirs(unit, x, y, FALSE, plausible_move_dir, 
+                                sort_directions, dirs);
+#endif
+    /* If that fails then either no path exists, or else our pathfinder 
+       algorithm does not do a more thorough search such as with Astar. */
+    if (!numdirs)
+      return NODIR;
+    for (i = 0; i < numdirs; ++i) {
+       interior_point_in_dir(unit->x, unit->y, dirs[i], &ix, &iy);
+       if (!side_thinks_it_can_put_type_at(unit->side, unit->type, ix, iy)) 
+         continue;
+       /* (Should put other tests here to make this function emulate 
+           the 'choose_move_direction' from Peter's pathfinder as much as 
+           is sane.) */
+       rslt = dirs[i];
+       break;
+    }
+    return rslt;
+}
+
 /* This weird-looking routine computes next directions for moving to a
    given spot.  The number of directions ranges from 1 to 4, depending
    on whether there is a straight-line path to the dest, and whether we are
    required to take a direct path or are allowed to move in dirs that don't
-   the unit any closer (we never increase our distance though).
+   bring the unit any closer (we never increase our distance though).
    Some trickinesses:  if area wraps, must resolve ambiguity about
    getting to the same place going either direction (we pick shortest). */
 
@@ -1689,7 +2143,6 @@ choose_move_dirs(Unit *unit, int tx, int ty, int shortest,
 
     dist = distance(unit->x, unit->y, tx, ty);
     dx = tx - unit->x;  dy = ty - unit->y;
-
     if (area.xwrap) {
        dxa = (tx + area.width) - unit->x;
        if (ABS(dx) > ABS(dxa))
@@ -1769,17 +2222,19 @@ plausible_move_dir(Unit *unit, int dir)
     if ((ut_vanishes_on(u, t) || ut_wrecks_on(u, t))
         && !can_move_via_conn(unit, nx, ny))
       return FALSE;
+    speed = unit_speed(unit, nx, ny);
     /* Try the easy test first. */
-    /* (should reconsider - what if unit is slow due to damage?) */
-    if (ut_mp_to_enter(u, t) <= u_acp(u))
+    if (ut_mp_to_enter(u, t) <= ((new_acp_for_turn(unit) * speed) / 100))
       return TRUE;
     u2 = (unit->transport ? unit->transport->type : NONUTYPE);
     totcost = total_move_cost(u, u2, ux, uy, 0, nx, ny, 0);
-    speed = unit_speed(unit, nx, ny);
     /* Check if unit can possibly have enough mp for the move. */
+    /* (We need to be careful here, since this function is not 
+       actually privy to all knowledge. Should use type generic 
+       functions instead.) */
     maxmpavail =
-      (((u_acp_max(u) > 0 ? u_acp_max(u) : u_acp(u)) - u_acp_min(u)) * speed)
-      / 100
+      (((u_acp_max(u) > 0 ? u_acp_max(u) : new_acp_for_turn(unit)) 
+       - u_acp_min(u)) * speed) / 100
       + u_free_mp(u);
     if (maxmpavail >= totcost)
       return TRUE;
@@ -1790,7 +2245,7 @@ plausible_move_dir(Unit *unit, int dir)
            && connection_at(ux, uy, dir, c)
            && ((cost = ut_mp_to_traverse(u, c)) >= 0)) {
            if ((ut_mp_to_enter(u, c) + cost + ut_mp_to_leave(u, c))
-               <= u_acp(u))
+               <= ((new_acp_for_turn(unit) * speed) / 100))
              return TRUE;
        }
     }
@@ -1813,7 +2268,7 @@ compare_directions(CONST void *a0, CONST void *a1)
     int ux = tmpunit->x, uy = tmpunit->y, u2 = NONUTYPE;
     int cost0 = 0, cost1 = 0, s, ps0, ps1, surr0, surr1, rslt;
     int cs0, cs1;
-    extern int *any_people_surrenders;
+    extern short *any_people_surrenders;
 
     i0 = *((int *) a0);  i1 = *((int *) a1);
     t0 = terrs[i0];  t1 = terrs[i1];
@@ -1910,6 +2365,7 @@ void
 add_task(Unit *unit, int pos, Task *task)
 {
     int numcleared;
+    Task *agenda = NULL;
 
     if (unit->plan == NULL && !completed(unit)) {
        init_unit_plan(unit);
@@ -1922,124 +2378,65 @@ add_task(Unit *unit, int pos, Task *task)
     DMprintf("%s add task %s",
            unit_desig(unit), task_desig(task));
     if (pos == CLEAR_AGENDA) {
-       numcleared = clear_task_agenda(unit->plan);
+       numcleared = clear_task_agenda(unit);
        DMprintf(" (cleared %d existing tasks)", numcleared);
     }
     DMprintf("\n");
     switch (pos) {
-      case 0:
+      // Put the task on the front of the agenda. 
+      case ADD_TO_AGENDA_AS_LIFO:
       case CLEAR_AGENDA:
-       /* Put the task on the front of the agenda. */
        task->next = unit->plan->tasks;
        unit->plan->tasks = task;
        break;
+      // Put the task on the end of the agenda.
+      case ADD_TO_AGENDA_AS_FIFO:
+       task->next = NULL;
+       agenda = unit->plan->tasks;
+       if (!agenda)
+           unit->plan->tasks = task;
+       else {
+           for (; agenda && agenda->next; agenda = agenda->next);
+           agenda->next = task;
+       }
+       break;
       default:
-       run_error("can't do this yet");
+       run_error("Tried to add a task to a task agenda in a strange manner");
        break;
     }
-    /* Shouldn't be asleep any longer. */
+    // Shouldn't be asleep any longer. 
     unit->plan->asleep = FALSE;
-    /* We're not in reserve. */
+    // We're not in reserve.
     unit->plan->reserve = FALSE;
-    /* Presumably we're no longer waiting to be told what to do. */
+    // Presumably we're no longer waiting to be told what to do. 
     set_waiting_for_tasks(unit, FALSE);
-    /* Reflect all this on displays. */
+    // Reflect all this on displays. 
     update_unit_display(unit->side, unit, FALSE);
 }
 
 /* This routine sets up a task to build a unit of the given type. */
 
 Task *
-create_build_task(Unit *unit, int u2, int run, int x, int y)
+create_build_task(Unit *unit, int id, int cp)
 {
-    Task *task = create_task(TASK_BUILD);
+    Task *task = NULL;
 
-    task->args[0] = u2;
-    task->args[3] = run;
-    task->args[4] = x;  task->args[5] = y;
+    task = create_task(TASK_BUILD);
+    task->args[0] = id;
+    task->args[1] = cp;
     return task;
 }
 
 void
-set_build_task(Unit *unit, int u2, int run, int x, int y)
-{
-    /* First check if we are building something. */
-    if (unit->plan && unit->plan->tasks
-       && unit->plan->tasks->type == TASK_BUILD) {
-
-       Unit *unit2 = find_unit(unit->plan->tasks->args[1]);
-
-       /* Return if we already are building the desired unit. */
-       if (is_unit(unit2) && !completed(unit2) && unit2->type == u2) {
-           return;
-           /* Maybe disband incomplete unit of wrong type. */
-       } else if (is_unit(unit2) && !completed(unit2)
-                  && side_can_disband(unit->side, unit2)
-                  && g_disband_unfinished_units()) {
-           /* But first salvage either its cps or materials if
-               permitted to do so. */
-           if (g_salvage_unfinished_cps())
-             unit->cp_stash += unit2->cp;                              
-           else if (g_salvage_unfinished_materials()) {
-               
-               int     m, u = unit2->type;
-               
-               for_all_material_types(m) {
-                   if (um_consumption_on_creation(u, m) > 0)
-                     unit->supply[m] += um_consumption_on_creation(u, m);
-                   if (um_consumption_per_cp(u2, m) > 0)
-                     unit->supply[m] += (unit2->cp - uu_creation_cp(unit->type, u))
-                       * um_consumption_per_cp(u2, m);
-               }                                               
-           }
-           kill_unit(unit2, H_UNIT_DISBANDED);
-       }               
-    }                  
-    add_task(unit, CLEAR_AGENDA, create_build_task(unit, u2, run, x, y));
-}
-
-void
-push_build_task(Unit *unit, int u2, int run, int x, int y)
-{
-    /* First check if we are building something. */
-    if (unit->plan && unit->plan->tasks
-       && unit->plan->tasks->type == TASK_BUILD) {
-
-       Unit *unit2 = find_unit(unit->plan->tasks->args[1]);
-
-       /* Return if we already are building the desired unit. */
-       if (is_unit(unit2) && !completed(unit2) && unit2->type == u2) {
-           return;
-           /* Maybe disband incomplete unit of wrong type. */
-       } else if (is_unit(unit2) && !completed(unit2)
-                  && side_can_disband(unit->side, unit2)
-                  && g_disband_unfinished_units()) {
-           /* But first salvage either its cps or materials if permitted to do so. */
-           if (g_salvage_unfinished_cps())
-             unit->cp_stash += unit2->cp;                              
-           else if (g_salvage_unfinished_materials()) {
-               
-               int     m, u = unit2->type;
-               
-               for_all_material_types(m) {
-                   if (um_consumption_on_creation(u, m) > 0)
-                     unit->supply[m] += um_consumption_on_creation(u, m);
-                   if (um_consumption_per_cp(u2, m) > 0)
-                     unit->supply[m] += (unit2->cp - uu_creation_cp(unit->type, u))
-                       * um_consumption_per_cp(u2, m);
-               }                                               
-           }
-           kill_unit(unit2, H_UNIT_DISBANDED);
-       }               
-    }                  
-    add_task(unit, 0, create_build_task(unit, u2, run, x, y));
+set_build_task(Unit *unit, int id, int cp)
+{
+    add_task(unit, CLEAR_AGENDA, create_build_task(unit, id, cp));
 }
 
 void
-resume_build_task(Unit *unit, Unit *unit2, int run, int x, int y)
+push_build_task(Unit *unit, int id, int cp)
 {
-       set_build_task(unit, unit2->type, run, x, y);
-       unit->plan->tasks->args[1] = unit2->id;         
+    add_task(unit, 0, create_build_task(unit, id, cp));
 }
 
 Task *
@@ -2222,16 +2619,58 @@ push_produce_task(Unit *unit, int m, int n)
     add_task(unit, 0, create_produce_task(unit, m, n));
 }
 
+/* Construct Task */
+
 Task *
-create_repair_task(Unit *unit)
+create_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
 {
-    return create_task(TASK_REPAIR);
+    Task *task = NULL;
+
+    task = create_task(TASK_CONSTRUCT);
+    task->args[0] = u;
+    task->args[1] = run;
+    task->args[2] = transid;
+    task->args[3] = x;
+    task->args[4] = y;
+    return task;
+}
+
+void
+set_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
+{
+    add_task(
+       unit, CLEAR_AGENDA, create_construct_task(unit, u, run, transid, x, y));
+}
+
+void
+push_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
+{
+    add_task(unit, 0, create_construct_task(unit, u, run, transid, x, y));
+}
+
+/* Repair Task */
+
+Task *
+create_repair_task(Unit *unit, int id, int hp)
+{
+    Task *task = NULL;
+
+    task = create_task(TASK_REPAIR);
+    task->args[0] = id;
+    task->args[1] = hp;
+    return task;
+}
+
+void
+set_repair_task(Unit *unit, int id, int hp)
+{
+    add_task(unit, CLEAR_AGENDA, create_repair_task(unit, id, hp));
 }
 
 void
-set_repair_task(Unit *unit)
+push_repair_task(Unit *unit, int id, int hp)
 {
-    add_task(unit, CLEAR_AGENDA, create_repair_task(unit));
+    add_task(unit, 0, create_repair_task(unit, id, hp));
 }
 
 /* This routine sets up a task to develop a unit of the given type. */
@@ -2247,9 +2686,15 @@ create_develop_task(Unit *unit, int u2, int n)
 }
 
 void
-push_develop_task(Unit *unit, int u2, int n)
+set_develop_task(Unit *unit, int u2, int techgoal)
+{
+    add_task(unit, CLEAR_AGENDA, create_develop_task(unit, u2, techgoal));
+}
+
+void
+push_develop_task(Unit *unit, int u2, int techgoal)
 {
-    add_task(unit, 0, create_develop_task(unit, u2, n));
+    add_task(unit, 0, create_develop_task(unit, u2, techgoal));
 }
 
 Task *
@@ -2436,7 +2881,7 @@ parse_task(Side *side, char *str, Task **taskp)
            rest = get_next_arg(str, substr, &arg);
            /* (should check for end of command or not?) */
        }
-       *taskp = create_task(tasktype);
+       *taskp = create_task((TaskType)tasktype);
        for (i = 0; i < numargs; ++i) {
            (*taskp)->args[i] = taskargs[i];
        }
@@ -2453,7 +2898,7 @@ task_desig(Task *task)
     char *argtypes;
 
     if (taskbuf == NULL)
-      taskbuf = xmalloc(BUFSIZE);
+      taskbuf = (char *)xmalloc(BUFSIZE);
     if (task) {
        sprintf(taskbuf, "{%s", taskdefns[task->type].name);
        argtypes = taskdefns[task->type].argtypes;