Index: ai/aiunit.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v retrieving revision 1.232 diff -u -r1.232 aiunit.c --- ai/aiunit.c 4 Dec 2002 13:34:26 -0000 1.232 +++ ai/aiunit.c 4 Dec 2002 16:33:38 -0000 @@ -55,6 +55,11 @@ #include "aiunit.h" +#define LOGLEVEL_RECOVERY LOG_DEBUG + +static struct city *find_nearest_friendly_port(struct unit *punit); +static struct city *find_nearest_friendly_barrack(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, @@ -1420,6 +1425,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))) { @@ -1491,6 +1497,15 @@ } } + /* is the unit damaged? */ + if ((punit->ai.ai_role == AIUNIT_RECOVER + && punit->hp < punittype->hp) + || punit->hp < punittype->hp * 0.50) { + UNIT_LOG(LOGLEVEL_RECOVERY, punit, "set to hp recovery"); + ai_unit_new_role(punit, AIUNIT_RECOVER, -1, -1); + 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 */ @@ -1883,14 +1898,15 @@ return(best); } -/********************************************************************** +/********************************************************************** Find safe harbour (preferably with PORT). An allied player's city is just as good as one of our own, since both replenish our hitpoints and reduce unhappiness. ***********************************************************************/ -static bool find_nearest_friendly_port(struct unit *punit) +static struct city *find_nearest_friendly_port(struct unit *punit) { struct player *pplayer = unit_owner(punit); + struct city *acity = NULL; int best = 6 * THRESHOLD + 1, cur; generate_warmap(map_get_city(punit->x, punit->y), punit); players_iterate(aplayer) { @@ -1899,19 +1915,51 @@ cur = warmap.seacost[pcity->x][pcity->y]; if (city_got_building(pcity, B_PORT)) cur /= 3; if (cur < best) { - punit->goto_dest_x = pcity->x; - punit->goto_dest_y = pcity->y; best = cur; + acity = pcity; } } city_list_iterate_end; } } players_iterate_end; - if (best > 6 * THRESHOLD) return FALSE; - freelog(LOG_DEBUG, "Friendly port 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; + if (best > 6 * THRESHOLD) { + return NULL; + } + return acity; +} + +/********************************************************************** + 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. +***********************************************************************/ +static struct city *find_nearest_friendly_barrack(struct unit *punit) +{ + struct player *pplayer = unit_owner(punit); + struct city *acity = NULL; + 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) + || city_got_building(pcity, B_BARRACKS2) + || city_got_building(pcity, B_BARRACKS3)) { + cur /= 3; + } + if (cur < best) { + acity = pcity; + best = cur; + } + } city_list_iterate_end; + } + } players_iterate_end; + if (best > 6 * THRESHOLD) { + return NULL; + } + return acity; } /************************************************************************* @@ -1925,6 +1973,7 @@ int dest_x, dest_y; int id = punit->id; int ct = 10; + struct city *pcity = NULL; assert(punit != NULL); if (punit->activity == ACTIVITY_GOTO) { @@ -1988,9 +2037,9 @@ return; } if (is_sailing_unit(punit) - && find_nearest_friendly_port(punit)) { + && (pcity = find_nearest_friendly_port(punit))) { /* Sail somewhere */ - (void) ai_unit_gothere(punit); + (void) ai_unit_goto(punit, pcity->x, pcity->y); } else if (!is_barbarian(pplayer)) { /* Nothing else to do. Worst case, this function will send us back home */ @@ -2103,8 +2152,10 @@ { /* 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; - int best = 4 * unit_type(punit)->move_rate, x = punit->x, y = punit->y; + 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; if (!unit_list_find(&map_get_tile(punit->x, punit->y)->units, punit->ai.passenger)) @@ -2129,6 +2180,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 */ + UNIT_LOG(LOGLEVEL_RECOVERY, punit, "waiting in %s to recover hitpoints " + "before ferrying", pcity->name); + 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); @@ -2136,9 +2197,9 @@ (void) ai_unit_gothere(punit); } else if (n == 0 && !map_get_city(punit->x, punit->y)) { /* rest in a city, for unhap */ x = punit->goto_dest_x; y = punit->goto_dest_y; - if (find_nearest_friendly_port(punit) - && !ai_unit_gothere(punit)) { - return; /* oops! */ + if ((port = find_nearest_friendly_port(punit)) + && !ai_unit_goto(punit, port->x, port->y)) { + return; /* oops! */ } punit->goto_dest_x = x; punit->goto_dest_y = y; send_unit_info(pplayer, punit); /* to get the crosshairs right -- Syela */ @@ -2219,6 +2280,62 @@ 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); + struct city *pcity = map_get_city(punit->x, punit->y); + struct city *port = NULL; + 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 */ + if ((is_sailing_unit(punit) && (port = find_nearest_friendly_port(punit))) + || (is_ground_unit(punit) + && (port = find_nearest_friendly_barrack(punit)))) { + pcity = map_get_city(punit->goto_dest_x, punit->goto_dest_y); + UNIT_LOG(LOGLEVEL_RECOVERY, punit, "going to %s to recover", pcity->name); + if (!ai_unit_goto(punit, port->x, port->y)) { + freelog(LOGLEVEL_RECOVERY, "died trying to hide and recover"); + return; + } + } else { + /* oops */ + UNIT_LOG(LOGLEVEL_RECOVERY, punit, "didn't find a city to recover in!"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + ai_military_attack(pplayer, punit); + return; + } + } + + /* is the unit still damaged? if true recover hit points, if not idle */ + if (punit->hp == punittype->hp) { + /* we are ready to go out and kick ass again */ + UNIT_LOG(LOGLEVEL_RECOVERY, punit, "ready to kick ass again!"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + return; + } +} + /************************************************************************** decides what to do with a military unit. **************************************************************************/ @@ -2257,6 +2374,9 @@ return; /* when you pillage, you have moves left, avoid later fortify */ case AIUNIT_EXPLORE: (void) ai_manage_explorer(punit); + break; + case AIUNIT_RECOVER: + ai_manage_hitpoint_recovery(punit); break; default: assert(FALSE); Index: common/unit.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/unit.h,v retrieving revision 1.90 diff -u -r1.90 unit.h --- common/unit.h 15 Nov 2002 21:24:30 -0000 1.90 +++ common/unit.h 4 Dec 2002 16:33:39 -0000 @@ -50,7 +50,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 }; enum goto_move_restriction { GOTO_MOVE_ANY,