Index: ai/aitools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v retrieving revision 1.71 diff -u -r1.71 aitools.c --- ai/aitools.c 2003/01/02 12:52:48 1.71 +++ ai/aitools.c 2003/01/02 14:40:29 @@ -179,11 +179,13 @@ /************************************************************************** Ensure unit sanity by telling charge that we won't bodyguard it anymore, - add and remove city spot reservation, and set destination. + tell bodyguard it can roam free if our job is done, add and remove city + spot reservation, and set destination. **************************************************************************/ void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, int x, int y) { struct unit *charge = find_unit_by_id(punit->ai.charge); + struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard); if (punit->activity == ACTIVITY_GOTO) { /* It would indicate we're going somewhere otherwise */ @@ -206,6 +208,10 @@ punit->goto_dest_x = x; punit->goto_dest_y = y; */ + if (punit->ai.ai_role == AIUNIT_NONE && bodyguard) { + ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1); + } + if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { assert(is_normal_map_pos(x, y)); add_city_to_minimap(x, y); @@ -255,6 +261,12 @@ assert(pplayer != NULL); punit = find_unit_by_id(bodyguard->ai.charge); assert(punit != NULL); + + assert(punit->ai.bodyguard == bodyguard->id); + assert(bodyguard->ai.charge == punit->id); + + assert(punit->ai.bodyguard == bodyguard->id); + assert(bodyguard->ai.charge == punit->id); if (!is_tiles_adjacent(x, y, bodyguard->x, bodyguard->y)) { return; Index: ai/aiunit.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v retrieving revision 1.248 diff -u -r1.248 aiunit.c --- ai/aiunit.c 2003/01/02 11:59:29 1.248 +++ ai/aiunit.c 2003/01/02 14:40:29 @@ -1025,18 +1025,20 @@ { struct unit *aunit = player_find_unit_by_id(pplayer, punit->ai.charge); struct city *acity = find_city_by_id(punit->ai.charge); - int x, y, id = punit->id; + int x, y; - if (aunit && aunit->owner == punit->owner) { + if (aunit && aunit->owner == punit->owner) { /* protect a unit */ - x = aunit->x; - y = aunit->y; - } else if (acity && acity->owner == punit->owner) { + x = aunit->x; + y = aunit->y; + aunit->ai.bodyguard = punit->id; /* always notify charge */ + } else if (acity && acity->owner == punit->owner) { /* protect a city */ - x = acity->x; - y = acity->y; - } else { + x = acity->x; + y = acity->y; + } else { /* should be impossible */ + UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "we lost our charge"); ai_unit_new_role(punit, AIUNIT_NONE, -1 , -1); return; } @@ -1059,12 +1061,6 @@ /* I had these guys set to just fortify, which is so dumb. -- Syela */ (void) ai_military_rampage(punit, 40 * SHIELD_WEIGHTING); } - - /* is this insanity supposed to be a sanity check? -- per */ - if (aunit && unit_list_find(&map_get_tile(x, y)->units, id) - && aunit->ai.bodyguard != BODYGUARD_NONE) { - aunit->ai.bodyguard = id; - } } /************************************************************************* @@ -1311,6 +1307,11 @@ bodyguards and building want estimation code. Returns desirability for using this unit as a bodyguard or for defending a city. + We do not consider units with higher movement than us, or units that + have different move type than us, as potential charges. Nor do we + attempt to bodyguard units with higher defence than us, or military + units with higher attack than us. + Requires an initialized warmap! **************************************************************************/ int look_for_charge(struct player *pplayer, struct unit *punit, @@ -1324,36 +1325,36 @@ return 0; } + /* Unit bodyguard */ unit_list_iterate(pplayer->units, buddy) { - if (buddy->ai.bodyguard == BODYGUARD_NONE /* should be != BODYGUARD_WANTED */ + if (buddy->ai.bodyguard != BODYGUARD_WANTED || !goto_is_sane(punit, buddy->x, buddy->y, TRUE) || unit_type(buddy)->move_rate > unit_type(punit)->move_rate + || DEFENCE_POWER(buddy) >= DEFENCE_POWER(punit) + || (is_military_unit(buddy) && get_transporter_capacity(buddy) == 0 + && ATTACK_POWER(buddy) <= ATTACK_POWER(punit)) || unit_type(buddy)->move_type != unit_type(punit)->move_type) { continue; } dist = unit_move_turns(punit, buddy->x, buddy->y); - def = (toughness - unit_vulnerability_virtual(buddy)) >> dist; - freelog(LOG_DEBUG, "(%d,%d)->(%d,%d), %d turns, def=%d", - punit->x, punit->y, buddy->x, buddy->y, dist, def); - - /* sanity check: if we already have a bodyguard, but don't know about it, - we don't need a new one... this check should be superfluous eventually */ - unit_list_iterate(pplayer->units, body) { - if (body->ai.charge == buddy->id) { - /* if buddy already has a bodyguard, ignore it */ - def = 0; - /* we should buddy->bodyguard = body->id; here */ - } - } unit_list_iterate_end; - if (def > best && ai_fuzzy(pplayer, TRUE)) { + def = (toughness - unit_vulnerability_virtual(buddy)); + if (get_transporter_capacity(buddy) == 0) { + /* Reduce want based on distance. We can't do this for + * transports since they move around all the time, leading + * to hillarious flip-flops */ + def = def >> (dist/2); + } + if (def > best) { *aunit = buddy; best = def; } } unit_list_iterate_end; - city_list_iterate(pplayer->cities, mycity) { + /* City bodyguard */ + if (unit_type(punit)->move_type == LAND_MOVING) { + city_list_iterate(pplayer->cities, mycity) { if (!goto_is_sane(punit, mycity->x, mycity->y, TRUE) - || mycity->ai.urgency == 0) { + || mycity->ai.urgency == 0) { continue; } dist = unit_move_turns(punit, mycity->x, mycity->y); @@ -1362,7 +1363,8 @@ *acity = mycity; best = def; } - } city_list_iterate_end; + } city_list_iterate_end; + } freelog(LOG_DEBUG, "%s: %s (%d@%d,%d) looking for charge; %d/%d", pplayer->name, unit_type(punit)->name, punit->id, @@ -1415,11 +1417,12 @@ /* keep barbarians aggresive and primitive */ if (is_barbarian(pplayer)) { if (can_unit_do_activity(punit, ACTIVITY_PILLAGE) - && is_land_barbarian(pplayer)) + && is_land_barbarian(pplayer)) { /* land barbarians pillage */ ai_unit_new_role(punit, AIUNIT_PILLAGE, -1, -1); - else + } else { ai_unit_new_role(punit, AIUNIT_ATTACK, -1, -1); + } return; } @@ -2261,7 +2264,7 @@ It is well constructed of teak, and looks seaworthy. */ struct city *pcity; struct city *port = NULL; - struct unit *bodyguard; + 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; @@ -2272,6 +2275,11 @@ unit_list_iterate(map_get_tile(punit->x, punit->y)->units, aunit) 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); pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y); @@ -2335,6 +2343,12 @@ punit->goto_dest_x = 0; /* FIXME: -1 */ punit->goto_dest_y = 0; /* FIXME: -1 */ + /* Release bodyguard and let it roam */ + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + if (bodyguard) { + ai_military_attack(pplayer, bodyguard); + } + if (IS_ATTACKER(punit)) { if (punit->moves_left > 0) ai_manage_military(pplayer, punit); return; @@ -2533,6 +2547,8 @@ **************************************************************************/ static void ai_manage_unit(struct player *pplayer, struct unit *punit) { + struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard); + /* retire useless barbarian units here, before calling the management function */ if( is_barbarian(pplayer) ) { @@ -2547,6 +2563,13 @@ return; } } + + /* Check if we have lost our bodyguard. If we never had one, all + * fine. If we had one and lost it, ask for a new one. */ + if (!bodyguard && punit->ai.bodyguard > BODYGUARD_NONE) { + UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "lost bodyguard, asking for new"); + punit->ai.bodyguard = BODYGUARD_WANTED; + } if ((unit_flag(punit, F_DIPLOMAT)) || (unit_flag(punit, F_SPY))) {