Index: common/diptreaty.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/diptreaty.h,v retrieving revision 1.10 diff -u -r1.10 diptreaty.h --- common/diptreaty.h 17 Apr 2003 20:06:36 -0000 1.10 +++ common/diptreaty.h 10 May 2003 21:13:42 -0000 @@ -18,7 +18,7 @@ enum clause_type { CLAUSE_ADVANCE, CLAUSE_GOLD, CLAUSE_MAP, CLAUSE_SEAMAP, CLAUSE_CITY, CLAUSE_CEASEFIRE, CLAUSE_PEACE, CLAUSE_ALLIANCE, - CLAUSE_VISION}; + CLAUSE_VISION, CLAUSE_LAST }; #define is_pact_clause(x) \ ((x == CLAUSE_CEASEFIRE) || (x == CLAUSE_PEACE) || (x == CLAUSE_ALLIANCE)) Index: common/player.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/player.c,v retrieving revision 1.117 diff -u -r1.117 player.c --- common/player.c 18 Apr 2003 10:08:53 -0000 1.117 +++ common/player.c 10 May 2003 21:13:42 -0000 @@ -36,6 +36,29 @@ #include "player.h" /*************************************************************** + Returns true iff p1 can ally p2. There is only one condition: + We are not at war with any of p2's allies. Note that for an + alliance to be made, we need to check this both ways. + + The reason for this is to avoid the dread 'love-love-hate' + triad, in which p1 is allied to p2 is allied to p3 is at + war with p1. These lead to strange situations. +***************************************************************/ +bool pplayer_can_ally(struct player *p1, struct player *p2) +{ + players_iterate(pplayer) { + if (pplayers_allied(p2, pplayer) + && pplayers_at_war(p1, pplayer) + && pplayer != p1 + && pplayer != p2 + && pplayer->is_alive) { + return FALSE; + } + } players_iterate_end; + return TRUE; +} + +/*************************************************************** Check if pplayer has an embassy with pplayer2. We always have an embassy with ourselves. ***************************************************************/ Index: common/player.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/player.h,v retrieving revision 1.98 diff -u -r1.98 player.h --- common/player.h 17 Apr 2003 20:06:36 -0000 1.98 +++ common/player.h 10 May 2003 21:13:42 -0000 @@ -255,6 +255,8 @@ *pplayer, const struct player *pplayer2); + +bool pplayer_can_ally(struct player *p1, struct player *p2); bool pplayers_at_war(const struct player *pplayer, const struct player *pplayer2); bool pplayers_allied(const struct player *pplayer, Index: server/diplhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/diplhand.c,v retrieving revision 1.69 diff -u -r1.69 diplhand.c --- server/diplhand.c 30 Apr 2003 21:14:35 -0000 1.69 +++ server/diplhand.c 10 May 2003 21:13:42 -0000 @@ -181,6 +181,15 @@ return; } break; + case CLAUSE_ALLIANCE: + if (!pplayer_can_ally(pplayer, other)) { + notify_player(pplayer, + _("Game: You are at war with one of %s's " + "allies - an alliance with %s is impossible."), + other->name, other->name); + return; + } + break; case CLAUSE_GOLD: if (pplayer->economic.gold < pclause->value) { notify_player(pplayer, @@ -263,6 +272,19 @@ goto cleanup; } break; + case CLAUSE_ALLIANCE: + if (!pplayer_can_ally(other, pplayer)) { + notify_player(pplayer, + _("Game: %s is at war with one of your " + "allies - an alliance with %s is impossible."), + other->name, other->name); + notify_player(other, + _("Game: You are at war with one of %s's " + "allies - an alliance with %s is impossible."), + pplayer->name, pplayer->name); + goto cleanup; + } + break; case CLAUSE_GOLD: if (other->economic.gold < pclause->value) { notify_player(plr0, @@ -397,6 +419,9 @@ _("Game: %s gives you shared vision."), pgiver->name); break; + case CLAUSE_LAST: + freelog(LOG_ERROR, "Received bad clause type"); + break; } } clause_list_iterate_end; Index: server/plrhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v retrieving revision 1.271 diff -u -r1.271 plrhand.c --- server/plrhand.c 10 May 2003 19:20:16 -0000 1.271 +++ server/plrhand.c 10 May 2003 21:13:42 -0000 @@ -800,6 +800,12 @@ /************************************************************************** Handles a player cancelling a "pact" with another player. + + packet.id is id of player we want to cancel a pact with + packet.val1 is a special value indicating what kind of treaty we want + to break. If this is CLAUSE_VISION we break shared vision. If it is + a pact treaty type, we break one pact level. If it is CLAUSE_LAST + we break _all_ treaties and go straight to war. **************************************************************************/ void handle_player_cancel_pact(struct player *pplayer, struct packet_generic_values *packet) @@ -808,7 +814,7 @@ enum diplstate_type new_type; struct player *pplayer2; int reppenalty = 0; - bool has_senate; + bool has_senate, repeat = FALSE; int other_player = packet->id; int clause = packet->value1; @@ -842,6 +848,7 @@ /* else, breaking a treaty */ +repeat_break_treaty: /* check what the new status will be, and what will happen to our reputation */ switch(old_type) { @@ -869,10 +876,11 @@ /* if there's a reason to cancel the pact, do it without penalty */ if (pplayer->diplstates[pplayer2->player_no].has_reason_to_cancel > 0) { pplayer->diplstates[pplayer2->player_no].has_reason_to_cancel = 0; - if (has_senate) + if (has_senate && !repeat) { notify_player(pplayer, _("The senate passes your bill because of the " "constant provocations of the %s."), get_nation_name_plural(pplayer2->nation)); + } } /* no reason to cancel, apply penalty (and maybe suffer a revolution) */ /* FIXME: according to civII rules, republics and democracies @@ -882,7 +890,7 @@ pplayer->reputation = MAX(pplayer->reputation - reppenalty, 0); notify_player(pplayer, _("Game: Your reputation is now %s."), reputation_text(pplayer->reputation)); - if (has_senate) { + if (has_senate && pplayer->revolution == 0) { if (myrand(GAME_MAX_REPUTATION) > pplayer->reputation) { notify_player(pplayer, _("Game: The senate decides to dissolve " "rather than support your actions any longer.")); @@ -899,6 +907,15 @@ pplayer2->diplstates[pplayer->player_no].turns_left = 16; + /* We want to go all the way to war, whatever the cost! + * This is only used when declaring war against an alliance + * and by the AI. */ + if (clause == CLAUSE_LAST && new_type != DS_WAR) { + repeat = TRUE; + old_type = new_type; + goto repeat_break_treaty; + } + send_player_info(pplayer, NULL); send_player_info(pplayer2, NULL); @@ -928,6 +945,22 @@ get_nation_name_plural(pplayer2->nation), get_nation_name_plural(pplayer->nation), diplstate_text(new_type)); + + /* Check fall-out of a war declaration. */ + players_iterate(other) { + if (other->is_alive && other != pplayer && other != pplayer2 + && (pplayer->team == TEAM_NONE || pplayer->team != pplayer2->team) + && new_type == DS_WAR && pplayers_allied(pplayer2, other) + && !pplayers_at_war(pplayer, other)) { + struct packet_generic_values packet; + + /* A declaration of war by A against B also means A declares + * war against all of B's allies. Yes, A gets the blame. */ + packet.id = other->player_no; + packet.value1 = CLAUSE_LAST; + handle_player_cancel_pact(pplayer, &packet); + } + } players_iterate_end; } /**************************************************************************