Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2005:
[Freeciv-Dev] (PR#12572) Cleanup of hunter code
Home

[Freeciv-Dev] (PR#12572) Cleanup of hunter code

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#12572) Cleanup of hunter code
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Mon, 21 Mar 2005 04:49:11 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=12572 >

I got tired of seeing the AI assigns mech inf as hunters, so I added hints
in the ruleset for which types of units are allowed as hunters.

Annoyed at seeing hunters pass closer and better targets, because they
were hunting for some other target, I combined the findjob and the manage
functions so that the hunter can recheck its targets every turn. Hunter
code now uses pf exclusively. While rechecking every turn may be slower,
not doing first a pf then a warmap path-finding should compensate.

We now use real path-finding to find work, so no more setting naval units
caught in an inlank lake to hunt for naval targets in the big ocean.

Hunters now know the value of gameloss units (beware!).

PS Before commit, add the AI hints to the other rulesets as well.

Speed test with a large savegame for two turns:
Pre-patch:   user    0m41.189s
Post-patch:  user    0m42.116s

So no big difference in speed.

  - Per

Index: ai/aihunt.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihunt.c,v
retrieving revision 1.15
diff -u -r1.15 aihunt.c
--- ai/aihunt.c 19 Mar 2005 23:45:34 -0000      1.15
+++ ai/aihunt.c 20 Mar 2005 11:56:52 -0000
@@ -193,7 +193,7 @@
   int want = 0;
 
   virtualunit = create_unit_virtual(pplayer, pcity, best_type, veteran);
-  want = ai_hunter_findjob(pplayer, virtualunit);
+  want = ai_hunter_manage(pplayer, virtualunit);
   destroy_unit_virtual(virtualunit);
   if (want > choice->want) {
     CITY_LOG(LOGLEVEL_HUNT, pcity, "pri hunter w/ want %d", want);
@@ -235,126 +235,18 @@
 }
 
 /**************************************************************************
-  Does this unit qualify as a hunter? FIXME: Should also check for
-  combat damage? Or should repair code preempt this code? Just saying
-  FALSE for damaged units does NOT work.
+  Does this unit qualify as a hunter?
 **************************************************************************/
 bool ai_hunter_qualify(struct player *pplayer, struct unit *punit)
 {
-  struct unit_type *punittype = get_unit_type(punit->type);
-
   if (is_barbarian(pplayer)
-      || !(is_sailing_unit(punit) || is_ground_unit(punit))
-      || punittype->move_rate < 2 * SINGLE_MOVE
-      || ATTACK_POWER(punit) <= 1
       || punit->owner != pplayer->player_no) {
     return FALSE;
   }
-  if (unit_flag(punit, F_PARTIAL_INVIS)) {
+  if (unit_has_role(punit->type, L_HUNTER)) {
     return TRUE;
   }
-  /* TODO: insert better algo here */
-  return IS_ATTACKER(punit);
-}
-
-/**************************************************************************
-  Return want for making this (possibly virtual) unit into a hunter. Sets
-  punit->ai.target to target's id.
-**************************************************************************/
-int ai_hunter_findjob(struct player *pplayer, struct unit *punit)
-{
-  int best_id = -1, best_val = -1;
-
-  assert(!is_barbarian(pplayer));
-  assert(pplayer->is_alive);
-
-  players_iterate(aplayer) {
-    if (!aplayer->is_alive || !is_player_dangerous(pplayer, aplayer)) {
-      continue;
-    }
-    /* Note that we need not (yet) be at war with aplayer */
-    unit_list_iterate(aplayer->units, target) {
-      struct tile *ptile = target->tile;
-      int dist1, dist2, stackthreat = 0, stackcost = 0;
-      struct unit *defender;
-
-      if (ptile->city
-          || TEST_BIT(target->ai.hunted, pplayer->player_no)
-          || (!is_ocean(ptile->terrain) && is_sailing_unit(punit))
-          || (is_ocean(ptile->terrain) && is_ground_unit(punit))
-          || (!is_sailing_unit(target) && is_sailing_unit(punit))
-          || (is_sailing_unit(target) && !is_sailing_unit(punit))
-          || !goto_is_sane(punit, target->tile, TRUE)) {
-        /* Can't hunt this one. */
-        continue;
-      }
-      if (target->ai.cur_pos && target->ai.prev_pos) {
-        dist1 = real_map_distance(punit->tile, *target->ai.cur_pos);
-        dist2 = real_map_distance(punit->tile, *target->ai.prev_pos);
-      } else {
-        dist1 = dist2 = 0;
-      }
-      UNIT_LOG(LOGLEVEL_HUNT, punit, "considering chasing %s(%d, %d) id %d "
-               "dist1 %d dist2 %d",
-              unit_type(target)->name, TILE_XY(target->tile),
-               target->id, dist1, dist2);
-      /* We can't attack units stationary in cities. */
-      if (map_get_city(target->tile) 
-          && (dist2 == 0 || dist1 == dist2)) {
-        continue;
-      }
-      /* We can't chase if we aren't faster or on intercept vector */
-      if (unit_type(punit)->move_rate < unit_type(target)->move_rate
-          && dist1 >= dist2) {
-        UNIT_LOG(LOGLEVEL_HUNT, punit, "giving up racing %s (%d, %d)->(%d, 
%d)",
-                 unit_type(target)->name,
-                target->ai.prev_pos ? (*target->ai.prev_pos)->x : -1,
-                 target->ai.prev_pos ? (*target->ai.prev_pos)->y : -1,
-                 TILE_XY(target->tile));
-        continue;
-      }
-      unit_list_iterate(ptile->units, sucker) {
-        stackthreat += ATTACK_POWER(sucker);
-        if (unit_flag(sucker, F_DIPLOMAT)) {
-          stackthreat += 500;
-        }
-        stackcost += unit_type(sucker)->build_cost;
-      } unit_list_iterate_end;
-      defender = get_defender(punit, target->tile);
-      if (stackcost < unit_type(punit)->build_cost
-          && unit_win_chance(punit, defender) < 0.6) {
-        UNIT_LOG(LOGLEVEL_HUNT, punit, "chickening out from attacking %s"
-                 "(%d, %d)", unit_type(defender)->name,
-                 TILE_XY(defender->tile));
-        continue;
-      }
-      stackthreat *= 9; /* WAG */
-      stackthreat += stackcost;
-      stackthreat /= real_map_distance(punit->tile, target->tile) + 1;
-      UNIT_LOG(LOGLEVEL_HUNT, punit, "considering hunting %s's %s(%d, %d) id "
-               "id %d with want %d, dist1 %d, dist2 %d", 
-               unit_owner(defender)->name, unit_type(defender)->name, 
-               TILE_XY(defender->tile), defender->id, stackthreat, dist1,
-               dist2);
-      /* TO DO: probably ought to WAG down targets of players we are not (yet)
-       * at war with */
-      /* Ok, now we FINALLY have a candidate */
-      if (stackthreat > best_val) {
-        best_val = stackthreat;
-        best_id = target->id;
-      }
-    } unit_list_iterate_end;
-  } players_iterate_end;
-
-  punit->ai.target = best_id;
-  if (best_val < MORT) {
-    /* Safety against silly missions. Unset our role. */
-    best_val = -1;
-  } else {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "suggest chasing %d with want %d",
-             best_id, best_val);
-  }
-  return best_val;
+  return FALSE;
 }
 
 /**************************************************************************
@@ -435,68 +327,186 @@
 }
 
 /**************************************************************************
-  Manage a hunter unit. We assume it has AIUNIT_HUNTER role and a valid
-  target in punit->ai.hunt.
+  Calculate desire to crush this target.
+**************************************************************************/
+static void ai_hunter_juiciness(struct player *pplayer, struct unit *punit,
+                                struct unit *target, int *stackthreat,
+                                int *stackcost)
+{
+  *stackthreat = 0;
+  *stackcost = 0;
 
-  Returns FALSE if we could not use unit. If we return TRUE, unit might
-  be dead.
+  unit_list_iterate(target->tile->units, sucker) {
+    *stackthreat += ATTACK_POWER(sucker);
+    if (unit_flag(sucker, F_GAMELOSS)) {
+      *stackcost += 1000;
+      *stackthreat += 5000;
+    }
+    if (unit_flag(sucker, F_DIPLOMAT)) {
+      *stackthreat += 500; /* extra threatening */
+    }
+    *stackcost += unit_type(sucker)->build_cost;
+  } unit_list_iterate_end;
+
+  *stackthreat *= 9; /* WAG - reduced by distance later */
+  *stackthreat += *stackcost;
+}
+
+/**************************************************************************
+  Manage a (possibly virtual) hunter. Return the want for building a 
+  hunter like this. If we return 0, then we have nothing to do with
+  the hunter. If we return -1, then we succeeded, and can try again.
+  If we return > 0 then we are hunting but ran out of moves (this is
+  also used for construction want).
+
+  We try to keep track of our original target, but also opportunistically
+  snatch up closer targts if they are better.
+
+  We set punit->ai.target to target's id.
 **************************************************************************/
-bool ai_hunter_manage(struct player *pplayer, struct unit *punit)
+int ai_hunter_manage(struct player *pplayer, struct unit *punit)
 {
-  struct unit *target = find_unit_by_id(punit->ai.target);
-  int sanity_own = punit->id;
-  int sanity_target;
-
-  CHECK_UNIT(punit);
-  assert(punit->ai.ai_role == AIUNIT_HUNTER);
-
-  /* Check that target is valid. */
-  if (!target
-      || !goto_is_sane(punit, target->tile, TRUE)
-      || map_get_city(target->tile)
-      || !is_player_dangerous(pplayer, unit_owner(target))) {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "target vanished");
-    ai_unit_new_role(punit, AIUNIT_NONE, NULL);
-    return FALSE;
-  }
-  UNIT_LOG(LOGLEVEL_HUNT, punit, "hunting %d(%d, %d)",
-          target->id, TILE_XY(target->tile));
-  sanity_target = target->id;
-
-  /* Check if we can nuke it */
-  ai_hunter_try_launch(pplayer, punit, target);
-  /* Check if we have nuked it */
-  if (target != find_unit_by_id(sanity_target)) {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished");
-    ai_unit_new_role(punit, AIUNIT_NONE, NULL);
-    return TRUE;
-  }
+  bool is_virtual = (punit->id == 0);
+  struct pf_parameter parameter;
+  struct pf_map *map;
+  int limit = unit_move_rate(punit) * 6;
+  struct unit *original_target = find_unit_by_id(punit->ai.target);
+  int original_threat = 0, original_cost = 0;
 
-  /* Go towards it. */
-  if (!ai_unit_goto(punit, target->tile)) {
-    return TRUE;
-  }
+  assert(!is_barbarian(pplayer));
+  assert(pplayer->is_alive);
 
-  /* Check if we can nuke it now */
-  ai_hunter_try_launch(pplayer, punit, target);
-  if (target != find_unit_by_id(sanity_target)) {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished");
-    ai_unit_new_role(punit, AIUNIT_NONE, NULL);
-    return TRUE;
-  }
+  pft_fill_unit_parameter(&parameter, punit);
+  map = pf_create_map(&parameter);
 
-  /* If we are adjacent - RAMMING SPEED! */
-  if (is_tiles_adjacent(punit->tile, target->tile)) {
-    ai_unit_attack(punit, target->tile);
+  if (original_target) {
+    ai_hunter_juiciness(pplayer, punit, original_target, 
+                        &original_threat, &original_cost);
   }
 
-  if (!find_unit_by_id(sanity_own)) {
-    return TRUE;
-  }
-  if (target != find_unit_by_id(sanity_target)) {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished");
-    ai_unit_new_role(punit, AIUNIT_NONE, NULL);
-  }
+  pf_iterator(map, pos) {
+    /* End faster if we have a target */
+    if (pos.total_MC > limit) {
+      UNIT_LOG(LOGLEVEL_HUNT, punit, "gave up finding hunt target");
+      pf_destroy_map(map);
+      return 0;
+    }
+    unit_list_iterate_safe(pos.tile->units, target) {
+      struct player *aplayer = unit_owner(target);
+      int dist1, dist2, stackthreat = 0, stackcost = 0;
+      int sanity_target = target->id;
+
+      /* Note that we need not (yet) be at war with aplayer */
+      if (!is_player_dangerous(pplayer, aplayer)) {
+        continue;
+      }
+      if (pos.tile->city
+          || !can_unit_attack_tile(punit, pos.tile)
+          || TEST_BIT(target->ai.hunted, pplayer->player_no)) {
+        /* Can't hunt this one.  The bit is cleared in the beginning
+         * of each turn. */
+        continue;
+      }
+      if (!unit_flag(target, F_DIPLOMAT)
+          && get_transporter_capacity(target) == 0
+          && !unit_flag(target, F_GAMELOSS)) {
+        /* Won't hunt this one. */
+        continue;
+      }
 
-  return TRUE;
+      /* Figure out whether unit is coming closer */
+      if (target->ai.cur_pos && target->ai.prev_pos) {
+        dist1 = real_map_distance(punit->tile, *target->ai.cur_pos);
+        dist2 = real_map_distance(punit->tile, *target->ai.prev_pos);
+      } else {
+        dist1 = dist2 = 0;
+      }
+      UNIT_LOG(LOGLEVEL_HUNT, punit, "considering chasing %s(%d, %d) id %d "
+               "dist1 %d dist2 %d",
+              unit_type(target)->name, TILE_XY(target->tile),
+               target->id, dist1, dist2);
+
+      /* We can't chase if we aren't faster or on intercept vector */
+      if (unit_type(punit)->move_rate < unit_type(target)->move_rate
+          && dist1 >= dist2) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "giving up racing %s (%d, %d)->(%d, 
%d)",
+                 unit_type(target)->name,
+                target->ai.prev_pos ? (*target->ai.prev_pos)->x : -1,
+                 target->ai.prev_pos ? (*target->ai.prev_pos)->y : -1,
+                 TILE_XY(target->tile));
+        continue;
+      }
+
+      /* Calculate juiciness of target, compare with existing target,
+       * if any. */
+      ai_hunter_juiciness(pplayer, punit, target, &stackthreat, &stackcost);
+      stackcost *= unit_win_chance(punit, get_defender(punit, target->tile));
+      if (stackcost < unit_type(punit)->build_cost) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "%d is too expensive (it %d vs us %d)", 
+                 target->id, stackcost, unit_type(punit)->build_cost);
+        continue; /* Too expensive */
+      }
+      stackthreat /= pos.total_MC + 1;
+      if (!is_virtual 
+          && original_target != target
+          && original_threat > stackthreat) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "Unit %d is not worse than %d", 
+                 target->id, original_target->id);
+        continue; /* The threat we found originally was worse than this! */
+      }
+      if (stackthreat < unit_type(punit)->build_cost) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "%d is not worth it", target->id);
+        continue; /* Not worth it */
+      }
+
+      UNIT_LOG(LOGLEVEL_HUNT, punit, "hunting %s's %s(%d, %d) "
+               "id %d with want %d, dist1 %d, dist2 %d", 
+               unit_owner(target)->name, unit_type(target)->name, 
+               TILE_XY(target->tile), target->id, stackthreat, dist1,
+               dist2);
+      /* Ok, now we FINALLY have a target worth destroying! */
+      punit->ai.target = target->id;
+      if (is_virtual) {
+        pf_destroy_map(map);
+        return stackthreat;
+      }
+
+      /* This assigns missiles to us */
+      ai_unit_new_role(punit, AIUNIT_HUNTER, target->tile);
+
+      /* Check if we can nuke it */
+      ai_hunter_try_launch(pplayer, punit, target);
+
+      /* Check if we have nuked it */
+      if (target != find_unit_by_id(sanity_target)) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished by cargo (pre)");
+        ai_unit_new_role(punit, AIUNIT_NONE, NULL);
+        pf_destroy_map(map);
+        return -1; /* try again */
+      }
+
+      /* Go towards it. */
+      if (!ai_unit_execute_path(punit, pf_get_path(map, target->tile))) {
+        pf_destroy_map(map);
+        return 0;
+      }
+
+      /* Check if we can nuke it now */
+      ai_hunter_try_launch(pplayer, punit, target);
+      if (target != find_unit_by_id(sanity_target)) {
+        UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished by cargo (post)");
+        ai_unit_new_role(punit, AIUNIT_NONE, NULL);
+        pf_destroy_map(map);
+        return -1; /* try again */
+      }
+
+      pf_destroy_map(map);
+      punit->ai.done = TRUE;
+      return stackthreat; /* still have work to do */
+    } unit_list_iterate_safe_end;
+  } pf_iterator_end;
+
+  UNIT_LOG(LOGLEVEL_HUNT, punit, "ran out of map finding hunt target");
+  pf_destroy_map(map);
+  return 0; /* found nothing */
 }
