Complete.Org: Mailing Lists: Archives: freeciv-ai: October 2003:
[freeciv-ai] Re: (PR#6308) Cleanup of ferry handling
Home

[freeciv-ai] Re: (PR#6308) Cleanup of ferry handling

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Gregory.Berkolaiko@xxxxxxxxxxxx
Subject: [freeciv-ai] Re: (PR#6308) Cleanup of ferry handling
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Wed, 1 Oct 2003 07:41:32 -0700
Reply-to: rt@xxxxxxxxxxxxxx

As requested, I've hacked on this patch, and here is a new version of it.

CHANGES:
 - replaced ugly boat register. now aidata keeps track of this stuff.
 - replaced ugly 'find passengers' iteration (leftover Syelaism)
 - we now try hard to get bodyguard along on the ride
 - stack_attack_value() removed and replaced by two additional lines of
   code in the only function that called it (duh)
 - renamed FERRY_SEEKING to FERRY_AVAILABLE

I think it is ready to go in. Greg, please have a last look at it and
check that I didn't mess up something ;)

  - Per

Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.20
diff -u -r1.20 aidata.c
--- ai/aidata.c 8 Aug 2003 22:11:41 -0000       1.20
+++ ai/aidata.c 1 Oct 2003 14:34:11 -0000
@@ -189,7 +189,10 @@
 
   ai->stats.workers = fc_calloc(ai->num_continents + 1, sizeof(int));
   ai->stats.cities = fc_calloc(ai->num_continents + 1, sizeof(int));
+  ai->stats.passengers = fc_calloc(ai->num_continents + 1, sizeof(int));
   ai->stats.average_production = 0;
+  ai->stats.boats = 0;
+  ai->stats.available_boats = 0;
   city_list_iterate(pplayer->cities, pcity) {
     ai->stats.cities[(int)map_get_continent(pcity->x, pcity->y)]++;
     ai->stats.average_production += pcity->shield_surplus;
@@ -199,6 +202,15 @@
   unit_list_iterate(pplayer->units, punit) {
     struct tile *ptile = map_get_tile(punit->x, punit->y);
 
+    if (punit->ai.passenger == FERRY_AVAILABLE) {
+      ai->stats.available_boats++;
+    }
+    if (is_sailing_unit(punit) && is_ground_units_transport(punit)) {
+      ai->stats.boats++;
+    }
+    if (punit->ai.ferryboat == FERRY_WANTED) {
+      ai->stats.passengers++;
+    }
     if (!is_ocean(ptile->terrain) && unit_flag(punit, F_SETTLERS)) {
       ai->stats.workers[(int)map_get_continent(punit->x, punit->y)]++;
     }
@@ -352,6 +364,38 @@
     ai->diplomacy.player_intel[i].asked_about_alliance = 0;
     ai->diplomacy.player_intel[i].asked_about_ceasefire = 0;
     ai->diplomacy.player_intel[i].warned_about_space = 0;
+  }
+}
+
+/**************************************************************************
+  Use this wrapper to correctly update the statistics. Use NULL to
+  unregister any ferry that might be there.
+**************************************************************************/
+void ai_set_ferry(struct unit *punit, struct unit *ferry)
+{
+  if (!ferry && punit->ai.ferryboat != FERRY_WANTED) {
+    struct ai_data *ai = ai_data_get(unit_owner(punit));
+
+    ai->stats.passengers++;
+    punit->ai.ferryboat = FERRY_WANTED;
+  } else if (ferry) {
+    punit->ai.ferryboat = ferry->id;
+  }
+}
+
+/**************************************************************************
+  Use this wrapper to correctly update the statistics. Use NULL to
+  unregister any passenger that might be there.
+**************************************************************************/
+void ai_set_passenger(struct unit *punit, struct unit *passenger)
+{
+  if (!passenger && punit->ai.passenger != FERRY_AVAILABLE) {
+    struct ai_data *ai = ai_data_get(unit_owner(punit));
+
+    ai->stats.available_boats++;
+    punit->ai.passenger = FERRY_AVAILABLE;
+  } else if (passenger) {
+    punit->ai.passenger = passenger->id;
   }
 }
 
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.10
diff -u -r1.10 aidata.h
--- ai/aidata.h 8 Aug 2003 22:11:41 -0000       1.10
+++ ai/aidata.h 1 Oct 2003 14:34:11 -0000
@@ -86,11 +86,14 @@
     bool sea_done;    /* nothing more to explore at sea */
   } explore;
 
-  /* This struct is used for statistical unit building, to ensure
-     that we don't build too few or too many units of a given type */
+  /* This struct is used for statistical unit building, eg to ensure
+   * that we don't build too few or too many units of a given type. */
   struct {
     int *workers; /* cities to workers on continent*/
     int *cities;  /* number of cities on continent */
+    int *passengers; /* number of passengers on continent */
+    int boats;
+    int available_boats;
     int average_production;
     bv_id diplomat_reservations;
   } stats;
@@ -131,5 +134,8 @@
 void ai_data_done(struct player *pplayer);
 
 struct ai_data *ai_data_get(struct player *pplayer);
+
+void ai_set_passenger(struct unit *punit, struct unit *passenger);
+void ai_set_ferry(struct unit *punit, struct unit *ferry);
 
 #endif
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.92
diff -u -r1.92 aitools.c
--- ai/aitools.c        28 Sep 2003 01:04:59 -0000      1.92
+++ ai/aitools.c        1 Oct 2003 14:34:12 -0000
@@ -137,6 +137,8 @@
 /**************************************************************************
   This will eventually become the ferry-enabled goto. For now, it just
   wraps ai_unit_goto()
+
+  TODO: Kill me.  Use ai_gothere instead.
 **************************************************************************/
 bool ai_unit_gothere(struct unit *punit)
 {
@@ -146,6 +148,238 @@
   } else {
     return FALSE; /* we died */
   }
+}
+
+/****************************************************************************
+  A helper function for ai_gothere.  Estimates the dangers we will
+  be facing at our destination and tries to find/request a bodyguard if 
+  needed.
+****************************************************************************/
+static void ai_gothere_bodyguard(struct unit *punit, int dest_x, int dest_y)
+{
+  int danger = 0;
+  struct city *dcity;
+  struct tile *ptile;
+  
+  if (is_barbarian(unit_owner(punit))) {
+    /* barbarians must have more courage (ie less brains) */
+    punit->ai.bodyguard = BODYGUARD_NONE;
+    return;
+  }
+
+  /* Estimate enemy attack power. */
+  unit_list_iterate(map_get_tile(dest_x, dest_y)->units, aunit) {
+    danger += unit_att_rating(aunit);
+  } unit_list_iterate_end;
+  if ((dcity = map_get_city(dest_x, dest_y))) {
+    /* Assume enemy will build another defender, add it's attack strength */
+    int d_type = ai_choose_defender_versus(dcity, punit->type);
+    danger += 
+      unittype_att_rating(d_type, do_make_unit_veteran(dcity, d_type), 
+                          SINGLE_MOVE, unit_types[d_type].hp);
+  }
+  danger *= POWER_DIVIDER;
+
+  /* If we are fast, there is less danger. */
+  danger /= (unit_type(punit)->move_rate / SINGLE_MOVE);
+  if (unit_flag(punit, F_IGTER)) {
+    danger /= 1.5;
+  }
+
+  ptile = map_get_tile(punit->x, punit->y);
+  /* We look for the bodyguard where we stand. */
+  if (!unit_list_find(&ptile->units, punit->ai.bodyguard)) {
+    int my_def = (punit->hp * (punit->veteran ? 15 : 10)
+                  * unit_type(punit)->defense_strength);
+    
+    /* FIXME: danger is multiplied by POWER_FACTOR, my_def isn't. */
+    if (danger >= my_def) {
+      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, 
+               "wants a bodyguard, danger=%d, my_def=%d", danger, my_def);
+      punit->ai.bodyguard = BODYGUARD_WANTED;
+    } else {
+      punit->ai.bodyguard = BODYGUARD_NONE;
+    }
+  }
+
+  /* What if we have a bodyguard, but don't need one? */
+}
+
+#define LOGLEVEL_GOTHERE LOG_DEBUG
+/****************************************************************************
+  This is ferry-enabled goto.  Should not normally be used for non-ferried 
+  units (i.e. planes or ships), use ai_unit_goto instead.
+
+  Return values: TRUE if got to or next to our destination, FALSE otherwise. 
+
+  TODO: A big one is rendezvous points.  When this is implemented, we won't
+  have to be at the coast to ask for a boat to come to us.
+****************************************************************************/
+bool ai_gothere(struct player *pplayer, struct unit *punit,
+                int dest_x, int dest_y)
+{
+  struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
+
+  CHECK_UNIT(punit);
+
+  if (same_pos(dest_x, dest_y, punit->x, punit->y)) {
+    /* Nowhere to go */
+    return TRUE;
+  }
+
+  /* See if we need a bodyguard at our destination */
+  /* FIXME: If bodyguard is _really_ necessary, don't go anywhere */
+  ai_gothere_bodyguard(punit, dest_x, dest_y);
+
+  /* If we can, we will walk, take boat only if necessary */
+  if (punit->transported_by > 0 
+      || !goto_is_sane(punit, dest_x, dest_y, TRUE)) {
+    int boatid = punit->transported_by;
+    struct unit *ferryboat = NULL;
+
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "will have to go to (%d,%d) by boat",
+             dest_x, dest_y);
+
+    if (boatid <= 0) {
+      int bx, by;
+      boatid = find_boat(pplayer, &bx, &by, 2);
+    } 
+    ferryboat = find_unit_by_id(boatid);
+
+    if (!ferryboat) {
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "No boat found.");
+      ai_set_ferry(punit, NULL);
+      return FALSE;
+    } else {
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Found boat at (%d,%d)",
+               ferryboat->x, ferryboat->y);
+    }
+
+    punit->ai.ferryboat = boatid;
+    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)
+        && (!is_at_coast(punit->x, punit->y) 
+            || is_tiles_adjacent(punit->x, punit->y, 
+                                 ferryboat->x, ferryboat->y))) {
+      /* Go to the boat only if it cannot reach us or it's parked 
+       * next to us.  Otherwise just wait (boats are normally faster) */
+      /* FIXME: agree on a rendez-vous point */
+      /* FIXME: this can lose bodyguard */
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "going to boat[%d](%d,%d).", boatid,
+               ferryboat->x, ferryboat->y);
+      if (!ai_unit_goto(punit, ferryboat->x, ferryboat->y)) { 
+        /* Died. */
+        return FALSE;
+      }
+    }
+    
+    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
+      /* Didn't get to the boat */
+      if (is_at_coast(punit->x, punit->y)) {
+        /* At least got to the coast, wave to the boats! */
+        UNIT_LOG(LOGLEVEL_GOTHERE, punit, "asking a boat to come nearer");
+        ai_set_ferry(punit, NULL);
+      }
+      return FALSE;
+    }
+
+    /* Check if we are the passenger-in-charge */
+    if (ferryboat->ai.passenger <= 0
+        || ferryboat->ai.passenger == punit->id) {
+      int beach_x, beach_y;     /* Destination for the boat */
+      struct tile *dest_tile = map_get_tile(dest_x, dest_y);
+
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "got boat[%d], going (%d,%d)",
+               ferryboat->id, dest_x, dest_y);
+      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
+      ferryboat->ai.passenger = punit->id;
+
+      /* If the location is not accessible directly from sea
+       * or is defended and we are not marines, we will need a 
+       * landing beach */
+      if (!is_at_coast(dest_x, dest_y)
+          ||((is_non_allied_city_tile(dest_tile, pplayer) 
+              || is_non_allied_unit_tile(dest_tile, pplayer))
+             && !unit_flag(punit, F_MARINES))) {
+        if (!find_beachhead(punit, dest_x, dest_y, &beach_x, &beach_y)) {
+          /* Nowhere to go */
+          return FALSE;
+        }
+        UNIT_LOG(LOGLEVEL_GOTHERE, punit, 
+                 "Found beachhead (%d,%d)", beach_x, beach_y);
+      } else {
+        beach_x = dest_x;
+        beach_y = dest_y;
+      }
+
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "All aboard!");
+      set_goto_dest(ferryboat, beach_x, beach_y);
+      set_goto_dest(punit, dest_x, dest_y);
+      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
+      /* Grab bodyguard */
+      if (bodyguard
+          && !same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y)) {
+        if (!goto_is_sane(bodyguard, punit->x, punit->y, TRUE)
+            || !ai_unit_goto(punit, punit->x, punit->y)) {
+          /* Bodyguard can't get there or died en route */
+          punit->ai.bodyguard = BODYGUARD_WANTED;
+          bodyguard = NULL;
+        } else if (bodyguard->moves_left <= 0) {
+          /* Wait for me, I'm cooooming!! */
+          UNIT_LOG(LOGLEVEL_GOTHERE, punit, "waiting for bodyguard");
+          return TRUE;
+        } else {
+          /* Crap bodyguard. Got stuck somewhere. Ditch it! */
+          UNIT_LOG(LOGLEVEL_GOTHERE, punit, "ditching useless bodyguard");
+          punit->ai.bodyguard = BODYGUARD_WANTED;
+          ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1);
+          bodyguard = NULL;
+        }
+      }
+      if (bodyguard) {
+        assert(same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y));
+        handle_unit_activity_request(bodyguard, ACTIVITY_SENTRY);
+      }
+      if (!ai_unit_goto(ferryboat, beach_x, beach_y)) {
+        /* died */
+        return FALSE;
+      }
+      if (!is_tiles_adjacent(ferryboat->x, ferryboat->y, beach_x, beach_y)
+          && !same_pos(ferryboat->x, ferryboat->y, beach_x, beach_y)) {
+        /* We are in still transit */
+        return FALSE;
+      }
+    } else {
+      /* Waiting for the boss to load and move us */
+      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Cannot command boat [%d],"
+               " its boss is [%d]", 
+               ferryboat->id, ferryboat->ai.passenger);
+      return FALSE;
+    }
+
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Our boat has arrived");
+    handle_unit_activity_request(punit, ACTIVITY_IDLE);
+  }
+
+  /* Go where we should be going if we can, and are at our destination 
+   * if we are on a ferry */
+  if (goto_is_sane(punit, dest_x, dest_y, TRUE) && punit->moves_left > 0) {
+    set_goto_dest(punit, dest_x, dest_y);
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Walking to (%d,%d)", dest_x, dest_y);
+    if (!ai_unit_goto(punit, dest_x, dest_y)) {
+      /* died */
+      return FALSE;
+    }
+    /* liable to bump into someone that will kill us.  Should avoid? */
+  } else {
+    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Not moving");
+    return FALSE;
+  }
+  
+  /* Dead unit shouldn't reach this point */
+  CHECK_UNIT(punit);
+  
+  return (same_pos(punit->x, punit->y, dest_x, dest_y) 
+          || is_tiles_adjacent(punit->x, punit->y, dest_x, dest_y));
 }
 
 /**************************************************************************
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.39
diff -u -r1.39 aitools.h
--- ai/aitools.h        27 Sep 2003 11:22:40 -0000      1.39
+++ ai/aitools.h        1 Oct 2003 14:34:12 -0000
@@ -38,15 +38,21 @@
   BODYGUARD_NONE
 };
 
+#define FERRY_WANTED      -1      /* For passengers in need of a boat */
+#define FERRY_AVAILABLE   -1      /* For boats looking for a passenger */
+
 int military_amortize(struct player *pplayer, struct city *pcity, 
                       int value, int delay, int build_cost);
 int stack_cost(struct unit *pdef);
 
 bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
 bool ai_unit_gothere(struct unit *punit);
