Complete.Org: Mailing Lists: Archives: freeciv-dev: November 2005:
[Freeciv-Dev] (PR#14548) New treaties rules
Home

[Freeciv-Dev] (PR#14548) New treaties rules

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#14548) New treaties rules
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sat, 5 Nov 2005 15:23:41 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14548 >

As previously discussed on the list and the wiki, this patch does:
 * The Neutral treaty is removed, and War becomes the default.
 * Cease-fire works as before, but ends up back in War. You can only
suggest Cease-fire when in War.
 * The AI will automatically suggest and accept Cease-fire when you first
meet. This should make the AI feel more 'interacting'.
 * New treaty Armistice. You cannot enter into Armistice directly, it
starts automatically when entering Peace treaty from War or Cease-fire. It
counts down to Peace treaty, just like a Cease-fire counts down to War.
 * Breaking a Peace/Armistice treaty drops you straight to War (dropping
to Cease-fire might be neat, but would be annoying, I think).
 * Breaking an Alliance drops you to Armistice.

Next patch (after this is committed) will do this:
 * Armistice disallows you from entering new units into an enemy's
borders, but leaves existing units alone.
 * When a Peace treaty kicks in, all military units belonging to
peace-treaty players inside your border are immediately disbanded. They
cannot send military units through your borders, and non-military units
are prohibited from most actions.
 * The AI will use the Armistice to move its units out of enemy territory.
 * Non-military units are also prohibited from doing actions that would
cause 'reason for war' under Cease-fire, Armistice, Peace and Alliance.
The 'reason for war' concept is retired - it was never transparent, and
abusable.
 * Under Alliance, players may enter each others' borders at will.
 * A shortcut key cycles through units in enemy territory for easy mass
exodus.

  - Per

Index: server/srv_main.c
===================================================================
--- server/srv_main.c   (revision 11221)
+++ server/srv_main.c   (working copy)
@@ -400,41 +400,42 @@
 }
 
 /**************************************************************************
- check for cease-fires running out; update cancelling reasons
+  Check for cease-fires and armistices running out; update cancelling 
+  reasons and contact information.
 **************************************************************************/
 static void update_diplomatics(void)
 {
-  players_iterate(player1) {
-    players_iterate(player2) {
-      struct player_diplstate *pdiplstate =
-         &player1->diplstates[player2->player_no];
+  players_iterate(plr1) {
+    players_iterate(plr2) {
+      struct player_diplstate *state = &plr1->diplstates[plr2->player_no];
 
-      pdiplstate->has_reason_to_cancel =
-         MAX(pdiplstate->has_reason_to_cancel - 1, 0);
+      state->has_reason_to_cancel = MAX(state->has_reason_to_cancel - 1, 0);
+      state->contact_turns_left = MAX(state->contact_turns_left - 1, 0);
 
-      pdiplstate->contact_turns_left =
-         MAX(pdiplstate->contact_turns_left - 1, 0);
+      if (state->type == DS_ARMISTICE) {
+        state->turns_left--;
+        if (state->turns_left <= 0) {
+          state->type = DS_PEACE;
+        }
+      }
 
-      if(pdiplstate->type == DS_CEASEFIRE) {
-       switch(--pdiplstate->turns_left) {
-       case 1:
-         notify_player(player1, NULL, E_DIPLOMACY,
-                       _("Concerned citizens point "
-                         "out that the cease-fire with %s will run out soon."),
-                       player2->name);
-         break;
-       case 0:
-         notify_player(player1, NULL, E_DIPLOMACY,
-                       _("The cease-fire with %s has "
-                         "run out. You are now neutral towards the %s."),
-                       player2->name,
-                       get_nation_name_plural(player2->nation));
-         pdiplstate->type = DS_NEUTRAL;
-         check_city_workers(player1);
-         check_city_workers(player2);
-         break;
-       }
+      if (state->type == DS_CEASEFIRE) {
+        switch(--state->turns_left) {
+        case 1:
+          notify_player(plr1, NULL, E_DIPLOMACY,
+                        _("Concerned citizens point out that the cease-fire "
+                          "with %s will run out soon."), plr2->name);
+          break;
+        case 0:
+          notify_player(plr1, NULL, E_DIPLOMACY, _("The cease-fire with "
+                        "%s has run out. You are now at war with the %s."),
+                        plr2->name, get_nation_name_plural(plr2->nation));
+          state->type = DS_WAR;
+          check_city_workers(plr1);
+          check_city_workers(plr2);
+          break;
         }
+      }
     } players_iterate_end;
   } players_iterate_end;
 }
Index: server/gamelog.c
===================================================================
--- server/gamelog.c    (revision 11220)
+++ server/gamelog.c    (working copy)
@@ -51,7 +51,7 @@
   "Map",
   "Seamap",
   "City",
-  "Ceasefire",
+  "Cease-fire",
   "Peace",
   "Alliance",
   "Team",
@@ -293,7 +293,7 @@
                  "<m>Treaty - City (%s) given to the %s from the %s</m>");
       break;
     case GL_CEASEFIRE:
-      sz_strlcpy(msg, "<m>Treaty - Ceasefire between the %s and the %s</m>");
+      sz_strlcpy(msg, "<m>Treaty - Cease-fire between the %s and the %s</m>");
       break;
     case GL_PEACE:
       sz_strlcpy(msg, "<m>Treaty - Peace between the %s and the %s</m>");
Index: server/diplhand.c
===================================================================
--- server/diplhand.c   (revision 11220)
+++ server/diplhand.c   (working copy)
@@ -54,6 +54,9 @@
 
 static struct treaty_list *treaties = NULL;
 
+/* FIXME: Should this be put in a ruleset somewhere? */
+#define TURNS_LEFT 16
+
 /**************************************************************************
 ...
 **************************************************************************/
@@ -98,6 +101,7 @@
   struct Treaty *ptreaty;
   struct player *pother;
   bool *player_accept, *other_accept;
+  enum dipl_reason diplcheck;
 
   if (!is_valid_player_id(counterpart) || pplayer->player_no == counterpart) {
     return;
@@ -181,22 +185,24 @@
            return;
          }
          break;
-       case CLAUSE_ALLIANCE:
-          if (!pplayer_can_ally(pplayer, pother)) {
-           notify_player(pplayer, NULL, E_DIPLOMACY,
-                         _("You are at war with one of %s's "
-                           "allies - an alliance with %s is impossible."),
-                         pother->name, pother->name);
+       case CLAUSE_CEASEFIRE:
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_CEASEFIRE);
+          if (diplcheck != DIPL_OK) {
             return;
           }
-          if (!pplayer_can_ally(pother, pplayer)) {
-           notify_player(pplayer, NULL, E_DIPLOMACY,
-                         _("%s is at war with one of your allies "
-                           "- an alliance with %s is impossible."),
-                         pother->name, pother->name);
+          break;
+       case CLAUSE_PEACE:
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_PEACE);
+          if (diplcheck != DIPL_OK) {
             return;
           }
           break;
+       case CLAUSE_ALLIANCE:
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_PEACE);
+          if (diplcheck != DIPL_OK) {
+            return;
+          }
+          break;
        case CLAUSE_GOLD:
          if (pplayer->economic.gold < pclause->value) {
            notify_player(pplayer, NULL, E_DIPLOMACY,
@@ -286,18 +292,23 @@
        case CLAUSE_ALLIANCE:
           /* We need to recheck this way since things might have
            * changed. */
-          if (!pplayer_can_ally(pother, pplayer)) {
-           notify_player(pplayer, NULL, E_DIPLOMACY,
-                         _("%s is at war with one of your "
-                           "allies - an alliance with %s is impossible."),
-                         pother->name, pother->name);
-           notify_player(pother, NULL, E_DIPLOMACY,
-                         _("You are at war with one of %s's "
-                           "allies - an alliance with %s is impossible."),
-                         pplayer->name, pplayer->name);
-           goto cleanup;
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_PEACE);
+          if (diplcheck != DIPL_OK) {
+            goto cleanup;
           }
           break;
+  case CLAUSE_PEACE:
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_PEACE);
+          if (diplcheck != DIPL_OK) {
+            goto cleanup;
+          }
+          break;
+  case CLAUSE_CEASEFIRE:
+          diplcheck = pplayer_can_make_treaty(pplayer, pother, DS_CEASEFIRE);
+          if (diplcheck != DIPL_OK) {
+            goto cleanup;
+          }
+          break;
        case CLAUSE_GOLD:
          if (pother->economic.gold < pclause->value) {
            notify_player(pplayer, NULL, E_DIPLOMACY,
@@ -420,9 +431,9 @@
        }
       case CLAUSE_CEASEFIRE:
        pgiver->diplstates[pdest->player_no].type=DS_CEASEFIRE;
-       pgiver->diplstates[pdest->player_no].turns_left=16;
+       pgiver->diplstates[pdest->player_no].turns_left = TURNS_LEFT;
        pdest->diplstates[pgiver->player_no].type=DS_CEASEFIRE;
-       pdest->diplstates[pgiver->player_no].turns_left=16;
+       pdest->diplstates[pgiver->player_no].turns_left = TURNS_LEFT;
        notify_player(pgiver, NULL, E_TREATY_CEASEFIRE,
                         _("You agree on a cease-fire with %s."),
                         pdest->name);
@@ -437,18 +448,22 @@
        check_city_workers(pother);
        break;
       case CLAUSE_PEACE:
-       pgiver->diplstates[pdest->player_no].type=DS_PEACE;
-       pdest->diplstates[pgiver->player_no].type=DS_PEACE;
+       pgiver->diplstates[pdest->player_no].type = DS_ARMISTICE;
+       pdest->diplstates[pgiver->player_no].type = DS_ARMISTICE;
+       pgiver->diplstates[pdest->player_no].turns_left = TURNS_LEFT;
+       pdest->diplstates[pgiver->player_no].turns_left = TURNS_LEFT;
        pgiver->diplstates[pdest->player_no].max_state = 
           MAX(DS_PEACE, pgiver->diplstates[pdest->player_no].max_state);
        pdest->diplstates[pgiver->player_no].max_state = 
           MAX(DS_PEACE, pdest->diplstates[pgiver->player_no].max_state);
        notify_player(pgiver, NULL, E_TREATY_PEACE,
-                        _("You agree on a peace treaty with %s."),
-                        pdest->name);
+                        _("You agree on an armistice with %s. In %d turns it 
will turn "
+         "into a peace treaty. Move your units out of %s's territory."),
+                        pdest->name, TURNS_LEFT, pdest->name);
        notify_player(pdest, NULL, E_TREATY_PEACE,
-                        _("You agree on a peace treaty with %s."),
-                        pgiver->name);
+                        _("You agree on an armistice with %s. In %d turns it 
will turn "
+         "into a peace treaty. Move your units out of %s's territory."),
+                        pgiver->name, TURNS_LEFT, pgiver->name);
         gamelog(GAMELOG_TREATY, GL_PEACE, pgiver, pdest);
        if (old_diplstate == DS_ALLIANCE) {
          update_players_after_alliance_breakup(pgiver, pdest);
Index: server/sanitycheck.c
===================================================================
--- server/sanitycheck.c        (revision 11221)
+++ server/sanitycheck.c        (working copy)
@@ -450,7 +450,8 @@
       if (pplayers_allied(pplayer, pplayer2)
           && pplayer->is_alive
           && pplayer2->is_alive) {
-        SANITY_CHECK(pplayer_can_ally(pplayer, pplayer2));
+        SANITY_CHECK(pplayer_can_make_treaty(pplayer, pplayer2, DS_ALLIANCE)
+                     != DIPL_ALLIANCE_PROBLEM);
       }
     } players_iterate_end;
 
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 11221)
+++ server/savegame.c   (working copy)
@@ -2003,8 +2003,11 @@
       secfile_lookup_int_default(file, DS_WAR,
                                 "player%d.diplstate%d.type", plrno, i);
     plr->diplstates[i].max_state = 
-      secfile_lookup_int_default(file, DS_NEUTRAL,
+      secfile_lookup_int_default(file, DS_WAR,
                                 "player%d.diplstate%d.max_state", plrno, i);
+    plr->diplstates[i].first_contact_turn = 
+      secfile_lookup_int_default(file, 0,
+                                "player%d.diplstate%d.first_contact_turn", 
plrno, i);
     plr->diplstates[i].turns_left = 
       secfile_lookup_int_default(file, -2,
                                 "player%d.diplstate%d.turns_left", plrno, i);
@@ -2019,7 +2022,7 @@
   /* We don't need this info, but savegames carry it anyway.
      To avoid getting "unused" warnings we touch the values like this. */
   for (i = game.info.nplayers; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
-    secfile_lookup_int_default(file, DS_NEUTRAL,
+    secfile_lookup_int_default(file, DS_WAR,
                               "player%d.diplstate%d.type", plrno, i);
     secfile_lookup_int_default(file, 0,
                               "player%d.diplstate%d.turns_left", plrno, i);
@@ -2665,6 +2668,8 @@
                       "player%d.diplstate%d.type", plrno, i);
     secfile_insert_int(file, plr->diplstates[i].max_state,
                       "player%d.diplstate%d.max_state", plrno, i);
+    secfile_insert_int(file, plr->diplstates[i].first_contact_turn,
+                      "player%d.diplstate%d.first_contact_turn", plrno, i);
     secfile_insert_int(file, plr->diplstates[i].turns_left,
                       "player%d.diplstate%d.turns_left", plrno, i);
     secfile_insert_int(file, plr->diplstates[i].has_reason_to_cancel,
@@ -3661,7 +3666,8 @@
     players_iterate(plr) {
       players_iterate(aplayer) {
         if (pplayers_allied(plr, aplayer)
-            && !pplayer_can_ally(plr, aplayer)) {
+            && pplayer_can_make_treaty(plr, aplayer, DS_ALLIANCE) 
+               == DIPL_ALLIANCE_PROBLEM) {
           freelog(LOG_ERROR, _("Illegal alliance structure detected: "
                   "%s's alliance to %s reduced to peace treaty."),
                   plr->name, aplayer->name);
Index: server/plrhand.c
===================================================================
--- server/plrhand.c    (revision 11220)
+++ server/plrhand.c    (working copy)
@@ -499,6 +499,7 @@
   enum diplstate_type new_type;
   struct player *pplayer2;
   bool repeat = FALSE;
+  enum dipl_reason diplcheck;
 
   if (!is_valid_player_id(other_player_id)) {
     return;
@@ -507,13 +508,20 @@
   old_type = pplayer->diplstates[other_player_id].type;
   pplayer2 = get_player(other_player_id);
 
-  /* can't break a pact with yourself */
-  if (pplayer == pplayer2) {
+  diplcheck = pplayer_can_cancel_treaty(pplayer, pplayer2);
+
+  /* The senate may not allow you to break the treaty.  In this case you
+   * must first dissolve the senate then you can break it. */
+  if (diplcheck == DIPL_SENATE_BLOCKING) {
+    notify_player(pplayer, NULL, E_TREATY_BROKEN,
+                    _("The senate will not allow you to break treaty "
+                      "with the %s.  You must either dissolve the senate "
+                      "or wait until a more timely moment."),
+                    get_nation_name_plural(pplayer2->nation));
     return;
   }
 
-  /* Can't break up a team, period. */
-  if (players_on_same_team(pplayer, pplayer2)) {
+  if (diplcheck != DIPL_OK) {
     return;
   }
 
@@ -528,40 +536,20 @@
     return;
   }
 
-  if (old_type == DS_WAR) {
-    return; /* Duh */
-  }
-
   reject_all_treaties(pplayer);
   reject_all_treaties(pplayer2);
   /* else, breaking a treaty */
-repeat_break_treaty:
 
-  /* The senate may not allow you to break the treaty.  In this case you
-   * must first dissolve the senate then you can break it. */
-  if (!pplayer_can_declare_war(pplayer, pplayer2)) {
-    notify_player(pplayer, NULL, E_TREATY_BROKEN,
-                    _("The senate will not allow you to break treaty "
-                      "with the %s.  You must either dissolve the senate "
-                      "or wait until a more timely moment."),
-                    get_nation_name_plural(pplayer2->nation));
-    return;
-  }
-
   /* check what the new status will be */
   switch(old_type) {
   case DS_NO_CONTACT: /* possible if someone declares war on our ally */
-  case DS_NEUTRAL:
-    new_type = DS_WAR;
-    break;
+  case DS_ARMISTICE:
   case DS_CEASEFIRE:
-    new_type = DS_NEUTRAL;
-    break;
   case DS_PEACE:
-    new_type = DS_NEUTRAL;
+    new_type = DS_WAR;
     break;
   case DS_ALLIANCE:
-    new_type = DS_PEACE;
+    new_type = DS_ARMISTICE;
     break;
   default:
     freelog(LOG_ERROR, "non-pact diplstate in handle_player_cancel_pact");
@@ -585,14 +573,6 @@
     update_players_after_alliance_breakup(pplayer, pplayer2);
   }
 
-  /* We want to go all the way to war, whatever the cost! 
-   * This is only used by the AI. */
-  if (clause == CLAUSE_LAST && new_type != DS_WAR) {
-    repeat = TRUE;
-    old_type = new_type;
-    goto repeat_break_treaty;
-  }
-
   /* if there's a reason to cancel the pact, do it without penalty */
   /* FIXME: in the current implementation if you break more than one
    * treaty simultaneously it may partially succed: the first treaty-breaking
@@ -618,7 +598,6 @@
   send_player_info(pplayer, NULL);
   send_player_info(pplayer2, NULL);
 
-
   if (old_type == DS_ALLIANCE) {
     /* Inform clients about units that have been hidden.  Units in cities
      * and transporters are visible to allies but not visible once the
@@ -630,7 +609,6 @@
     remove_allied_visibility(pplayer2, pplayer);
   }
 
-
   /* 
    * Refresh all cities which have a unit of the other side within
    * city range. 
@@ -981,7 +959,7 @@
     }
 
     for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
-      packet->diplstates[i].type       = DS_NEUTRAL;
+      packet->diplstates[i].type       = DS_WAR;
       packet->diplstates[i].turns_left = 0;
       packet->diplstates[i].has_reason_to_cancel = 0;
       packet->diplstates[i].contact_turns_left = 0;
@@ -1161,24 +1139,19 @@
 
   if (pplayer1 == pplayer2
       || !pplayer1->is_alive
-      || !pplayer2->is_alive
-      || get_player_bonus(pplayer1, EFT_NO_DIPLOMACY) > 0
-      || get_player_bonus(pplayer2, EFT_NO_DIPLOMACY) > 0) {
+      || !pplayer2->is_alive) {
     return;
   }
-
-  pplayer1->diplstates[player2].contact_turns_left = game.info.contactturns;
-  pplayer2->diplstates[player1].contact_turns_left = game.info.contactturns;
-
+  if (get_player_bonus(pplayer1, EFT_NO_DIPLOMACY) == 0
+      && get_player_bonus(pplayer2, EFT_NO_DIPLOMACY) == 0) {
+    pplayer1->diplstates[player2].contact_turns_left = game.info.contactturns;
+    pplayer2->diplstates[player1].contact_turns_left = game.info.contactturns;
+  }
   if (pplayer_get_diplstate(pplayer1, pplayer2)->type == DS_NO_CONTACT) {
-    /* Set default new diplomatic state depending on game.info.diplomacy
-     * server setting. Default is zero, which gives DS_NEUTRAL. */
-    enum diplstate_type dipstate = diplomacy_possible(pplayer1,pplayer2)
-                                    ? DS_NEUTRAL : DS_WAR;
-
-    pplayer1->diplstates[player2].type
-      = pplayer2->diplstates[player1].type
-      = dipstate;
+    pplayer1->diplstates[player2].type = DS_WAR;
+    pplayer2->diplstates[player1].type = DS_WAR;
+    pplayer1->diplstates[player2].first_contact_turn = game.info.turn;
+    pplayer2->diplstates[player1].first_contact_turn = game.info.turn;
     notify_player(pplayer1, ptile,
                     E_FIRST_CONTACT,
                     _("You have made contact with the %s, ruled by %s."),
@@ -1187,13 +1160,16 @@
                     E_FIRST_CONTACT,
                     _("You have made contact with the %s, ruled by %s."),
                     get_nation_name_plural(pplayer1->nation), pplayer1->name);
+    if (pplayer1->ai.control) {
+      ai_diplomacy_first_contact(pplayer1, pplayer2);
+    }
+    if (pplayer2->ai.control && !pplayer1->ai.control) {
+      ai_diplomacy_first_contact(pplayer2, pplayer1);
+    }
     send_player_info(pplayer1, pplayer2);
     send_player_info(pplayer2, pplayer1);
     send_player_info(pplayer1, pplayer1);
     send_player_info(pplayer2, pplayer2);
-
-    check_city_workers(pplayer1);
-    check_city_workers(pplayer2);
     return;
   } else {
     assert(pplayer_get_diplstate(pplayer2, pplayer1)->type != DS_NO_CONTACT);
Index: common/events.c
===================================================================
--- common/events.c     (revision 11220)
+++ common/events.c     (working copy)
@@ -113,7 +113,7 @@
   GEN_EV(N_("Tech: Learned New Tech"),                E_TECH_LEARNED),
   GEN_EV(N_("Treaty: Alliance"),                      E_TREATY_ALLIANCE),
   GEN_EV(N_("Treaty: Broken"),                        E_TREATY_BROKEN),
-  GEN_EV(N_("Treaty: Ceasefire"),                     E_TREATY_CEASEFIRE),
+  GEN_EV(N_("Treaty: Cease-fire"),                    E_TREATY_CEASEFIRE),
   GEN_EV(N_("Treaty: Peace"),                         E_TREATY_PEACE),
   GEN_EV(N_("Treaty: Shared Vision"),                 E_TREATY_SHARED_VISION),
   GEN_EV(N_("Unit: Attack Failed"),                   E_UNIT_LOST_ATT),
Index: common/diptreaty.c
===================================================================
--- common/diptreaty.c  (revision 11220)
+++ common/diptreaty.c  (working copy)
@@ -142,6 +142,7 @@
   
   if (is_pact_clause(type)
       && ((ds == DS_PEACE && type == CLAUSE_PEACE)
+          || (ds == DS_ARMISTICE && type == CLAUSE_PEACE)
           || (ds == DS_ALLIANCE && type == CLAUSE_ALLIANCE)
           || (ds == DS_CEASEFIRE && type == CLAUSE_CEASEFIRE))) {
     /* we already have this diplomatic state */
Index: common/player.c
===================================================================
--- common/player.c     (revision 11220)
+++ common/player.c     (working copy)
@@ -34,41 +34,51 @@
 #include "player.h"
 
 /***************************************************************
-  Returns true iff p1 can declare war on p2.
+  Returns true iff p1 can cancel treaty on p2.
 
   The senate may not allow you to break the treaty.  In this 
   case you must first dissolve the senate then you can break 
   it.  This is waived if you have statue of liberty since you 
   could easily just dissolve and then recreate it. 
 ***************************************************************/
-bool pplayer_can_declare_war(const struct player *p1, 
-                             const struct player *p2)
+enum dipl_reason pplayer_can_cancel_treaty(const struct player *p1, 
+                                           const struct player *p2)
 {
-  return (p1->diplstates[p2->player_no].has_reason_to_cancel > 0
-          || get_player_bonus(p1, EFT_HAS_SENATE) == 0
-          || get_player_bonus(p1, EFT_ANY_GOVERNMENT) > 0);
+  enum diplstate_type ds = pplayer_get_diplstate(p1, p2)->type;
+
+  if (p1 == p2 || ds == DS_WAR) {
+    return DIPL_ERROR;
+  }
+  if (players_on_same_team(p1, p2)
+      && ds == DS_ALLIANCE) {
+    return DIPL_ERROR;
+  }
+  if (p1->diplstates[p2->player_no].has_reason_to_cancel == 0
+      && ds != DS_TEAM
+      && get_player_bonus(p1, EFT_HAS_SENATE) > 0
+      && get_player_bonus(p1, EFT_ANY_GOVERNMENT) == 0) {
+    return DIPL_SENATE_BLOCKING;
+  }
+  return DIPL_OK;
 }
 
 /***************************************************************
-  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.
+  Returns true iff p1 can be in alliance with p2.
 
+  Check that 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(const struct player *p1, const struct player *p2)
+static bool is_valid_alliance(const struct player *p1, 
+                              const struct player *p2)
 {
-  if (p1 == p2) {
-    return TRUE; /* duh! */
-  }
-  if (get_player_bonus(p1, EFT_NO_DIPLOMACY)
-      || get_player_bonus(p2, EFT_NO_DIPLOMACY)) {
-    return FALSE;
-  }
   players_iterate(pplayer) {
     enum diplstate_type ds = pplayer_get_diplstate(p1, pplayer)->type;
+
     if (pplayer != p1
         && pplayer != p2
         && pplayers_allied(p2, pplayer)
@@ -77,10 +87,62 @@
       return FALSE;
     }
   } players_iterate_end;
+
   return TRUE;
 }
 
 /***************************************************************
+  Returns true iff p1 can make given treaty with p2.
+
+  We cannot regress in a treaty chain. So we cannot suggest
+  'Peace' if we are in 'Alliance'. Then you have to cancel.
+
+  For alliance there is only one condition: We are not at war 
+  with any of p2's allies.
+***************************************************************/
+enum dipl_reason pplayer_can_make_treaty(const struct player *p1,
+                                         const struct player *p2,
+                                         enum diplstate_type treaty)
+{
+  enum diplstate_type existing = pplayer_get_diplstate(p1, p2)->type;
+
+  if (p1 == p2) {
+    return DIPL_ERROR; /* duh! */
+  }
+  if (get_player_bonus(p1, EFT_NO_DIPLOMACY)
+      || get_player_bonus(p2, EFT_NO_DIPLOMACY)) {
+    return DIPL_ERROR;
+  }
+  if (treaty == DS_WAR 
+      || treaty == DS_NO_CONTACT 
+      || treaty == DS_ARMISTICE 
+      || treaty == DS_LAST) {
+    return DIPL_ERROR; /* these are not positive treaties */
+  }
+  if (treaty == DS_CEASEFIRE && existing != DS_WAR) {
+    return DIPL_ERROR; /* only available from war */
+  }
+  if (treaty == DS_PEACE 
+      && (existing != DS_WAR && existing != DS_CEASEFIRE)) {
+    return DIPL_ERROR;
+  }
+  if (treaty == DS_TEAM
+      && (!players_on_same_team(p1, p2)
+          || existing == DS_TEAM)) {
+    return DIPL_ERROR;
+  }
+  if (treaty == DS_ALLIANCE
+      && (!is_valid_alliance(p1, p2) || !is_valid_alliance(p2, p1))) {
+    return DIPL_ALLIANCE_PROBLEM;
+  }
+  /* this check must be last: */
+  if (treaty == existing) {
+    return DIPL_ERROR;
+  }
+  return DIPL_OK;
+}
+
+/***************************************************************
   Check if pplayer has an embassy with pplayer2. We always have
   an embassy with ourselves.
 ***************************************************************/
@@ -596,7 +658,7 @@
 {
   static const char *ds_names[DS_LAST] = 
   {
-    N_("?diplomatic_state:Neutral"),
+    N_("?diplomatic_state:Armistice"),
     N_("?diplomatic_state:War"), 
     N_("?diplomatic_state:Cease-fire"),
     N_("?diplomatic_state:Peace"),
@@ -683,7 +745,7 @@
   if (is_barbarian(pplayer) || is_barbarian(pplayer2)) {
     return FALSE;
   }
-  return (ds == DS_PEACE || ds == DS_CEASEFIRE || ds == DS_NEUTRAL);
+  return (ds == DS_PEACE || ds == DS_CEASEFIRE || ds == DS_ARMISTICE);
 }
 
 /**************************************************************************
Index: common/player.h
===================================================================
--- common/player.h     (revision 11220)
+++ common/player.h     (working copy)
@@ -119,7 +119,7 @@
  * Adding to or reordering this array will break many things.
  */
 enum diplstate_type {
-  DS_NEUTRAL = 0,
+  DS_ARMISTICE = 0,
   DS_WAR,
   DS_CEASEFIRE,
   DS_PEACE,
@@ -129,10 +129,15 @@
   DS_LAST      /* leave this last */
 };
 
+enum dipl_reason {
+  DIPL_OK, DIPL_ERROR, DIPL_SENATE_BLOCKING, DIPL_ALLIANCE_PROBLEM
+};
+
+/* the following are for "pacts" */
 struct player_diplstate {
   enum diplstate_type type;    /* this player's disposition towards other */
-  /* the following are for "pacts" */
   enum diplstate_type max_state; /* maximum treaty level ever had */
+  int first_contact_turn; /* turn we had first contact with this player */
   int turns_left;              /* until pact (e.g., cease-fire) ends */
   int has_reason_to_cancel;    /* 0: no, 1: this turn, 2: this or next turn */
   int contact_turns_left;      /* until contact ends */
@@ -254,9 +259,11 @@
                                                     *pplayer2);
 bool are_diplstates_equal(const struct player_diplstate *pds1,
                          const struct player_diplstate *pds2);
-bool pplayer_can_declare_war(const struct player *p1,
-                             const struct player *p2);
-bool pplayer_can_ally(const struct player *p1, const struct player *p2);
+enum dipl_reason pplayer_can_make_treaty(const struct player *p1,
+                                         const struct player *p2,
+                                         enum diplstate_type treaty);
+enum dipl_reason pplayer_can_cancel_treaty(const struct player *p1,
+                                           const struct player *p2);
 bool pplayers_at_war(const struct player *pplayer,
                    const struct player *pplayer2);
 bool pplayers_allied(const struct player *pplayer,
Index: ai/aitools.c
===================================================================
--- ai/aitools.c        (revision 11220)
+++ ai/aitools.c        (working copy)
@@ -131,7 +131,7 @@
   
   ds = pplayer_get_diplstate(pplayer, aplayer)->type;
   
-  if (ds == DS_WAR || ds == DS_CEASEFIRE || ds == DS_NEUTRAL) {
+  if (ds == DS_WAR || ds == DS_CEASEFIRE) {
     /* It's already a war or aplayer can declare it soon */
     return TRUE;
   }
Index: ai/advdiplomacy.c
===================================================================
--- ai/advdiplomacy.c   (revision 11220)
+++ ai/advdiplomacy.c   (working copy)
@@ -63,6 +63,9 @@
 /* turn this off when we don't want functions to message players */
 static bool diplomacy_verbose = TRUE;
 
+/* turns to wait after contact before taking aim for war */
+#define TURNS_BEFORE_TARGET 15
+
 /**********************************************************************
   Send a diplomatic message. Use this instead of notify directly
   because we may want to highligh/present these messages differently
@@ -288,19 +291,6 @@
       break;
     }
 
-    /* Reduce treaty level?? */
-    {
-      enum diplstate_type ds = pplayer_get_diplstate(pplayer, aplayer)->type;
-
-      if ((pclause->type == CLAUSE_PEACE && ds > DS_PEACE)
-          || (pclause->type == CLAUSE_CEASEFIRE && ds > DS_CEASEFIRE)) {
-        notify(aplayer, _("*%s (AI)* I will not let you go that easy, %s. "
-               "The current treaty stands."), pplayer->name, aplayer->name);
-        worth = -BIG_NUMBER;
-        break;
-      }
-    }
-
     /* Steps of the treaty ladder */
     if (pclause->type == CLAUSE_PEACE) {
       struct player_diplstate *ds = &pplayer->diplstates[aplayer->player_no];
@@ -309,9 +299,10 @@
         notify(aplayer, _("*%s (AI)* Let us first cease hostilies, %s."),
                pplayer->name, aplayer->name);
         worth = -BIG_NUMBER;
-      } else if (ds->type == DS_CEASEFIRE && ds->turns_left > 2) {
+      } else if (ds->type == DS_CEASEFIRE && ds->turns_left > 4) {
         notify(aplayer, _("*%s (AI)* I wish to see you keep the current "
-               "ceasefire first, %s."), pplayer->name, aplayer->name);
+               "ceasefire for a bit longer first, %s."), pplayer->name, 
+               aplayer->name);
         worth = -BIG_NUMBER;
       } else if (adip->countdown >= 0 && adip->countdown < -1) {
         worth = -BIG_NUMBER; /* but say nothing */
@@ -341,8 +332,18 @@
          ai_players_can_agree_on_ceasefire(pplayer, aplayer)) {
         worth = 0;
       } else {
-        worth = greed(pplayer->ai.love[aplayer->player_no]
-                      - ai->diplomacy.req_love_for_ceasefire);
+        int turns = game.info.turn;
+
+        turns -= pplayer_get_diplstate(pplayer, aplayer)->first_contact_turn;
+        if (turns < TURNS_BEFORE_TARGET) {
+          worth = 0; /* show some good faith */
+          break;
+        } else {
+          worth = greed(pplayer->ai.love[aplayer->player_no]);
+          DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "ceasefire worth=%d love=%d "
+                    "turns=%d", worth, pplayer->ai.love[aplayer->player_no],
+                    turns);
+        }
       }
     }
 
@@ -530,18 +531,27 @@
   /* If we are at war, and no peace is offered, then no deal, unless
    * it is just gifts, in which case we gratefully accept. */
   if (ds_after == DS_WAR && !only_gifts) {
+    DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "no peace offered, must refuse");
     return;
   }
 
   if (given_cities > 0
       && city_list_size(pplayer->cities) - given_cities <= 2) {
     /* always keep at least two cities */
+    DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "cannot give last cities");
     return;
   }
 
   /* Accept if balance is good */
   if (total_balance >= 0) {
     handle_diplomacy_accept_treaty_req(pplayer, aplayer->player_no);
+    DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "balance was good: %d", 
+              total_balance);
+  } else {
+    notify(aplayer, _("*%s (AI)* This deal was not very good for us, %s!"),
+           pplayer->name, aplayer->name);
+    DIPLO_LOG(LOG_DIPL2, pplayer, aplayer, "balance was bad: %d", 
+              total_balance);
   }
 }
 
@@ -626,7 +636,7 @@
    * he or she offers us gifts. It is only a gift if _all_ the clauses
    * are beneficial to us. */
   if (total_balance > 0 && gift) {
-    int i = total_balance / ((city_list_size(pplayer->cities) * 50) + 1);
+    int i = total_balance / ((city_list_size(pplayer->cities) * 10) + 1);
 
     i = MIN(i, ai->diplomacy.love_incr * 150) * 10;
     pplayer->ai.love[aplayer->player_no] += i;
@@ -728,7 +738,7 @@
 
     /* Remember: pplayers_allied() returns true when target == eplayer */
     if (!cancel_excuse && pplayers_allied(target, eplayer)) {
-      if (ds == DS_NEUTRAL) {
+      if (ds == DS_ARMISTICE) {
         want -= abs(want) / 10; /* 10% off */
       } else if (ds == DS_CEASEFIRE) {
         want -= abs(want) / 7; /* 15% off */
@@ -781,6 +791,18 @@
 }
 
 /********************************************************************** 
+  What to do when we first meet. pplayer is the AI player.
+***********************************************************************/
+void ai_diplomacy_first_contact(struct player *pplayer,
+                                struct player *aplayer)
+{
+  notify(aplayer, _("*%s (AI)* Greetings %s! May we suggest a ceasefire "
+         "while we get to know each other better?"),
+         pplayer->name, aplayer->name);
+  ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE, 0);
+}
+
+/********************************************************************** 
   Calculate our diplomatic predispositions here. Don't do anything.
 
   Only ever called for AI players and never for barbarians.
@@ -1090,13 +1112,20 @@
   }
 
   /* Check for Senate obstruction.  If so, dissolve it. */
-  if (!pplayer_can_declare_war(pplayer, target)) {
+  if (pplayer_can_cancel_treaty(pplayer, target) == DIPL_SENATE_BLOCKING) {
     handle_player_change_government(pplayer, 
                                     game.info.government_when_anarchy_id);
   }
 
   /* This will take us straight to war. */
-  handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  while (pplayer_get_diplstate(pplayer, target)->type != DS_WAR) {
+    if (pplayer_can_cancel_treaty(pplayer, target) != DIPL_OK) {
+      DIPLO_LOG(LOG_ERROR, pplayer, target, "Wanted to cancel treaty but "
+                "was unable to.");
+      return;
+    }
+    handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  }
 
   /* Throw a tantrum */
   if (pplayer->ai.love[target->player_no] > 0) {
@@ -1261,11 +1290,19 @@
   /*** Declare war against somebody if we are out of targets ***/
 
   players_iterate(aplayer) {
+    int turns; /* turns since contact */
+
+    if (NEVER_MET(pplayer, aplayer)) {
+      continue;
+    }
+    turns = game.info.turn;
+    turns -= pplayer_get_diplstate(pplayer, aplayer)->first_contact_turn;
     if (aplayer->is_alive 
         && WAR(pplayer, aplayer)) {
       need_targets = FALSE;
     } else if (aplayer->is_alive
-               && pplayer->ai.love[aplayer->player_no] < most_hatred) {
+               && pplayer->ai.love[aplayer->player_no] < most_hatred
+               && turns > TURNS_BEFORE_TARGET) {
       most_hatred = pplayer->ai.love[aplayer->player_no];
       target = aplayer;
     }
@@ -1357,8 +1394,7 @@
         || aplayer == target     /* no mercy */
         || !aplayer->is_alive
         || adip->countdown >= 0
-        || !could_meet_with_player(pplayer, aplayer)
-        || adip->at_war_with_ally) {
+        || !could_meet_with_player(pplayer, aplayer)) {
       continue;
     }
 
@@ -1390,6 +1426,9 @@
       break;
     case DS_ALLIANCE:
       /* See if our allies are diligently declaring war on our enemies... */
+      if (adip->at_war_with_ally) {
+        break;
+      }
       target = NULL;
       players_iterate(eplayer) {
         int e = eplayer->player_no;
@@ -1448,6 +1487,9 @@
       break;
 
     case DS_PEACE:
+      if (adip->at_war_with_ally) {
+        break;
+      }
       clause.type = CLAUSE_ALLIANCE;
       if (ai_goldequiv_clause(pplayer, aplayer,
                               &clause, ai, FALSE, DS_ALLIANCE) < 0
@@ -1462,7 +1504,9 @@
       break;
 
     case DS_CEASEFIRE:
-    case DS_NEUTRAL:
+      if (adip->at_war_with_ally) {
+        break;
+      }
       clause.type = CLAUSE_PEACE;
       if (ai_goldequiv_clause(pplayer, aplayer, &clause,
                               ai, FALSE, CLAUSE_PEACE) < 0
@@ -1477,10 +1521,20 @@
 
     case DS_NO_CONTACT: /* but we do have embassy! weird. */
     case DS_WAR:
+    {
+      int worth;
+
+      DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "considering ceasefire");
+      if (adip->asked_about_ceasefire > 0 && !aplayer->ai.control) {
+        DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "... do not ask");
+        break;
+      }
+      worth = ai_goldequiv_clause(pplayer, aplayer,
+                                  &clause, ai, FALSE, CLAUSE_CEASEFIRE);
       clause.type = CLAUSE_CEASEFIRE;
-      if (ai_goldequiv_clause(pplayer, aplayer,
-                              &clause, ai, FALSE, CLAUSE_CEASEFIRE) < 0
-          || (adip->asked_about_ceasefire > 0 && !aplayer->ai.control)) {
+      if (worth < 0) {
+        DIPLO_LOG(LOG_DIPL, pplayer, aplayer, "... unacceptable (w=%d)",
+                  worth);
         break; /* Fight until the end! */
       }
       ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_CEASEFIRE, 0);
@@ -1489,6 +1543,9 @@
              "bloodshed. May we suggest a cessation of hostilities?"), 
              pplayer->name);
       break;
+    }
+    case DS_ARMISTICE:
+      break;
     default:
       die("Unknown pact type");
       break;
Index: ai/advdiplomacy.h
===================================================================
--- ai/advdiplomacy.h   (revision 11220)
+++ ai/advdiplomacy.h   (working copy)
@@ -41,4 +41,7 @@
 
 bool ai_on_war_footing(struct player *pplayer);
 
+void ai_diplomacy_first_contact(struct player *pplayer,
+                                struct player *aplayer);
+
 #endif
Index: ai/aidata.c
===================================================================
--- ai/aidata.c (revision 11220)
+++ ai/aidata.c (working copy)
@@ -593,7 +593,6 @@
   ai->diplomacy.love_incr = MAX_AI_LOVE * 3 / 100;
   ai->diplomacy.req_love_for_peace = MAX_AI_LOVE / 8;
   ai->diplomacy.req_love_for_alliance = MAX_AI_LOVE / 4;
-  ai->diplomacy.req_love_for_ceasefire = 0;
 
   for (i = 0; i < MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS; i++) {
     ai->diplomacy.player_intel[i].spam = i % 5; /* pseudorandom */
Index: ai/aidata.h
===================================================================
--- ai/aidata.h (revision 11220)
+++ ai/aidata.h (working copy)
@@ -78,7 +78,6 @@
     char love_incr;           /* Modify love with this fixed amount */
     int req_love_for_peace;
     int req_love_for_alliance;
-    int req_love_for_ceasefire;
     struct player *spacerace_leader; /* who is leading the space pack */
     struct player *production_leader;
   } diplomacy;
Index: client/gui-gtk-2.0/plrdlg.c
===================================================================
--- client/gui-gtk-2.0/plrdlg.c (revision 11220)
+++ client/gui-gtk-2.0/plrdlg.c (working copy)
@@ -626,7 +626,7 @@
       weight = PANGO_WEIGHT_BOLD;
       style = PANGO_STYLE_NORMAL;
       break;
-    case DS_NEUTRAL:
+    case DS_ARMISTICE:
     case DS_CEASEFIRE:
     case DS_PEACE:
     case DS_NO_CONTACT:
Index: client/text.c
===================================================================
--- client/text.c       (revision 11220)
+++ client/text.c       (working copy)
@@ -76,12 +76,12 @@
   struct unit *pfocus_unit = get_unit_in_focus();
   const char *diplo_nation_plural_adjectives[DS_LAST] =
     {Q_("?nation:Neutral"), Q_("?nation:Hostile"),
-     "" /* unused, DS_CEASEFIRE*/,
+     Q_("?nation:Neutral"),
      Q_("?nation:Peaceful"), Q_("?nation:Friendly"), 
      Q_("?nation:Mysterious"), Q_("?nation:Friendly(team)")};
   const char *diplo_city_adjectives[DS_LAST] =
     {Q_("?city:Neutral"), Q_("?city:Hostile"),
-     "" /*unused, DS_CEASEFIRE */,
+     Q_("?nation:Neutral"),
      Q_("?city:Peaceful"), Q_("?city:Friendly"), Q_("?city:Mysterious"),
      Q_("?city:Friendly(team)")};
   int infracount;
@@ -114,9 +114,9 @@
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
-       /* TRANS: "Polish territory (5 turn ceasefire)" */
-       astr_add_line(&str, PL_("%s territory (%d turn ceasefire)",
-                               "%s territory (%d turn ceasefire)",
+       /* TRANS: "Polish territory (5 turn cease-fire)" */
+       astr_add_line(&str, PL_("%s territory (%d turn cease-fire)",
+                               "%s territory (%d turn cease-fire)",
                                turns),
                      get_nation_name(owner->nation), turns);
       } else {
@@ -147,9 +147,9 @@
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
-       /* TRANS:  "City: Warsaw (Polish, 5 turn ceasefire)" */
-        astr_add_line(&str, PL_("City: %s (%s, %d turn ceasefire)",
-                               "City: %s (%s, %d turn ceasefire)",
+       /* TRANS:  "City: Warsaw (Polish, 5 turn cease-fire)" */
+        astr_add_line(&str, PL_("City: %s (%s, %d turn cease-fire)",
+                               "City: %s (%s, %d turn cease-fire)",
                                turns),
                      pcity->name,
                      get_nation_name(owner->nation),
@@ -211,9 +211,9 @@
       if (ds[owner->player_no].type == DS_CEASEFIRE) {
        int turns = ds[owner->player_no].turns_left;
 
-       /* TRANS:  "Unit: Musketeers (Polish, 5 turn ceasefire)" */
-        astr_add_line(&str, PL_("Unit: %s (%s, %d turn ceasefire)",
-                               "Unit: %s (%s, %d turn ceasefire)",
+       /* TRANS:  "Unit: Musketeers (Polish, 5 turn cease-fire)" */
+        astr_add_line(&str, PL_("Unit: %s (%s, %d turn cease-fire)",
+                               "Unit: %s (%s, %d turn cease-fire)",
                                turns),
                      ptype->name,
                      get_nation_name(owner->nation),
Index: client/plrdlg_common.c
===================================================================
--- client/plrdlg_common.c      (revision 11220)
+++ client/plrdlg_common.c      (working copy)
@@ -130,7 +130,7 @@
     return "-";
   } else {
     pds = pplayer_get_diplstate(game.player_ptr, player);
-    if (pds->type == DS_CEASEFIRE) {
+    if (pds->type == DS_CEASEFIRE || pds->type == DS_ARMISTICE) {
       my_snprintf(buf, sizeof(buf), "%s (%d)",
                  diplstate_text(pds->type), pds->turns_left);
       return buf;

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#14548) New treaties rules, Per I. Mathisen <=