Index: ai/aihunt.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihunt.h,v
retrieving revision 1.3
diff -u -r1.3 aihunt.h
--- ai/aihunt.h 3 Sep 2004 04:22:36 -0000       1.3
+++ ai/aihunt.h 20 Mar 2005 11:56:52 -0000
@@ -22,7 +22,6 @@
 void ai_hunter_choice(struct player *pplayer, struct city *pcity,
                       struct ai_choice *choice);
 bool ai_hunter_qualify(struct player *pplayer, struct unit *punit);
-int ai_hunter_findjob(struct player *pplayer, struct unit *punit);
-bool ai_hunter_manage(struct player *pplayer, struct unit *punit);
+int ai_hunter_manage(struct player *pplayer, struct unit *punit);
 
 #endif
Index: common/unittype.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unittype.c,v
retrieving revision 1.50
diff -u -r1.50 unittype.c
--- common/unittype.c   14 Mar 2005 20:26:25 -0000      1.50
+++ common/unittype.c   20 Mar 2005 11:56:52 -0000
@@ -53,7 +53,7 @@
   "Ferryboat", "Barbarian", "BarbarianTech", "BarbarianBoat",
   "BarbarianBuild", "BarbarianBuildTech", "BarbarianLeader",
   "BarbarianSea", "BarbarianSeaTech", "Cities", "Settlers",
