diff -uNrX freeciv/diff_ignore freeciv-phased/ai/advdomestic.c freeciv/ai/advdomestic.c --- freeciv-phased/ai/advdomestic.c 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/ai/advdomestic.c 2002-09-03 18:56:38.000000000 +0200 @@ -943,11 +943,6 @@ /* Oh dear, better think of something! */ unit_type = best_role_unit(pcity, F_TRADE_ROUTE); - if (unit_type == U_LAST) { - unit_type = best_role_unit(pcity, F_DIPLOMAT); - /* Someday, real diplomat code will be here! */ - } - if (unit_type != U_LAST) { choice->want = 1; choice->type = CT_NONMIL; diff -uNrX freeciv/diff_ignore freeciv-phased/ai/advmilitary.c freeciv/ai/advmilitary.c --- freeciv-phased/ai/advmilitary.c 2002-09-03 22:22:36.000000000 +0200 +++ freeciv/ai/advmilitary.c 2002-09-03 22:14:09.000000000 +0200 @@ -23,10 +23,13 @@ #include "cityturn.h" #include "gotohand.h" /* warmap has been redeployed */ #include "settlers.h" +#include "diplomats.h" +#include "player.h" #include "aicity.h" #include "aitools.h" #include "aiunit.h" +#include "aidiplomat.h" #include "advmilitary.h" @@ -287,8 +290,6 @@ int danger5 = 0; /* linear for SDI */ struct player *pplayer; bool pikemen = FALSE; - bool diplomat = FALSE; /* TRUE mean that this town can defend - * against diplomats or spies */ int urgency = 0; bool igwall; int badmojo = 0; @@ -305,10 +306,11 @@ pcity->ai.grave_danger = 0; pcity->ai.diplomat_threat = FALSE; + pcity->ai.has_diplomat = FALSE; unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) if (unit_flag(punit, F_PIKEMEN)) pikemen = TRUE; - if (unit_flag(punit, F_DIPLOMAT)) diplomat = TRUE; + if (unit_flag(punit, F_DIPLOMAT)) pcity->ai.has_diplomat = TRUE; unit_list_iterate_end; players_iterate(aplayer) { @@ -356,8 +358,9 @@ (v * m / (dist*2))); } - if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * m)) - pcity->ai.diplomat_threat = !diplomat; + if (unit_flag(funit, F_DIPLOMAT) && (dist <= 2 * m)) { + pcity->ai.diplomat_threat = TRUE; + } v *= v; @@ -399,7 +402,7 @@ } if (unit_flag(punit, F_DIPLOMAT) && (dist <= 2 * m)) - pcity->ai.diplomat_threat = !diplomat; + pcity->ai.diplomat_threat = TRUE; v *= v; @@ -1135,6 +1138,7 @@ /* TODO: recognize units that can DEFEND_HOME but are in the field. -- Syela */ + /* Note: assess_danger() creates a warmap for us */ 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 */ @@ -1142,22 +1146,7 @@ freelog(LOG_DEBUG, "Assessed danger for %s = %d, Def = %d", pcity->name, danger, def); - if (pcity->ai.diplomat_threat && def != 0){ - /* It's useless to build a diplomat as the last defender of a town. --nb */ - - Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT); - if (uname); - choice->want = 16000; /* diplomat more important than soldiers */ - pcity->ai.urgency = 1; - choice->type = CT_DEFENDER; - choice->choice = u; - return; - } else if (num_role_units(F_DIPLOMAT)>0) { - u = get_role_unit(F_DIPLOMAT, 0); - pplayer->ai.tech_want[get_unit_type(u)->tech_requirement] += 16000; - } - } + ai_choose_diplomat_defensive(pplayer, pcity, choice, def); 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 @@ -1264,6 +1253,7 @@ virtualunit.hp = unit_types[v].hp; kill_something_with(pplayer, pcity, &virtualunit, choice); } /* ok. can now mung seamap for ferryboat code. Proceed! */ + ai_choose_diplomat_offensive(pplayer, pcity, choice); v = ai_choose_attacker_ground(pcity); virtualunit.type = v; /* virtualunit.veteran = do_make_unit_veteran(pcity, v);*/ diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aidiplomat.c freeciv/ai/aidiplomat.c --- freeciv-phased/ai/aidiplomat.c 1970-01-01 01:00:00.000000000 +0100 +++ freeciv/ai/aidiplomat.c 2002-09-03 22:38:25.000000000 +0200 @@ -0,0 +1,639 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +#include +#include +#include +#include + +#include "city.h" +#include "combat.h" +#include "game.h" +#include "government.h" +#include "log.h" +#include "map.h" +#include "mem.h" +#include "packets.h" +#include "player.h" +#include "rand.h" +#include "shared.h" +#include "timing.h" +#include "unit.h" + +#include "barbarian.h" +#include "citytools.h" +#include "cityturn.h" +#include "diplomats.h" +#include "gotohand.h" +#include "maphand.h" +#include "settlers.h" +#include "unithand.h" +#include "unittools.h" + +#include "advmilitary.h" +#include "aicity.h" +#include "aihand.h" +#include "aitools.h" +#include "aiunit.h" + +#include "aidiplomat.h" + +#define LOG_DIPLOMAT LOG_DEBUG + +/****************************************************************************** + ... +******************************************************************************/ +static int count_sabotagable_improvements(struct city *pcity) +{ + int count = 0; + + built_impr_iterate(pcity, index) { + if (!is_wonder(index) && index != B_PALACE) { + count++; + } + } built_impr_iterate_end; + + return count; +} + +/****************************************************************************** + ... +******************************************************************************/ +static int count_stealable_techs(struct player *pplayer, struct player *tplayer) +{ + int index, count = 0; + + for (index = A_FIRST; index < game.num_tech_types; index++) { + if ((get_invention(pplayer, index) != TECH_KNOWN) + && (get_invention(tplayer, index) == TECH_KNOWN)) { + count++; + } + } + + return count; +} + +/********************************************************************** + Calculates our need for diplomats as defensive units. May replace + values in choice. The values 16000 and 3000 used below are totally + arbitrary but seem to work. +***********************************************************************/ +void ai_choose_diplomat_defensive(struct player *pplayer, + struct city *pcity, + struct ai_choice *choice, int def) +{ + /* Build a diplomat if our city is threatened by enemy diplomats, and + we have other defensive troops, and we don't already have a diplomat + to protect us. If we see an enemy diplomat and we don't have diplomat + tech... race it! */ + if (def != 0 && pcity->ai.diplomat_threat && !pcity->ai.has_diplomat) { + Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT); + + if (u < U_LAST) { + freelog(LOG_VERBOSE, "A defensive diplomat will be built in city %s.", + pcity->name); + choice->want = 16000; /* diplomat more important than soldiers */ + pcity->ai.urgency = 1; + choice->type = CT_DEFENDER; + choice->choice = u; + } else if (num_role_units(F_DIPLOMAT) > 0) { + /* We don't know diplomats yet... */ + freelog(LOG_VERBOSE, "A defensive diplomat is wanted badly in city %s.", + pcity->name); + u = get_role_unit(F_DIPLOMAT, 0); + /* 3000 is a just a large number, but not hillariously large as the + previously used one. This is important for diplomacy later - Per */ + pplayer->ai.tech_want[get_unit_type(u)->tech_requirement] += 3000; + } + } +} + +/********************************************************************** + Calculates our need for diplomats as offensive units. May replace + values in choice. + + Requires an initialized warmap! +***********************************************************************/ +void ai_choose_diplomat_offensive(struct player *pplayer, + struct city *pcity, + struct ai_choice *choice) +{ + Unit_Type_id u = best_role_unit(pcity, F_DIPLOMAT); + + if (u >= U_LAST) { + /* We don't know diplomats yet! */ + return; + } + + if (ai_handicap(pplayer, H_DIPLOMAT)) { + /* Diplomats are too tough on newbies */ + return; + } + + /* Do we have a good reason for building diplomats? */ + { + struct unit_type *ut = get_unit_type(u); + struct city *acity = find_city_to_diplomat(pplayer, pcity->x, + pcity->y, FALSE); + int want, loss, p_success, p_failure, time_to_dest; + int gain_incite = 0, gain_theft = 0, gain = 1; + int incite_cost; + + if (acity == NULL || acity->ai.already_considered_for_diplomat) { + /* Found no target or city already considered */ + return; + } + city_incite_cost(acity); /* prime variable */ + incite_cost = acity->incite_revolt_cost; + if (pplayer->player_no == acity->original) { + incite_cost /= 2; + } + if (pplayers_at_war(pplayer, city_owner(acity)) + && (find_palace(city_owner(acity)) != acity) + && !government_has_flag(get_gov_pplayer(city_owner(acity)), + G_UNBRIBABLE) + && (acity->incite_revolt_cost < + pplayer->economic.gold - pplayer->ai.est_upkeep)) { + /* incite gain (FIXME: we should count wonders too but need to + cache that somehow to avoid CPU hog -- Per) */ + gain_incite = acity->food_prod * FOOD_WEIGHTING + + acity->shield_prod * SHIELD_WEIGHTING + + (acity->luxury_total + + acity->tax_total + + acity->science_total) * TRADE_WEIGHTING; + gain_incite *= SHIELD_WEIGHTING; /* cost to take city otherwise */ + gain_incite -= acity->incite_revolt_cost; + } + if (city_owner(acity)->research.techs_researched < + pplayer->research.techs_researched + && !pplayers_allied(pplayer, city_owner(acity))) { + /* tech theft gain */ + gain_theft = total_bulbs_required(pplayer) * TRADE_WEIGHTING; + } + gain = MAX(gain_incite, gain_theft); + loss = ut->build_cost * SHIELD_WEIGHTING; + + /* Probability to succeed, assuming no defending diplomat */ + p_success = game.diplchance; + /* Probability to lose our unit */ + p_failure = (unit_type_flag(u, F_SPY) ? 100 - p_success : 100); + + time_to_dest = warmap.cost[acity->x][acity->y] + * SINGLE_MOVE / ut->move_rate; + time_to_dest *= (time_to_dest/2); /* No long treks, please */ + + /* Almost kill_desire */ + want = (p_success * gain - p_failure * loss) / 100 + - SHIELD_WEIGHTING * time_to_dest; + if (want <= 0) { + return; + } + + want = military_amortize(want, time_to_dest, ut->build_cost); + + if (!player_has_embassy(pplayer, city_owner(acity))) { + freelog(LOG_VERBOSE, "A diplomat desired in %s to establish an " + "embassy with %s in %s", pcity->name, + city_owner(acity)->name, acity->name); + want = MAX(want, 99); + } + if (want > choice->want) { + freelog(LOG_DEBUG, "%s,%s: %s is desired with want %d (was %d) to spy " + "in %s (incite desire %d, tech theft desire %d)", + pplayer->name, pcity->name, ut->name, want, choice->want, + acity->name, gain_incite, gain_theft); + choice->want = want; + choice->type = CT_ATTACKER; + choice->choice = u; + acity->ai.already_considered_for_diplomat = TRUE; + } + } +} + +/************************************************************************** + Check if something is on our receiving end for some nasty diplomat + business! Note that pdiplomat may die or be moved during this function. + + We try to make embassy first, and abort if we already have one and target + is allied. Then we incite, steal, sabotage or poison the city, in that + order of priority. +**************************************************************************/ +static void ai_diplomat_city(struct unit *pdiplomat, struct city *ctarget) +{ + struct packet_diplomat_action packet; + struct player *pplayer = unit_owner(pdiplomat); + struct player *tplayer = city_owner(ctarget); + int count_impr = count_sabotagable_improvements(ctarget); + int count_tech = count_stealable_techs(pplayer, tplayer); + int gold_avail = pplayer->economic.gold; + int incite_cost; + + if (pdiplomat->moves_left == 0) { + UNIT_LOG(LOG_ERROR, pdiplomat, "no moves left in ai_diplomat_city()!"); + } + + if (pplayer->ai.control) { + gold_avail -= pplayer->ai.est_upkeep; + } + packet.diplomat_id = pdiplomat->id; + packet.target_id = ctarget->id; + +#define T(my_act,my_val) \ + if (diplomat_can_do_action(pdiplomat, my_act, ctarget->x, \ + ctarget->y)) { \ + packet.action_type = my_act; \ + packet.value = my_val; \ + freelog(LOG_DIPLOMAT, "Player %s's diplomat %d does " #my_act \ + " on %s", pplayer->name, pdiplomat->id, ctarget->name); \ + handle_diplomat_action(pplayer, &packet); \ + return; \ + } + + if (!pdiplomat->foul) T(DIPLOMAT_EMBASSY,0); + if (pplayers_allied(pplayer, tplayer)) return; + city_incite_cost(ctarget); + incite_cost = ctarget->incite_revolt_cost; + if (pplayer->player_no == ctarget->original) { + incite_cost /= 2; + } + if (incite_cost + pplayer->ai.est_upkeep <= gold_avail) { + T(DIPLOMAT_INCITE,0); + } else { + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "%s too expensive!", ctarget->name); + } + if (count_tech > 0 + && (ctarget->steal == 0 || unit_flag(pdiplomat, F_SPY))) { + T(DIPLOMAT_STEAL,0); + } else { + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "We have already stolen from %s!", + ctarget->name); + } + if (count_impr > 0) T(DIPLOMAT_SABOTAGE, B_LAST+1); + T(SPY_POISON, 0); /* absolutely last resort */ +#undef T + + /* This can happen for a number of odd and esoteric reasons */ + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "decides to stand idle outside " + "enemy city %s!", ctarget->name); + ai_unit_ready(pdiplomat); +} + +/************************************************************************** + Find a city to send diplomats against, or NULL if none available on + this continent. x,y are coordinates of diplomat or city which wishes + to build diplomats, and foul is TRUE if diplomat has done something bad + before. + + + Requires an initialized warmap! +**************************************************************************/ +struct city *find_city_to_diplomat(struct player *pplayer, int x, int y, + bool foul) +{ + bool has_embassy; + int incite_cost = 0; /* incite cost */ + int move_cost = 0; /* move cost */ + int best_dist = MAX(map.xsize, map.ysize); + int continent = map_get_continent(x, y); + bool dipldef; /* whether target is protected by diplomats */ + bool handicap = ai_handicap(pplayer, H_TARGETS); + struct city *ctarget = NULL; + + players_iterate(aplayer) { + /* sneaky way of avoiding foul diplomat capture -AJS */ + has_embassy = player_has_embassy(pplayer, aplayer) || foul; + + /* Note: it is possible to lose an embassy to an allied player + if we have Marco's and lose it */ + if (aplayer == pplayer || is_barbarian(aplayer) + || (pplayers_allied(pplayer,aplayer) && has_embassy)) { + continue; + } + + city_list_iterate(aplayer->cities, acity) { + struct city *capital = find_palace(city_owner(acity)); + + if (handicap && !map_get_known(acity->x, acity->y, pplayer)) { + /* Target is not visible */ + continue; + } + if (continent != map_get_continent(acity->x, acity->y)) { + continue; + } + + /* Figure out incite cost */ + city_incite_cost(acity); /* FIXME: remove need for this */ + incite_cost = acity->incite_revolt_cost; + if (pplayer->player_no == acity->original) { + incite_cost /= 2; + } + + move_cost = warmap.cost[acity->x][acity->y]; + dipldef = (count_diplomats_on_tile(acity->x, acity->y) > 0); + if (!ctarget || (best_dist > move_cost)) { + if (!has_embassy + || (acity->steal == 0 && pplayer->research.techs_researched < + city_owner(acity)->research.techs_researched && !dipldef) + || (incite_cost < (pplayer->economic.gold - pplayer->ai.est_upkeep) + && !government_has_flag(get_gov_pplayer(city_owner(acity)), + G_UNBRIBABLE) + && acity != capital && !dipldef)) { + /* We have the closest enemy city so far on the same continent */ + ctarget = acity; + best_dist = move_cost; + } + } + } city_list_iterate_end; + } players_iterate_end; + + return ctarget; +} + +/************************************************************************** + Go to nearest/most threatened city. + + Requires an initialized warmap! +**************************************************************************/ +static struct city *ai_diplomat_defend(struct player *pplayer, int x, int y, + Unit_Type_id utype) +{ + int dist, urgency; + int best_dist = 30; /* any city closer than this is better than none */ + int best_urgency = 0; + struct city *ctarget = NULL; + int continent = map_get_continent(x, y); + + city_list_iterate(pplayer->cities, acity) { + if (continent != map_get_continent(acity->x, acity->y)) { + continue; + } + urgency = acity->ai.urgency + 1; + if (unit_type_flag(utype, F_DIPLOMAT)) { + if (!acity->ai.has_diplomat && acity->ai.diplomat_threat) { + urgency *= 5; /* we can help */ + } else if (acity->ai.has_diplomat) { + urgency /= 3; /* we are not really needed there */ + } + } + dist = warmap.cost[acity->x][acity->y]; + /* I don't know if this formula is optimal, but it works. */ + if (dist > best_dist) { + /* punish city for being so far away */ + urgency /= (float)(dist/best_dist); + } + if (urgency > best_urgency) { + ctarget = acity; + best_urgency = urgency; + best_dist = MAX(dist,1); /* squelch divide-by-zero */ + } + } city_list_iterate_end; + return ctarget; +} + +/************************************************************************** + Find units that we can reach, and bribe them. Returns TRUE if survived + the ordeal, FALSE if not or we expended all our movement. + + Requires an initialized warmap! +**************************************************************************/ +static bool ai_diplomat_bribe_nearby(struct player *pplayer, + struct unit *pdiplomat) +{ + struct packet_diplomat_action packet; + int move_rate = unit_type(pdiplomat)->move_rate; + struct unit *pvictim; + struct tile *ptile; + int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep; + int cost, destx, desty; + bool target = FALSE; + enum goto_result result; + + /* Check ALL possible targets */ + do { + whole_map_iterate(x, y) { + ptile = map_get_tile(x, y); + if (warmap.cost[x][y] > move_rate && ptile->terrain != T_OCEAN) { + /* Can't get there */ + continue; + } + destx = x; + desty = y; + if (ptile->terrain == T_OCEAN) { + /* Try to bribe a ship on the coast */ + int best = 9999; + adjc_iterate(x, y, x2, y2) { + if (best > warmap.cost[x2][y2]) { + best = warmap.cost[x2][y2]; + destx = x2; + desty = y2; + } + } adjc_iterate_end; + if (best >= move_rate) { + /* Can't get there, either */ + continue; + } + } + pvictim = unit_list_get(&ptile->units, 0); + if (!pvictim || !pplayers_at_war(pplayer, unit_owner(pvictim))) { + continue; + } + if (unit_list_size(&ptile->units) > 1) { + /* Can't do a stack */ + continue; + } + if (map_get_city(x, y)) { + /* Can't do city */ + continue; + } + if (government_has_flag(get_gov_pplayer(unit_owner(pvictim)), + G_UNBRIBABLE)) { + /* Can't bribe */ + continue; + } + cost = pvictim->bribe_cost = unit_bribe_cost(pvictim); + if (cost > gold_avail) { + /* Can't afford */ + continue; + } + /* Found someone! */ + pdiplomat->goto_dest_x = destx; + pdiplomat->goto_dest_y = desty; + result = do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE); + if (result == GR_DIED || pdiplomat->moves_left == 0) { + return FALSE; + } + if (diplomat_can_do_action(pdiplomat, DIPLOMAT_BRIBE, x, y)) { + packet.diplomat_id = pdiplomat->id; + packet.target_id = pvictim->id; + packet.action_type = DIPLOMAT_BRIBE; + handle_diplomat_action(pplayer, &packet); + } else { + /* usually because we ended move early due to another unit */ + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "could not bribe target " + " (%d, %d), %d moves left", x, y, pdiplomat->moves_left); + } + } whole_map_iterate_end; + } while (target); + + return (pdiplomat->moves_left > 0); +} + +/************************************************************************** + If we are the only diplomat in a threatened city, defend against enemy + actions. The passive defense is set by game.diplchance. The active + defense is to bribe units which end their move nearby. Our next trick is + to look for enemy cities on our continent and do our diplomat things. + + FIXME: It is important to establish contact with all civilizations, so + we should send diplomats by boat eventually. I just don't know how that + part of the code works, yet - Per +**************************************************************************/ +void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat) +{ + struct city *pcity, *ctarget = NULL; + Unit_Type_id sanity = pdiplomat->id; + + assert((pplayer != NULL) && (pdiplomat != NULL)); + + pcity = map_get_city(pdiplomat->x, pdiplomat->y); + generate_warmap(pcity, pdiplomat); + + /* Look for someone to bribe */ + if (!ai_diplomat_bribe_nearby(pplayer, pdiplomat)) { + /* Died or ran out of moves */ + return; + } + /* Note that the warmap may now be slightly wrong, but we + * don't care, its accuracy is good enough for our purposes */ + + /* If we are the only diplomat in a threatened city, then stay to defend */ + pcity = map_get_city(pdiplomat->x, pdiplomat->y); /* we may have moved */ + if (pcity && (count_diplomats_on_tile(pdiplomat->x, pdiplomat->y) == 1) + && (pcity->ai.diplomat_threat || pcity->ai.grave_danger)) { + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "stays to defend %s", pcity->name); + return; + } + + if (pdiplomat->activity == ACTIVITY_GOTO) { + /* Goto coordinates should always be normalized */ + if (!is_normal_map_pos(pdiplomat->goto_dest_x, pdiplomat->goto_dest_y)) { + UNIT_LOG(LOG_FATAL, pdiplomat, "had non-normalized map pos"); + assert(FALSE); + exit(EXIT_FAILURE); + } + ctarget = map_get_city(pdiplomat->goto_dest_x, pdiplomat->goto_dest_y); + } + + /* Check if we can do something with our destination now. */ + if (ctarget && pdiplomat->ai.ai_role == AIUNIT_ATTACK) { + int dist = real_map_distance(pdiplomat->x, pdiplomat->y, + pdiplomat->goto_dest_x, + pdiplomat->goto_dest_y); + + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "attack, dist %d to %s (%s goto)" + "[%d mc]", dist, ctarget ? ctarget->name : "(none)", + pdiplomat->activity == ACTIVITY_GOTO ? "has" : "no", + warmap.cost[pdiplomat->goto_dest_x][pdiplomat->goto_dest_y]); + if (dist == 1 && pdiplomat->moves_left > 0) { + /* Do our stuff */ + ai_unit_ready(pdiplomat); + ai_diplomat_city(pdiplomat, ctarget); + } else if (warmap.cost[ctarget->x][ctarget->y] + <= unit_type(pdiplomat)->move_rate * 2) { + /* Prepare to do our stuff (ie raise taxes) */ + int incite_cost; + + city_incite_cost(ctarget); + incite_cost = ctarget->incite_revolt_cost; + if (pplayer->player_no == ctarget->original) { + incite_cost /= 2; + } + pplayer->ai.maxbuycost = MAX(incite_cost, pplayer->ai.maxbuycost); + } + } + + /* Sanity check */ + if (find_unit_by_id(sanity) == NULL || pdiplomat->moves_left == 0) { + return; + } + + /* Check if existing target still makes sense */ + if (ctarget && pdiplomat->activity == ACTIVITY_GOTO + && pdiplomat->ai.ai_role == AIUNIT_ATTACK) { + /* We probably incited this city with another diplomat */ + if (pplayers_allied(pplayer, city_owner(ctarget)) + && player_has_embassy(pplayer, city_owner(ctarget))) { + ai_unit_ready(pdiplomat); + } + } + + /* If we are not busy, acquire a target. */ + if (pdiplomat->activity != ACTIVITY_GOTO) { + if ((ctarget = find_city_to_diplomat(pplayer, pdiplomat->x, pdiplomat->y, + pdiplomat->foul)) != NULL) { + ai_unit_new_role(pdiplomat, AIUNIT_ATTACK); + pdiplomat->ai.bodyguard = -1; /* want one */ + } else if ((ctarget = ai_diplomat_defend(pplayer, pdiplomat->x, pdiplomat->y, + pdiplomat->type)) != NULL) { + ai_unit_new_role(pdiplomat, AIUNIT_DEFEND_HOME); + } else { + /* + * This should only happen if the entire continent was suddenly + * conquered. So we head for closest coastal city and wait for someone + * to code ferrying for diplomats, or hostile attacks from the sea. + */ + ctarget = find_closest_owned_city(pplayer, pdiplomat->x, pdiplomat->y, + TRUE, NULL); + } + } + + /* GOTO (unless we want to stay) */ + if (ctarget && pdiplomat->moves_left > 0 + && !same_pos(pdiplomat->x, pdiplomat->y, ctarget->x, ctarget->y)) { + enum goto_result result; + + pdiplomat->goto_dest_x = ctarget->x; + pdiplomat->goto_dest_y = ctarget->y; + handle_unit_activity_request(pdiplomat, ACTIVITY_GOTO); + result = do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE); + switch (result) { + case GR_ARRIVED: + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "arrived at target"); + ai_unit_ready(pdiplomat); + break; + case GR_DIED: + case GR_OUT_OF_MOVEPOINTS: + break; + case GR_FAILED: + if (pdiplomat->moves_left == 0) { + return; /* this can happen */ + } + handle_unit_activity_request(pdiplomat, ACTIVITY_IDLE); + /* check if we can achieve something */ + if (real_map_distance(pdiplomat->x, pdiplomat->y, + pdiplomat->goto_dest_x, pdiplomat->goto_dest_y) == 1) { + ai_diplomat_city(pdiplomat, ctarget); + } else { + ai_diplomat_bribe_nearby(pplayer, pdiplomat); + } + break; + case GR_FOUGHT: + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "diplomat fought??"); + break; + } + } else { + /* Very unlikely but remotely possible */ + UNIT_LOG(LOG_DIPLOMAT, pdiplomat, "could not find a target, " + "a city to defend or even a coastal city!"); + } +} diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aidiplomat.h freeciv/ai/aidiplomat.h --- freeciv-phased/ai/aidiplomat.h 1970-01-01 01:00:00.000000000 +0100 +++ freeciv/ai/aidiplomat.h 2002-09-03 20:19:00.000000000 +0200 @@ -0,0 +1,30 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ +#ifndef FC__AIDIPLOMAT_H +#define FC__AIDIPLOMAT_H + +struct player; +struct unit; + +struct city *find_city_to_diplomat(struct player *pplayer, int x, int y, + bool foul); +void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat); +void ai_choose_diplomat_defensive(struct player *pplayer, + struct city *pcity, + struct ai_choice *choice, int def); +void ai_choose_diplomat_offensive(struct player *pplayer, + struct city *pcity, + struct ai_choice *choice); + + +#endif /* FC__AIDIPLOMAT_H */ diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aitools.c freeciv/ai/aitools.c --- freeciv-phased/ai/aitools.c 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/ai/aitools.c 2002-09-03 20:23:39.000000000 +0200 @@ -37,6 +37,19 @@ #include "aitools.h" /************************************************************************** + When a manage function is done with a unit and cannot or will not use + it anymore, call this to free its tasks so that another manage function + can sanely take it over. +**************************************************************************/ +void ai_unit_ready(struct unit *punit) +{ + ai_unit_new_role(punit, AIUNIT_NONE); /* also takes care of charge */ + handle_unit_activity_request(punit, ACTIVITY_IDLE); + punit->goto_dest_x = -1; + punit->goto_dest_y = -1; +} + +/************************************************************************** Ensure unit sanity **************************************************************************/ void ai_unit_new_role(struct unit *punit, enum ai_unit_task task) @@ -70,6 +83,9 @@ 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; @@ -105,7 +121,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; @@ -154,7 +170,11 @@ assert(punit); assert(unit_owner(punit)->ai.control); assert(is_normal_map_pos(x, y)); - assert(is_tiles_adjacent(punit->x, punit->y, x, y)); + if (!is_tiles_adjacent(punit->x, punit->y, x, y)) { + freelog(LOG_ERROR, "ai_unit_move: move to non-adjacent coords"); + assert(FALSE); + return FALSE; + } /* if enemy, stop and let ai attack function take this case */ if (is_enemy_unit_tile(ptile, pplayer) @@ -168,7 +188,7 @@ } /* don't leave bodyguard behind */ - if (has_bodyguard(punit) + if (has_bodyguard(punit) && (bodyguard = find_unit_by_id(punit->ai.bodyguard)) && same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y) && bodyguard->moves_left == 0) { @@ -247,13 +267,15 @@ } /************************************************************************** -... Credits the AI wants to have in reserves. + Credits the AI wants to have in reserves. We need some gold to bribe + and incite cities. + + "I still don't trust this function" -- Syela **************************************************************************/ int ai_gold_reserve(struct player *pplayer) { - int i = total_player_citizens(pplayer)*2; + int i = total_player_citizens(pplayer) * 5; return MAX(pplayer->ai.maxbuycost, i); -/* I still don't trust this function -- Syela */ } /************************************************************************** diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aitools.h freeciv/ai/aitools.h --- freeciv-phased/ai/aitools.h 2002-08-31 18:11:22.000000000 +0200 +++ freeciv/ai/aitools.h 2002-09-03 18:56:38.000000000 +0200 @@ -16,6 +16,10 @@ #include "shared.h" /* bool type */ #include "unit.h" +/* Map sanity */ +#define HAS_GOTO(punit) \ + (punit->goto_dest_x != -1 && punit->goto_dest_y != -1) + /* * Change these and remake to watch logs from a specific * part of the AI code. @@ -42,10 +46,11 @@ #define UNIT_LOG(level, punit, msg...) \ { \ char buffer[500]; \ - sprintf(buffer, "%s's %s[%d] (%d,%d)->(%d,%d) ", \ + sprintf(buffer, "%s's %s[%d] (%d,%d)->(%d,%d){%d} ", \ unit_owner(punit)->name, unit_type(punit)->name, \ punit->id, punit->x, punit->y, \ - punit->goto_dest_x, punit->goto_dest_y); \ + punit->goto_dest_x, punit->goto_dest_y, \ + punit->ai.bodyguard); \ cat_snprintf(buffer, sizeof(buffer), msg); \ freelog(MIN(LOGLEVEL_UNIT, level), buffer); \ } @@ -67,12 +72,17 @@ { \ char buffer[500]; \ struct unit *pcharge = find_unit_by_id(punit->ai.charge); \ - sprintf(buffer, "%s's bodyguard %s[%d] (%d,%d)[%d@%d,%d]->(%d,%d) ",\ + struct city *pcity = find_city_by_id(punit->ai.charge); \ + int x = -1, y = -1, id = -1; char *s = "none"; \ + if (pcharge) { \ + x = pcharge->x; y = pcharge->y; id = pcharge->id; \ + s = unit_type(pcharge)->name; \ + } else if (pcity) { \ + x = pcity->x; y = pcity->y; id = pcity->id; s = pcity->name; \ + } \ + sprintf(buffer, "%s's bodyguard %s[%d] (%d,%d){%s:%d@%d,%d} ",\ unit_owner(punit)->name, unit_type(punit)->name, \ - punit->id, punit->x, punit->y, \ - pcharge ? pcharge->id : -1, pcharge ? pcharge->x : -1,\ - pcharge ? pcharge->y : -1, \ - punit->goto_dest_x, punit->goto_dest_y); \ + punit->id, punit->x, punit->y, s, id, x, y); \ cat_snprintf(buffer, sizeof(buffer), msg); \ freelog(MIN(LOGLEVEL_BODYGUARD, level), buffer); \ } @@ -87,6 +97,7 @@ BODYGUARD_NONE }; +void ai_unit_ready(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); diff -uNrX freeciv/diff_ignore freeciv-phased/ai/aiunit.c freeciv/ai/aiunit.c --- freeciv-phased/ai/aiunit.c 2002-09-03 22:22:36.000000000 +0200 +++ freeciv/ai/aiunit.c 2002-09-03 20:26:09.000000000 +0200 @@ -42,13 +42,13 @@ #include "advmilitary.h" #include "aicity.h" +#include "aidata.h" +#include "aidiplomat.h" #include "aihand.h" #include "aitools.h" -#include "aidata.h" #include "aiunit.h" -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); static void ai_manage_barbarian_leader(struct player *pplayer, @@ -1070,19 +1070,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) { /* protect a unit */ - x = aunit->x; - y = aunit->y; + 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 */ - ai_unit_new_role(punit, AIUNIT_NONE); + ai_unit_ready(punit); return; } @@ -1099,12 +1100,10 @@ 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"); - } + GOTO_LOG(LOGLEVEL_BODYGUARD, punit, result, "bodyguard meet"); } else { /* can't possibly get there to help */ - ai_unit_new_role(punit, AIUNIT_NONE); + ai_unit_ready(punit); } } else { /* I had these guys set to just fortify, which is so dumb. -- Syela */ @@ -1115,12 +1114,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; - } } /************************************************************************* @@ -1480,20 +1473,24 @@ return; } - if (punit->ai.charge != BODYGUARD_NONE) { /* I am a bodyguard */ + /* Bodyguard sanity */ + if (punit->ai.charge > BODYGUARD_NONE) { /* I am a bodyguard */ 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); + ai_unit_ready(punit); } } @@ -1538,15 +1535,11 @@ if (acity) { ai_unit_new_role(punit, AIUNIT_ESCORT); punit->ai.charge = acity->id; - freelog(LOG_DEBUG, "%s@(%d, %d) going to defend %s@(%d, %d)", - unit_type(punit)->name, punit->x, punit->y, - acity->name, acity->x, acity->y); + BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend city"); } 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)", - unit_type(punit)->name, punit->x, punit->y, - unit_type(aunit)->name, aunit->x, aunit->y); + BODYGUARD_LOG(LOG_DEBUG, punit, "going to defend unit"); } else if (ai_unit_attack_desirability(punit->type) != 0 || (pcity && !same_pos(pcity->x, pcity->y, punit->x, punit->y))) ai_unit_new_role(punit, AIUNIT_ATTACK); @@ -1628,7 +1621,7 @@ unit_list_iterate(pplayer->units, aunit) if (aunit == punit) continue; - if (aunit->activity == ACTIVITY_GOTO && + if (aunit->activity == ACTIVITY_GOTO && HAS_GOTO(aunit) && (pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y))) { if (unit_type(aunit)->attack_strength > unit_type(aunit)->transport_capacity) { /* attacker */ @@ -1927,10 +1920,9 @@ } } 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); + freelog(LOG_DEBUG, "Friendly port nearest to (%d,%d) is at (%d,%d) [%d]", + punit->x, punit->y, + punit->goto_dest_x, punit->goto_dest_y, best); return TRUE; } @@ -2088,7 +2080,7 @@ static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit) { /* 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 *pcity = NULL; struct unit *bodyguard; int best = 4 * unit_type(punit)->move_rate, x = punit->x, y = punit->y; int n = 0, p = 0; @@ -2101,7 +2093,9 @@ if (punit->ai.passenger == 0) punit->ai.passenger = aunit->id; /* oops */ 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); + if (HAS_GOTO(aunit)) { + pcity = map_get_city(aunit->goto_dest_x, aunit->goto_dest_y); + } if (aunit->ai.bodyguard == BODYGUARD_NONE || bodyguard || (pcity && pcity->ai.invasion >= 2)) { if (pcity) { @@ -2207,29 +2201,15 @@ **************************************************************************/ 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) + /* FIXME: This should be pushed further back */ + 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 - { - struct city *pcity; - if (unit_flag(punit, F_FIELDUNIT) - && (pcity = map_get_city(punit->x, punit->y))) { - freelog(LOG_DEBUG, "%s in %s is going to %d", unit_name(punit->type), - pcity->name, punit->ai.ai_role); - } - } -#endif - switch (punit->ai.ai_role) { case AIUNIT_AUTO_SETTLER: case AIUNIT_BUILD_CITY: @@ -2325,14 +2305,8 @@ } } - if ((unit_flag(punit, F_DIPLOMAT)) - || (unit_flag(punit, F_SPY))) { + if (unit_flag(punit, F_DIPLOMAT)) { ai_manage_diplomat(pplayer, punit); - /* the right test if the unit is in a city and - there is no other diplomat it musn't move. - This unit is a bodyguard against enemy diplomats. - Right now I don't know how to use bodyguards! (17/12/98) (--NB) - */ return; } else if (unit_flag(punit, F_SETTLERS) ||unit_flag(punit, F_CITIES)) { @@ -2473,148 +2447,6 @@ return FALSE; } -/************************************************************************** - If we are the only diplomat in a city, defend against enemy actions. - The passive defense is set by game.diplchance. The active defense is - to bribe units which end their move nearby. - Our next trick is to look for enemy units and cities on our continent. - If we find a city, we look first to establish an embassy. The - information gained this way is assumed to be more useful than anything - else we could do. Making this come true is for future code. -AJS -**************************************************************************/ -static void ai_manage_diplomat(struct player *pplayer, struct unit *pdiplomat) -{ - bool handicap, has_emb; - int continent, dist, rmd, oic, did; - struct packet_unit_request req; - struct packet_diplomat_action dact; - struct city *pcity, *ctarget; - struct tile *ptile; - struct unit *ptres; - - if (pdiplomat->activity != ACTIVITY_IDLE) - handle_unit_activity_request(pdiplomat, ACTIVITY_IDLE); - - pcity = map_get_city(pdiplomat->x, pdiplomat->y); - - if (pcity && count_diplomats_on_tile(pdiplomat->x, pdiplomat->y) == 1) { - /* We're the only diplomat in a city. Defend it. */ - if (pdiplomat->homecity != pcity->id) { - /* this may be superfluous, but I like it. -AJS */ - req.unit_id=pdiplomat->id; - req.city_id=pcity->id; - req.name[0]='\0'; - handle_unit_change_homecity(pplayer, &req); - } - } else { - if (pcity) { - /* - * More than one diplomat in a city: may try to bribe trespassers. - * We may want this to be a city_map_iterate, or a warmap distance - * check, but this will suffice for now. -AJS - */ - adjc_iterate(pcity->x, pcity->y, x, y) { - if (diplomat_can_do_action(pdiplomat, DIPLOMAT_BRIBE, x, y)) { - /* A lone trespasser! Seize him! -AJS */ - ptile = map_get_tile(x, y); - ptres = unit_list_get(&ptile->units, 0); - ptres->bribe_cost = unit_bribe_cost(ptres); - if (ptres->bribe_cost < - (pplayer->economic.gold - pplayer->ai.est_upkeep)) { - dact.diplomat_id = pdiplomat->id; - dact.target_id = ptres->id; - dact.action_type = DIPLOMAT_BRIBE; - handle_diplomat_action(pplayer, &dact); - return; - } - } - } - adjc_iterate_end; - } - /* - * We're wandering in the desert, or there is more than one diplomat - * here. Go elsewhere. - * First, we look for an embassy, to steal a tech, or for a city to - * subvert. Then we look for a city of our own without a defending - * diplomat. This may not prove to work so very well, but it's - * possible otherwise that all diplomats we ever produce are home - * guard, and that's kind of silly. -AJS, 20000130 - */ - ctarget = NULL; - dist=MAX(map.xsize, map.ysize); - continent=map_get_continent(pdiplomat->x, pdiplomat->y); - handicap = ai_handicap(pplayer, H_TARGETS); - players_iterate(aplayer) { - /* don't target ourselves or friendly players that we already - have embassies with */ - if ((!pplayers_at_war(pplayer, aplayer)) - && (player_has_embassy(pplayer, aplayer))) continue; - /* sneaky way of avoiding foul diplomat capture -AJS */ - has_emb=player_has_embassy(pplayer, aplayer) || pdiplomat->foul; - city_list_iterate(aplayer->cities, acity) - if (handicap && !map_get_known(acity->x, acity->y, pplayer)) continue; - if (continent != map_get_continent(acity->x, acity->y)) continue; - city_incite_cost(acity); - /* figure our incite cost */ - oic = acity->incite_revolt_cost; - if (pplayer->player_no == acity->original) oic = oic / 2; - rmd=real_map_distance(pdiplomat->x, pdiplomat->y, acity->x, acity->y); - if (!ctarget || (dist > rmd)) { - if (!has_emb || acity->steal == 0 || (oic < - pplayer->economic.gold - pplayer->ai.est_upkeep)) { - /* We have the closest enemy city so far on the same continent */ - ctarget = acity; - dist = rmd; - } - } - city_list_iterate_end; - } players_iterate_end; - - if (!ctarget) { - /* No enemy cities are useful. Check our own. -AJS */ - city_list_iterate(pplayer->cities, acy) - if (continent != map_get_continent(acy->x, acy->y)) continue; - if (count_diplomats_on_tile(acy->x, acy->y) == 0) { - ctarget=acy; - dist=real_map_distance(pdiplomat->x, pdiplomat->y, acy->x, acy->y); - /* keep dist's integrity, and we can use it later. -AJS */ - } - city_list_iterate_end; - } - if (ctarget) { - /* Otherwise, we just kinda sit here. -AJS */ - did = -1; - if ((dist == 1) && (pplayer->player_no != ctarget->owner)) { - dact.diplomat_id=pdiplomat->id; - dact.target_id=ctarget->id; - if (!pdiplomat->foul && diplomat_can_do_action(pdiplomat, - DIPLOMAT_EMBASSY, ctarget->x, ctarget->y)) { - did=pdiplomat->id; - dact.action_type=DIPLOMAT_EMBASSY; - handle_diplomat_action(pplayer, &dact); - } else if (ctarget->steal == 0 && diplomat_can_do_action(pdiplomat, - DIPLOMAT_STEAL, ctarget->x, ctarget->y)) { - did=pdiplomat->id; - dact.action_type=DIPLOMAT_STEAL; - handle_diplomat_action(pplayer, &dact); - } else if (diplomat_can_do_action(pdiplomat, DIPLOMAT_INCITE, - ctarget->x, ctarget->y)) { - did=pdiplomat->id; - dact.action_type=DIPLOMAT_INCITE; - handle_diplomat_action(pplayer, &dact); - } - } - if ((did < 0) || find_unit_by_id(did)) { - pdiplomat->goto_dest_x=ctarget->x; - pdiplomat->goto_dest_y=ctarget->y; - set_unit_activity(pdiplomat, ACTIVITY_GOTO); - do_unit_goto(pdiplomat, GOTO_MOVE_ANY, FALSE); - } - } - } - return; -} - /************************************************************************* Barbarian leader tries to stack with other barbarian units, and if it's not possible it runs away. When on coast, it may disappear with 33% chance. diff -uNrX freeciv/diff_ignore freeciv-phased/ai/Makefile.am freeciv/ai/Makefile.am --- freeciv-phased/ai/Makefile.am 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/ai/Makefile.am 2002-09-03 18:56:38.000000000 +0200 @@ -36,4 +36,6 @@ aitools.c \ aitools.h \ aiunit.c \ - aiunit.h + aiunit.h \ + aidiplomat.c \ + aidiplomat.h diff -uNrX freeciv/diff_ignore freeciv-phased/client/control.c freeciv/client/control.c --- freeciv-phased/client/control.c 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/client/control.c 2002-09-03 18:56:38.000000000 +0200 @@ -513,8 +513,10 @@ pcity = find_city_by_id(victim_id); punit = find_unit_by_id(victim_id); - if (!pdiplomat || !unit_flag(pdiplomat, F_DIPLOMAT)) + if (!pdiplomat || !unit_flag(pdiplomat, F_DIPLOMAT)) { + assert(0); /* this is always an error */ continue; + } if (punit && is_diplomat_action_available(pdiplomat, DIPLOMAT_ANY_ACTION, diff -uNrX freeciv/diff_ignore freeciv-phased/common/city.h freeciv/common/city.h --- freeciv-phased/common/city.h 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/common/city.h 2002-09-03 18:56:38.000000000 +0200 @@ -186,8 +186,8 @@ /* building desirabilities - easiest to handle them here -- Syela */ int building_want[B_LAST]; /* not sure these will always be < 256 */ int danger; /* danger to be compared to assess_defense */ - bool diplomat_threat; /* an enemy diplomat or spy is near the city, - and this city has no diplomat or spy defender */ + bool diplomat_threat; /* an enemy diplomat or spy is near the city */ + bool has_diplomat; /* this city has diplomat or spy defender */ int urgency; /* how close the danger is; if zero, bodyguards can leave */ int grave_danger; /* danger that is upon us, should show positive feedback */ int wallvalue; /* how much it helps for defenders to be ground units */ @@ -212,6 +212,9 @@ int invasion; /* who's coming to kill us, for attack co-ordination */ int attack, bcost; /* This is also for invasion - total power and value of * all units coming to kill us. */ + + /* Used by _other_ cities temporarily while assigning diplomat targets */ + bool already_considered_for_diplomat; }; struct city { diff -uNrX freeciv/diff_ignore freeciv-phased/common/player.h freeciv/common/player.h --- freeciv-phased/common/player.h 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/common/player.h 2002-09-03 18:56:38.000000000 +0200 @@ -40,7 +40,7 @@ enum handicap_type { H_NONE=0, /* no handicaps */ - H_RIGIDPROD=1, /* can't switch to/from building_unit without penalty */ + H_DIPLOMAT=1, /* can't build offensive diplomats */ H_MAP=2, /* only knows map_get_known tiles */ H_TECH=4, /* doesn't know what enemies have researched */ H_CITYBUILDINGS=8, /* doesn't know what buildings are in enemy cities */ diff -uNrX freeciv/diff_ignore freeciv-phased/server/cityturn.c freeciv/server/cityturn.c --- freeciv-phased/server/cityturn.c 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/server/cityturn.c 2002-09-03 18:56:50.000000000 +0200 @@ -397,6 +397,7 @@ void begin_cities_turn(struct player *pplayer) { city_list_iterate(pplayer->cities, pcity) + pcity->ai.already_considered_for_diplomat = FALSE; /* ai hack */ define_orig_production_values(pcity); city_list_iterate_end; } diff -uNrX freeciv/diff_ignore freeciv-phased/server/settlers.c freeciv/server/settlers.c --- freeciv-phased/server/settlers.c 2002-08-31 17:16:20.000000000 +0200 +++ freeciv/server/settlers.c 2002-09-03 18:56:50.000000000 +0200 @@ -33,6 +33,7 @@ #include "aicity.h" #include "aiunit.h" #include "aidata.h" +#include "aitools.h" #include "settlers.h" @@ -905,7 +906,7 @@ generate_warmap(mycity, punit); - if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + if (punit->ai.ai_role == AIUNIT_BUILD_CITY && HAS_GOTO(punit)) { remove_city_from_minimap(punit->goto_dest_x, punit->goto_dest_y); } punit->ai.ai_role = AIUNIT_AUTO_SETTLER; /* here and not before! -- Syela */ @@ -1410,7 +1411,7 @@ unit_list_iterate(pplayer->units, punit) if (unit_flag(punit, F_SETTLERS) || unit_flag(punit, F_CITIES)) { - if (punit->activity == ACTIVITY_GOTO) { + if (punit->activity == ACTIVITY_GOTO && HAS_GOTO(punit)) { ptile = map_get_tile(punit->goto_dest_x, punit->goto_dest_y); ptile->assigned = ptile->assigned | i; /* assigned for us only */ } else { diff -uNrX freeciv/diff_ignore freeciv-phased/server/stdinhand.c freeciv/server/stdinhand.c --- freeciv-phased/server/stdinhand.c 2002-09-03 22:22:55.000000000 +0200 +++ freeciv/server/stdinhand.c 2002-09-03 18:56:50.000000000 +0200 @@ -1612,7 +1612,7 @@ int h[11] = { -1, H_NONE, H_NONE, - H_RATES | H_TARGETS | H_HUTS | H_DEFENSIVE, + H_RATES | H_TARGETS | H_HUTS | H_DEFENSIVE | H_DIPLOMAT, H_NONE, H_RATES | H_TARGETS | H_HUTS, H_NONE,