+bool ai_gothere(struct player *pplayer, struct unit *punit, 
+                int dest_x, int dest_y);
 bool ai_unit_goto(struct unit *punit, int x, int y);
-void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, int x, int 
y);
 
+void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, 
+                      int x, int y);
 bool ai_unit_make_homecity(struct unit *punit, struct city *pcity);
 bool ai_unit_attack(struct unit *punit, int x, int y);
 bool ai_unit_move(struct unit *punit, int x, int y);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.297
diff -u -r1.297 aiunit.c
--- ai/aiunit.c 28 Sep 2003 09:33:21 -0000      1.297
+++ ai/aiunit.c 1 Oct 2003 14:34:12 -0000
@@ -61,6 +61,7 @@
 
 #define LOGLEVEL_RECOVERY LOG_DEBUG
 
+static void ai_manage_unit(struct player *pplayer, struct unit *punit);
 static void ai_manage_military(struct player *pplayer,struct unit *punit);
 static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
 static void ai_manage_barbarian_leader(struct player *pplayer,
@@ -804,19 +805,6 @@
   return v * v;
 }
 
-/********************************************************************** 
-  Total attack power of the units in this square.
-***********************************************************************/
-static int stack_attack_value(int x, int y)
-{
-  int val = 0;
-  struct tile *ptile = map_get_tile(x, y);
-  unit_list_iterate(ptile->units, aunit)
-    val += unit_att_rating(aunit);
-  unit_list_iterate_end;
-  return val;
-}
-
 /**************************************************************************
 Compute how much we want to kill certain victim we've chosen, counted in
 SHIELDs.
@@ -1123,8 +1111,13 @@
 
   if (aunit && aunit->owner == punit->owner) {
     /* protect a unit */