-  "GameLoss", "Diplomat"
+  "GameLoss", "Diplomat", "Hunter"
 };
 static const char *unit_class_names[] = {
   "Air",
Index: common/unittype.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unittype.h,v
retrieving revision 1.41
diff -u -r1.41 unittype.h
--- common/unittype.h   14 Mar 2005 20:26:25 -0000      1.41
+++ common/unittype.h   20 Mar 2005 11:56:52 -0000
@@ -152,6 +152,7 @@
   L_SETTLERS,          /* can improve terrain */
   L_GAMELOSS,          /* loss results in loss of game */
   L_DIPLOMAT,          /* can do diplomat actions */
+  L_HUNTER,             /* AI hunter type unit */
   L_LAST
 };
 #define L_MAX 64
Index: data/default/units.ruleset
===================================================================
RCS file: /home/freeciv/CVS/freeciv/data/default/units.ruleset,v
retrieving revision 1.66
diff -u -r1.66 units.ruleset
--- data/default/units.ruleset  13 Mar 2005 17:59:40 -0000      1.66
+++ data/default/units.ruleset  20 Mar 2005 11:56:53 -0000
@@ -191,6 +191,7 @@
 ; "GameLoss"   = losing one of these units means you lose the game, but it
 ;                is produced without homecity and upkeep
 ; "Diplomat"   = can do diplomat actions (see diplchance server option)
