diff -urNX freeciv/diff_ignore freeciv/ai/advdiplomacy.c freeciv-dipl/ai/advdiplomacy.c --- freeciv/ai/advdiplomacy.c 1970-01-01 01:00:00.000000000 +0100 +++ freeciv-dipl/ai/advdiplomacy.c 2002-11-02 00:50:29.000000000 +0100 @@ -0,0 +1,658 @@ +/********************************************************************** + 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 "city.h" +#include "tech.h" +#include "diptreaty.h" +#include "player.h" +#include "game.h" +#include "support.h" +#include "shared.h" +#include "packets.h" +#include "rand.h" +#include "log.h" +#include "events.h" +#include "mem.h" + +#include "diplhand.h" +#include "spaceship.h" +#include "plrhand.h" +#include "citytools.h" + +#include "aidata.h" +#include "aitools.h" +#include "advmilitary.h" + +#include "advdiplomacy.h" + +#define LOG_DIPL LOG_NORMAL + +/* Prototypes */ +static int ai_goldequiv_city(struct city *pcity); +static int ai_goldequiv_tech(struct player *pplayer, Tech_Type_id tech); +static int ai_war_desire(struct player *pplayer, struct player *aplayer, + struct ai_data *ai); +static int ai_war_fear(struct player *pplayer, struct player *aplayer, + struct ai_data *ai); +static bool ai_attitude(struct ai_data *at, enum diplomacy_type dtype); +static int ai_goldequiv_clause(struct player *pplayer, struct player *aplayer, + struct Clause *pclause, struct ai_data *ai); + +/* PATCH TODO LIST: + -> stdinhand.c, add game.diplomacy set option + -> advmilitary.c, fix diplomat tech want excess + -> ai_suggest_clauses(...), if balance is not ok? + -> make embassies work if one side has it for diplomacy? + -> allow AIs to cheat an embassy on meet in beginning? +*/ + +/********************************************************************** +NOTES ON THIS AI DIPLOMACY FILE: + -> Be very careful with infinities - they can lead to overflows. + -> Everything is measured in gold, even treaties. However, treaties + are meant to be broken, if that is measured profitable. + -> Some players can be give attitudes (see aidata.h). These can + severely impair an ai's ability to pursue a diplomacy to its + own gain, but it is very good for modpacks and easy difficulties. + -> We rely on omniscience here. Really. +***********************************************************************/ + +/********************************************************************** + Send a diplomatic message. Use this instead of notify because we + may want to highligh/present these messages differently in the + future. + + TODO: Replace E_DIPLOMATIC_INCIDENT with a new type. + TODO: Rewrite ... +***********************************************************************/ +#define diplo_notify(pplayer, text, ...) \ + notify_player_ex(pplayer, -1, -1, E_DIPLOMATIC_INCIDENT, text, __VA_ARGS__); + +/********************************************************************** + Diplomat production is in advmilitary.c, we will add Marco Polo's + here later. +***********************************************************************/ +void diplomacy_advisor_choose_tech(struct player *pplayer, + struct ai_choice *choice) +{ + /* Nothing yet */ + choice->choice = A_NONE; + choice->want = 0; +} + +/********************************************************************** + Evaluate gold worth of a single clause in a treaty. Note that it + sometimes matter a great deal who is giving what to whom, and + sometimes (such as with treaties) it does not matter at all. +***********************************************************************/ +static int ai_goldequiv_clause(struct player *pplayer, + struct player *aplayer, + struct Clause *pclause, + struct ai_data *ai) +{ + int worth[MAX_NUM_PLAYERS]; /* worth of what X gives */ + int i; + bool give = (pplayer == pclause->from); + int receiver, giver; + + for (i = 0; i < MAX_NUM_PLAYERS; i++) { + worth[i] = 0; + } + + giver = pclause->from->player_no; + if (give) { + receiver = aplayer->player_no; + } else { + receiver = pplayer->player_no; + } + + switch (pclause->type) { + case CLAUSE_ADVANCE: { + int tech_cost; + + if (give) { + tech_cost = ai_goldequiv_tech(aplayer, pclause->value); + } else { + tech_cost = ai_goldequiv_tech(pplayer, pclause->value); + } + + /* Share and expect being shared brotherly between allies */ + if (pplayers_allied(pplayer, aplayer)) { + tech_cost /= 2; + } + + /* Calculate in tech leak to our enemies, guess 50% chance */ + if (give) { + players_iterate(eplayer) { + if (pplayers_allied(aplayer, eplayer) && aplayer != eplayer + && get_invention(eplayer, pclause->value) != TECH_KNOWN) { + worth[eplayer->player_no] -= + ai_goldequiv_tech(eplayer, pclause->value) / 2; + } + } players_iterate_end; + } + worth[receiver] += tech_cost; + /* FIXME: We should calculate use of tech for allies too, but we + need to look just how the tech exchange code turns out + first. - Per */ + } break; + + case CLAUSE_ALLIANCE: + case CLAUSE_PEACE: + case CLAUSE_CEASEFIRE: { + /* TODO: Note that right now most of this code is dead code because + * we set war_desire to zero for everyone but our no 1 target at + * another place in the code. - Per */ + int war_desire = ai->diplomacy.other[aplayer->player_no].war_desire; + + /* Attitude adjustment: Don't be too eager to get into a relationship + * that you can't stomach getting out of. */ + if (ai_attitude(ai, DIP_BACKSTABBER)) { + war_desire /= 1.5; + } + if (ai_attitude(ai, DIP_TRUSTWORTHY)) { + war_desire *= 1.5; + } + + /* Breaking treaties give us penalties on future diplomacy, so + * avoid flip-flopping treaty/war with our enemy. */ + if (aplayer == ai->diplomacy.target + && !ai_attitude(ai, DIP_BACKSTABBER)) { + war_desire *= 3; + } + + /* No peace to our chosen victim if we are militarist */ + if (aplayer == ai->diplomacy.target + && ai_attitude(ai, DIP_MILITARIST)) { + worth[pplayer->player_no] = -FC_INFINITY; + } + + /* We don't want treaties that obstruct our aims, but if war_desire + is negative then we want 'em this much. */ + worth[pplayer->player_no] += -(war_desire * TRADE_WEIGHTING + * city_list_size(&aplayer->cities)); + freelog(LOG_DIPL, "(%s ai diplo) Calculating pact with %s as (%d*%d*%d=)%d", + pplayer->name, aplayer->name, + war_desire, TRADE_WEIGHTING, city_list_size(&aplayer->cities), + worth[pplayer->player_no]); + + /* Modify for peace */ + if (pclause->type == CLAUSE_PEACE) { + if (!pplayers_non_attack(pplayer, aplayer)) { + diplo_notify(aplayer, "%s says: Let us first cease hostilies, %s", + pplayer->name, aplayer->name); + return -FC_INFINITY; + } else { + worth[pplayer->player_no] *= 1.5; + } + } + + /* Modify for alliance */ + if (pclause->type == CLAUSE_ALLIANCE) { + if (!pplayers_in_peace(pplayer, aplayer)) { + diplo_notify(aplayer, "%s says: Let us first cease hostilies, %s", + pplayer->name, aplayer->name); + return -FC_INFINITY; + } else { + worth[pplayer->player_no] *= 3; + } + } + + /* Modify for reputation */ + i = (GAME_DEFAULT_REPUTATION - aplayer->reputation) + * city_list_size(&aplayer->cities); + if (i != 0) { + worth[pplayer->player_no] -= i; + freelog(LOG_DIPL, "(%s ai diplo) %s has reputation %d, penalised with %d", + pplayer->name, aplayer->name, aplayer->reputation, i); + } + } break; + + case CLAUSE_GOLD: + worth[receiver] += pclause->value; + break; + + case CLAUSE_SEAMAP: + /* Very silly algorithm 1: Sea map more worth if enemy has more + cities. Reasoning is he has more use of seamap for settling + new areas the more cities he has already. */ + if (!give || pplayers_allied(pplayer, aplayer)) { + /* Useless to us - we're omniscient! And allies get it for free! */ + worth[receiver] = 0; + break; + } + worth[receiver] += 25 * city_list_size(&aplayer->cities); + if (give && ai_attitude(ai, DIP_XENOPHOBE)) { + worth[receiver] *= 3; /* "And what exactly do you want with this...?" */ + } + break; + + case CLAUSE_MAP: + /* Very silly algorithm 2: Land map more worth the more cities + we have, since we expose all of these to the enemy. */ + if (!give || pplayers_allied(pplayer, aplayer)) { + /* Useless to us - we're omniscient! And allies get it for free! */ + worth[receiver] = 0; + break; + } + worth[receiver] += 75 * city_list_size(&pplayer->cities); + /* Inflate numbers if not peace */ + if (!pplayers_in_peace(pplayer, aplayer)) { + if (!give && ai_attitude(ai, DIP_TRADER)) { + worth[receiver] *= 2; + } else if (give && ai_attitude(ai, DIP_MILITARIST)) { + worth[receiver] *= 10; + } else { + worth[receiver] *= 3; + } + } + if (give && ai_attitude(ai, DIP_XENOPHOBE)) { + worth[giver] -= FC_INFINITY; /* NEVER!!! */ + } + break; + + case CLAUSE_CITY: + worth[receiver] += ai_goldequiv_city( + city_list_find_id(&(pclause->from)->cities, pclause->value)); + if (give) { + /* AI must be crazy to trade away its cities, but traders do/are */ + if (ai_attitude(ai, DIP_TRADER) + && pplayers_in_peace(pplayer, aplayer)) { + worth[receiver] *= 2; + } else { + worth[receiver] *= 15; + } + } + break; + + case CLAUSE_VISION: + if (give) { + if (pplayers_allied(pplayer, aplayer)) { + worth[receiver] = 0; + } else { + /* so out of the question */ + worth[receiver] += FC_INFINITY; + } + } else { + worth[receiver] = 1; /* We are omniscient, so... */ + } + break; + } /* end of switch */ + + i = 0; /* balance */ + + /* Check externalities */ + players_iterate(eplayer) { + /* We are supposed to give away something, check how this benefits + our enemies */ + if (give && eplayer != aplayer && pplayers_at_war(eplayer, pplayer)) { + i += worth[eplayer->player_no]; + } + /* We receive something, check how this benefits our friends */ + if (!give && eplayer != pplayer && eplayer != aplayer + && pplayers_allied(eplayer, pplayer)) { + i -= worth[eplayer->player_no]; + } + } players_iterate_end; + + /* wrt pplayer */ + i += worth[pplayer->player_no]; + i -= worth[aplayer->player_no]; + + /* Returns balance of what they offer vs what we offer */ + if (give) { + freelog(LOG_DIPL, "(%s ai diplo) evaluating clause from %s to %s as worth %d", + pplayer->name, pplayer->name, aplayer->name, i); + } else { + freelog(LOG_DIPL, "(%s ai diplo) evaluating clause from %s to %s as worth %d", + pplayer->name, aplayer->name, pplayer->name, i); + } + return i; +} + +/********************************************************************** + pplayer is AI player, aplayer is the other player involved, treaty + is the treaty being considered. It is all a question about money :-) +***********************************************************************/ +void ai_evaluate_treaty(struct player *pplayer, struct player *aplayer, + struct Treaty *ptreaty) +{ + struct packet_diplomacy_info packet; + int balance = 0; + bool has_treaty = FALSE; + struct ai_data *ai = ai_data_get(pplayer); + + assert(!is_barbarian(pplayer)); + + packet.plrno0 = pplayer->player_no; + packet.plrno1 = aplayer->player_no; + packet.plrno_from = pplayer->player_no; + + /* Evaluate clauses */ + clause_list_iterate(ptreaty->clauses, pclause) { + balance += ai_goldequiv_clause(pplayer, aplayer, pclause, ai); + if (is_pact_clause(pclause->type)) { + has_treaty = TRUE; + } + } clause_list_iterate_end; + + /* If we are at war, and no peace is offered, then no deal */ + if (pplayers_at_war(pplayer, aplayer) && !has_treaty) { + return; + } + + /* Accept if balance is good */ + if (balance >= 0) { +/* + clause_list_iterate(ptreaty->clauses, pclause) { + if (pclause->type == CLAUSE_ALLIANCE) + diplo_notify(aplayer, "%s says: Yes, may we forever stand united, %s", + pplayer->name, aplayer->name); + if (pclause->type == CLAUSE_PEACE) + diplo_notify(aplayer, "%s says: Yes, peace in our time!", + pplayer->name); + if (pclause->type == CLAUSE_CEASEFIRE) + diplo_notify(aplayer, "%s says: Agreed. No more hostilities, %s", + pplayer->name, aplayer->name); + } clause_list_iterate_end; +*/ /* <<<--- this must be after BOTH sides have agreed! */ + handle_diplomacy_accept_treaty(pplayer, &packet); + } +} + +/********************************************************************** + Return TRUE iff given player has given attitude. +***********************************************************************/ +static bool ai_attitude(struct ai_data *ai, enum diplomacy_type dtype) +{ + return BOOL_VAL(ai->diplomacy.personality & dtype); +} + +/********************************************************************** + Calculate our desire to go to war against aplayer (or our hatred of + aplayer). +***********************************************************************/ +static int ai_war_desire(struct player *pplayer, struct player *aplayer, + struct ai_data *ai) +{ + int kill_desire, arrival, our_arrival; + struct player_spaceship *ship = &aplayer->spaceship; + struct player_spaceship *our_ship = &pplayer->spaceship; + + /* Show some weakness */ + if (!pplayers_at_war(pplayer, aplayer) + && ai_attitude(ai, DIP_TRUSTWORTHY)) { + return 0; + } + + /* We can aim for an allied victory */ + if (!ai_attitude(ai, DIP_MEGALOMANIAC) + && pplayers_allied(pplayer, aplayer)) { + return 0; + } + + /* Number of cities is a player's base potential. */ + kill_desire = city_list_size(&aplayer->cities); + + /* Count settlers in production for us, indicating our expansionism, + * while counting all enemy settlers as (worst case) indicators of + * enemy expansionism */ + city_list_iterate(pplayer->cities, pcity) { + if (pcity->is_building_unit + && unit_type_flag(pcity->currently_building, F_CITIES)) { + kill_desire -= 1; + } + } city_list_iterate_end; + unit_list_iterate(aplayer->units, punit) { + if (unit_flag(punit, F_CITIES)) { kill_desire += 1; } + } unit_list_iterate_end; + + /* Count big cities as twice the threat */ + city_list_iterate(aplayer->cities, pcity) { + kill_desire += pcity->size > 8 ? 1 : 0; + } city_list_iterate_end; + + /* Tech lead is worrisome */ + kill_desire += MAX(aplayer->research.techs_researched - + pplayer->research.techs_researched, 0); + + /* Spacerace loss we will not allow! */ + if (ship->state >= SSHIP_STARTED) { + /* add potential */ + kill_desire += city_list_size(&aplayer->cities); + } + if (our_ship->state >= SSHIP_LAUNCHED) { + our_arrival = MAX((int) our_ship->travel_time - + (game.year - our_ship->launch_year), 1); + } else { our_arrival = FC_INFINITY; } + if (ship->state >= SSHIP_LAUNCHED) { + arrival = MAX((int) ship->travel_time - + (game.year - ship->launch_year), 1); + if (arrival < our_arrival) { + /* The first division is for the unlikely case of several + simultaneous ship launches, the second to avoid overflows */ + kill_desire = FC_INFINITY / arrival / 2; + ai->diplomacy.timer = FC_INFINITY; + ai->diplomacy.strategy = WIN_CAPITAL; + } + } + + /* Jealousy */ + /* FIME: add score comparisons here, but add code for cacheing + score results first, or we will spam the CPU - Per */ + + return kill_desire; +} + +/********************************************************************** + Calculate relative military strength of us vs him + TODO: special checks for nukes (they inflate our numbers), and + add in allies +***********************************************************************/ +static int ai_war_fear(struct player *pplayer, struct player *aplayer, + struct ai_data *ai) +{ + int them = 1; + + /* relative military strength */ + unit_list_iterate(aplayer->units, punit) { + them += ai_unit_attack_desirability(punit->type); + } unit_list_iterate_end; + + return (them / ai->diplomacy.mil_strength) + - (ai->diplomacy.mil_strength / them); +} + +/********************************************************************** + How much is tech worth to player measured in gold +***********************************************************************/ +static int ai_goldequiv_tech(struct player *pplayer, Tech_Type_id tech) +{ + int worth; + + if (get_invention(pplayer, tech) == TECH_KNOWN) { + return 0; + } + worth = total_bulbs_required_for_goal(pplayer, tech) * TRADE_WEIGHTING; + worth += pplayer->ai.tech_want[tech] / 2; /* often way too much */ + if (get_invention(pplayer, tech) == TECH_REACHABLE) { + worth /= 2; + } + freelog(LOG_DIPL, "(%s ai diplo) %s goldequiv'ed to %d (had want %d)", + pplayer->name, get_tech_name(pplayer, tech), worth, + pplayer->ai.tech_want[tech]); + + return worth; +} + +/********************************************************************** + How much is city worth measured in gold +***********************************************************************/ +static int ai_goldequiv_city(struct city *pcity) +{ + int worth; + struct player *pplayer = city_owner(pcity); + + worth = pcity->size * 125; /* reasonable base cost */ + built_impr_iterate(pcity, impr) { + if (improvement_types[impr].is_wonder && !wonder_obsolete(impr)) { + worth += improvement_types[impr].build_cost; + } else { + worth += (improvement_types[impr].build_cost / 4); + } + } built_impr_iterate_end; + if (city_unhappy(pcity)) { + worth *= 0.75; + } + freelog(LOG_DIPL, "(%s ai diplo) city %s goldequiv'ed to %d", + pplayer->name, pcity->name, worth); + + return worth; +} + +/********************************************************************** + Only ever called for AI players and never for barbarians. +***********************************************************************/ +void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai) +{ + int war_desire = 0; + int war_fear = 0; + struct player *target = NULL; + + /* Stop war against a dead player */ + if (ai->diplomacy.target && !ai->diplomacy.target->is_alive) { + freelog(LOG_DIPL, "(%s ai diplo) Target player %s is dead. Victory!", + pplayer->name, ai->diplomacy.target->name); + ai->diplomacy.timer = 0; + ai->diplomacy.target = NULL; + if (ai->diplomacy.strategy == WIN_CAPITAL) { + ai->diplomacy.strategy = WIN_OPEN; + } + } + + /* Calculate our military strength */ + ai->diplomacy.mil_strength = 1; + unit_list_iterate(pplayer->units, punit) { + ai->diplomacy.mil_strength += ai_unit_attack_desirability(punit->type); + } unit_list_iterate_end; + + /* Ensure that we don't prematurely end an ongoing war */ + if (ai->diplomacy.timer-- > 0) { + return; + } + + /* Calculate our fears and desires, and find desired war target */ + players_iterate(aplayer) { + enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type; + + /* We don't fear/hate ourselves, those we don't know and those we're + * allied to (unless we're the backstabbing type) */ + if (aplayer == pplayer + || ds == DS_NO_CONTACT + || (ds == DS_ALLIANCE && !ai_attitude(ai, DIP_BACKSTABBER))) { + continue; + } + ai->diplomacy.other[aplayer->player_no].war_desire = + ai_war_desire(pplayer, aplayer, ai); + ai->diplomacy.other[aplayer->player_no].war_fear = + ai_war_fear(pplayer, aplayer, ai); + + /* Loyalty: hate our allies' enemies */ + players_iterate(eplayer) { + if (pplayers_allied(pplayer, eplayer) + && !ai_attitude(ai, DIP_BACKSTABBER) + && pplayers_at_war(aplayer, eplayer)) { + ai->diplomacy.other[aplayer->player_no].war_desire *= 2; + } + } players_iterate_end; + + /* Strongly prefer players we are at war with already */ + if (pplayers_non_attack(pplayer, aplayer)) { + ai->diplomacy.other[aplayer->player_no].war_desire /= 2; + } + freelog(LOG_DIPL, "(%s ai diplo) Against %s we have war desire %d and fear %d", + pplayer->name, aplayer->name, + ai->diplomacy.other[aplayer->player_no].war_desire, + ai->diplomacy.other[aplayer->player_no].war_fear); + + /* Find best target */ + if (ai->diplomacy.other[aplayer->player_no].war_desire > war_desire) { + target = aplayer; + war_desire = ai->diplomacy.other[aplayer->player_no].war_desire; + war_fear = ai->diplomacy.other[aplayer->player_no].war_fear; + } + } players_iterate_end; + + if (!target) { + freelog(LOG_DIPL, "(%s ai diplo) Found no target.", pplayer->name); + return; + } + + if (target != ai->diplomacy.target) { + if (ai->diplomacy.target) { + freelog(LOG_DIPL, "(%s ai diplo) Changing target from %s to %s", + pplayer->name, ai->diplomacy.target->name, target->name); + } else { + freelog(LOG_DIPL, "(%s ai diplo) Setting target to %s", + pplayer->name, target->name); + } + ai->diplomacy.target = target; + } + if (!pplayers_at_war(pplayer, target)) { + diplo_notify(target, "%s says: Peace in ... some other time", pplayer->name); + handle_player_cancel_pact(pplayer, target->player_no); + /* continue war at least in this arbitrary number of turns to show some spine */ + ai->diplomacy.timer = 6; + } + + /* Opportunism, Inc */ + players_iterate(aplayer) { + if (ai->diplomacy.other[aplayer->player_no].spam > 0) { + ai->diplomacy.other[aplayer->player_no].spam--; + } + if (is_barbarian(aplayer) /* no diplomacy */ + || aplayer == pplayer /* no self-indulgence */ + || aplayer == target) { /* and no mercy */ + continue; + } + /* Talk is cheap, and so is peace */ + ai->diplomacy.other[aplayer->player_no].war_desire = 0; + /* TODO: actually add code to do peace treaty here */ + if (ai->diplomacy.other[aplayer->player_no].spam > 0) { + /* Don't spam */ + continue; + } + /* Canvass support from existing friends for our war, and try to + * make friends with enemies. Then we wait some turns until next time + * we spam them with our gibbering chatter. */ + ai->diplomacy.other[aplayer->player_no].spam = myrand(4)+3; + if (pplayers_allied(pplayer, aplayer)) { + diplo_notify(aplayer, "%s says: Greetings our most trustworthy ally, " + "we call upon you to destroy our enemy, %s", pplayer->name, + target->name); + freelog(LOG_DIPL, "(%s ai diplo) demanding support from %s to crush %s", + pplayer->name, aplayer->name, target->name); + } else if (pplayers_non_attack(pplayer, aplayer)) { + diplo_notify(aplayer, "%s says: Greetings friend, may we suggest " + "a joint campaign against %s?", pplayer->name, target->name); + freelog(LOG_DIPL, "(%s ai diplo) requesting support from %s to crush %s", + pplayer->name, aplayer->name, target->name); + /* add check if we want to get allied here, if reputation good */ + } else if (pplayers_at_war(pplayer, aplayer)) { + diplo_notify(aplayer, "%s says: %s is threatening us both, may we suggest" + " a cessation of hostilities?", pplayer->name, target->name); + freelog(LOG_DIPL, "(%s ai diplo) requesting peace from %s to crush %s", + pplayer->name, aplayer->name, target->name); + } + } players_iterate_end; +} diff -urNX freeciv/diff_ignore freeciv/ai/advdiplomacy.h freeciv-dipl/ai/advdiplomacy.h --- freeciv/ai/advdiplomacy.h 1970-01-01 01:00:00.000000000 +0100 +++ freeciv-dipl/ai/advdiplomacy.h 2002-11-01 23:34:46.000000000 +0100 @@ -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__ADVATTITUDE_H +#define FC__ADVATTITUDE_H + +struct player; +struct ai_choice; +struct Treaty; +struct Clause; +struct ai_data; + +void diplomacy_advisor_choose_tech(struct player *pplayer, + struct ai_choice *choice); + +void ai_evaluate_treaty(struct player *pplayer, struct player *aplayer, + struct Treaty *ptreaty); + +void ai_diplomacy_calculate(struct player *pplayer, struct ai_data *ai); + +#endif /* FC__ADVATTITUDE_H */ diff -urNX freeciv/diff_ignore freeciv/ai/aidata.c freeciv-dipl/ai/aidata.c --- freeciv/ai/aidata.c 2002-09-27 14:18:56.000000000 +0200 +++ freeciv-dipl/ai/aidata.c 2002-11-02 00:09:35.000000000 +0100 @@ -25,6 +25,7 @@ #include "unittools.h" #include "maphand.h" +#include "advdiplomacy.h" #include "advmilitary.h" #include "aicity.h" #include "aitools.h" @@ -183,6 +184,12 @@ ai->stats.workers[ptile->continent]++; } } unit_list_iterate_end; + + /* Diplomacy */ + + if (pplayer->ai.control && game.diplomacy && !is_barbarian(pplayer)) { + ai_diplomacy_calculate(pplayer, ai); + } } /************************************************************************** @@ -197,6 +204,26 @@ } /************************************************************************** + Initialize with sane values +**************************************************************************/ +void ai_data_init(struct player *pplayer) { + struct ai_data *ai = &aidata[pplayer->player_no]; + int i; + + ai->diplomacy.target = NULL; + ai->diplomacy.strategy = WIN_OPEN; + ai->diplomacy.timer = 0; + ai->diplomacy.personality = 0; + ai->diplomacy.mil_strength = 0; + + for (i = 0; i < MAX_NUM_PLAYERS; i++) { + ai->diplomacy.other[i].war_desire = 0; + ai->diplomacy.other[i].war_fear = 0; + ai->diplomacy.other[i].spam = 0; + } +} + +/************************************************************************** Do some sanity checks then return a pointer to our data **************************************************************************/ struct ai_data *ai_data_get(struct player *pplayer) { diff -urNX freeciv/diff_ignore freeciv/ai/aidata.h freeciv-dipl/ai/aidata.h --- freeciv/ai/aidata.h 2002-08-31 02:55:28.000000000 +0200 +++ freeciv-dipl/ai/aidata.h 2002-11-02 00:09:12.000000000 +0100 @@ -19,7 +19,42 @@ * start of every turn. */ +enum winning_strategy { + WIN_OPEN, /* still undetermined */ + WIN_WAR, /* we have no other choice than to crush all opposition */ + WIN_SPACE, /* we will race for space, peace very important */ + WIN_HUNT, /* enemy has F_GAMELOSS unit which we will hunt down */ + WIN_CAPITAL /* we cannot win unless we take war_target's capital */ +}; + +/* FIXME: these should be set by difficulty and civworld */ +enum diplomacy_type { + DIP_NONE=0, /* No attitude */ + DIP_PACIFIST=1, /* War desire -50% if positive */ + DIP_MILITARIST=2, /* War desire +50% if positive */ + DIP_TRADER=4, /* Worth of our offer -25%, trade cities */ + DIP_GREEDY=8, /* Worth of our offer +50% */ + DIP_XENOPHOBE=16, /* Only ever agrees to ceasefire and tech */ + DIP_TRUSTWORTHY=32, /* Does not break treaties unless violated */ + DIP_BACKSTABBER=64, /* Happily agrees to anything and then... */ + DIP_MEGALOMANIAC=128, /* Does not share victory */ +}; + struct ai_data { + /* AI diplomacy */ + struct { + enum winning_strategy strategy; + int timer; /* pursue our goals with some stubbornness */ + struct { + int war_desire; /* desire for war or peace */ + int war_fear; /* fear of this player's military might */ + int spam; /* timer to avoid spamming a player with chat */ + } other[MAX_NUM_PLAYERS]; + struct player *target; /* concentrate on this player */ + int mil_strength; /* Estimate of our military strength */ + int personality; /* Personality traits */ + } diplomacy; + /* Long-term threats, not to be confused with short-term danger */ struct { bool invasions; /* check if we need to consider invasions */ @@ -47,6 +82,8 @@ int num_continents; /* last time we updated our continent data */ }; +void ai_data_init(struct player *pplayer); + void ai_data_turn_init(struct player *pplayer); void ai_data_turn_done(struct player *pplayer); diff -urNX freeciv/diff_ignore freeciv/ai/Makefile.am freeciv-dipl/ai/Makefile.am --- freeciv/ai/Makefile.am 2002-11-01 17:21:28.000000000 +0100 +++ freeciv-dipl/ai/Makefile.am 2002-11-01 21:16:16.000000000 +0100 @@ -21,6 +21,8 @@ advmilitary.h \ advscience.c \ advscience.h \ + advdiplomacy.c \ + advdiplomacy.h \ advspace.c \ advspace.h \ advtrade.c \ diff -urNX freeciv/diff_ignore freeciv/client/gui-gtk/plrdlg.c freeciv-dipl/client/gui-gtk/plrdlg.c --- freeciv/client/gui-gtk/plrdlg.c 2002-08-19 23:28:01.000000000 +0200 +++ freeciv-dipl/client/gui-gtk/plrdlg.c 2002-11-02 00:16:18.000000000 +0100 @@ -479,10 +479,11 @@ if (pplayer->is_alive && pplayer != game.player_ptr && player_has_embassy(game.player_ptr, pplayer)) { - if (pplayer->is_connected) + if (pplayer->is_connected || pplayer->ai.control) { gtk_widget_set_sensitive(players_meet_command, TRUE); - else + } else { gtk_widget_set_sensitive(players_meet_command, FALSE); + } gtk_widget_set_sensitive(players_int_command, TRUE); return; } diff -urNX freeciv/diff_ignore freeciv/client/gui-gtk-2.0/plrdlg.c freeciv-dipl/client/gui-gtk-2.0/plrdlg.c --- freeciv/client/gui-gtk-2.0/plrdlg.c 2002-08-13 01:06:07.000000000 +0200 +++ freeciv-dipl/client/gui-gtk-2.0/plrdlg.c 2002-11-02 00:16:36.000000000 +0100 @@ -479,10 +479,11 @@ if (pplayer->is_alive && pplayer != game.player_ptr && player_has_embassy(game.player_ptr, pplayer)) { - if (pplayer->is_connected) + if (pplayer->is_connected || pplayer->ai.control) { gtk_widget_set_sensitive(players_meet_command, TRUE); - else + } else { gtk_widget_set_sensitive(players_meet_command, FALSE); + } gtk_widget_set_sensitive(players_int_command, TRUE); return; } diff -urNX freeciv/diff_ignore freeciv/client/gui-win32/plrdlg.c freeciv-dipl/client/gui-win32/plrdlg.c --- freeciv/client/gui-win32/plrdlg.c 2002-08-09 00:07:44.000000000 +0200 +++ freeciv-dipl/client/gui-win32/plrdlg.c 2002-11-02 00:18:00.000000000 +0100 @@ -239,10 +239,11 @@ if (pplayer->is_alive && pplayer != game.player_ptr && player_has_embassy(game.player_ptr, pplayer)) { - if (pplayer->is_connected) + if (pplayer->is_connected || pplayer->ai.control) { EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_MEET), TRUE); - else + } else { EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_MEET), FALSE); + } EnableWindow(GetDlgItem(players_dialog,ID_PLAYERS_INT), TRUE); return; } diff -urNX freeciv/diff_ignore freeciv/client/gui-xaw/plrdlg.c freeciv-dipl/client/gui-xaw/plrdlg.c --- freeciv/client/gui-xaw/plrdlg.c 2002-08-09 00:07:44.000000000 +0200 +++ freeciv-dipl/client/gui-xaw/plrdlg.c 2002-11-02 00:17:05.000000000 +0100 @@ -302,10 +302,11 @@ if (pplayer->is_alive && pplayer != game.player_ptr && player_has_embassy(game.player_ptr, pplayer)) { - if(pplayer->is_connected) + if (pplayer->is_connected || pplayer->ai.control) { XtSetSensitive(players_meet_command, TRUE); - else + } else { XtSetSensitive(players_meet_command, FALSE); + } XtSetSensitive(players_int_command, TRUE); return; } diff -urNX freeciv/diff_ignore freeciv/common/game.c freeciv-dipl/common/game.c --- freeciv/common/game.c 2002-10-24 16:47:55.000000000 +0200 +++ freeciv-dipl/common/game.c 2002-11-01 21:43:30.000000000 +0100 @@ -665,6 +665,7 @@ game.researchcost = GAME_DEFAULT_RESEARCHCOST; game.diplcost = GAME_DEFAULT_DIPLCOST; game.diplchance = GAME_DEFAULT_DIPLCHANCE; + game.diplomacy = GAME_DEFAULT_DIPLOMACY; game.freecost = GAME_DEFAULT_FREECOST; game.conquercost = GAME_DEFAULT_CONQUERCOST; game.settlers = GAME_DEFAULT_SETTLERS; diff -urNX freeciv/diff_ignore freeciv/common/game.h freeciv-dipl/common/game.h --- freeciv/common/game.h 2002-09-29 21:59:54.000000000 +0200 +++ freeciv-dipl/common/game.h 2002-11-01 21:43:46.000000000 +0100 @@ -77,6 +77,7 @@ int researchcost; /* Multiplier on cost of new research */ int diplcost, freecost, conquercost; int diplchance; + bool diplomacy; int cityfactor; int citymindist; int civilwarsize; @@ -311,6 +312,7 @@ #define GAME_DEFAULT_DIPLCOST 0 #define GAME_MIN_DIPLCOST 0 #define GAME_MAX_DIPLCOST 100 +#define GAME_DEFAULT_DIPLOMACY TRUE #define GAME_DEFAULT_FOGOFWAR TRUE diff -urNX freeciv/diff_ignore freeciv/common/player.c freeciv-dipl/common/player.c --- freeciv/common/player.c 2002-09-29 21:59:54.000000000 +0200 +++ freeciv-dipl/common/player.c 2002-11-01 21:16:16.000000000 +0100 @@ -573,6 +573,20 @@ } /*************************************************************** +returns true iff players are allied or at peace +***************************************************************/ +bool pplayers_in_peace(const struct player *pplayer, + const struct player *pplayer2) +{ + enum diplstate_type ds = pplayer_get_diplstate(pplayer, pplayer2)->type; + if (pplayer == pplayer2) + return TRUE; + if (is_barbarian(pplayer) || is_barbarian(pplayer2)) + return FALSE; + return (ds == DS_ALLIANCE || ds == DS_PEACE); +} + +/*************************************************************** returns true iff players have peace or cease-fire ***************************************************************/ bool pplayers_non_attack(const struct player *pplayer, diff -urNX freeciv/diff_ignore freeciv/common/player.h freeciv-dipl/common/player.h --- freeciv/common/player.h 2002-11-01 17:21:28.000000000 +0100 +++ freeciv-dipl/common/player.h 2002-11-01 21:16:16.000000000 +0100 @@ -253,6 +253,8 @@ const struct player *pplayer2); bool pplayers_allied(const struct player *pplayer, const struct player *pplayer2); +bool pplayers_in_peace(const struct player *pplayer, + const struct player *pplayer2); bool pplayers_non_attack(const struct player *pplayer, const struct player *pplayer2); diff -urNX freeciv/diff_ignore freeciv/server/diplhand.c freeciv-dipl/server/diplhand.c --- freeciv/server/diplhand.c 2002-09-29 21:59:54.000000000 +0200 +++ freeciv-dipl/server/diplhand.c 2002-11-01 21:32:22.000000000 +0100 @@ -36,6 +36,8 @@ #include "settlers.h" #include "unittools.h" +#include "advdiplomacy.h" + #include "diplhand.h" static struct genlist treaties; @@ -416,6 +418,12 @@ PACKET_DIPLOMACY_REMOVE_CLAUSE, packet); lsend_packet_diplomacy_info(&plr1->connections, PACKET_DIPLOMACY_REMOVE_CLAUSE, packet); + if (plr0->ai.control) { + ai_evaluate_treaty(plr0, plr1, ptreaty); + } + if (plr1->ai.control) { + ai_evaluate_treaty(plr1, plr0, ptreaty); + } } } @@ -459,6 +467,12 @@ lsend_packet_diplomacy_info(&plr1->connections, PACKET_DIPLOMACY_CREATE_CLAUSE, packet); + if (plr0->ai.control) { + ai_evaluate_treaty(plr0, plr1, ptreaty); + } + if (plr1->ai.control) { + ai_evaluate_treaty(plr1, plr0, ptreaty); + } } } } @@ -524,18 +538,26 @@ if (!check_packet(packet, FALSE)) return; + assert(plr0 != plr1); + plr0=&game.players[packet->plrno0]; plr1=&game.players[packet->plrno1]; if (!find_treaty(plr0, plr1)) { - if (plr0->ai.control || plr1->ai.control) { - notify_player(plr0, _("AI controlled players cannot participate in " - "diplomatic meetings.")); + if (is_barbarian(plr0) || is_barbarian(plr1)) { + notify_player(plr0, _("Your diplomatic envoy was decapitated!")); return; } - - if (player_has_embassy(plr0, plr1) && plr0->is_connected && - plr0->is_alive && plr1->is_connected && plr1->is_alive) { + if (!game.diplomacy && (plr0->ai.control || plr1->ai.control)) { + notify_player(plr0, _("AI controlled players cannot participate in " + "diplomatic meetings.")); + return; + } + if (player_has_embassy(plr0, plr1) + && (plr0->is_connected || plr0->ai.control) + && plr0->is_alive + && (plr1->is_connected || plr1->ai.control) + && plr1->is_alive) { struct Treaty *ptreaty; ptreaty=fc_malloc(sizeof(struct Treaty)); @@ -573,6 +595,7 @@ return; } players_iterate(other_player) { + if ( (ptreaty=find_treaty(pplayer, other_player))) { struct packet_diplomacy_info packet; diff -urNX freeciv/diff_ignore freeciv/server/plrhand.c freeciv-dipl/server/plrhand.c --- freeciv/server/plrhand.c 2002-11-01 19:00:26.000000000 +0100 +++ freeciv-dipl/server/plrhand.c 2002-11-01 21:16:16.000000000 +0100 @@ -1562,10 +1562,10 @@ * but for now AI players are always at war. */ players_iterate(other_player) { - cplayer->diplstates[other_player->player_no].type = DS_WAR; + cplayer->diplstates[other_player->player_no].type = DS_NEUTRAL; cplayer->diplstates[other_player->player_no].has_reason_to_cancel = 0; cplayer->diplstates[other_player->player_no].turns_left = 0; - other_player->diplstates[cplayer->player_no].type = DS_WAR; + other_player->diplstates[cplayer->player_no].type = DS_NEUTRAL; other_player->diplstates[cplayer->player_no].has_reason_to_cancel = 0; other_player->diplstates[cplayer->player_no].turns_left = 0; @@ -1591,7 +1591,7 @@ for(i = 0; iresearch.inventions[i] = pplayer->research.inventions[i]; cplayer->turn_done = TRUE; /* Have other things to think about - paralysis*/ - cplayer->embassy = 0; /* all embassys destroyed */ + cplayer->embassy = 0; /* all embassies destroyed */ /* Do the ai */ diff -urNX freeciv/diff_ignore freeciv/server/srv_main.c freeciv-dipl/server/srv_main.c --- freeciv/server/srv_main.c 2002-10-24 16:48:00.000000000 +0200 +++ freeciv-dipl/server/srv_main.c 2002-11-01 23:30:02.000000000 +0100 @@ -89,6 +89,7 @@ #include "unithand.h" #include "unittools.h" +#include "advdiplomacy.h" #include "advmilitary.h" #include "aidata.h" #include "aihand.h" @@ -1859,6 +1860,11 @@ (void) send_server_info_to_metaserver(TRUE, FALSE); + /* Initialize AI data structures */ + players_iterate(pplayer) { + ai_data_init(pplayer); + } players_iterate_end; + if(game.is_new_game) { load_rulesets(); /* otherwise rulesets were loaded when savegame was loaded */