-    x = aunit->x;
-    y = aunit->y;
+    if (is_sailing_unit(aunit)) {
+      x = goto_dest_x(aunit);
+      y = goto_dest_y(aunit);
+    } else {
+      x = aunit->x;
+      y = aunit->y;
+    }
   } else if (acity && acity->owner == punit->owner) {
     /* protect a city */
     x = acity->x;
@@ -1238,174 +1231,6 @@
   *y = best_y;
 }
 
-/**************************************************************************
-  A helper function for ai_military_gothere.  Estimates the dangers we will
-  be facing a out destination and tries to find/request a bodyguard if 
-  needed.
-**************************************************************************/
-static void ai_gothere_bodyguard(struct unit *punit, int dest_x, int dest_y)
-{
-  int danger;
-  struct city *dcity;
-  struct tile *ptile;
-
-  
-  if (is_barbarian(unit_owner(punit)))  {
-    /* barbarians must have more courage (= less brains) */
-    punit->ai.bodyguard = BODYGUARD_NONE;
-    return;
-  }
-
-  /* Estimate enemy attack power. */
-  danger = stack_attack_value(dest_x, dest_y);
-  if ((dcity = map_get_city(dest_x, dest_y))) {
-    /* Assume enemy will build another defender, add it's attack strength */
-    int d_type = ai_choose_defender_versus(dcity, punit->type);
-    danger += 
-      unittype_att_rating(d_type, do_make_unit_veteran(dcity, d_type), 
-                          SINGLE_MOVE, unit_types[d_type].hp);
-  }
-  danger *= POWER_DIVIDER;
-
-  /* If we are fast, there is less danger. */
-  danger /= (unit_type(punit)->move_rate / SINGLE_MOVE);
-  if (unit_flag(punit, F_IGTER)) {
-    danger /= 1.5;
-  }
-
-  ptile = map_get_tile(punit->x, punit->y);
-  /* We look for the bodyguard where we stand. */
-  if (!unit_list_find(&ptile->units, punit->ai.bodyguard)) {
-    int my_def = (punit->hp * (punit->veteran ? 15 : 10)
-                  * unit_type(punit)->defense_strength);
-    
-    /* FIXME: danger is multiplied by POWER_FACTOR, my_def isn't. */
-    if (danger >= my_def) {
-      UNIT_LOG(LOGLEVEL_BODYGUARD, punit, 
-               "wants a bodyguard, danger=%d, my_def=%d", danger, my_def);
-      punit->ai.bodyguard = BODYGUARD_WANTED;
-    } else {
-      punit->ai.bodyguard = BODYGUARD_NONE;
-    }
-  }
-
-  /* What if we have a bodyguard, but don't need one? */
-}
-
-/**************************************************************************
-  Return values: FALSE if died or stuck, TRUE otherwise. (This function
-  is not server-side autoattack safe.)
-**************************************************************************/
-static bool ai_military_gothere(struct player *pplayer, struct unit *punit,
-                                int dest_x, int dest_y)
-{
-  int id, x, y, boatid = 0, bx = -1, by = -1;
-  struct unit *ferryboat = NULL;
-  struct unit *def;
-  struct tile *ptile;
-  bool boat_arrived;
-
-  CHECK_UNIT(punit);
-
-  id = punit->id; x = punit->x; y = punit->y;
-
-  if (same_pos(dest_x, dest_y, x, y)) {
-    /* Nowhere to go */
-    return FALSE;
-  }
-
-  if (is_ground_unit(punit)) { 
-    boatid = find_boat(pplayer, &bx, &by, 2);
-    /* NB: ferryboat is set only if the boat is where we are */
-    ferryboat = unit_list_find(&(map_get_tile(x, y)->units), boatid);
-  }
-
-  /* See if we need a bodyguard at our destination */
-  ai_gothere_bodyguard(punit, dest_x, dest_y);
-  
-  if (!goto_is_sane(punit, dest_x, dest_y, TRUE) 
-      || (ferryboat && goto_is_sane(ferryboat, dest_x, dest_y, TRUE))) {
-    punit->ai.ferryboat = boatid;
-    UNIT_LOG(LOG_DEBUG, punit, "Looking for boat[%d].", boatid);
-    if (boatid > 0 && !same_pos(x, y, bx, by)) {
-      /* Go to the boat */
-      /* FIXME: this can lose bodyguard */
-      if (!ai_unit_goto(punit, bx, by)) {
-        /* Died. */
-        return FALSE;
-      }
-    }
-    ptile = map_get_tile(punit->x, punit->y);
-    ferryboat = unit_list_find(&ptile->units, punit->ai.ferryboat);
-
-    if (ferryboat && (ferryboat->ai.passenger == 0 
-                      || ferryboat->ai.passenger == punit->id)) {
-      int boat_x, boat_y;
-
-      UNIT_LOG(LOG_DEBUG, punit, "Found boat[%d], going (%d,%d)",
-               ferryboat->id, dest_x, dest_y);
-      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
-      ferryboat->ai.passenger = punit->id;
-
-      /* Last ingredient: a beachhead. */
-      if (find_beachhead(punit, dest_x, dest_y, &boat_x, &boat_y)) {
-        UNIT_LOG(LOG_DEBUG, punit, "Found beachhead (%d,%d), all aboard", 
-                 boat_x, boat_y);
-       set_goto_dest(ferryboat, boat_x, boat_y);
-       set_goto_dest(punit, dest_x, dest_y);
-        unit_list_iterate(ptile->units, mypass) {
-          if (mypass->ai.ferryboat == ferryboat->id
-              && punit->owner == mypass->owner) {
-            handle_unit_activity_request(mypass, ACTIVITY_SENTRY);
-            def = unit_list_find(&ptile->units, mypass->ai.bodyguard);
-            if (def) {
-              handle_unit_activity_request(def, ACTIVITY_SENTRY);
-            }
-          }
-        } unit_list_iterate_end; /* passengers are safely stowed away */
-        if (!ai_unit_goto(ferryboat, dest_x, dest_y)) {
-          return FALSE; /* died */
-        }
-      }
-    } 
-  }
-
-  if (ferryboat && is_goto_dest_set(ferryboat)) {
-    /* we are on a ferry! did we arrive? */
-    boat_arrived = same_pos(ferryboat->x, ferryboat->y,
-                            goto_dest_x(ferryboat), goto_dest_y(ferryboat))
-      || is_tiles_adjacent(ferryboat->x, ferryboat->y,
-                           goto_dest_x(ferryboat), goto_dest_y(ferryboat));
-  } else {
-    boat_arrived = FALSE;
-  }
-
-  if (boat_arrived) {
-    handle_unit_activity_request(punit, ACTIVITY_IDLE);
-    UNIT_LOG(LOG_DEBUG, punit, "Our boat has arrived");
-  }
-
-  /* Go where we should be going if we can, and are at our destination 
-   * if we are on a ferry */
-  if (goto_is_sane(punit, dest_x, dest_y, TRUE) && punit->moves_left > 0
-      && (!ferryboat || boat_arrived)) {
-    set_goto_dest(punit, dest_x, dest_y);
-    UNIT_LOG(LOG_DEBUG, punit, "Attempt to walk to (%d,%d)", dest_x, dest_y);
-    if (!ai_unit_goto(punit, dest_x, dest_y)) {
-      /* died */
-      return FALSE;
-    }
-    /* liable to bump into someone that will kill us.  Should avoid? */
-  } else {
-    UNIT_LOG(LOG_DEBUG, punit, "Not moving");
-  }
-  
-  /* Dead unit shouldn't reach this point */
-  CHECK_UNIT(punit);
-  
-  return (!same_pos(punit->x, punit->y, x, y));
-}
-
 /*************************************************************************
   Does the unit with the id given have the flag L_DEFEND_GOOD?
 **************************************************************************/
