Index: ai/advmilitary.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v retrieving revision 1.111 diff -u -r1.111 advmilitary.c --- ai/advmilitary.c 2002/09/01 20:44:57 1.111 +++ ai/advmilitary.c 2002/09/02 01:10:39 @@ -46,6 +46,76 @@ /* This function hasn't been implemented yet. */ } +/************************************************************************** + Choose best attacker based on movement type. It chooses based on unit + desirability without regard to cost, unless costs are equal. This is + very wrong. FIXME, use amortize on time to build. +**************************************************************************/ +static Unit_Type_id ai_choose_attacker(struct city *pcity, + enum unit_move_type which) +{ + Unit_Type_id bestid = -1; + int best = 0; + int cur; + + simple_ai_unit_type_iterate(i) { + cur = ai_unit_attack_desirability(i); + if (which == unit_types[i].move_type) { + if (can_build_unit(pcity, i) + && (cur > best + || (cur == best + && get_unit_type(i)->build_cost + <= get_unit_type(bestid)->build_cost))) { + best = cur; + bestid = i; + } + } + } simple_ai_unit_type_iterate_end; + + return bestid; +} + +/************************************************************************** + Choose best defender based on movement type. It chooses based on unit + desirability without regard to cost, unless costs are equal. This is + very wrong. FIXME, use amortize on time to build. + + We should only be passed with L_DEFEND_GOOD role for now, since this + is the only role being considered worthy of bodyguarding in findjob. +**************************************************************************/ +static Unit_Type_id ai_choose_bodyguard(struct city *pcity, + enum unit_move_type move_type, + enum unit_role_id role) +{ + Unit_Type_id bestid = -1; + int j, best = 0; + + simple_ai_unit_type_iterate(i) { + /* Only consider units of given role, or any if L_LAST */ + if (role != L_LAST) { + if (!unit_has_role(i, role)) { + continue; + } + } + + /* Only consider units of same move type */ + if (unit_types[i].move_type != move_type) { + continue; + } + + /* Now find best */ + if (can_build_unit(pcity, i)) { + j = ai_unit_defence_desirability(i); + if (j > best || (j == best && get_unit_type(i)->build_cost <= + get_unit_type(bestid)->build_cost)) { + best = j; + bestid = i; + } + } + } simple_ai_unit_type_iterate_end; + return bestid; +} + /********************************************************************** Helper for assess_defense_quadratic and assess_defense_unit. ***********************************************************************/ @@ -1090,29 +1160,31 @@ } /********************************************************************** -Checks if there is a port or a ship being built within certain distance. +... this function should assign a value to choice and want and type, + where want is a value between 1 and 100. + if want is 0 this advisor doesn't want anything ***********************************************************************/ -static bool port_is_within(struct player *pplayer, int distance) +static void ai_unit_consider_bodyguard(struct city *pcity, + Unit_Type_id unit_type, + struct ai_choice *choice) { - city_list_iterate(pplayer->cities, pcity) { - if (warmap.seacost[pcity->x][pcity->y] <= distance) { - if (city_got_building(pcity, B_PORT)) - return TRUE; - - if (!pcity->is_building_unit && pcity->currently_building == B_PORT - && pcity->shield_stock >= improvement_value(B_PORT)) - return TRUE; - - if (!player_knows_improvement_tech(pplayer, B_PORT) - && pcity->is_building_unit - && is_water_unit(pcity->currently_building) - && unit_types[pcity->currently_building].attack_strength > - unit_types[pcity->currently_building].transport_capacity) - return TRUE; - } - } city_list_iterate_end; + struct unit *virtualunit; + struct player *pplayer = city_owner(pcity); + struct unit *aunit = NULL; + struct city *acity = NULL; + + virtualunit = create_unit_virtual(pplayer, pcity->x, pcity->y, unit_type, + do_make_unit_veteran(pcity, unit_type)); - return FALSE; + if (choice->want < 100) { + int want = look_for_charge(pplayer, virtualunit, &aunit, &acity); + if (want > choice->want) { + choice->want = want; + choice->choice = unit_type; + choice->type = CT_DEFENDER; + } + } + destroy_unit_virtual(virtualunit); } /********************************************************************** @@ -1123,18 +1195,13 @@ void military_advisor_choose_build(struct player *pplayer, struct city *pcity, struct ai_choice *choice) { - Unit_Type_id v; - int def, danger, urgency, want; - struct unit *myunit = 0; + Unit_Type_id unit_type; + int def, danger, urgency; struct tile *ptile = map_get_tile(pcity->x, pcity->y); - struct unit virtualunit; - struct city *acity = 0; - struct unit *aunit = 0; + struct unit *virtualunit; init_choice(choice); -/* TODO: recognize units that can DEFEND_HOME but are in the field. -- Syela */ - urgency = assess_danger(pcity); /* calling it now, rewriting old wall code */ def = assess_defense_quadratic(pcity); /* has to be AFTER assess_danger thanks to wallvalue */ /* changing to quadratic to stop AI from building piles of small units -- Syela */ @@ -1160,19 +1227,12 @@ } if (danger != 0) { /* otherwise might be able to wait a little longer to defend */ -/* old version had danger -= def in it, which was necessary before disband/upgrade -code was added and walls got built, but now danger -= def would be very bad -- Syela */ if (danger >= def) { if (urgency == 0) danger = 100; /* don't waste money otherwise */ else if (danger >= def * 2) danger = 200 + urgency; else { danger *= 100; danger /= def; danger += urgency; } -/* without the += urgency, wasn't buying with danger == def. Duh. -- Syela */ } else { danger *= 100; danger /= def; } if (pcity->shield_surplus <= 0 && def != 0) danger = 0; -/* this is somewhat of an ugly kluge, but polar cities with no ability to -increase prod were buying alpines, panicking, disbanding them, buying alpines -and so on every other turn. This will fix that problem, hopefully without -creating any other problems that are worse. -- Syela */ if (pcity->ai.building_want[B_CITY] != 0 && def != 0 && can_build_improvement(pcity, B_CITY) && (danger < 101 || unit_list_size(&ptile->units) > 1 || /* walls before a second defender, unless we need it RIGHT NOW */ @@ -1216,62 +1276,53 @@ if (pcity->shield_surplus <= 0 || /* must be able to upkeep units */ pcity->ppl_unhappy[4] > pcity->ppl_unhappy[2]) return; /* and no disorder! */ - memset(&virtualunit, 0, sizeof(struct unit)); -/* this memset didn't work because of syntax that -the intrepid David Pfitzner discovered was in error. -- Syela */ - virtualunit.owner = pplayer->player_no; - virtualunit.x = pcity->x; - virtualunit.y = pcity->y; - virtualunit.id = 0; - v = ai_choose_defender_by_type(pcity, LAND_MOVING); /* Temporary -- Syela */ - virtualunit.type = v; - virtualunit.veteran = do_make_unit_veteran(pcity, v); - virtualunit.hp = unit_types[v].hp; - - if (choice->want < 100) { - want = look_for_charge(pplayer, &virtualunit, &aunit, &acity); - if (want > choice->want) { - choice->want = want; - choice->choice = v; - choice->type = CT_NONMIL; /* Why not CT_DEFENDER? -- Caz */ - } + /* Consider making a land bodyguard */ + unit_type = ai_choose_bodyguard(pcity, LAND_MOVING, L_DEFEND_GOOD); + if (unit_type >= 0) { + ai_unit_consider_bodyguard(pcity, unit_type, choice); } - if (choice->want > 100) { - /* We are in severe danger, don't try to build attackers */ + /* If we are in severe danger, don't consider attackers. This is probably + too general. In many cases we will want to buy attackers to counterattack. + -- Per */ + if (choice->want > 100 && pcity->ai.grave_danger > 0) { + CITY_LOG(LOGLEVEL_BUILD, pcity, "severe danger (want %d), pick defender", + choice->want); return; } - unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) - if (((unit_type(punit)->attack_strength * 4 > - unit_type(punit)->defense_strength * 5) || - unit_flag(punit, F_FIELDUNIT)) && - punit->activity != ACTIVITY_GOTO) /* very important clause, this -- Syela */ - myunit = punit; - unit_list_iterate_end; -/* if myunit is non-null, it is an attacker forced to defend */ -/* and we will use its attack values, otherwise we will use virtualunit */ - if (myunit) kill_something_with(pplayer, pcity, myunit, choice); - else { - freelog(LOG_DEBUG, "Killing with virtual unit in %s", pcity->name); - v = ai_choose_attacker_sailing(pcity); - if (v > 0 && /* have to put sailing first before we mung the seamap */ - (city_got_building(pcity, B_PORT) || /* only need a few ports */ - !port_is_within(pplayer, 18))) { /* using move_rate is quirky -- Syela */ - virtualunit.type = v; -/* virtualunit.veteran = do_make_unit_veteran(pcity, v); */ - virtualunit.veteran = (player_knows_improvement_tech(pplayer, B_PORT)); - virtualunit.hp = unit_types[v].hp; - kill_something_with(pplayer, pcity, &virtualunit, choice); - } /* ok. can now mung seamap for ferryboat code. Proceed! */ - v = ai_choose_attacker_ground(pcity); - virtualunit.type = v; -/* virtualunit.veteran = do_make_unit_veteran(pcity, v);*/ - virtualunit.veteran = TRUE; - virtualunit.hp = unit_types[v].hp; - kill_something_with(pplayer, pcity, &virtualunit, choice); + /* Consider making a sea bodyguard */ + unit_type = ai_choose_bodyguard(pcity, SEA_MOVING, L_DEFEND_GOOD); + if (unit_type >= 0) { + ai_unit_consider_bodyguard(pcity, unit_type, choice); } - return; + + /* Check if we want a sailing attacker. Have to put sailing first + before we mung the seamap */ + unit_type = ai_choose_attacker(pcity, SEA_MOVING); + if (unit_type >= 0) { + virtualunit = create_unit_virtual(pplayer, pcity->x, pcity->y, unit_type, + player_knows_improvement_tech(pplayer, B_PORT)); + kill_something_with(pplayer, pcity, virtualunit, choice); + destroy_unit_virtual(virtualunit); + } + + /* According to Pille, ports are usually a good idea, so let's try -- Per */ + if (player_knows_improvement_tech(pplayer, B_PORT) + && !city_got_building(pcity, B_PORT)) { + /* Build ports first */ + choice->choice = B_PORT; + choice->type = CT_BUILDING; + } + + /* Consider a land attacker */ + unit_type = ai_choose_attacker(pcity, LAND_MOVING); + if (unit_type >= 0) { + virtualunit = create_unit_virtual(pplayer, pcity->x, pcity->y, unit_type, + TRUE); /* why assume veteran? -- Per */ + kill_something_with(pplayer, pcity, virtualunit, choice); + destroy_unit_virtual(virtualunit); + } } /************************************************************************** Index: ai/aicity.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v retrieving revision 1.119 diff -u -r1.119 aicity.c --- ai/aicity.c 2002/09/01 19:46:13 1.119 +++ ai/aicity.c 2002/09/02 01:10:39 @@ -176,12 +176,6 @@ } } else { - -/* obsolete code destroyed */ -/* military_advisor_choose_build(pplayer, pcity, &curchoice); */ -/* this is now handled in manage_city thanks to our friend ->ai.choice */ -/* copy_if_better_choice(&curchoice, &bestchoice); */ - copy_if_better_choice(&pcity->ai.choice, &bestchoice); if (bestchoice.want <= 100 || pcity->ai.urgency == 0) { /* soldier at 101 cannot be denied */ @@ -554,48 +548,8 @@ { ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, choice->want); } - -/************************************************************************** - don't ask me why this is in aicity, I can't even remember -- Syela -**************************************************************************/ -static Unit_Type_id ai_choose_attacker(struct city *pcity, - enum unit_move_type which) -{ - Unit_Type_id bestid = 0; /* ??? Zero is legal value! (Settlers by default) */ - int best = 0; - int cur; - - simple_ai_unit_type_iterate(i) { - cur = ai_unit_attack_desirability(i); - if (which == unit_types[i].move_type) { - if (can_build_unit(pcity, i) && (cur > best || (cur == best && - get_unit_type(i)->build_cost <= get_unit_type(bestid)->build_cost))) { - best = cur; - bestid = i; - } - } - } simple_ai_unit_type_iterate_end; - - return bestid; -} - -/************************************************************************** - ... -**************************************************************************/ -Unit_Type_id ai_choose_attacker_ground(struct city *pcity) -{ - return(ai_choose_attacker(pcity, LAND_MOVING)); -} -/************************************************************************** - ... -**************************************************************************/ -Unit_Type_id ai_choose_attacker_sailing(struct city *pcity) -{ - return(ai_choose_attacker(pcity, SEA_MOVING)); -} - -/************************************************************************** +/************************************************************************** ... **************************************************************************/ Unit_Type_id ai_choose_defender_versus(struct city *pcity, Unit_Type_id v) Index: ai/aicity.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aicity.h,v retrieving revision 1.15 diff -u -r1.15 aicity.h --- ai/aicity.h 2002/03/02 18:03:06 1.15 +++ ai/aicity.h 2002/09/02 01:10:39 @@ -27,8 +27,6 @@ Unit_Type_id ai_choose_defender_by_type(struct city *pcity, enum unit_move_type which); Unit_Type_id ai_choose_defender(struct city *pcity); -Unit_Type_id ai_choose_attacker_ground(struct city *pcity); -Unit_Type_id ai_choose_attacker_sailing(struct city *pcity); int ai_make_elvis(struct city *pcity); void ai_scientists_taxmen(struct city *pcity); bool ai_fix_unhappy(struct city *pcity); Index: ai/aitools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v retrieving revision 1.53 diff -u -r1.53 aitools.c --- ai/aitools.c 2002/08/25 13:34:56 1.53 +++ ai/aitools.c 2002/09/02 01:10:40 @@ -19,11 +19,13 @@ #include "government.h" #include "log.h" #include "map.h" +#include "mem.h" #include "packets.h" #include "player.h" #include "shared.h" #include "unit.h" +#include "barbarian.h" #include "unithand.h" #include "citytools.h" #include "cityturn.h" @@ -37,6 +39,69 @@ #include "aitools.h" /************************************************************************** + Create a virtual unit to use in build want estimation +**************************************************************************/ +struct unit *create_unit_virtual(struct player *pplayer, int x, int y, + Unit_Type_id type, bool make_veteran) +{ + struct unit *punit; + punit=fc_calloc(1,sizeof(struct unit)); + + punit->type=type; + punit->owner=pplayer->player_no; + CHECK_MAP_POS(x, y); + punit->x = x; + punit->y = y; + punit->goto_dest_x = -1; + punit->goto_dest_y = -1; + punit->veteran = make_veteran; + punit->homecity = 0; + punit->upkeep = 0; + punit->upkeep_food = 0; + punit->upkeep_gold = 0; + punit->unhappiness = 0; + /* A unit new and fresh ... */ + punit->foul = FALSE; + punit->fuel = unit_type(punit)->fuel; + punit->hp = unit_type(punit)->hp; + punit->moves_left = unit_move_rate(punit); + punit->moved = FALSE; + punit->paradropped = FALSE; + if (is_barbarian(pplayer)) { + punit->fuel = BARBARIAN_LIFE; + } + /* AI.control is probably always true... */ + punit->ai.control = FALSE; + punit->ai.ai_role = AIUNIT_NONE; + punit->ai.ferryboat = 0; + punit->ai.passenger = 0; + punit->ai.bodyguard = 0; + punit->ai.charge = 0; + punit->bribe_cost = -1; /* flag value */ + punit->transported_by = -1; + punit->pgr = NULL; + set_unit_activity(punit, ACTIVITY_IDLE); + + return punit; +} + +/************************************************************************** + It is assumed (since it's virtual) that it's not registered or + listed anywhere. +**************************************************************************/ +void destroy_unit_virtual(struct unit *punit) +{ + if (punit->pgr) { + /* Should never happen, but do it anyway for completeness */ + free(punit->pgr->pos); + free(punit->pgr); + punit->pgr = NULL; + } + + free(punit); +} + +/************************************************************************** Ensure unit sanity **************************************************************************/ void ai_unit_new_role(struct unit *punit, enum ai_unit_task task) @@ -70,8 +135,10 @@ punit = find_unit_by_id(bodyguard->ai.charge); assert(punit); + assert(punit->ai.bodyguard == bodyguard->id); + assert(bodyguard->ai.charge == punit->id); + if (!is_tiles_adjacent(x, y, bodyguard->x, bodyguard->y)) { - BODYGUARD_LOG(LOG_DEBUG, bodyguard, "is too far from its charge"); return; } @@ -81,8 +148,6 @@ return; } - BODYGUARD_LOG(LOG_DEBUG, bodyguard, "was dragged along by charge"); - handle_unit_activity_request(bodyguard, ACTIVITY_IDLE); (void) ai_unit_move(bodyguard, x, y); handle_unit_activity_request(bodyguard, ACTIVITY_FORTIFYING); @@ -105,7 +170,7 @@ return TRUE; } else { punit->ai.bodyguard = BODYGUARD_NONE; - UNIT_LOG(LOG_VERBOSE, punit, "bodyguard disappeared!"); + UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "bodyguard disappeared!"); } } return FALSE; Index: ai/aitools.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v retrieving revision 1.23 diff -u -r1.23 aitools.h --- ai/aitools.h 2002/08/31 16:04:59 1.23 +++ ai/aitools.h 2002/09/02 01:10:40 @@ -24,6 +24,8 @@ #define LOGLEVEL_UNIT LOG_DEBUG #define LOGLEVEL_GOTO LOG_DEBUG #define LOGLEVEL_CITY LOG_DEBUG +#define LOGLEVEL_FERRY LOG_DEBUG +#define LOGLEVEL_BUILD LOG_DEBUG /* General AI logging macros */ @@ -87,6 +89,9 @@ BODYGUARD_NONE }; +struct unit *create_unit_virtual(struct player *pplayer, int x, int y, + Unit_Type_id type, bool make_veteran); +void destroy_unit_virtual(struct unit *punit); void ai_unit_new_role(struct unit *punit, enum ai_unit_task utask); void 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.211 diff -u -r1.211 aiunit.c --- ai/aiunit.c 2002/09/01 20:44:57 1.211 +++ ai/aiunit.c 2002/09/02 01:10:40 @@ -59,7 +59,7 @@ static void ai_military_attack(struct player *pplayer,struct unit *punit); static int unit_move_turns(struct unit *punit, int x, int y); -static bool unit_can_defend(Unit_Type_id type); +static bool unit_role_defender(Unit_Type_id type); /* * Cached values. Updated by update_simple_ai_types. @@ -1070,22 +1070,24 @@ { 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) { /* protect a unit */ x = aunit->x; - y = aunit->y; + 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 { - /* should be impossible */ + } else { + /* We lost our charge, maybe we didn't get there in time? */ + UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "we lost our charge"); ai_unit_new_role(punit, AIUNIT_NONE); return; } - + if (aunit) { freelog(LOG_DEBUG, "%s#%d@(%d,%d) to meet charge %s#%d@(%d,%d)[body=%d]", unit_type(punit)->name, punit->id, punit->x, punit->y, @@ -1095,13 +1097,9 @@ if (!same_pos(punit->x, punit->y, x, y)) { if (goto_is_sane(punit, x, y, TRUE)) { - enum goto_result result; punit->goto_dest_x = x; punit->goto_dest_y = y; - result = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); - if (result != GR_DIED) { - GOTO_LOG(LOGLEVEL_BODYGUARD, punit, result, "bodyguard meet"); - } + do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); } else { /* can't possibly get there to help */ ai_unit_new_role(punit, AIUNIT_NONE); @@ -1115,12 +1113,6 @@ /* otherwise don't bother, but free cities are free cities and must be snarfed. -- Syela */ } - - /* 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; - } } /************************************************************************* @@ -1357,11 +1349,14 @@ } /************************************************************************* -... + Does the unit with the id given have the flag L_DEFEND_GOOD? **************************************************************************/ -static bool unit_can_defend(Unit_Type_id type) +static bool unit_role_defender(Unit_Type_id type) { - if (unit_types[type].move_type != LAND_MOVING) return FALSE; /* temporary kluge */ + if (unit_types[type].move_type != LAND_MOVING + && unit_types[type].move_type != SEA_MOVING) { + return FALSE; /* temporary kluge */ + } return (unit_has_role(type, L_DEFEND_GOOD)); } @@ -1370,6 +1365,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, @@ -1383,36 +1383,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); @@ -1421,7 +1421,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, @@ -1484,13 +1485,16 @@ aunit = player_find_unit_by_id(pplayer, punit->ai.charge); acity = find_city_by_id(punit->ai.charge); - /* another crazy duct-tape sanity check to ensure we don't do something stupid */ - if ((aunit && aunit->ai.bodyguard != BODYGUARD_NONE - && unit_vulnerability_virtual(punit) > - unit_vulnerability_virtual(aunit)) || - (acity && acity->owner == punit->owner && acity->ai.urgency != 0 && - acity->ai.danger > assess_defense_quadratic(acity))) { - punit->ai.ai_role = AIUNIT_ESCORT; /* do not use ai_unit_new_role() */ + /* Check if city we are on our way to rescue is still in danger, + * or unit we should protect is still alive */ + if ((acity && acity->owner == punit->owner && acity->ai.urgency != 0 && + acity->ai.danger > assess_defense_quadratic(acity)) + || aunit) { + if (punit->ai.ai_role != AIUNIT_ESCORT) { + /* We forgot what we were doing. Incredible! */ + BODYGUARD_LOG(LOG_ERROR, punit, "lost role as bodyguard in findjob"); + punit->ai.ai_role = AIUNIT_ESCORT; /* do not use ai_unit_new_role() */ + } return; } else { ai_unit_new_role(punit, AIUNIT_NONE); @@ -1521,14 +1525,12 @@ } val = 0; acity = NULL; aunit = NULL; - if (unit_can_defend(punit->type)) { - -/* This is a defending unit that doesn't need to stay put. -It needs to defend something, but not necessarily where it's at. -Therefore, it will consider becoming a bodyguard. -- Syela */ - + if (unit_role_defender(punit->type)) { + /* This is a defending unit that doesn't need to stay put. + * It needs to defend something, but not necessarily where it's at. + * Therefore, it will consider becoming a bodyguard. -- Syela + */ val = look_for_charge(pplayer, punit, &aunit, &acity); - } if (q > val && ai_fuzzy(pplayer, TRUE)) { ai_unit_new_role(punit, AIUNIT_DEFEND_HOME); @@ -1544,7 +1546,7 @@ } else if (aunit) { ai_unit_new_role(punit, AIUNIT_ESCORT); punit->ai.charge = aunit->id; - freelog(LOG_DEBUG, "%s@(%d, %d) going to defend %s@(%d, %d)", + freelog(LOGLEVEL_BODYGUARD, "%s@(%d, %d) going to defend %s@(%d, %d)", unit_type(punit)->name, punit->x, punit->y, unit_type(aunit)->name, aunit->x, aunit->y); } else if (ai_unit_attack_desirability(punit->type) != 0 || @@ -2099,6 +2101,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(LOGLEVEL_FERRY, 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); @@ -2151,6 +2158,19 @@ punit->goto_dest_x = punit->x; punit->goto_dest_y = punit->y; + /* Release bodyguard and let it roam */ + bodyguard = find_unit_by_id(punit->ai.bodyguard); + if (bodyguard) { + UNIT_LOG(LOGLEVEL_FERRY, punit, "released our %s bodyguard at target", + unit_type(bodyguard)->name); + punit->ai.bodyguard = BODYGUARD_NONE; + bodyguard->ai.charge = BODYGUARD_NONE; + ai_unit_new_role(bodyguard, AIUNIT_NONE); + if (bodyguard->moves_left > 0) { + ai_manage_military(pplayer, bodyguard); + } + } + if (unit_type(punit)->attack_strength > unit_type(punit)->transport_capacity) { if (punit->moves_left > 0) ai_manage_military(pplayer, punit); @@ -2207,16 +2227,11 @@ **************************************************************************/ static void ai_manage_military(struct player *pplayer, struct unit *punit) { - int id; + int id = punit->id; - id = punit->id; - if (punit->activity != ACTIVITY_IDLE) handle_unit_activity_request(punit, ACTIVITY_IDLE); - punit->ai.ai_role = AIUNIT_NONE; /* this can't be right -- Per */ - /* 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); #ifdef DEBUG @@ -2310,6 +2325,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) ) { @@ -2324,6 +2341,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))) { Index: ai/aiunit.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v retrieving revision 1.34 diff -u -r1.34 aiunit.h --- ai/aiunit.h 2002/03/21 20:57:29 1.34 +++ ai/aiunit.h 2002/09/02 01:10:40 @@ -24,6 +24,14 @@ */ #define POWER_DIVIDER (POWER_FACTOR * 3) +/* Simple military power macros */ +#define DEFENCE_POWER(punit) \ + (unit_type(punit)->defense_strength * unit_type(punit)->hp \ + * unit_type(punit)->firepower) +#define ATTACK_POWER(punit) \ + (unit_type(punit)->attack_strength * unit_type(punit)->hp \ + * unit_type(punit)->firepower) + struct player; struct city; struct unit; Index: data/default/units.ruleset =================================================================== RCS file: /home/freeciv/CVS/freeciv/data/default/units.ruleset,v retrieving revision 1.38 diff -u -r1.38 units.ruleset --- data/default/units.ruleset 2002/07/23 19:01:17 1.38 +++ data/default/units.ruleset 2002/09/02 01:10:41 @@ -1299,7 +1299,7 @@ uk_food = 0 uk_gold = 0 flags = "" -roles = "" +roles = "DefendGood" [unit_aegis_cruiser] name = _("AEGIS Cruiser") @@ -1327,7 +1327,7 @@ uk_food = 0 uk_gold = 0 flags = "AEGIS" -roles = "" +roles = "DefendGood" [unit_battleship] name = _("Battleship")