+; "Hunter"      = AI hint: good for hunting other units
 
 [unit_settlers]
 name          = _("Settlers")
@@ -706,7 +707,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = "Horse"
-roles         = "AttackFast", "Hut", "Barbarian"
+roles         = "AttackFast", "Hut", "Barbarian", "Hunter"
 
 [unit_chariot]
 name          = _("Chariot")
@@ -734,7 +735,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = "Horse"
-roles         = "AttackFast", "Hut"
+roles         = "AttackFast", "Hut", "Hunter"
 
 [unit_knights]
 name          = _("Knights")
@@ -763,7 +764,7 @@
 uk_gold       = 0
 flags         = "Horse"
 roles         = "AttackFast", "HutTech", "BarbarianTech",
-                "BarbarianBuildTech", "BarbarianSeaTech"
+                "BarbarianBuildTech", "BarbarianSeaTech", "Hunter"
 
 [unit_dragoons]
 name          = _("Dragoons")
@@ -791,7 +792,8 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = "Horse"
-roles         = "AttackFast", "BarbarianBuildTech", "BarbarianSeaTech"
+roles         = "AttackFast", "BarbarianBuildTech", "BarbarianSeaTech",
+                "Hunter"
 
 [unit_cavalry]
 name          = _("Cavalry")
@@ -819,7 +821,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = ""
-roles         = "AttackFast"
+roles         = "AttackFast", "Hunter"
 
 [unit_armor]
 name          = _("Armor")