@@ -2249,32 +2074,28 @@
     /* Then find enemies the hard way */
     find_something_to_kill(pplayer, punit, &dest_x, &dest_y);
     if (!same_pos(punit->x, punit->y, dest_x, dest_y)) {
-     int repeat;
-
-     for(repeat = 0; repeat < 2; repeat++) {
 
       if (!is_tiles_adjacent(punit->x, punit->y, dest_x, dest_y)
-          || !can_unit_attack_tile(punit, dest_x, dest_y)
-          || (could_unit_move_to_tile(punit, dest_x, dest_y) == 0)) {
-        /* Can't attack or move usually means we are adjacent but
-         * on a ferry. This fixes the problem (usually). */
+          || !can_unit_attack_tile(punit, dest_x, dest_y)) {
+        /* Adjacent and can't attack usually means we are not marines
+         * and on a ferry. This fixes the problem (usually). */
         UNIT_LOG(LOG_DEBUG, punit, "mil att gothere -> (%d,%d)", 
                  dest_x, dest_y);
-        if (!ai_military_gothere(pplayer, punit, dest_x, dest_y)) {
+        if (!ai_gothere(pplayer, punit, dest_x, dest_y)) {
           /* Died or got stuck */
           return;
         }
-      } else {
-        /* Close combat. fstk sometimes want us to attack an adjacent
-         * enemy that rampage wouldn't */
-        UNIT_LOG(LOG_DEBUG, punit, "mil att bash -> %d, %d", dest_x, dest_y);
-        if (!ai_unit_attack(punit, dest_x, dest_y)) {
-          /* Died */
+        /* Must be adjacent now. */
+      }
+      
+      /* Close combat. fstk sometimes want us to attack an adjacent
+       * enemy that rampage wouldn't */
+      UNIT_LOG(LOG_DEBUG, punit, "mil att bash -> %d, %d", dest_x, dest_y);
+      if (!ai_unit_attack(punit, dest_x, dest_y)) {
+        /* Died */
           return;
-        }
       }
 
-     } /* for-loop */
     } else {
       /* FIXME: This happens a bit too often! */
       UNIT_LOG(LOG_DEBUG, punit, "fstk didn't find us a worthy target!");
@@ -2304,14 +2125,14 @@
     if ((pc = dist_nearest_city(pplayer, punit->x, punit->y, FALSE, TRUE))) {
       if (!is_ocean(map_get_terrain(punit->x, punit->y))) {
         UNIT_LOG(LOG_DEBUG, punit, "Barbarian marching to conquer %s", 
pc->name);
-        (void) ai_military_gothere(pplayer, punit, pc->x, pc->y);
+        (void) ai_gothere(pplayer, punit, pc->x, pc->y);
       } else {
         /* sometimes find_beachhead is not enough */
         if (!find_beachhead(punit, pc->x, pc->y, &fx, &fy)) {
           find_city_beach(pc, punit, &fx, &fy);
         }
         UNIT_LOG(LOG_DEBUG, punit, "Barbarian sailing to %s", pc->name);
-        (void) ai_military_gothere(pplayer, punit, fx, fy);
+        (void) ai_gothere(pplayer, punit, fx, fy);
       }
     }
   }
@@ -2406,164 +2227,188 @@
   }
 }
 
