diff -uNrX freeciv/diff_ignore freeciv/ai/aiunit.c freeciv-ai/ai/aiunit.c --- freeciv/ai/aiunit.c 2002-08-13 03:57:13.000000000 +0200 +++ freeciv-ai/ai/aiunit.c 2002-08-13 04:02:01.000000000 +0200 @@ -29,6 +29,7 @@ #include "shared.h" #include "timing.h" #include "unit.h" +#include "unittype.h" #include "barbarian.h" #include "citytools.h" @@ -47,6 +48,9 @@ #include "aiunit.h" +static bool find_nearest_friendly_port(struct unit *punit); +static bool find_nearest_friendly_barrack(struct unit *punit); + static void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat); static void ai_manage_military(struct player *pplayer,struct unit *punit); static void ai_manage_caravan(struct player *pplayer, struct unit *punit); @@ -1461,6 +1465,7 @@ struct unit *aunit; int val, def; int q = 0; + struct unit_type *punittype=get_unit_type(punit->type); /* tired of AI abandoning its cities! -- Syela */ if (punit->homecity != 0 && (pcity = find_city_by_id(punit->homecity))) { @@ -1530,6 +1535,14 @@ } } + /* is the unit damaged? */ + if ((punit->ai.ai_role == AIUNIT_RECOVER_HIT_POINTS + && punit->hp < punittype->hp) + || punit->hp < punittype->hp * 0.25) { + ai_unit_new_role(punit, AIUNIT_RECOVER_HIT_POINTS); + return; + } + /* I'm not 100% sure this is the absolute best place for this... -- Syela */ generate_warmap(map_get_city(punit->x, punit->y), punit); /* I need this in order to call unit_move_turns, here and in look_for_charge */ @@ -1946,6 +1959,43 @@ return TRUE; } +/********************************************************************** + Find safe city (preferably with Barracks). An allied player's city is + just as good as one of our own, since both replenish our hitpoints and + reduce unhappiness. + Returns TRUE with punit->goto_dest_* set or FALSE. + Be aware that the function destroys punit->goto_dest_* +***********************************************************************/ +static bool find_nearest_friendly_barrack(struct unit *punit) +{ + struct player *pplayer = unit_owner(punit); + int best = 6 * THRESHOLD + 1, cur; + + generate_warmap(map_get_city(punit->x, punit->y), punit); + players_iterate(aplayer) { + if (pplayers_allied(pplayer,aplayer)) { + city_list_iterate(aplayer->cities, pcity) { + cur = warmap.cost[pcity->x][pcity->y]; + /* we favour cities which barracks */ + if (city_got_building(pcity, B_BARRACKS)) cur /= 3; + if (city_got_building(pcity, B_BARRACKS2)) cur /= 3; + if (city_got_building(pcity, B_BARRACKS3)) cur /= 3; + if (cur < best) { + punit->goto_dest_x = pcity->x; + punit->goto_dest_y = pcity->y; + best = cur; + } + } city_list_iterate_end; + } + } players_iterate_end; + if (best > 6 * THRESHOLD) return FALSE; + freelog(LOG_DEBUG, "Friendly city nearest to (%d,%d) is %s@(%d,%d) [%d]", + punit->x, punit->y, + map_get_city(punit->goto_dest_x, punit->goto_dest_y)->name, + punit->goto_dest_x, punit->goto_dest_y, best); + return TRUE; +} + /************************************************************************* This does the attack until we have used up all our movement, unless we should safeguard a city. First we rampage on adjacent tiles, then we go @@ -2107,6 +2157,7 @@ struct unit *bodyguard; int best = 4 * unit_type(punit)->move_rate, x = punit->x, y = punit->y; int n = 0, p = 0; + struct unit_type *punittype=get_unit_type(punit->type); if (!unit_list_find(&map_get_tile(punit->x, punit->y)->units, punit->ai.passenger)) punit->ai.passenger = 0; @@ -2130,6 +2181,16 @@ } } unit_list_iterate_end; + + /* we try to recover hitpoints if we are in a city, before we leave */ + if (punit->hp < punittype->hp && (pcity = map_get_city(punit->x, punit->y))) { + /* don't do anything, just wait in the city */ + freelog(LOG_DEBUG, "%s: Waiting in %s to recover hitpoints before ferrying " + "to (%d,%d).", pplayer->name, pcity->name, punit->goto_dest_x, + punit->goto_dest_y); + return; + } + if (p != 0) { freelog(LOG_DEBUG, "%s#%d@(%d,%d), p=%d, n=%d", unit_name(punit->type), punit->id, punit->x, punit->y, p, n); @@ -2217,6 +2278,75 @@ return; } +/************************************************************************* + This function goes wait a unit in a city for the hitpoints to recover. + If something is attacking our city, kill it yeahhh!!!. +**************************************************************************/ +static void ai_manage_hitpoint_recovery(struct unit *punit) +{ + struct player *pplayer = unit_owner(punit); + int id = punit->id; + enum goto_result res; + struct city *pcity=map_get_city(punit->x, punit->y); + struct unit_type *punittype=get_unit_type(punit->type); + + if (pcity) { + /* rest in city until the hitpoints are recovered, but attempt + to protect city from attack */ + if ((punit = ai_military_rampage(punit, 2))) { + freelog(LOG_DEBUG, "%s's %s(%d) at (%d, %d) recovering hit points.", + pplayer->name, unit_type(punit)->name, punit->id, punit->x, + punit->y); + } else { + return; /* we died heroically defending our city */ + } + } else { + /* goto to nearest city to recover hit points */ + /* just before, check to see if we can occupy at enemy city undefended */ + if (!(punit = ai_military_rampage(punit, 99999))) { + return; /* oops, we died */ + } + + /* find city to stay and go there */ + res = -1; + if (is_sailing_unit(punit)) { + if (find_nearest_friendly_port(punit)) { + res=do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); + } + } else { + if (find_nearest_friendly_barrack(punit)) { + res=do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); + } + } + punit = find_unit_by_id(id); + if (punit) { + if (res == -1) { + freelog(LOG_DEBUG, "%s's %s(%d) at (%d, %d) failed to go to recover hit points, fortying.", + pplayer->name, unit_type(punit)->name, punit->id, + punit->x, punit->y); + /* TODO: find best tile to fortify */ + handle_unit_activity_request(punit, ACTIVITY_FORTIFYING); + } else { + freelog(LOG_DEBUG, "%s's %s(%d) at (%d, %d) going to (%d, %d) to recover hit points.", + pplayer->name, unit_type(punit)->name, punit->id, punit->x, punit->y, + punit->goto_dest_x, punit->goto_dest_y); + } + } else { + freelog(LOG_DEBUG, "%s's unit (%d) died trying to recover hit points.", + pplayer->name, id); + } + } + + /* is the unit still damaged? if true recover hit points, if not idle */ + if (punit) { + if (punit->hp == punittype->hp) { + /* we are ready to go out and kick ass again */ + ai_unit_new_role(punit, AIUNIT_NONE); + return; + } + } +} + /************************************************************************** decides what to do with a military unit. **************************************************************************/ @@ -2229,7 +2359,10 @@ if (punit->activity != ACTIVITY_IDLE) handle_unit_activity_request(punit, ACTIVITY_IDLE); - punit->ai.ai_role = AIUNIT_NONE; /* this can't be right -- Per */ + if (!punit->ai.ai_role == AIUNIT_RECOVER_HIT_POINTS) { + /* this can't be right but leave it like this for now -- Per */ + punit->ai.ai_role = AIUNIT_NONE; + } /* was getting a bad bug where a settlers caused a defender to leave home */ /* and then all other supported units went on DEFEND_HOME/goto */ ai_military_findjob(pplayer, punit); @@ -2271,6 +2404,9 @@ case AIUNIT_EXPLORE: ai_manage_explorer(punit); break; + case AIUNIT_RECOVER_HIT_POINTS: + ai_manage_hitpoint_recovery(punit); + return; default: abort(); } diff -uNrX freeciv/diff_ignore freeciv/common/unit.h freeciv-ai/common/unit.h --- freeciv/common/unit.h 2002-08-07 15:23:41.000000000 +0200 +++ freeciv-ai/common/unit.h 2002-08-13 03:41:02.000000000 +0200 @@ -51,7 +51,7 @@ enum ai_unit_task { AIUNIT_NONE, AIUNIT_AUTO_SETTLER, AIUNIT_BUILD_CITY, AIUNIT_DEFEND_HOME, AIUNIT_ATTACK, AIUNIT_FORTIFY, AIUNIT_RUNAWAY, AIUNIT_ESCORT, AIUNIT_EXPLORE, - AIUNIT_PILLAGE }; + AIUNIT_PILLAGE, AIUNIT_RECOVER_HIT_POINTS }; enum goto_move_restriction { GOTO_MOVE_ANY,