@@ -847,7 +849,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = ""
-roles         = "AttackFast"
+roles         = "AttackFast", "Hunter"
 
 [unit_catapult]
 name          = _("Catapult")
@@ -1225,7 +1227,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = ""
-roles         = ""
+roles         = "Hunter"
 
 [unit_ironclad]
 name          = _("Ironclad")
@@ -1253,7 +1255,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = ""
-roles         = ""
+roles         = "Hunter"
 
 [unit_destroyer]
 name          = _("Destroyer")
@@ -1281,7 +1283,7 @@
 uk_food       = 0
 uk_gold       = 0
 flags         = ""
-roles         = ""
+roles         = "Hunter"
 helptext      = _("\
 TIP:  A very fast unit, which is very useful for hunting down enemy\
  Transports.\
@@ -1398,7 +1400,7 @@
 uk_gold       = 0
 flags         = "Partial_Invis", 
                "Missile_Carrier", "No_Land_Attack"
-roles         = ""
+roles         = "Hunter"
 helptext      = _("\
 Submarines have a very high strategic value, but have a weak\
  defence.\
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.234
diff -u -r1.234 srv_main.c
--- server/srv_main.c   20 Mar 2005 09:08:48 -0000      1.234
+++ server/srv_main.c   20 Mar 2005 11:56:53 -0000
@@ -607,6 +607,11 @@
 
   /* AI end of turn activities */
   auto_settlers_init();
+  players_iterate(pplayer) {
+    unit_list_iterate(pplayer->units, punit) {
+      punit->ai.hunted = 0;
+    } unit_list_iterate_end;
+  } players_iterate_end;
   phase_players_iterate(pplayer) {
     if (pplayer->ai.control) {
       ai_settler_init(pplayer);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.346
diff -u -r1.346 aiunit.c
--- ai/aiunit.c 14 Mar 2005 20:26:24 -0000      1.346
+++ ai/aiunit.c 20 Mar 2005 12:11:01 -0000
@@ -1094,19 +1094,6 @@
     return;
   }
 
-  /* Make unit a seahunter? */
-  if (punit->ai.ai_role == AIUNIT_HUNTER) {
-    return; /* Continue mission. */
-  }
-  if (ai_hunter_qualify(pplayer, punit)) {
-    UNIT_LOG(LOGLEVEL_HUNT, punit, "is qualified as hunter");
-    if (ai_hunter_findjob(pplayer, punit) > 0) {
-      UNIT_LOG(LOGLEVEL_HUNT, punit, "set as HUNTER");
-      ai_unit_new_role(punit, AIUNIT_HUNTER, NULL);
-      return;
-    }
-  }
-
 /* I'm not 100% sure this is the absolute best place for this... -- Syela */
   generate_warmap(map_get_city(punit->tile), punit);
 /* I need this in order to call unit_move_turns, here and in look_for_charge */
@@ -2038,6 +2025,30 @@
      we must make sure that previously reserved ferry is freed. */
   aiferry_clear_boat(punit);
 
+  /* Try hunting with this unit */
+  if (ai_hunter_qualify(pplayer, punit)) {
+    int result, sanity = punit->id;
+
+    UNIT_LOG(LOGLEVEL_HUNT, punit, "is qualified as hunter");
+    result = ai_hunter_manage(pplayer, punit);
+    if (!find_unit_by_id(sanity)) {
+      return; /* died */
+    }
+    if (result == -1) {
+      (void) ai_hunter_manage(pplayer, punit); /* More carnage */
+      return;
+    } else if (result >= 1) {
+      return; /* Done moving */
+    } else if (punit->ai.ai_role == AIUNIT_HUNTER) {
+      /* This should be very rare */
+      ai_unit_new_role(punit, AIUNIT_NONE, NULL);
+    }
+  } else if (punit->ai.ai_role == AIUNIT_HUNTER) {
+    ai_unit_new_role(punit, AIUNIT_NONE, NULL);
+  }
+
+  /* Do we have a specific job for this unit? If not, we default
+   * to attack. */
   ai_military_findjob(pplayer, punit);
 
   switch (punit->ai.ai_role) {
@@ -2069,7 +2080,7 @@
     ai_manage_hitpoint_recovery(punit);
     break;
   case AIUNIT_HUNTER:
-    ai_hunter_manage(pplayer, punit);
+    assert(FALSE); /* dealt with above */
     break;
   default:
     assert(FALSE);

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#12572) Cleanup of hunter code, Per I. Mathisen <=