-/**************************************************************************
-This seems to manage the ferryboat. When it carries units on their way
-to invade something, it goes there. If it carries other units, it returns home.
-When empty, it tries to find some units to carry or goes home or explores.
-Military units handled by ai_manage_military()
-**************************************************************************/
-static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
-{ /* It's about 12 feet square and has a capacity of almost 1000 pounds.
-     It is well constructed of teak, and looks seaworthy. */
-  struct city *pcity;
-  struct city *port = NULL;
-  struct unit *bodyguard = NULL;
-  struct unit_type *punittype = get_unit_type(punit->type);
-  int best = 4 * punittype->move_rate, x = punit->x, y = punit->y;
-  int n = 0, p = 0;
+#define LOGLEVEL_FERRY LOG_DEBUG
+/****************************************************************************
+  A helper for ai_manage_ferryboat.  Finds a passenger for the ferry.
+  Potential passengers signal the boats by setting their ai.ferry field to
+  FERRY_WANTED.
+
+  TODO: lift the path off the map
+****************************************************************************/
+static bool ai_ferry_findcargo(struct unit *punit)
+{
+  /* Path-finding stuff */
+  struct pf_map *map;
+  struct pf_parameter parameter;
+  
+  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is looking for cargo.");
 
-  CHECK_UNIT(punit);
+  pft_fill_default_parameter(&parameter);
+  pft_fill_unit_overlap_param(&parameter, punit);
+  /* We are looking for our units, no need to look into the unknown */
+  parameter.get_TB = no_fights_or_unknown;
+  parameter.omniscience = FALSE;
+  
+  map = pf_create_map(&parameter);
+  while (pf_next(map)) {
+    struct pf_position pos;
 
-  if (!unit_list_find(&map_get_tile(punit->x, punit->y)->units, 
punit->ai.passenger)) {
-    punit->ai.passenger = 0;
+    pf_next_get_position(map, &pos);
+    
+    unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
+      if (aunit->ai.ferryboat == FERRY_WANTED) {
+        UNIT_LOG(LOGLEVEL_FERRY, punit, 
+                 "Found a potential cargo %s[%d](%d,%d), going there",
+                 unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
+        set_goto_dest(punit, aunit->x, aunit->y);
+        /* Exchange phone numbers */
+        ai_set_passenger(punit, aunit);
+        ai_set_ferry(aunit, punit);
+        pf_destroy_map(map);
+        return TRUE;
+      }
+    } unit_list_iterate_end;
   }
+  
+  pf_destroy_map(map);
+  return FALSE;
+}
 
-  unit_list_iterate(map_get_tile(punit->x, punit->y)->units, aunit)
-    if (punit->owner != aunit->owner) {
-      continue;
-    }
-    if (aunit->ai.ferryboat == punit->id) {
-      if (punit->ai.passenger == 0) {
-        punit->ai.passenger = aunit->id; /* oops */
-      }
-      if (is_military_unit(aunit) && punit->ai.bodyguard == BODYGUARD_NONE) {
-        /* Acquire some protection as we deliver an invasion army */
-        UNIT_LOG(LOG_DEBUG, punit, "shout out for a bodyguard");
-        punit->ai.bodyguard = BODYGUARD_WANTED;
-      }
-      p++;
-      bodyguard = unit_list_find(&map_get_tile(punit->x, punit->y)->units, 
aunit->ai.bodyguard);
-      if (is_goto_dest_set(aunit)) { /* HACK */
-       pcity = map_get_city(goto_dest_x(aunit), goto_dest_y(aunit));
-      } else {
-       pcity = NULL;
-      }
-      if (aunit->ai.bodyguard == BODYGUARD_NONE || bodyguard ||
-         (pcity && pcity->ai.invasion >= 2)) {
-       if (pcity) {
-         UNIT_LOG(LOG_DEBUG, punit, "Ferrying to %s to %s, invasion = %d, body 
= %d",
-                 unit_name(aunit->type), pcity->name,
-                 pcity->ai.invasion, aunit->ai.bodyguard);
-       }
-        n++;
-        handle_unit_activity_request(aunit, ACTIVITY_SENTRY);
-        if (bodyguard) {
-          handle_unit_activity_request(bodyguard, ACTIVITY_SENTRY);
-        }
-      }
-    }
-  unit_list_iterate_end;
+/****************************************************************************
+  It's about 12 feet square and has a capacity of almost 1000 pounds.
+  It is well constructed of teak, and looks seaworthy.
+
+  Manage ferryboat.  If there is a passenger-in-charge, we let it drive the 
+  boat.  If there isn't, appoint one from those we have on board.
+
+  If there is no one aboard, look for potential cargo.  If none found, 
+  explore and then go to the nearest port.
+****************************************************************************/
+static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
+{
+  struct city *pcity;
+  struct tile *ptile = map_get_tile(punit->x, punit->y);
+
+  CHECK_UNIT(punit);
 
-  /* we try to recover hitpoints if we are in a city, before we leave */
-  if (punit->hp < punittype->hp 
+  /* Try to recover hitpoints if we are in a city, before we do anything */
+  if (punit->hp < unit_type(punit)->hp 
       && (pcity = map_get_city(punit->x, punit->y))) {
-    /* Don't do anything, just wait in the city */
-    UNIT_LOG(LOG_DEBUG, punit, "waiting in %s to recover hitpoints "
-            "before ferrying", pcity->name);
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints", 
+             pcity->name);
     return;
   }
 
-  if (p != 0) {
-    UNIT_LOG(LOG_DEBUG, punit, "in manage_ferryboat p=%d, n=%d", p, n);
-    if (is_goto_dest_set(punit) && punit->moves_left > 0 && n != 0) {
-      (void) ai_unit_gothere(punit);
-    } else if (n == 0 && !map_get_city(punit->x, punit->y)) { /* rest in a 
city, for unhap */
-      port = find_nearest_safe_city(punit);
-      if (port && !ai_unit_goto(punit, port->x, port->y)) {
-        return; /* oops! */
-      }
-      send_unit_info(pplayer, punit); /* to get the crosshairs right -- Syela 
*/
-    } else {
-      UNIT_LOG(LOG_DEBUG, punit, "Ferryboat %d@(%d,%d) stalling.",
-                   punit->id, punit->x, punit->y);
-      if(is_barbarian(pplayer)) /* just in case */
-        (void) ai_manage_explorer(punit);
-    }
+  /* Check if we are an empty barbarian boat and so not needed */
+  if (is_barbarian(pplayer) 
+      && unit_list_size(&ptile->units) < 2 ) {
+    wipe_unit(punit);
     return;
   }
 
-  /* check if barbarian boat is empty and so not needed - the crew has landed 
*/
-  if( is_barbarian(pplayer) && unit_list_size(&map_get_tile(punit->x, 
punit->y)->units)<2 ) {
-    wipe_unit(punit);
-    return;
+  /* Do we have the passenger-in-charge on board? */
+  if (punit->ai.passenger > 0 
+      && !unit_list_find(&ptile->units, punit->ai.passenger)) {
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "lost passenger-in-charge[%d], resetting",
+             punit->ai.passenger);
+    punit->ai.passenger = 0;
   }
 
-  /* ok, not carrying anyone, even the ferryman */
-  punit->ai.passenger = 0;
-  UNIT_LOG(LOG_DEBUG, punit, "Ferryboat is lonely.");
-  handle_unit_activity_request(punit, ACTIVITY_IDLE);
-
-  /* Release bodyguard and let it roam */
-  ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
-  if (bodyguard) {
-    ai_military_attack(pplayer, bodyguard);
+  if (punit->ai.passenger <= 0) {
+    struct unit *bodyguard = NULL;
+    
+    /* Try to select passanger-in-charge from among their passengers */
+    unit_list_iterate(ptile->units, aunit) {
+      if (aunit->ai.ferryboat != punit->id) {
+        continue;
+      }
+      
+      if (aunit->ai.ai_role != AIUNIT_ESCORT) {
+        /* Bodyguards shouldn't be in charge of boats... */
+        UNIT_LOG(LOGLEVEL_FERRY, punit, 
+                 "appointed %s[%d] our passenger-in-charge",
+                 unit_type(aunit)->name, aunit->id);
+        punit->ai.passenger = aunit->id;
+        break;
+      } else {
+        bodyguard = aunit;
+      }
+    } unit_list_iterate_end;
+    
+    if (punit->ai.passenger <= 0 && bodyguard) {
+      UNIT_LOG(LOGLEVEL_FERRY, punit, 
+               "has to take %s[%d] as our passenger-in-charge",
+               unit_type(bodyguard)->name, bodyguard->id);
+      punit->ai.passenger = bodyguard->id;
+    }
   }
 
-  if (IS_ATTACKER(punit)) {
-    if (punit->moves_left > 0) ai_manage_military(pplayer, punit);
-    return;
-  } /* AI used to build frigates to attack and then use them as ferries -- 
Syela */
+  if (punit->ai.passenger > 0) {
+    int bossid = punit->ai.passenger;    /* For reference */
+    struct unit *boss = find_unit_by_id(bossid);
+    int id = punit->id;                  /* To check if survived */
+    int moves_left = punit->moves_left;  /* Loop prevention */
 
-  /*** Find work ***/
-  CHECK_UNIT(punit);
+    assert(boss);
 
-  generate_warmap(map_get_city(punit->x, punit->y), punit);
-  p = 0; /* yes, I know it's already zero. -- Syela */
-  best = 9999;
-  x = -1; y = -1;
-  unit_list_iterate(pplayer->units, aunit) {
-    if (aunit->ai.ferryboat != 0
-       && WARMAP_SEACOST(aunit->x, aunit->y) < best
-       && ground_unit_transporter_capacity(aunit->x, aunit->y, pplayer) <= 0
-        && is_at_coast(aunit->x, aunit->y)) {
-      UNIT_LOG(LOG_DEBUG, punit, "Found a potential pickup %d@(%d, %d)",
-                   aunit->id, aunit->x, aunit->y);
-      x = aunit->x;
-      y = aunit->y;
-      best = WARMAP_SEACOST(x, y);
+    if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
+      /* Temporary hack: settlers all go in the end, forcing them 
+       * earlier might mean uninitialised cache, so just wait for them */
+      return;
     }
-    if (is_sailing_unit(aunit)
-       && is_ocean(map_get_terrain(aunit->x, aunit->y))) {
-      p++;
+
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "passing control to %s[%d]",
+             unit_type(boss)->name, bossid);
+    ai_manage_unit(pplayer, boss);
+    
+    if (!find_unit_by_id(id) || punit->moves_left < moves_left) {
+      return;
     }
-  } unit_list_iterate_end;
-  if (best < 4 * unit_type(punit)->move_rate) {
-    /* Pickup is within 4 turns to grab, so move it! */
-    set_goto_dest(punit, x, y);
-    UNIT_LOG(LOG_DEBUG, punit, "Found a friend and going to him @(%d, %d)",
-             x, y);
-    (void) ai_unit_gothere(punit);
-    return;
-  }
+    /* We are alive and didn't spend any moves.  We are stuck! 
+     * NB: it can be that punit->ai.passenger has changed by now,
+     * for example if the boss has landed */
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "taking control back from [%d]", 
+             bossid);
 
-  /* do cool stuff here */
-  CHECK_UNIT(punit);
+  } else {
+    /* Not carrying anyone, even the ferryman */
 
-  if (punit->moves_left == 0) return;
-  pcity = find_city_by_id(punit->homecity);
-  if (pcity) {
-    if (!ai_handicap(pplayer, H_TARGETS) ||
-        unit_move_turns(punit, pcity->x, pcity->y) < p) {
-      set_goto_dest(punit, pcity->x, pcity->y);
-      UNIT_LOG(LOG_DEBUG, punit, "No friends.  Going home.");
-      (void) ai_unit_gothere(punit);
+   if (IS_ATTACKER(punit) && punit->moves_left > 0) {
+      /* AI used to build frigates to attack and then use them as ferries 
+       * -- Syela */
+      ai_manage_military(pplayer, punit);
+      return;
+    }
+
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is not carrying anyone.");
+    ai_set_passenger(punit, NULL);
+    handle_unit_activity_request(punit, ACTIVITY_IDLE);
+    CHECK_UNIT(punit);
+
+    /* Try to find passengers */
+    if (ai_ferry_findcargo(punit)) {
+      ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
       return;
     }
   }
-  if (is_ocean(map_get_terrain(punit->x, punit->y))) {
-    /* thanks, Tony */
-    (void) ai_manage_explorer(punit);
+
+  CHECK_UNIT(punit);
+  
+  if (punit->moves_left == 0 
+      || !is_ocean(map_get_terrain(punit->x, punit->y))) {
+    return;
+  }
+  
+  (void) ai_manage_explorer(punit);
+  if (punit->moves_left > 0) {
+    struct city *pcity = find_nearest_safe_city(punit);
+    if (pcity) {
+      set_goto_dest(punit, pcity->x, pcity->y);
+      UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
+      (void) ai_unit_goto(punit, pcity->x, pcity->y);
+    }
   }
+ 
   return;
 }
 
Index: server/settlers.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v
retrieving revision 1.173
diff -u -r1.173 settlers.c
--- server/settlers.c   23 Sep 2003 15:59:05 -0000      1.173
+++ server/settlers.c   1 Oct 2003 14:34:14 -0000
@@ -54,6 +54,8 @@
 BV_DEFINE(enemy_mask, MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS);
 static enemy_mask enemies[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
 
+static void auto_settler_findwork(struct player *pplayer, 
+                                  struct unit *punit);
 static void auto_settlers_player(struct player *pplayer); 
 static bool is_already_assigned(struct unit *myunit, struct player *pplayer,
                                int x, int y);
@@ -1188,75 +1190,6 @@
 }
 
 /**************************************************************************
-  Handles GOTO for settlers. Only ever used in auto_settler_findwork 
-  below.
-**************************************************************************/
-static bool ai_gothere(struct unit *punit, int gx, int gy, struct unit 
*ferryboat)
-{
-  struct player *pplayer = unit_owner(punit);
-
-  if (same_pos(gx, gy, punit->x, punit->y)) {
-    return TRUE;
-  }
-
-  /*
-   * Go by boat if we can't go by foot, or we have already found a boat
-   * (since boat travel is usually faster). 
-   */
-  if (!goto_is_sane(punit, gx, gy, TRUE)
-      || (ferryboat
-          && goto_is_sane(ferryboat, gx, gy, TRUE)
-          && (!is_tiles_adjacent(punit->x, punit->y, gx, gy)
-              || could_unit_move_to_tile(punit, gx, gy) == 0))) {
-    int x, y;
-
-    punit->ai.ferryboat = find_boat(pplayer, &x, &y, 1);
-    /* TODO: check if we found a boat */
-    freelog(LOG_DEBUG, "%d@(%d, %d): Looking for BOAT.",
-           punit->id, punit->x, punit->y);
-    if (!same_pos(x, y, punit->x, punit->y)) {
-      if (!ai_unit_goto(punit, x, y)) {
-        return FALSE; /* died */
-      }
-    }
-    ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units),
-                               punit->ai.ferryboat);
-    set_goto_dest(punit, gx, gy);
-
-    if (ferryboat && (ferryboat->ai.passenger == 0
-                      || ferryboat->ai.passenger == punit->id)) {
-      UNIT_LOG(LOG_DEBUG, punit, "We have FOUND BOAT %d, ABOARD",
-               ferryboat->id);
-      handle_unit_activity_request(punit, ACTIVITY_SENTRY);
-      ferryboat->ai.passenger = punit->id;
-      set_goto_dest(ferryboat, gx, gy);
-      if (!ai_unit_goto(ferryboat, gx, gy)) {
-        return FALSE; /* died */
-      }
-      handle_unit_activity_request(punit, ACTIVITY_IDLE);
-    } /* need to zero pass & ferryboat at some point. */
-  }
-
-  /*
-   * Now check if we can walk by foot to our destination
-   * (possibly exiting our ferry)
-   */
-  if (goto_is_sane(punit, gx, gy, TRUE)
-      && punit->moves_left > 0
-      && (!ferryboat
-          || (is_tiles_adjacent(punit->x, punit->y, gx, gy)
-              && could_unit_move_to_tile(punit, gx, gy) != 0))) {
-    set_goto_dest(punit, gx, gy);
-    if (!ai_unit_goto(punit, gx, gy)) {
-      return FALSE; /* died */
-    }
-    punit->ai.ferryboat = 0;
-  }
-
-  return TRUE;
-}
-
-/**************************************************************************
   find some work for the settler
 **************************************************************************/
 static void auto_settler_findwork(struct player *pplayer, struct unit *punit)
@@ -1311,8 +1244,8 @@
   }
 
   /* We've now worked out what to do; go to it! */
-  if (!ai_gothere(punit, gx, gy, ferryboat)) {
-    /* died */
+  if (!ai_gothere(pplayer, punit, gx, gy)) {
+    /* Died or got stuck */
     return;
   }
 
@@ -1384,7 +1317,7 @@
   freelog(LOG_DEBUG, "Warmth = %d, game.globalwarming=%d",
          pplayer->ai.warmth, game.globalwarming);
   unit_list_iterate(pplayer->units, punit) {
-    if (punit->ai.control
+    if ((punit->ai.control || pplayer->ai.control)
        && (unit_flag(punit, F_SETTLERS)
            || unit_flag(punit, F_CITIES))) {
       freelog(LOG_DEBUG, "%s's settler at (%d, %d) is ai controlled.",

[Prev in Thread] Current Thread [Next in Thread]