Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2004:
[Freeciv-Dev] New alliances v2 (PR#8394)
Home

[Freeciv-Dev] New alliances v2 (PR#8394)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] New alliances v2 (PR#8394)
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Thu, 15 Apr 2004 04:35:06 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8394 >

RULES CHANGES
  - don't lose reputation if you leave an alliance of one
  - show team leader in pregame /list
  - if you die, you leave your team; so if you want to be part of
    that team victory, stay alive!

FIXES
  - ensure newly elected leader after a leader dies isn't also dead
  - ensure that player cannot choose a dead player as leader
  - ensure that a new leader chosen when a player resigns from an
    alliance is not dead, also give the new leader a notify, and
    update client info of the booted player
  - wrapped all dlsend_packet_team_info usage in a utility function
  - fix bug where we sent wrong new leader to client
  - fix bug where joining an alliance meant declaring war against
    anyone existing team members didn't have contact with; and
    fix bugs to do with treating anyone you have not met as
    enemies or someone you already are at war with
    (pplayers_at_war() reading no contact as war caught me again)
  - fix bugs where we did less checking when making new alliance
    than when leaving existing
  - fix bugs with team command in pregame
  - can no longer create new team with same name as existing team (bugs)
  - ensure that team names are sane
  - added notify on all war declarations by allies to victim(s)
  - add some more sanity checks

MORE TODO (later patches)
  - AI should dissolve one-man alliances it is in so that it can
    join other alliances instead
  - load default alliance names from nation rulesets

KNOWN ODDITIES:
  - "get away with your crimes" message from AI instead of "peace some
    other time" sometimes, even though I didn't do it anything. Should
    be changed so this does not happen.

I think most bugs should be nailed now. Thanks a lot to Genevieve for
playtesting and providing bugreports.

I still need someone to review it.

  - Per

Index: ai/advdiplomacy.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdiplomacy.c,v
retrieving revision 1.19
diff -u -r1.19 advdiplomacy.c
--- ai/advdiplomacy.c   10 Apr 2004 01:40:16 -0000      1.19
+++ ai/advdiplomacy.c   15 Apr 2004 11:31:15 -0000
@@ -40,6 +40,7 @@
 
 #include "citytools.h"
 #include "diplhand.h"
+#include "hand_gen.h"
 #include "plrhand.h"
 #include "maphand.h"
 #include "settlers.h"  /* amortize */
@@ -52,6 +53,12 @@
 #include "advdiplomacy.h"
 
 /*
+ * TODO: Team leader mode. We will want to consider all of our
+ * team members as if ourselves when we consider possible enemies
+ * to pick on, and allies to befriend.
+ */
+
+/*
 
   "When a lobsterman leaves a trap out in the sea and can't get
   to it for a while, he will find the remains of many lobsters,
@@ -237,28 +244,6 @@
   case CLAUSE_ALLIANCE:
   case CLAUSE_PEACE:
   case CLAUSE_CEASEFIRE:
-    /* This guy is at war with our alliance and we're not alliance
-     * leader. */
-    if (pplayer != ai->diplomacy.alliance_leader
-        && pplayers_at_war(aplayer, ai->diplomacy.alliance_leader)) {
-      notify(aplayer, _("*%s (AI)* %s leads our alliance. You must contact "
-             "and make peace with him first."), pplayer->name, 
-             ai->diplomacy.alliance_leader->name);
-      worth = -BIG_NUMBER;
-      break;
-    }
-
-    /* And this guy is allied to one of our enemies. Only accept
-     * ceasefire. */
-    if (adip->is_allied_with_enemy
-        && pclause->type != CLAUSE_CEASEFIRE) {
-      notify(aplayer, _("*%s (AI)* First break alliance with %s, %s"),
-             pplayer->name, adip->is_allied_with_enemy->name,
-             aplayer->name);
-      worth = -BIG_NUMBER;
-      break;
-    }
-
     /* Check if we can trust this guy. If we want to blast off to space,
      * we don't care, though. */
     if (ai->diplomacy.acceptable_reputation > aplayer->reputation
@@ -282,29 +267,6 @@
       }
     }
 
-    /* Let's all hold hands in one happy family! */
-    if (adip->is_allied_with_ally) {
-      worth = 0;
-      break;
-    }
-
-    /* If this lucky fella got a ceasefire with da boss, then
-     * let him live. */
-    if (pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
-        == DS_CEASEFIRE && pclause->type == CLAUSE_CEASEFIRE) {
-        notify(aplayer, _("*%s (AI)* %s recommended that I give you a 
ceasefire."
-               " This is your lucky day."), pplayer->name,
-               ai->diplomacy.alliance_leader->name);
-        if (ai->diplomacy.target == aplayer) {
-          /* Damn, we lost our target, too! Stupid boss! */
-          ai->diplomacy.target = NULL;
-          ai->diplomacy.timer = 0;
-          ai->diplomacy.countdown = 0;
-        }
-        worth = 0;
-        break;
-    }
-
     /* Breaking treaties give us penalties on future diplomacy, so
      * avoid flip-flopping treaty/war with our chosen enemy. */
     if (aplayer == ai->diplomacy.target) {
@@ -651,9 +613,17 @@
     return;
   }
 
+  if (what == DS_ALLIANCE) {
+    assert(pplayer->team != TEAM_NONE || aplayer->team != TEAM_NONE);
+    assert(pplayer->team != aplayer->team);
+    assert(pplayer_can_ally(pplayer, aplayer) 
+           || pplayer_can_ally(aplayer, pplayer));
+  }
+
   handle_diplomacy_init_meeting_req(pplayer, aplayer->player_no);
   handle_diplomacy_create_clause_req(pplayer, aplayer->player_no,
                                     pplayer->player_no, what, value);
+  team_consistency_check;
 }
 
 /********************************************************************** 
@@ -754,18 +724,11 @@
     struct ai_dip_intel *adip = 
&ai->diplomacy.player_intel[aplayer->player_no];
 
     /* We don't hate ourselves, those we don't know and those we're
-     * allied to or team members. Defer judgement on alliance members
-     * we're not (yet) allied to to the alliance leader. Always respect
-     * ceasefires the boss has signed. */
+     * allied to. */
     if (aplayer == pplayer
         || !aplayer->is_alive
         || ds == DS_NO_CONTACT
-        || (pplayer->team != TEAM_NONE && pplayer->team == aplayer->team)
-        || ds == DS_ALLIANCE
-        || (pplayer != ai->diplomacy.alliance_leader 
-            && adip->is_allied_with_ally)
-        || (pplayer_get_diplstate(aplayer, ai->diplomacy.alliance_leader)->type
-            == DS_CEASEFIRE)) {
+        || ds == DS_ALLIANCE) {
       continue;
     }
     war_desire[aplayer->player_no] = ai_war_desire(pplayer, aplayer, ai);
@@ -824,8 +787,9 @@
 {
   int index;
 
-  /* Only share techs with team mates */
-  if (pplayer->team != TEAM_NONE && pplayer->team == aplayer->team) {
+  /* Only share techs with team mates in team mode */
+  if (pplayer->team != TEAM_NONE && pplayer->team == aplayer->team
+      && game.team_mode) {
     for (index = A_FIRST; index < game.num_tech_types; index++) {
       if ((get_invention(pplayer, index) != TECH_KNOWN)
           && (get_invention(aplayer, index) == TECH_KNOWN)) {
@@ -859,13 +823,26 @@
                          struct player *target)
 {
   struct ai_dip_intel *adip = &ai->diplomacy.player_intel[target->player_no];
+  enum diplstate_type *ds = &pplayer_get_diplstate(pplayer, target)->type;
 
   if (gives_shared_vision(pplayer, target)) {
     remove_shared_vision(pplayer, target);
   }
 
-  /* will take us straight to war */
-  handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  /* Not very elegant, but rather safe, much unlike a while loop. */
+  if (*ds == DS_ALLIANCE) {
+    handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  }
+  if (*ds == DS_PEACE) {
+    handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  }
+  if (*ds == DS_CEASEFIRE) {
+    handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  }
+  if (*ds == DS_NEUTRAL) {
+    handle_diplomacy_cancel_pact(pplayer, target->player_no, CLAUSE_LAST);
+  }
+  assert(*ds == DS_WAR);
 
   /* Continue war at least in this arbitrary number of turns to show 
    * some spine */
@@ -899,7 +876,9 @@
   players_iterate(aplayer) {
     if (ai->diplomacy.acceptable_reputation > aplayer->reputation
         && ai->diplomacy.player_intel[aplayer->player_no].love < 0
-        && pplayer->diplstates[aplayer->player_no].has_reason_to_cancel >= 2) {
+        && pplayer->diplstates[aplayer->player_no].has_reason_to_cancel >= 2
+        && (pplayer->team == TEAM_NONE 
+            || team_get_by_id(pplayer->team)->leader == pplayer)) {
       PLAYER_LOG(LOG_DIPL2, pplayer, ai, "Declaring war on %s in revenge",
                  target->name);
       notify(target, _("*%s (AI)* I will NOT accept such behaviour! This "
@@ -928,8 +907,7 @@
         notify(aplayer, _("*%s (AI)* Your attempt to conquer space for "
                "yourself alone betray your true intentions, and I "
                "will have no more of our alliance!"), pplayer->name);
-       handle_diplomacy_cancel_pact(pplayer, aplayer->player_no,
-                                    CLAUSE_ALLIANCE);
+       handle_diplomacy_cancel_pact(pplayer, aplayer->player_no, CLAUSE_LAST);
         if (gives_shared_vision(pplayer, aplayer)) {
           remove_shared_vision(pplayer, aplayer);
         }
@@ -955,8 +933,11 @@
   if (target && pplayers_at_war(pplayer, target)) {
     ai->diplomacy.countdown = 0; /* cosmetic */
   }
-  if (target && !pplayers_at_war(pplayer, target)
-      && ai->diplomacy.countdown <= 0) {
+  if (target 
+      && !pplayers_at_war(pplayer, target)
+      && ai->diplomacy.countdown <= 0
+      && (pplayer->team == TEAM_NONE
+          || team_get_by_id(pplayer->team)->leader == pplayer)) {
     if (pplayers_allied(pplayer, target)) {
       PLAYER_LOG(LOG_ERROR, pplayer, ai, "Went to war against %s, who is "
                  "an ally!", target->name); /* Oh, my. */
@@ -978,21 +959,6 @@
     ai_go_to_war(pplayer, ai, target);
   }
 
-  /*** Declare war - against enemies of allies ***/
-
-  players_iterate(aplayer) {
-    struct ai_dip_intel *adip = 
&ai->diplomacy.player_intel[aplayer->player_no];
-
-    if (aplayer->is_alive
-        && adip->at_war_with_ally
-        && !adip->is_allied_with_ally
-        && !pplayers_at_war(pplayer, aplayer)) {
-      notify(aplayer, _("*%s (AI)* Your aggression against my allies was "
-                       "your last mistake!"), pplayer->name);
-      ai_go_to_war(pplayer, ai, aplayer);
-    }
-  } players_iterate_end;
-
   /*** Opportunism, Inc. Try to make peace with everyone else ***/
 
   players_iterate(aplayer) {
@@ -1088,8 +1054,7 @@
           PLAYER_LOG(LOG_DIPL2, pplayer, ai, "breaking useless alliance with ",
                      "%s", aplayer->name);
          /* to peace */
-         handle_diplomacy_cancel_pact(pplayer, aplayer->player_no,
-                                      CLAUSE_ALLIANCE);
+         handle_diplomacy_cancel_pact(pplayer, aplayer->player_no, 
CLAUSE_LAST);
           adip->love = MIN(adip->love, 0);
           if (gives_shared_vision(pplayer, aplayer)) {
             remove_shared_vision(pplayer, aplayer);
@@ -1106,6 +1071,23 @@
           || !target) {
         /* Note that we don't ever ask for alliance unless we have a target */
         break; 
+      }
+      if (aplayer->team != TEAM_NONE) {
+        break; /* Already member of an alliance. Doh. */
+      } else if (pplayer->team == TEAM_NONE) {
+        char teamname[MAX_LEN_NAME];
+
+        snprintf(teamname, sizeof(teamname), _("Pax %s"), 
+                 get_nation_name(pplayer->nation));
+        team_consistency_check;
+        handle_team_add_player(pplayer, pplayer->player_no, teamname);
+      }
+      if (team_get_by_id(pplayer->team)->leader != pplayer) {
+        break; /* We're not leader and can't decide this! */
+      }
+      if (!pplayer_can_ally(aplayer, pplayer)) {
+        PLAYER_LOG(LOG_ERROR, pplayer, ai, "%s cannot join our alliance");
+        break; /* cannot join for some reason */
       }
       ai_diplomacy_suggest(pplayer, aplayer, CLAUSE_ALLIANCE, 0);
       adip->asked_about_alliance = !aplayer->ai.control ? 13 : 0;
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.22
diff -u -r1.22 aidata.c
--- ai/aidata.c 28 Jan 2004 23:08:38 -0000      1.22
+++ ai/aidata.c 15 Apr 2004 11:31:15 -0000
@@ -66,8 +66,6 @@
   bool can_build_antiair =  can_player_build_improvement(pplayer, B_SAM);
   bool can_build_antinuke = can_player_build_improvement(pplayer, B_SDI);
   bool can_build_antimissile = can_player_build_improvement(pplayer, B_SDI);
-  int ally_strength = -1;
-  struct player *ally_strongest = NULL;
 
   /*** Threats ***/
 
@@ -250,14 +248,6 @@
     ai->diplomacy.player_intel[i].at_war_with_ally = NULL;
     ai->diplomacy.player_intel[i].is_allied_with_ally = NULL;
 
-    /* Determine who is the leader of our alliance. That is,
-     * whoever has the more cities. */
-    if (pplayers_allied(pplayer, aplayer)
-        && city_list_size(&aplayer->cities) > ally_strength) {
-      ally_strength = city_list_size(&aplayer->cities);
-      ally_strongest = aplayer;
-    }
-
     players_iterate(check_pl) {
       if (check_pl == pplayer
           || check_pl == aplayer
@@ -278,9 +268,6 @@
       }
     } players_iterate_end;
   }
-  if (ally_strongest != ai->diplomacy.alliance_leader) {
-    ai->diplomacy.alliance_leader = ally_strongest;
-  }
   ai->diplomacy.spacerace_leader = player_leading_spacerace();
 
   /*** Priorities ***/
@@ -356,7 +343,7 @@
   ai->diplomacy.req_love_for_peace = 8;
   ai->diplomacy.req_love_for_alliance = 8;
   ai->diplomacy.req_love_for_ceasefire = 0;
-  ai->diplomacy.alliance_leader = pplayer;
+  ai->diplomacy.spacerace_leader = NULL;
 
   for (i = 0; i < MAX_NUM_PLAYERS; i++) {
     ai->diplomacy.player_intel[i].spam = i; /* pseudorandom */
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.11
diff -u -r1.11 aidata.h
--- ai/aidata.h 9 Oct 2003 00:07:33 -0000       1.11
+++ ai/aidata.h 15 Apr 2004 11:31:15 -0000
@@ -65,7 +65,6 @@
     int req_love_for_peace;
     int req_love_for_alliance;
     int req_love_for_ceasefire;
-    struct player *alliance_leader; /* Who is leading our alliance */
     struct player *spacerace_leader; /* who is leading the space pack */
   } diplomacy;
 
Index: ai/ailog.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/ailog.c,v
retrieving revision 1.11
diff -u -r1.11 ailog.c
--- ai/ailog.c  24 Nov 2003 11:27:24 -0000      1.11
+++ ai/ailog.c  15 Apr 2004 11:31:15 -0000
@@ -56,13 +56,11 @@
               ai->diplomacy.player_intel[ai->diplomacy.target->player_no].love,
                 ai->diplomacy.target->name);
   }
-  my_snprintf(buffer, sizeof(buffer), "%s %s%s%s ", pplayer->name,
+  my_snprintf(buffer, sizeof(buffer), "%s %s%s ", pplayer->name,
               ai->diplomacy.target ? targetbuffer : "",
               ai->diplomacy.spacerace_leader &&
               ai->diplomacy.spacerace_leader->player_no == pplayer->player_no 
? 
-                "(spacelead) " : "",
-              ai->diplomacy.alliance_leader->player_no == pplayer->player_no ?
-                "(*)" : "");
+                "(spacelead) " : "");
 
   va_start(ap, msg);
   my_vsnprintf(buffer2, sizeof(buffer2), msg, ap);
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.186
diff -u -r1.186 civclient.c
--- client/civclient.c  10 Apr 2004 03:47:48 -0000      1.186
+++ client/civclient.c  15 Apr 2004 11:31:15 -0000
@@ -590,3 +590,27 @@
   return (get_client_state() == CLIENT_GAME_RUNNING_STATE
          || get_client_state() == CLIENT_GAME_OVER_STATE);
 }
+
+/**************************************************************************
+  Returns TRUE iff the client can make pplayer our alliance leader.
+**************************************************************************/
+bool can_make_alliance_leader(struct player *plr)
+{
+  return (can_client_issue_orders()
+          && game.player_ptr->team == plr->team
+          && plr->team != TEAM_NONE
+          && plr != game.player_ptr
+          && plr->is_alive
+          && team_get_by_id(plr->team)->leader == game.player_ptr);
+}
+
+/**************************************************************************
+  Returns TRUE iff the client can remove pplayer from our alliance.
+**************************************************************************/
+bool can_remove_from_alliance(struct player *plr)
+{
+  return (can_client_issue_orders()
+          && game.player_ptr->team == plr->team
+          && plr->team != TEAM_NONE
+          && team_get_by_id(plr->team)->leader == game.player_ptr);
+}
Index: client/civclient.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.h,v
retrieving revision 1.33
diff -u -r1.33 civclient.h
--- client/civclient.h  10 Apr 2004 03:47:48 -0000      1.33
+++ client/civclient.h  15 Apr 2004 11:31:15 -0000
@@ -61,6 +61,8 @@
 bool can_client_change_view(void);
 bool can_meet_with_player(struct player *pplayer);
 bool can_intel_with_player(struct player *pplayer);
+bool can_remove_from_alliance(struct player *pplayer);
+bool can_make_alliance_leader(struct player *pplayer);
 
 void client_game_init(void);
 void client_game_free(void);
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.362
diff -u -r1.362 packhand.c
--- client/packhand.c   14 Apr 2004 17:18:36 -0000      1.362
+++ client/packhand.c   15 Apr 2004 11:31:16 -0000
@@ -2127,11 +2127,6 @@
 
   city_styles_alloc(packet->style_count);
   tilespec_alloc_city_tiles(game.styles_count);
-
-  for(i = 0; i < MAX_NUM_TEAMS; i++) {
-    mystrlcpy(team_get_by_id(i)->name, packet->team_name[i],
-              MAX_LEN_NAME);
-  }
 }
 
 /**************************************************************************
@@ -2919,4 +2914,19 @@
 void handle_server_shutdown(void)
 {
   freelog(LOG_VERBOSE, "server shutdown");
+}
+
+/**************************************************************************
+  Receive team info packet.
+**************************************************************************/
+void handle_team_info(int team_no, char *name, int leader)
+{
+  struct team *pteam = team_get_by_id(team_no);
+
+  sz_strlcpy(pteam->name, name);
+  pteam->leader = get_player(leader);
+  pteam->id = team_no;
+  update_players_dialog();
+
+freelog(LOG_NORMAL, "received %s(%d) led by %s", pteam->name, pteam->id, 
pteam->leader->name);
 }
Index: client/gui-gtk-2.0/plrdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/plrdlg.c,v
retrieving revision 1.32
diff -u -r1.32 plrdlg.c
--- client/gui-gtk-2.0/plrdlg.c 1 Apr 2004 00:18:46 -0000       1.32
+++ client/gui-gtk-2.0/plrdlg.c 15 Apr 2004 11:31:16 -0000
@@ -37,6 +37,7 @@
 #include "clinet.h"
 #include "gui_main.h"
 #include "gui_stuff.h"
+#include "inputdlg.h"
 #include "inteldlg.h"
 #include "spaceshipdlg.h"
 #include "tilespec.h"
@@ -54,6 +55,9 @@
 static GtkWidget *players_war_command;
 static GtkWidget *players_vision_command;
 static GtkWidget *players_sship_command;
+static GtkWidget *players_team_add_command;
+static GtkWidget *players_team_remove_command;
+static GtkWidget *players_team_leader_command;
 static GtkListStore *store;
 
 static void create_players_dialog(void);
@@ -62,6 +66,9 @@
 static void players_vision_callback(GtkMenuItem *item, gpointer data);
 static void players_intel_callback(GtkMenuItem *item, gpointer data);
 static void players_sship_callback(GtkMenuItem *item, gpointer data);
+static void players_team_add_callback(GtkMenuItem *item, gpointer data);
+static void players_team_remove_callback(GtkMenuItem *item, gpointer data);
+static void players_team_leader_callback(GtkMenuItem *item, gpointer data);
 
 #define NUM_COLUMNS 14                /* number of columns in total */
 #define DEF_SORT_COLUMN 2             /* default sort column (2 = nation) */
@@ -106,6 +113,8 @@
   GtkTreeModel *model;
   GtkTreeIter it;
 
+  gtk_widget_set_sensitive(players_team_add_command, TRUE);
+
   if (gtk_tree_selection_get_selected(players_selection, &model, &it)) {
     struct player *plr;
     gint plrno;
@@ -113,6 +122,11 @@
     gtk_tree_model_get(model, &it, PLRNO_COLUMN, &plrno, -1);
     plr = &game.players[plrno];
   
+    gtk_widget_set_sensitive(players_team_remove_command, 
+                             can_remove_from_alliance(plr));
+    gtk_widget_set_sensitive(players_team_leader_command, 
+                             can_make_alliance_leader(plr));
+
     if (plr->spaceship.state != SSHIP_NONE) {
       gtk_widget_set_sensitive(players_sship_command, TRUE);
     } else {
@@ -141,6 +155,8 @@
 
   gtk_widget_set_sensitive(players_meet_command, FALSE);
   gtk_widget_set_sensitive(players_int_command, FALSE);
+  gtk_widget_set_sensitive(players_team_remove_command, FALSE);
+  gtk_widget_set_sensitive(players_team_leader_command, FALSE);
 }
 
 /**************************************************************************
@@ -349,6 +365,21 @@
   gtk_widget_set_sensitive(players_sship_command, FALSE);
   gtk_menu_shell_append(GTK_MENU_SHELL(menu), players_sship_command);
 
+  sep = gtk_separator_menu_item_new();
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
+
+  players_team_add_command = gtk_menu_item_new_with_mnemonic(_("Make _new 
alliance"));
+  gtk_widget_set_sensitive(players_team_add_command, FALSE);
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu), players_team_add_command);
+
+  players_team_remove_command = gtk_menu_item_new_with_mnemonic(_("_Remove 
from alliance"));
+  gtk_widget_set_sensitive(players_team_remove_command, FALSE);
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu), players_team_remove_command);
+
+  players_team_leader_command = gtk_menu_item_new_with_mnemonic(_("Make 
alliance _leader"));
+  gtk_widget_set_sensitive(players_team_leader_command, FALSE);
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu), players_team_leader_command);
+
   gtk_widget_add_accelerator(players_int_command,
     "activate", accel, GDK_I, 0, GTK_ACCEL_VISIBLE);
   gtk_widget_add_accelerator(players_meet_command,
@@ -359,6 +390,12 @@
     "activate", accel, GDK_W, 0, GTK_ACCEL_VISIBLE);
   gtk_widget_add_accelerator(players_sship_command,
     "activate", accel, GDK_S, 0, GTK_ACCEL_VISIBLE);
+  gtk_widget_add_accelerator(players_team_add_command,
+    "activate", accel, GDK_N, 0, GTK_ACCEL_VISIBLE);
+  gtk_widget_add_accelerator(players_team_remove_command,
+    "activate", accel, GDK_R, 0, GTK_ACCEL_VISIBLE);
+  gtk_widget_add_accelerator(players_team_leader_command,
+    "activate", accel, GDK_L, 0, GTK_ACCEL_VISIBLE);
 
   gtk_window_add_accel_group(GTK_WINDOW(players_dialog_shell), accel);
   gtk_widget_show_all(GTK_DIALOG(players_dialog_shell)->vbox);
@@ -373,6 +410,12 @@
     G_CALLBACK(players_intel_callback), NULL);
   g_signal_connect(players_sship_command, "activate",
     G_CALLBACK(players_sship_callback), NULL);
+  g_signal_connect(players_team_add_command, "activate",
+    G_CALLBACK(players_team_add_callback), NULL);
+  g_signal_connect(players_team_remove_command, "activate",
+    G_CALLBACK(players_team_remove_callback), NULL);
+  g_signal_connect(players_team_leader_command, "activate",
+    G_CALLBACK(players_team_leader_callback), NULL);
 
   gtk_list_store_clear(store);
   update_players_dialog();
@@ -432,26 +475,29 @@
 static void build_row(GtkTreeIter *it, int i)
 {
   static char dsbuf[32];
-  gchar *team, *state;
+  gchar *state;
+  char teamname[MAX_LEN_NAME+1];
   const struct player_diplstate *pds;
   gint idle;
   struct player *plr = get_player(i);
   GdkPixbuf *flag;
   GdkColor *state_col;
   GValue value = { 0, };
+  struct team *pteam = team_get_by_id(plr->team);
 
-  /* the team */
-  if (plr->team != TEAM_NONE) {
-    team = team_get_by_id(plr->team)->name;
+  /* the team / alliance */
+  if (pteam) {
+    snprintf(teamname, sizeof(teamname), "%s%s", pteam->name,
+             pteam->leader == plr ? "*" : "");
   } else {
-    team = "";
+    teamname[0] = '\0';
   }
 
   gtk_list_store_set(store, it,
     0, (gchar *)plr->name,                   /* the playername */
     2, (gchar *)get_nation_name(plr->nation), /* the nation */
     3, colors_standard[player_color(plr)],    /* the color */
-    4, (gchar *)team,
+    4, (gchar *)&teamname,
     PLRNO_COLUMN, (gint)i,                   /* the playerid */
     -1);
 
@@ -676,3 +722,65 @@
 
   popup_spaceship_dialog(&game.players[plrno]);
 }
+
+/**************************************************************************
+   ...
+**************************************************************************/
+static void name_new_alliance_callback(GtkWidget *w, gpointer data)
+{
+  if (GPOINTER_TO_INT(data) == 1) {
+    dsend_packet_team_add_player(&aconnection, game.player_ptr->player_no,
+                                 input_dialog_get_input(w));
+  } /* else cancelled */
+  input_dialog_destroy(w);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void players_team_add_callback(GtkMenuItem *item, gpointer data)
+{
+  input_dialog_create(GTK_WINDOW(toplevel),
+                     _("Create new alliance"),
+                     _("What should we call our new alliance?"), 
+                     "Alliance",
+                     G_CALLBACK(name_new_alliance_callback),
+                     GINT_TO_POINTER(1),
+                     G_CALLBACK(name_new_alliance_callback),
+                     GINT_TO_POINTER(0));
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void players_team_remove_callback(GtkMenuItem *item, gpointer data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter it;
+  gint plrno;
+
+  if (!gtk_tree_selection_get_selected(players_selection, &model, &it)) {
+    return;
+  }
+  gtk_tree_model_get(model, &it, PLRNO_COLUMN, &plrno, -1);
+
+  dsend_packet_team_remove_player(&aconnection, plrno);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void players_team_leader_callback(GtkMenuItem *item, gpointer data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter it;
+  gint plrno;
+
+  if (!gtk_tree_selection_get_selected(players_selection, &model, &it)) {
+    return;
+  }
+  gtk_tree_model_get(model, &it, PLRNO_COLUMN, &plrno, -1);
+
+  dsend_packet_team_change_leader(&aconnection, game.player_ptr->team, plrno);
+}
+
Index: common/capstr.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v
retrieving revision 1.164
diff -u -r1.164 capstr.c
--- common/capstr.c     14 Apr 2004 11:19:44 -0000      1.164
+++ common/capstr.c     15 Apr 2004 11:31:16 -0000
@@ -77,7 +77,7 @@
 #define CAPABILITY "+1.14.delta +last_turns_shield_surplus veteran +orders " \
                    "+starter +union +iso_maps +orders2client " \
                    "+change_production +tilespec1 +no_earth +trans " \
-                   "+want_hack invasions killstack bombard"
+                   "+want_hack invasions killstack bombard +alliances"
 
 /* "+1.14.delta" is the new delta protocol for 1.14.0-dev.
  *
@@ -114,6 +114,8 @@
  * "killstack" means ruleset option to ignore unit killstack effect
  *
  * "bombard" means units support the bombard ability.
+ * 
+ * "alliances" means new type of alliance structure.
  */
 
 void init_our_capability(void)
Index: common/diptreaty.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/diptreaty.c,v
retrieving revision 1.19
diff -u -r1.19 diptreaty.c
--- common/diptreaty.c  4 Nov 2003 10:21:56 -0000       1.19
+++ common/diptreaty.c  15 Apr 2004 11:31:16 -0000
@@ -41,8 +41,9 @@
           || (game.diplomacy == 2
               && pplayer->ai.control
               && aplayer->ai.control)
-          || (pplayer->team != TEAM_NONE
-              && pplayer->team == aplayer->team));
+           || (game.diplomacy == 3
+               && pplayer->team != TEAM_NONE
+               && pplayer->team == aplayer->team));
 }
 
 /**************************************************************************
Index: common/diptreaty.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/diptreaty.h,v
retrieving revision 1.15
diff -u -r1.15 diptreaty.h
--- common/diptreaty.h  6 Mar 2004 11:13:05 -0000       1.15
+++ common/diptreaty.h  15 Apr 2004 11:31:16 -0000
@@ -17,8 +17,9 @@
 
 enum clause_type { CLAUSE_ADVANCE, CLAUSE_GOLD, CLAUSE_MAP,
                   CLAUSE_SEAMAP, CLAUSE_CITY, 
-                  CLAUSE_CEASEFIRE, CLAUSE_PEACE, CLAUSE_ALLIANCE,
-                  CLAUSE_VISION, CLAUSE_TEAM, CLAUSE_EMBASSY, CLAUSE_LAST };
+                  CLAUSE_CEASEFIRE, CLAUSE_PEACE, 
+                   CLAUSE_ALLIANCE, CLAUSE_VISION, CLAUSE_TEAM, 
+                   CLAUSE_EMBASSY, CLAUSE_LAST };
 
 #define is_pact_clause(x)                                                   \
   ((x == CLAUSE_CEASEFIRE) || (x == CLAUSE_PEACE) || (x == CLAUSE_ALLIANCE) \
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.178
diff -u -r1.178 game.c
--- common/game.c       10 Apr 2004 23:05:51 -0000      1.178
+++ common/game.c       15 Apr 2004 11:31:17 -0000
@@ -246,6 +246,7 @@
   game.fogofwar_old= game.fogofwar;
   game.borders     = GAME_DEFAULT_BORDERS;
   game.slow_invasions = GAME_DEFAULT_SLOW_INVASIONS;
+  game.team_mode   = FALSE;
   game.auto_ai_toggle = GAME_DEFAULT_AUTO_AI_TOGGLE;
   game.notradesize    = GAME_DEFAULT_NOTRADESIZE;
   game.fulltradesize  = GAME_DEFAULT_FULLTRADESIZE;
@@ -301,6 +302,7 @@
   map_init();
   idex_init();
   cm_init();
+  team_init();
   
   for(i=0; i<MAX_NUM_PLAYERS+MAX_NUM_BARBARIANS; i++)
     player_init(&game.players[i]);
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.134
diff -u -r1.134 game.h
--- common/game.h       14 Apr 2004 10:57:26 -0000      1.134
+++ common/game.h       15 Apr 2004 11:31:17 -0000
@@ -163,6 +163,7 @@
   int borders;         /* distance of border from city; 0=disabled. */
   int diplomacy;        /* who can do it */
   bool slow_invasions;  /* land units lose all movement landing on shores */
+  bool team_mode;       /* rules change slightly when we use pregame teams */
 
   char rulesetdir[MAX_LEN_NAME];
   int firepower_factor;                /* See README.rulesets */
@@ -342,7 +343,7 @@
 
 #define GAME_DEFAULT_DIPLOMACY       0
 #define GAME_MIN_DIPLOMACY           0
-#define GAME_MAX_DIPLOMACY           3
+#define GAME_MAX_DIPLOMACY           4
 
 #define GAME_DEFAULT_DIPLCHANCE      80
 #define GAME_MIN_DIPLCHANCE          1
Index: common/nation.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.c,v
retrieving revision 1.37
diff -u -r1.37 nation.c
--- common/nation.c     15 Sep 2003 16:51:07 -0000      1.37
+++ common/nation.c     15 Apr 2004 11:31:17 -0000
@@ -355,6 +355,11 @@
 
   assert(pplayer != NULL && team_name != NULL);
 
+  /* remove from existing team, if any */
+  if (pplayer->team != TEAM_NONE) {
+    team_remove_player(pplayer);
+  }
+
   /* find or create team */
   team_id = team_find_by_name(team_name);
   if (team_id == TEAM_NONE) {
@@ -372,6 +377,7 @@
     /* add another team */
     teams[team_id].id = team_id;
     sz_strlcpy(teams[team_id].name, team_name);
+    teams[team_id].leader = pplayer;
   }
   pplayer->team = team_id;
 }
@@ -382,7 +388,7 @@
 ***************************************************************/
 void team_remove_player(struct player *pplayer)
 {
-  bool others = FALSE;
+  struct player *others = NULL;
 
   if (pplayer->team == TEAM_NONE) {
     return;
@@ -393,14 +399,16 @@
   /* anyone else using my team? */
   players_iterate(aplayer) {
     if (aplayer->team == pplayer->team && aplayer != pplayer) {
-      others = TRUE;
+      others = aplayer;
       break;
     }
   } players_iterate_end;
 
   /* no other team members left? remove team! */
-  if (!others) {
+  if (others == NULL) {
     teams[pplayer->team].id = TEAM_NONE;
+    teams[pplayer->team].leader = NULL;
+    teams[pplayer->team].name[0] = '\0';
   }
   pplayer->team = TEAM_NONE;
 }
@@ -418,5 +426,6 @@
     /* mark as unused */
     teams[i].id = TEAM_NONE;
     teams[i].name[0] = '\0';
+    teams[i].leader = NULL;
   }
 }
Index: common/nation.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/nation.h,v
retrieving revision 1.27
diff -u -r1.27 nation.h
--- common/nation.h     28 Jan 2004 18:03:14 -0000      1.27
+++ common/nation.h     15 Apr 2004 11:31:17 -0000
@@ -104,6 +104,7 @@
 struct team {
   char name[MAX_LEN_NAME];
   Team_Type_id id; /* equal to array index if active, else TEAM_NONE */
+  struct player *leader;
 };
 
 Nation_Type_id find_nation_by_name(const char *name);
@@ -128,6 +129,7 @@
 void team_add_player(struct player *pplayer, const char *team_name);
 void team_remove_player(struct player *pplayer);
 int team_count_members_alive(Team_Type_id id);
+void team_consistency_check(void);
 
 #define team_iterate(PI_team)                                                 \
 {                                                                             \
@@ -142,5 +144,49 @@
 #define team_iterate_end                                                      \
   }                                                                           \
 }
+
+/***************************************************************
+  Makes some consistency checks on teams to check
+  transitiveness.
+***************************************************************/
+#define team_consistency_check                                                \
+  players_iterate(plr1) {                                                     \
+    players_iterate(plr2) { \
+      if (plr2 == plr1) { \
+        continue; \
+      } \
+      if (plr1->team != TEAM_NONE \
+          && plr1->team == plr2->team) { \
+        players_iterate(plr3) { \
+          if (plr2 == plr3 || plr1 == plr3) { \
+            continue; \
+          } \
+          if ((pplayer_get_diplstate(plr1, plr3)->type == DS_WAR) \
+              != (pplayer_get_diplstate(plr2, plr3)->type == DS_WAR)) { \
+            die("%s war %s (%d) != %s war %s (%d)", plr1->name, plr3->name, \
+                pplayer_get_diplstate(plr1, plr3)->type, \
+                plr2->name, plr3->name, \
+                pplayer_get_diplstate(plr2, plr3)->type); \
+          } \
+          if (pplayers_allied(plr1, plr3) != pplayers_allied(plr2, plr3)) { \
+            die("%s alliance %s != %s alliance %s", plr1->name, plr3->name, \
+                plr2->name, plr3->name); \
+          } \
+        } players_iterate_end; \
+      } \
+    } players_iterate_end; \
+  } players_iterate_end; \
+  team_iterate(pteam) { \
+    if (!pteam->leader->is_alive) { \
+      die("%s is dead but leader of %s!", pteam->leader->name, pteam->name); \
+    } \
+    if (pteam->leader == NULL) { \
+      die("%s has no leader", pteam->name); \
+    } \
+    if (pteam->leader->team != pteam->id) { \
+      die("%s's leader %s is not a member of the team (but %d)!", pteam->name, 
\
+          pteam->leader->name, pteam->id); \
+    } \
+  } team_iterate_end;
 
 #endif  /* FC__NATION_H */
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.20
diff -u -r1.20 packets.def
--- common/packets.def  14 Apr 2004 11:19:44 -0000      1.20
+++ common/packets.def  15 Apr 2004 11:31:17 -0000
@@ -1220,11 +1220,8 @@
   UINT8 style_count;
   UINT8 borders;
   BOOL slow_invasions; add-cap(slow_invasions)
-
-  STRING team_name[MAX_NUM_TEAMS][MAX_LEN_NAME];
 end
 
-
 /*********************************************************
  Below are the packets that control single-player mode.  
 *********************************************************/
@@ -1272,4 +1269,24 @@
   STRING default_strval[MAX_LEN_PACKET];       /* space for string */
 
   UINT8 category;                              /* which category this is in */
+end
+
+PACKET_TEAM_ADD_PLAYER=114;cs,dsend,lsend
+  PLAYER player_no;
+  STRING name[MAX_LEN_NAME];
+end
+
+PACKET_TEAM_REMOVE_PLAYER=115;cs,dsend,lsend
+  PLAYER player_no;
+end
+
+PACKET_TEAM_CHANGE_LEADER=116;cs,dsend,lsend
+  TEAM team_no;
+  PLAYER player_no;
+end
+
+PACKET_TEAM_INFO=117;sc,dsend,lsend
+  TEAM team_no;
+  STRING name[MAX_LEN_NAME];
+  PLAYER leader;
 end
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.137
diff -u -r1.137 player.c
--- common/player.c     25 Feb 2004 16:13:29 -0000      1.137
+++ common/player.c     15 Apr 2004 11:31:17 -0000
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include "city.h"
+#include "diptreaty.h"
 #include "fcintl.h"
 #include "game.h"
 #include "government.h"
@@ -27,6 +28,7 @@
 #include "improvement.h"
 #include "map.h"
 #include "mem.h"
+#include "nation.h"
 #include "rand.h"
 #include "shared.h"
 #include "support.h"
@@ -36,26 +38,16 @@
 #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.
+  Returns true if p1 can join p2's alliance.
 ***************************************************************/
 bool pplayer_can_ally(struct player *p1, struct player *p2)
 {
-  players_iterate(pplayer) {
-    enum diplstate_type ds = pplayer_get_diplstate(p1, pplayer)->type;
-    if (pplayer != p1
-        && pplayer != p2
-        && pplayers_allied(p2, pplayer)
-        && ds == DS_WAR /* do not count 'never met' as war here */
-        && pplayer->is_alive) {
-      return FALSE;
-    }
-  } players_iterate_end;
+  if (p1 == p2 || !diplomacy_possible(p1, p2) || p2->team == TEAM_NONE) {
+    return FALSE;
+  }
+  if (team_get_by_id(p2->team)->leader != p2) {
+    return FALSE;
+  }
   return TRUE;
 }
 
@@ -675,7 +667,7 @@
   if (is_barbarian(pplayer) || is_barbarian(pplayer2)) {
     return FALSE;
   }
-  return (ds == DS_PEACE || ds == DS_ALLIANCE);
+  return (ds == DS_PEACE || ds == DS_ALLIANCE || ds == DS_TEAM);
 }
 
 /***************************************************************
Index: server/diplhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/diplhand.c,v
retrieving revision 1.80
diff -u -r1.80 diplhand.c
--- server/diplhand.c   6 Mar 2004 11:13:05 -0000       1.80
+++ server/diplhand.c   15 Apr 2004 11:31:18 -0000
@@ -180,21 +180,41 @@
            return;
          }
          break;
-       case CLAUSE_ALLIANCE:
-          if (!pplayer_can_ally(pplayer, pother)) {
+       case CLAUSE_CEASEFIRE:
+          if ((pother->team != TEAM_NONE
+               && team_get_by_id(pother->team)->leader != pother)
+              || (pplayer->team != TEAM_NONE
+                  && team_get_by_id(pplayer->team)->leader != pplayer)) {
            notify_player(pplayer,
-                         _("Game: You are at war with one of %s's "
-                           "allies - an alliance with %s is impossible."),
-                         pother->name, pother->name);
+                         _("Game: Only the alliance leader can make a "
+                            "ceasefire."));
             return;
           }
-          if (!pplayer_can_ally(pother, pplayer)) {
-           notify_player(pplayer,
-                         _("Game: %s is at war with one of your allies "
-                           "- an alliance with %s is impossible."),
-                         pother->name, pother->name);
+          /* no break here */
+        case CLAUSE_PEACE:
+          if (pplayers_allied(pother, pplayer)) {
+            return; /* impossible! */
+          }
+          break;
+       case CLAUSE_ALLIANCE:
+          if (pother->team == TEAM_NONE && pplayer->team == TEAM_NONE) {
+           notify_player(pplayer, _("Game: There is no alliance to join!"));
+            return;
+          }
+          if (pother->team != TEAM_NONE && pplayer->team != TEAM_NONE) {
+           notify_player(pplayer, _("Game: You are both members of an alliance 
"
+                                     "already!"));
             return;
           }
+          if ((pother->team != TEAM_NONE 
+               && team_get_by_id(pother->team)->leader != pother)
+              || (pplayer->team != TEAM_NONE
+                  && team_get_by_id(pplayer->team)->leader != pplayer)) {
+           notify_player(pplayer,
+                         _("Game: Only alliances leader can recruit new 
members."));
+            return;            
+          }
+          assert(pplayer->team != TEAM_NONE || pother->team != TEAM_NONE);
           break;
        case CLAUSE_GOLD:
          if (pplayer->economic.gold < pclause->value) {
@@ -204,6 +224,11 @@
            return;
          }
          break;
+        case CLAUSE_TEAM:
+          if (!game.team_mode) {
+            return;
+          }
+          break;
        default:
          ; /* nothing */
        }
@@ -251,6 +276,26 @@
       struct city *pcity;
       if (pclause->from == pother) {
        switch (pclause->type) {
+       case CLAUSE_CEASEFIRE:
+          if ((pother->team != TEAM_NONE
+               && team_get_by_id(pother->team)->leader != pother)
+              || (pplayer->team != TEAM_NONE
+                  && team_get_by_id(pplayer->team)->leader != pplayer)) {
+            goto cleanup;
+          }
+          /* no break here */
+        case CLAUSE_PEACE:
+          if (pplayers_allied(pother, pplayer)) {
+           goto cleanup;
+          }
+          break;
+        case CLAUSE_ALLIANCE:
+          if (!pplayer_can_ally(pplayer, pother) 
+              && !pplayer_can_ally(pother, pplayer)) {
+            goto cleanup;
+          }
+          assert(pplayer->team != TEAM_NONE || pother->team != TEAM_NONE);
+          break;
        case CLAUSE_CITY:
          pcity = find_city_by_id(pclause->value);
          if (!pcity) { /* Can't find out cityname any more. */
@@ -287,26 +332,13 @@
          break;
        case CLAUSE_TEAM:
           /* Limitation: Only for teams */
-          if (pother->team == TEAM_NONE || pother->team != pplayer->team) {
-            freelog(LOG_ERROR, "Attempted to make team in-game!");
+          if (pother->team == TEAM_NONE 
+              || pother->team != pplayer->team
+              || !game.team_mode) {
+            freelog(LOG_ERROR, "Attempted to make a team in-game!");
             goto cleanup;
           }
           break;
-       case CLAUSE_ALLIANCE:
-          /* We need to recheck this way since things might have
-           * changed. */
-          if (!pplayer_can_ally(pother, pplayer)) {
-           notify_player(pplayer,
-                         _("Game: %s is at war with one of your "
-                           "allies - an alliance with %s is impossible."),
-                         pother->name, pother->name);
-           notify_player(pother,
-                         _("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 (pother->economic.gold < pclause->value) {
            notify_player(pplayer,
@@ -422,45 +454,115 @@
          break;
        }
       case CLAUSE_CEASEFIRE:
-       pgiver->diplstates[pdest->player_no].type=DS_CEASEFIRE;
-       pgiver->diplstates[pdest->player_no].turns_left=16;
-       pdest->diplstates[pgiver->player_no].type=DS_CEASEFIRE;
-       pdest->diplstates[pgiver->player_no].turns_left=16;
-       notify_player_ex(pgiver, -1, -1, E_TREATY_CEASEFIRE,
-                        _("Game: You agree on a cease-fire with %s."),
-                        pdest->name);
-       notify_player_ex(pdest, -1, -1, E_TREATY_CEASEFIRE,
-                        _("Game: You agree on a cease-fire with %s."),
-                        pgiver->name);
-       check_city_workers(pplayer);
-       check_city_workers(pother);
+        /* Create ceasefire between two players, an alliance and a 
+         * player, or between two alliances. */
+        players_iterate(plr1) {
+          players_iterate(plr2) {
+            if (plr1 == plr2) {
+              continue;
+            }
+            if (pplayers_allied(plr2, pdest)
+                && (pplayers_allied(plr1, pgiver))) {
+              plr1->diplstates[plr2->player_no].type = DS_CEASEFIRE;
+              plr2->diplstates[plr1->player_no].type = DS_CEASEFIRE;
+              notify_player_ex(plr1, -1, -1, E_TREATY_PEACE,
+                               _("Game: You agree on a ceasefire with %s."),
+                               plr2->name);
+              notify_player_ex(plr2, -1, -1, E_TREATY_PEACE,
+                               _("Game: You agree on a ceasefire with %s."),
+                               plr1->name);
+              check_city_workers(plr1);
+              check_city_workers(plr2);
+            }
+          } players_iterate_end;
+        } players_iterate_end;
        break;
       case CLAUSE_PEACE:
        pgiver->diplstates[pdest->player_no].type=DS_PEACE;
+       pgiver->diplstates[pdest->player_no].turns_left=16;
        pdest->diplstates[pgiver->player_no].type=DS_PEACE;
-       notify_player_ex(pgiver, -1, -1, E_TREATY_PEACE,
+       pdest->diplstates[pgiver->player_no].turns_left=16;
+       notify_player_ex(pgiver, -1, -1, E_TREATY_CEASEFIRE,
                         _("Game: You agree on a peace treaty with %s."),
                         pdest->name);
-       notify_player_ex(pdest, -1, -1, E_TREATY_PEACE,
+       notify_player_ex(pdest, -1, -1, E_TREATY_CEASEFIRE,
                         _("Game: You agree on a peace treaty with %s."),
                         pgiver->name);
        check_city_workers(pplayer);
        check_city_workers(pother);
        break;
       case CLAUSE_ALLIANCE:
-       pgiver->diplstates[pdest->player_no].type=DS_ALLIANCE;
-       pdest->diplstates[pgiver->player_no].type=DS_ALLIANCE;
-       notify_player_ex(pgiver, -1, -1, E_TREATY_ALLIANCE,
-                        _("Game: You agree on an alliance with %s."),
-                        pdest->name);
-       notify_player_ex(pdest, -1, -1, E_TREATY_ALLIANCE,
-                        _("Game: You agree on an alliance with %s."),
-                        pgiver->name);
-       gamelog(GAMELOG_TECH, _("%s agree on an alliance with %s"),
-               get_nation_name_plural(pdest->nation), 
-               get_nation_name_plural(pgiver->nation));
-       check_city_workers(pplayer);
-       check_city_workers(pother);
+        {
+        struct player *joiner;
+        struct player *joined;
+
+        if (pgiver->team == TEAM_NONE) {
+          joiner = pgiver;
+          joined = pdest;
+        } else {
+          joiner = pdest;
+          joined = pgiver;
+        }
+        assert(team_get_by_id(joined->team)->leader == joined);
+        assert(joined->team != TEAM_NONE);
+        player_join_team(joiner, joined->team, joiner->team);
+        players_iterate(aplayer) {
+          if (aplayer == joiner
+              || aplayer->team == TEAM_NONE 
+              || aplayer->team != joiner->team) {
+            continue;
+          }
+          joiner->diplstates[aplayer->player_no].type = DS_ALLIANCE;
+          aplayer->diplstates[joiner->player_no].type = DS_ALLIANCE;
+          notify_player_ex(joiner, -1, -1, E_TREATY_ALLIANCE,
+                           _("Game: You agree on an alliance with %s."),
+                           aplayer->name);
+          notify_player_ex(aplayer, -1, -1, E_TREATY_ALLIANCE,
+                           _("Game: You agree on an alliance with %s."),
+                           joiner->name);
+          gamelog(GAMELOG_TECH, _("%s joins %s's %s alliance"),
+                  get_nation_name_plural(joined->nation), 
+                  team_get_by_id(joined->team)->name,
+                  get_nation_name_plural(joiner->nation));
+          check_city_workers(aplayer);
+          check_city_workers(joiner);
+        } players_iterate_end;
+        players_iterate(aplayer) {
+          if (joiner == aplayer || joined == aplayer) {
+            continue;
+          }
+          /* Copy alliance's war states */
+          if (pplayer_get_diplstate(joined, aplayer)->type == DS_WAR) {
+            joiner->reputation =
+              MAX(joiner->reputation
+                  - 
reputation_loss(joiner->diplstates[aplayer->player_no].type,
+                                    DS_WAR),
+                  0);
+            joiner->diplstates[aplayer->player_no].type = DS_WAR;
+            aplayer->diplstates[joiner->player_no].type = DS_WAR;
+            notify_player_ex(joiner, -1, -1, E_TREATY_ALLIANCE,
+                             _("Game: You enter into the alliance's war with "
+                               "%s."), aplayer->name);
+            notify_player_ex(aplayer, -1, -1, E_TREATY_ALLIANCE,
+                             _("Game: %s changes allegiances and declares "
+                               "war on you!"), joiner->name);
+            check_city_workers(aplayer);
+            check_city_workers(joiner);
+          } else if (pplayer_get_diplstate(joiner, aplayer)->type == DS_WAR) {
+            joiner->diplstates[aplayer->player_no].type = DS_NEUTRAL;
+            aplayer->diplstates[joiner->player_no].type = DS_NEUTRAL;
+            notify_player_ex(aplayer, -1, -1, E_TREATY_ALLIANCE,
+                             _("Game: %s has entered an alliance that is not "
+                               "at war with you."), joiner->name);
+            check_city_workers(aplayer);
+            check_city_workers(joiner);
+          }
+        } players_iterate_end;
+       joiner->diplstates[joined->player_no].type = DS_ALLIANCE;
+       joined->diplstates[joiner->player_no].type = DS_ALLIANCE;
+        }
+        assert(pgiver->team == pdest->team && pdest->team != TEAM_NONE);
+        team_consistency_check;
        break;
       case CLAUSE_TEAM:
         notify_player_ex(pgiver, -1, -1, E_TREATY_ALLIANCE,
@@ -527,6 +629,7 @@
     send_player_info(pplayer, NULL);
     send_player_info(pother, NULL);
   }
+  team_consistency_check;
 }
 
 /****************************************************************************
Index: server/diplhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/diplhand.h,v
retrieving revision 1.8
diff -u -r1.8 diplhand.h
--- server/diplhand.h   19 Feb 2004 21:06:42 -0000      1.8
+++ server/diplhand.h   15 Apr 2004 11:31:18 -0000
@@ -13,6 +13,11 @@
 #ifndef FC__DIPLHAND_H
 #define FC__DIPLHAND_H
 
+#define REPUTATION_LOSS_NEUTRAL (0)
+#define REPUTATION_LOSS_CEASEFIRE (GAME_MAX_REPUTATION/2)
+#define REPUTATION_LOSS_PEACE (GAME_MAX_REPUTATION/3)
+#define REPUTATION_LOSS_ALLIANCE (GAME_MAX_REPUTATION/5)
+
 struct Treaty;
 struct player;
 struct packet_diplomacy_info;
Index: server/gamelog.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gamelog.c,v
retrieving revision 1.31
diff -u -r1.31 gamelog.c
--- server/gamelog.c    5 Mar 2004 18:57:19 -0000       1.31
+++ server/gamelog.c    15 Apr 2004 11:31:18 -0000
@@ -24,10 +24,11 @@
 #include "log.h"
 #include "map.h"
 #include "mem.h"
+#include "score.h"
+#include "srv_main.h"
 #include "support.h"
 
 #include "gamelog.h"
-#include "score.h"
 
 int gamelog_level;             /* also accessed from stdinhand.c */
 static char *gamelog_filename;
@@ -138,7 +139,8 @@
   and status info.
 **************************************************************************/
 void gamelog_save(void) {
-  int i, count = 0;
+  int i, count = 0, highest = -1;
+  struct player *highest_plr = NULL;
   char buffer[4096];
   struct player_score_entry *size =
     fc_malloc(sizeof(struct player_score_entry) * game.nplayers);
@@ -151,45 +153,28 @@
       rank[count].idx = pplayer->player_no;
       size[count].value = total_player_citizens(pplayer);
       size[count].idx = pplayer->player_no;
+      if (rank[count].value > highest) {
+        highest = rank[count].value;
+        highest_plr = pplayer;
+      }
       count++;
     }
   } players_iterate_end;
 
-  /* average game scores for teams */
-  team_iterate(pteam) {
-    int team_members = 0;
-    int count = 0;
-    int team_score = 0;
-    int team_size = 0;
-
-    /* sum team score */
-    players_iterate(pplayer) {
-      if (pplayer->team == pteam->id) {
-        team_members++;
-        team_score += rank[count].value;
-        team_size += size[count].value;
+  /* Draws and allied victories */
+  count = 0;
+  players_iterate(pplayer) {
+    if (!is_barbarian(pplayer)) {
+      if ((BV_ISSET_ANY(srvarg.draw)
+           && BV_ISSET(srvarg.draw, pplayer->player_no))
+          || (pplayer->team != TEAM_NONE
+              && pplayer->team == highest_plr->team)) {
+        /* We win a shared victory, so equal the score. */
+        rank[count].value = highest;
       }
       count++;
-    } players_iterate_end;
-
-    if (team_members == 0) {
-      continue;
     }
-
-    /* average them */
-    team_score = team_score / team_members;
-    team_size = team_size / team_members;
-
-    /* set scores to average */
-    count = 0;
-    players_iterate(pplayer) {
-      if (pplayer->team == pteam->id) {
-        rank[count].value = team_score;
-        size[count].value = team_size;
-      }
-      count++;
-    } players_iterate_end;
-  } team_iterate_end;
+  } players_iterate_end;
 
   qsort(size, count, sizeof(struct player_score_entry), secompare1);
   buffer[0] = 0;
Index: server/plrhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v
retrieving revision 1.308
diff -u -r1.308 plrhand.c
--- server/plrhand.c    10 Apr 2004 03:47:50 -0000      1.308
+++ server/plrhand.c    15 Apr 2004 11:31:18 -0000
@@ -38,6 +38,7 @@
 #include "gamehand.h"
 #include "gamelog.h"
 #include "maphand.h"
+#include "score.h"
 #include "sernet.h"
 #include "settlers.h"
 #include "srv_main.h"
@@ -206,6 +207,10 @@
   pplayer->is_dying = FALSE; /* Can't get more dead than this. */
   pplayer->is_alive = FALSE;
 
+  /* Try to pass on the torch as alliance leader. Notice how we
+   * leave the team here, if we die. */
+  player_join_team(pplayer, TEAM_NONE, pplayer->team);
+
   /* Remove shared vision from dead player to friends. */
   players_iterate(aplayer) {
     if (gives_shared_vision(pplayer, aplayer)) {
@@ -215,7 +220,7 @@
     
   cancel_all_meetings(pplayer);
 
-  /* Show entire map for players who are *not* in a team. */
+  /* Show entire map for players who are *not* in an alliance. */
   if (pplayer->team == TEAM_NONE) {
     map_know_and_see_all(pplayer);
   }
@@ -262,6 +267,8 @@
   send_spaceship_info(pplayer, NULL);
 
   send_player_info_c(pplayer, &game.est_connections);
+
+team_consistency_check; /* TODO: remove me */
 }
 
 /**************************************************************************
@@ -441,7 +448,7 @@
     }
   }
 
-  /* Update Team */
+  /* Update team research */
   if (next_tech > A_NONE) {
     /* Avoid unnecessary recursion. */
     return;
@@ -517,7 +524,7 @@
       pplayer->research.bulbs_researched += bulbs;
     } else if (pplayer->diplstates[plr->player_no].type == DS_TEAM
                && pplayer->is_alive) {
-      /* Share with union partner(s). We'll get in return later. */
+      /* Share with team research partner(s). We'll get in return later. */
       pplayer->research.bulbs_researched += bulbs;
     }
   } players_iterate_end;
@@ -798,7 +805,7 @@
   choose_tech(pplayer, tech);
   send_player_info(pplayer, pplayer);
 
-  /* Notify Team members */
+  /* Notify team research members */
   players_iterate(aplayer) {
     if (pplayer != aplayer
        && aplayer->research.researching != tech
@@ -817,7 +824,7 @@
   choose_tech_goal(pplayer, tech);
   send_player_info(pplayer, pplayer);
 
-  /* Notify Team members */
+  /* Notify team research members */
   players_iterate(aplayer) {
     if (pplayer != aplayer
         && pplayer->diplstates[aplayer->player_no].type == DS_TEAM
@@ -933,23 +940,56 @@
 }
 
 /**************************************************************************
+  Reputation loss.
+**************************************************************************/
+int reputation_loss(enum diplstate_type old, enum diplstate_type goal)
+{
+  int loss = 0;
+
+  while (old != goal) {
+    if (old == DS_WAR) {
+      return 0; /* huh? */
+    } else if (old == DS_NO_CONTACT) {
+      return 0;
+    } else if (old == DS_NEUTRAL) {
+      loss += REPUTATION_LOSS_NEUTRAL;
+      old = DS_WAR;
+    } else if (old == DS_CEASEFIRE) {
+      loss += REPUTATION_LOSS_CEASEFIRE;
+      old = DS_NEUTRAL;
+    } else if (old == DS_PEACE) {
+      loss += REPUTATION_LOSS_PEACE;
+      old = DS_NEUTRAL;
+    } else if (old == DS_ALLIANCE) {
+      loss += REPUTATION_LOSS_ALLIANCE;
+      old = DS_PEACE;
+    } else if (old == DS_TEAM) {
+      old = DS_ALLIANCE;
+    }
+  }
+
+  return loss;
+}
+
+/**************************************************************************
   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.
+    a pact treaty type, we break one pact level.
 **************************************************************************/
 void handle_diplomacy_cancel_pact(struct player *pplayer,
                                  int other_player_id,
-                                 enum clause_type clause)
+                                  enum clause_type clause)
 {
   enum diplstate_type old_type;
   enum diplstate_type new_type;
   struct player *pplayer2;
-  int reppenalty = 0;
   bool has_senate, repeat = FALSE;
+  struct team *pteam = team_get_by_id(pplayer->team);
+
+  assert(pplayer->team == TEAM_NONE || pteam);
 
   if (!is_valid_player_id(other_player_id)) {
     return;
@@ -964,29 +1004,38 @@
     return;
   }
 
-  /* Can't break alliance with a team member, but can reduce a team
+  /* Can't break alliance with a team member, but can change a team
    * research to an alliance for stand-alone research. */
-  if (pplayer->team != TEAM_NONE && pplayer->team == pplayer2->team
+  if (game.team_mode
+      && pplayer->team != TEAM_NONE 
+      && pplayer->team == pplayer2->team
       && old_type != DS_TEAM) {
     return;
   }
 
+  if (old_type == DS_NEUTRAL
+      && pplayer->team != TEAM_NONE
+      && pteam->leader != pplayer) {
+    notify_player_ex(pplayer, -1, -1, E_TREATY_BROKEN,
+                     _("Game: You are not the alliance's leader and cannot "
+                       "declare war."));
+    return;
+  }
+
   if (clause == CLAUSE_VISION) {
     if (!gives_shared_vision(pplayer, pplayer2)) {
       return;
     }
     remove_shared_vision(pplayer, pplayer2);
     notify_player_ex(pplayer2, -1, -1, E_TREATY_BROKEN,
-                     _("%s no longer gives us shared vision!"),
+                     _("Game: %s no longer gives us shared vision!"),
                      pplayer->name);
     return;
   }
 
   /* else, breaking a treaty */
 
-repeat_break_treaty:
-  /* check what the new status will be, and what will happen to our
-     reputation */
+  /* 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:
@@ -994,15 +1043,16 @@
     break;
   case DS_CEASEFIRE:
     new_type = DS_NEUTRAL;
-    reppenalty += GAME_MAX_REPUTATION/6;
     break;
   case DS_PEACE:
     new_type = DS_NEUTRAL;
-    reppenalty += GAME_MAX_REPUTATION/5;
     break;
   case DS_ALLIANCE:
     new_type = DS_PEACE;
-    reppenalty += GAME_MAX_REPUTATION/4;
+    /* Team leader removes us formally. */
+    assert(pplayer->team != TEAM_NONE);
+    handle_team_remove_player(team_get_by_id(pplayer->team)->leader, 
+                              pplayer->player_no);
     break;
   case DS_TEAM:
     new_type = DS_ALLIANCE;
@@ -1020,28 +1070,6 @@
     pplayer2->diplstates[pplayer->player_no].turns_left =
     16;
 
-  /* If the old state was alliance, the players' units can share tiles
-     illegally, and we need to call resolve_unit_stacks() */
-  if (old_type == DS_ALLIANCE) {
-    resolve_unit_stacks(pplayer, pplayer2, TRUE);
-
-    /* Inform clients about units that have been hidden.  Units in cities
-     * and transporters are visible to allies but not visible once the
-     * alliance is broken.  We have to call this after resolve_unit_stacks
-     * because that function may change units' locations. */
-    remove_allied_visibility(pplayer, pplayer2);
-    remove_allied_visibility(pplayer2, pplayer);
-  }
-
-  /* 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;
-  }
-
   /* 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;
@@ -1057,7 +1085,11 @@
      have different chances of revolution; maybe we need to
      extend the govt rulesets to mimic this -- pt */
   else {
-    pplayer->reputation = MAX(pplayer->reputation - reppenalty, 0);
+    if (old_type != DS_ALLIANCE) {
+      /* player_join_team takes care of reputation for alliances */
+      pplayer->reputation = MAX(pplayer->reputation 
+                                - reputation_loss(old_type, new_type), 0);
+    }
     notify_player_ex(pplayer, -1, -1, E_TREATY_BROKEN,
                      _("Game: Your reputation is now %s."),
                      reputation_text(pplayer->reputation));
@@ -1071,9 +1103,6 @@
     }
   }
 
-  send_player_info(pplayer, NULL);
-  send_player_info(pplayer2, NULL);
-
   /* 
    * Refresh all cities which have a unit of the other side within
    * city range. 
@@ -1095,34 +1124,43 @@
                   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)) {
-      /* A declaration of war by A against B also means A declares
-       * war against all of B's allies.  If B has any allies in
-       * A's team, then they break off their alliance. */
-      if (other->team != TEAM_NONE && other->team == pplayer->team) {
-        notify_player_ex(other, -1, -1, E_TREATY_BROKEN,
-                         _("Game: Your team mate %s declared war on %s. "
-                           "You are obligated to cancel alliance with %s."),
-                         pplayer->name,
-                         get_nation_name_plural(pplayer2->nation),
-                         pplayer2->name);
-        handle_diplomacy_cancel_pact(other, pplayer2->player_no,
-                                     CLAUSE_ALLIANCE);
-      } else {
-        notify_player_ex(other, -1, -1, E_TREATY_BROKEN,
-                         _("Game: %s has attacked one of your allies! "
-                           "The alliance brings you into the war as well."),
-                         pplayer->name);
-        pplayer->diplstates[other->player_no].has_reason_to_cancel = 3;
-        handle_diplomacy_cancel_pact(pplayer, other->player_no, CLAUSE_LAST);
+  /* If we are in an alliance and go to war (we are the alliance leader), 
+   * make all our alliance members go to war against all the members of 
+   * the opposition. */
+  if (new_type == DS_WAR) {
+    players_iterate(pmate) {
+      if (pplayers_allied(pplayer, pmate)) {
+        players_iterate(pother) {
+          if (pplayers_allied(pother, pplayer2)
+              && pmate->diplstates[pother->player_no].type != DS_WAR) {
+            pmate->reputation = 
+              MAX(pmate->reputation
+                  - reputation_loss(pmate->diplstates[pother->player_no].type, 
+                                    DS_WAR), 
+                  0);
+            pother->diplstates[pmate->player_no].type = DS_WAR;
+            pmate->diplstates[pother->player_no].type = DS_WAR;
+            notify_player_ex(pmate, -1, -1, E_TREATY_BROKEN,
+                             _("Game: %s has declared war against %s on "
+                               "behalf of our alliance! "),
+                             pplayer->name, pother->name);
+            notify_player_ex(pother, -1, -1, E_TREATY_BROKEN,
+                             _("Game: %s has declared war against you "
+                               "as requested by %s! "),
+                             pmate->name, pplayer->name);
+            check_city_workers(pmate);
+            check_city_workers(pother);
+            send_player_info(pother, NULL);
+            send_player_info(pmate, NULL);
+          }
+        } players_iterate_end;
       }
-    }
-  } players_iterate_end;
+    } players_iterate_end;
+  }
+  send_player_info(pplayer, NULL);
+  send_player_info(pplayer2, NULL);
+
+  team_consistency_check;
 }
 
 /**************************************************************************
@@ -1529,7 +1567,8 @@
 }
 
 /**************************************************************************
-  Update contact info.
+  Update contact info. For alliances, make contact with all members of an 
+  alliance when you make contact with one member.
 **************************************************************************/
 void make_contact(struct player *pplayer1, struct player *pplayer2,
                  int x, int y)
@@ -1551,32 +1590,46 @@
     enum diplstate_type dipstate = diplomacy_possible(pplayer1,pplayer2)
                                     ? DS_NEUTRAL : DS_WAR;
 
-    pplayer1->diplstates[player2].type
-      = pplayer2->diplstates[player1].type
-      = dipstate;
-    notify_player_ex(pplayer1, x, y,
-                    E_FIRST_CONTACT,
-                    _("Game: You have made contact with the %s, ruled by %s."),
-                    get_nation_name_plural(pplayer2->nation), pplayer2->name);
-    notify_player_ex(pplayer2, x, y,
-                    E_FIRST_CONTACT,
-                    _("Game: You have made contact with the %s, ruled by %s."),
-                    get_nation_name_plural(pplayer1->nation), pplayer1->name);
-    send_player_info(pplayer1, pplayer2);
-    send_player_info(pplayer2, pplayer1);
+    if (pplayer2->team != TEAM_NONE) {
+      notify_player_ex(pplayer1, x, y, E_FIRST_CONTACT,
+                       _("Game: You have made contact with the %s alliance, 
led by "
+                       "%s of the %s."), team_get_by_id(pplayer2->team)->name,
+                       pplayer2->name, 
get_nation_name_plural(pplayer2->nation));
+    } else {
+      notify_player_ex(pplayer1, x, y, E_FIRST_CONTACT,
+                       _("Game: You have made contact with the %s, ruled by 
%s."),
+                       get_nation_name_plural(pplayer2->nation), 
pplayer2->name);
+    }
+    if (pplayer1->team != TEAM_NONE) {
+      notify_player_ex(pplayer2, x, y, E_FIRST_CONTACT,
+                       _("Game: You have made contact with the %s alliance, 
led by "
+                       "%s of the %s."), team_get_by_id(pplayer1->team)->name,
+                       pplayer1->name, 
get_nation_name_plural(pplayer1->nation));
+    } else {
+      notify_player_ex(pplayer2, x, y, E_FIRST_CONTACT,
+                       _("Game: You have made contact with the %s, ruled by 
%s."),
+                       get_nation_name_plural(pplayer1->nation), 
pplayer1->name);
+    }
+
+    players_iterate(plr1) {
+      players_iterate(plr2) {
+        if (pplayers_allied(pplayer1, plr1) && pplayers_allied(pplayer2, 
plr2)) {
+          plr1->diplstates[plr2->player_no].type = 
+            plr2->diplstates[plr1->player_no].type = dipstate;
+          send_player_info(plr1, plr2);
+          send_player_info(plr2, plr1);
+          send_player_info(plr1, plr1);
+          send_player_info(plr2, plr2);
+
+          check_city_workers(pplayer1);
+          check_city_workers(pplayer2);
+        }
+      } players_iterate_end;
+    } players_iterate_end;
+  } else {
     send_player_info(pplayer1, pplayer1);
     send_player_info(pplayer2, pplayer2);
-
-    check_city_workers(pplayer1);
-    check_city_workers(pplayer2);
-    return;
   }
-  if (player_has_embassy(pplayer1, pplayer2)
-      || player_has_embassy(pplayer2, pplayer1)) {
-    return; /* Avoid sending too much info over the network */
-  }
-  send_player_info(pplayer1, pplayer1);
-  send_player_info(pplayer2, pplayer2);
 }
 
 /**************************************************************************
@@ -1800,17 +1853,6 @@
   }
   pplayer->economic.gold = cplayer->economic.gold;
   pplayer->research.bulbs_researched = 0;
-  pplayer->embassy = 0; /* all embassies destroyed */
-
-  /* give splitted player the embassies to his team mates back, if any */
-  if (pplayer->team != TEAM_NONE) {
-    players_iterate(pdest) {
-      if (pplayer->team == pdest->team
-          && pplayer != pdest) {
-        establish_embassy(pplayer, pdest);
-      }
-    } players_iterate_end;
-  }
 
   player_limit_to_government_rates(pplayer);
 
@@ -2053,4 +2095,158 @@
   }
 
   send_packet_single_playerlist_reply(pconn, &packet);
+}
+
+/**************************************************************************
+  Player joins a team. No checks. We do all the work for quitting
+  an alliance here, and do not use any other way to leave an alliance.
+  Call this function with TEAM_NONE as the new_team to quit an alliance.
+**************************************************************************/
+void player_join_team(struct player *pplayer, Team_Type_id new_team,
+                      Team_Type_id old_team)
+{
+  bool alone = TRUE;
+
+  /* Do we quit our old team? */
+  if (old_team != TEAM_NONE) {
+    struct team *pteam = team_get_by_id(old_team);
+
+    players_iterate(pmate) {
+      if (pmate != pplayer && pmate->team == old_team) {
+        alone = FALSE;
+        pplayer->diplstates[pmate->player_no].type = DS_PEACE;
+        pmate->diplstates[pplayer->player_no].type = DS_PEACE;
+        notify_player_ex(pmate, -1, -1, E_TREATY_BROKEN,
+                         _("Game: %s has left the alliance."),
+                         pplayer->name);
+        resolve_unit_stacks(pplayer, pmate, TRUE);
+                                                                               
       
+        /* Inform clients about units that have been hidden.  Units in cities
+         * and transporters are visible to allies but not visible once the
+         * alliance is broken.  We have to call this after resolve_unit_stacks
+         * because that function may change units' locations. */
+        remove_allied_visibility(pplayer, pmate);
+        remove_allied_visibility(pmate, pplayer);
+
+        send_player_info(pmate, NULL);
+      }
+    } players_iterate_end;
+    if (!alone) {
+      /* you left your friends behind, boo! */
+      pplayer->reputation = MAX(pplayer->reputation
+                                - REPUTATION_LOSS_ALLIANCE, 0);
+    }
+    /* Maybe they need a new leader? */
+    if (pteam->leader == pplayer) {
+      int highest = -1;
+
+      players_iterate(leftover) {
+        if (leftover->team != TEAM_NONE
+            && leftover->team == pteam->id
+            && leftover->is_alive
+            && civ_score(leftover) > highest) {
+          pteam->leader = leftover;
+        }
+      } players_iterate_end;
+      notify_player_ex(pteam->leader, -1, -1, E_TREATY_BROKEN, 
+                       _("Game: You became leader of %s as %s left the "
+                       "alliance."), pteam->name, pplayer->name);
+    }
+    team_remove_player(pplayer);
+  }
+  pplayer->team = new_team; /* in case this is not done yet */
+  send_player_info(pplayer, NULL);
+}
+
+/**************************************************************************
+  Player creates a new team. Leadership assignment is handled 
+  automatically by team_add_player().
+**************************************************************************/
+void handle_team_add_player(struct player *pplayer, int player_no, char *name)
+{
+  int old_team = pplayer->team;
+
+  if (!name || pplayer->player_no != player_no) {
+    return;
+  }
+  if (!is_sane_name(name) || strlen(name) > 20) {
+    notify_player_ex(pplayer, -1, -1, E_TREATY_BROKEN, 
+                     _("Game: That is a stupid name for an alliance."));
+    return;
+  }
+  team_iterate(pteam) {
+    if (strcmp(name, pteam->name) == 0) {
+      notify_player_ex(pplayer, -1, -1, E_TREATY_BROKEN, 
+                       _("Game: There already is an alliance by that name!"));
+      return;
+    }
+  } team_iterate_end;
+
+  team_add_player(pplayer, name); /* sets pplayer->team to new team */
+  player_join_team(pplayer, pplayer->team, old_team);
+  send_team_info(team_get_by_id(pplayer->team));
+  send_player_info(pplayer, NULL);
+  team_consistency_check;
+}
+
+/**************************************************************************
+  Player boots a player from a team.
+**************************************************************************/
+void handle_team_remove_player(struct player *pplayer, int player_no)
+{
+  struct player *booted = get_player(player_no);
+  int team_no = booted->team;
+  struct team *pteam = team_get_by_id(team_no);
+
+  if (!booted || !pteam) {
+    return;
+  }
+
+  if (pplayer == booted || pplayer == pteam->leader) {
+    notify_player_ex(booted, -1, -1, E_TREATY_BROKEN, 
+                     _("Game: You were removed from the alliance."));
+    player_join_team(booted, TEAM_NONE, team_no);
+    send_team_info(pteam);
+    send_player_info(booted, NULL);
+  } else {
+    notify_player_ex(pplayer, -1, -1, E_TREATY_BROKEN, 
+                     _("Game: You cannot remove %s from an alliance."),
+                     booted->name);
+  }
+  team_consistency_check;
+}
+
+/**************************************************************************
+  Change alliance leader.
+**************************************************************************/
+void handle_team_change_leader(struct player *pplayer, int team_no, 
+                               int player_no)
+{
+  struct player *new_leader = get_player(player_no);
+  struct team *pteam = team_get_by_id(team_no);
+
+  if (!pteam 
+      || !new_leader
+      || !new_leader->is_alive
+      || pplayer == new_leader
+      || pteam->leader != pplayer
+      || new_leader->team != team_no
+      || new_leader->team != pplayer->team) {
+    return;
+  }
+  pteam->leader = new_leader;
+  notify_player_ex(pplayer, -1, -1, E_TREATY_ALLIANCE, 
+                   _("Game: %s made you leader of the %s alliance."),
+                   pplayer->name, pteam->name);
+  send_team_info(pteam);
+}
+
+/**************************************************************************
+  Send team info to all players.
+**************************************************************************/
+void send_team_info(struct team *pteam)
+{
+  dlsend_packet_team_info(&game.game_connections, pteam->id, pteam->name,
+                          pteam->leader != NULL ? pteam->leader->player_no
+                          : -1);
 }
Index: server/plrhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.h,v
retrieving revision 1.63
diff -u -r1.63 plrhand.h
--- server/plrhand.h    10 Apr 2004 03:47:50 -0000      1.63
+++ server/plrhand.h    15 Apr 2004 11:31:18 -0000
@@ -81,6 +81,10 @@
 void shuffle_players(void);
 struct player *shuffled_player(int i);
 
+int reputation_loss(enum diplstate_type old, enum diplstate_type goal);
+void player_join_team(struct player *pplayer, Team_Type_id new_team,
+                      Team_Type_id old_team);
+
 #define shuffled_players_iterate(pplayer)                                   \
 {                                                                           \
   struct player *pplayer;                                                   \
@@ -98,5 +102,6 @@
 void civil_war(struct player *pplayer);
 
 void handle_single_playerlist_req(struct connection *pconn);
+void send_team_info(struct team *pteam);
 
 #endif  /* FC__PLRHAND_H */
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.169
diff -u -r1.169 ruleset.c
--- server/ruleset.c    14 Apr 2004 11:19:45 -0000      1.169
+++ server/ruleset.c    15 Apr 2004 11:31:18 -0000
@@ -1958,10 +1958,6 @@
   packet.playable_nation_count = game.playable_nation_count;
   packet.style_count = game.styles_count;
 
-  for(i = 0; i < MAX_NUM_TEAMS; i++) {
-    sz_strlcpy(packet.team_name[i], team_get_by_id(i)->name);
-  }
-
   lsend_packet_ruleset_control(dest, &packet);
 }
 
Index: server/sanitycheck.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/sanitycheck.c,v
retrieving revision 1.40
diff -u -r1.40 sanitycheck.c
--- server/sanitycheck.c        25 Jan 2004 08:04:53 -0000      1.40
+++ server/sanitycheck.c        15 Apr 2004 11:31:19 -0000
@@ -339,11 +339,21 @@
     players_iterate(pplayer2) {
       assert(pplayer->diplstates[pplayer2->player_no].type
             == pplayer2->diplstates[pplayer->player_no].type);
+      if (pplayer->diplstates[pplayer2->player_no].type == DS_ALLIANCE
+          && (pplayer->team == TEAM_NONE || pplayer->team != pplayer2->team)) {
+        die("%s and %s are allied but in different teams (%s and %s)!",
+            pplayer->name, pplayer2->name,
+            pplayer->team != TEAM_NONE ? team_get_by_id(pplayer->team)->name
+              : "None",
+            pplayer2->team != TEAM_NONE ? team_get_by_id(pplayer2->team)->name
+              : "None");
+      }
       if (pplayer->diplstates[pplayer2->player_no].type == DS_CEASEFIRE)
        assert(pplayer->diplstates[pplayer2->player_no].turns_left
               == pplayer2->diplstates[pplayer->player_no].turns_left);
     } players_iterate_end;
   } players_iterate_end;
+  team_consistency_check;
 
   /* Sanity checks on living and dead players. */
   for (player_no = 0; player_no < ARRAY_SIZE(game.players); player_no++) {
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.149
diff -u -r1.149 savegame.c
--- server/savegame.c   26 Mar 2004 17:31:55 -0000      1.149
+++ server/savegame.c   15 Apr 2004 11:31:19 -0000
@@ -176,7 +176,8 @@
    and rulesets */
 #define SAVEFILE_OPTIONS "startoptions spacerace2 rulesets" \
 " diplchance_percent worklists2 map_editor known32fix turn " \
-"attributes watchtower rulesetdir client_worklists orders"
+"attributes watchtower rulesetdir client_worklists orders " \
+"alliances "
 
 static const char hex_chars[] = "0123456789abcdef";
 static const char terrain_chars[] = "adfghjm prstu";
@@ -673,9 +674,13 @@
 
     sz_strlcpy(tmp, secfile_lookup_str(file, "player%d.team", plrno));
     team_add_player(plr, tmp);
-    plr->team = team_find_by_name(tmp);
+    if (secfile_lookup_bool_default(file, FALSE, "player%d.team_leader", 
+                                    plrno)) {
+      team_get_by_id(plr->team)->leader = plr;
+    }
   } else {
     plr->team = TEAM_NONE;
+    (void) section_file_lookup(file, "player%d.team_leader", plrno);
   }
   if (is_barbarian(plr)) {
     plr->nation=game.nation_count-1;
@@ -825,19 +830,6 @@
     secfile_lookup_int_default(file, 0,
                           "player%d.diplstate%d.contact_turns_left", plrno, i);
   }
-  /* Sanity check alliances, prevent allied-with-ally-of-enemy */
-  players_iterate(aplayer) {
-    if (pplayers_allied(plr, aplayer)
-        && !pplayer_can_ally(plr, aplayer)) {
-      freelog(LOG_ERROR, _("Illegal alliance structure detected: "
-              "%s's alliance to %s reduced to peace treaty."),
-              plr->name, aplayer->name);
-      plr->diplstates[aplayer->player_no].type = DS_PEACE;
-      aplayer->diplstates[plr->player_no].type = DS_PEACE;
-      resolve_unit_stacks(plr, aplayer, FALSE);
-    }
-  } players_iterate_end;
-
   { /* spacerace */
     struct player_spaceship *ship = &plr->spaceship;
     char prefix[32];
@@ -1432,6 +1424,9 @@
   if (plr->team != TEAM_NONE) {
     secfile_insert_str(file, (char *) team_get_by_id(plr->team)->name, 
                        "player%d.team", plrno);
+    if (team_get_by_id(plr->team)->leader == plr) {
+      secfile_insert_bool(file, TRUE, "player%d.team_leader", plrno);
+    }
   }
   secfile_insert_int(file, plr->government, "player%d.government", plrno);
   secfile_insert_int(file, plr->embassy, "player%d.embassy", plrno);
@@ -2286,6 +2281,28 @@
     for(i=0; i<game.nplayers; i++) {
       player_load(&game.players[i], i, file); 
     }
+ 
+    if (!has_capability("alliances", savefile_options)) {
+      /* Any pre-modern alliance has to be converted into a peace
+       * treaty. */
+      freelog(LOG_NORMAL, _("Old style alliances have been turned into "
+              "peace treaties."));
+      players_iterate(plr1) {
+        if (plr1->team != TEAM_NONE) {
+          team_remove_player(plr1);
+        }
+        players_iterate(plr2) {
+          if (pplayers_allied(plr1, plr2)) {
+            plr1->diplstates[plr2->player_no].type = DS_PEACE;
+            plr2->diplstates[plr1->player_no].type = DS_PEACE;
+            resolve_unit_stacks(plr1, plr2, FALSE);
+            remove_allied_visibility(plr1, plr2);
+            remove_allied_visibility(plr2, plr1);
+          }
+        } players_iterate_end;
+      } players_iterate_end;
+    }
+    team_consistency_check;
 
     cities_iterate(pcity) {
       /* Update all city information.  This must come after all cities are
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.158
diff -u -r1.158 srv_main.c
--- server/srv_main.c   10 Apr 2004 03:47:50 -0000      1.158
+++ server/srv_main.c   15 Apr 2004 11:31:19 -0000
@@ -171,12 +171,10 @@
   srvarg.script_filename = NULL;
 
   srvarg.quitidle = 0;
+  BV_CLR_ALL(srvarg.draw);
 
   srvarg.extra_metaserver_info[0] = '\0';
 
-  /* initialize teams */
-  team_init();
-
   /* mark as initialized */
   has_been_srv_init = TRUE;
 
@@ -295,6 +293,9 @@
   send_all_known_cities(dest);
   send_all_known_units(dest);
   send_player_turn_notifications(dest);
+  team_iterate(pteam) {
+    send_team_info(pteam);
+  } team_iterate_end;
 }
 
 /**************************************************************************
@@ -1671,6 +1672,7 @@
      players_iterate(pdest) {
       if (pplayer->team == pdest->team && pplayer->team != TEAM_NONE
           && pplayer->player_no != pdest->player_no) {
+        game.team_mode = TRUE;
         pplayer->diplstates[pdest->player_no].type = DS_TEAM;
         give_shared_vision(pplayer, pdest);
         pplayer->embassy |= (1 << pdest->player_no);
@@ -1726,4 +1728,5 @@
   diplhand_free();
   game_free();
   stdinhand_free();
+  BV_CLR_ALL(srvarg.draw);
 }
Index: server/srv_main.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.h,v
retrieving revision 1.18
diff -u -r1.18 srv_main.h
--- server/srv_main.h   10 Apr 2004 03:47:50 -0000      1.18
+++ server/srv_main.h   15 Apr 2004 11:31:19 -0000
@@ -19,6 +19,8 @@
 struct connection;
 struct unit;
 
+BV_DEFINE(bv_draw, MAX_NUM_PLAYERS);
+
 struct server_arguments {
   /* metaserver information */
   bool metaserver_no_send;
@@ -40,6 +42,8 @@
   char extra_metaserver_info[256];
   /* quit if there no players after a given time interval */
   int quitidle;
+  /* what kind of end game we should use */
+  bv_draw draw;
 };
 
 void srv_init(void);
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.313
diff -u -r1.313 stdinhand.c
--- server/stdinhand.c  10 Apr 2004 03:47:50 -0000      1.313
+++ server/stdinhand.c  15 Apr 2004 11:31:20 -0000
@@ -84,7 +84,7 @@
 static bool take_command(struct connection *caller, char *name, bool check);
 static bool detach_command(struct connection *caller, char *name, bool check);
 static bool start_command(struct connection *caller, char *name, bool check);
-static bool end_command(struct connection *caller, char *name, bool check);
+static bool end_command(struct connection *caller, char *str, bool check);
 
 struct voting {
   char command[MAX_LEN_CONSOLE_LINE]; /* [0] == \0 if none in action */
@@ -646,7 +646,8 @@
          N_("If set to 0 (default), diplomacy is enabled for all.\n"
             "If set to 1, diplomacy is only allowed between human players.\n"
             "If set to 2, diplomacy is only allowed between AI players.\n"
-             "If set to 3, diplomacy is disabled for all.\n"
+             "If set to 3, diplomacy is restricted to teams.\n"
+             "If set to 4, diplomacy is disabled for all.\n"
              "You can always do diplomacy with players on your team."), NULL,
          GAME_MIN_DIPLOMACY, GAME_MAX_DIPLOMACY, GAME_DEFAULT_DIPLOMACY)
 
@@ -1385,9 +1386,11 @@
       "concert with the option \"timeout\". Defaults are 0 0 0 1")
   },
   {"endgame",  ALLOW_CTRL,
-   "endgame",
-   N_("End the game."),
-   N_("This command ends the game immediately.")
+   /* TRANS: translate text between <> only */
+   N_("endgame <player1 player2 player3 ...>"),
+   N_("End the game.  If players are listed, these win the game."),
+   N_("This command ends the game immediately and credits the given players, "
+      "if any, with winning it.")
   },
   {"remove",   ALLOW_CTRL,
    /* TRANS: translate text between <> only */
@@ -3136,7 +3139,11 @@
 }
 
 /******************************************************************
-...
+  This function can only be used with new games, not savegames,
+  since it would create situations in regards to alliance then
+  which it could not fix since we are in pregame, and pregame
+  does not yet have vital data set up that would allow us to
+  fix these problems using the usual functions.
 ******************************************************************/
 static bool team_command(struct connection *caller, char *str, bool check)
 {
@@ -3147,7 +3154,7 @@
   int ntokens = 0, i;
   bool res = FALSE;
 
-  if (server_state != PRE_GAME_STATE) {
+  if (server_state != PRE_GAME_STATE || !game.is_new_game) {
     cmd_reply(CMD_TEAM, caller, C_SYNTAX,
               _("Cannot change teams once game has begun."));
     return FALSE;
@@ -3169,8 +3176,21 @@
     goto cleanup;
   }
 
+  /* Remove from old team */
   if (!check && pplayer->team != TEAM_NONE) {
+    struct team *pteam = team_get_by_id(pplayer->team);
+
+    /* Change leaders */
+    if (pteam->leader == pplayer) {
+      players_iterate(aplayer) {
+        if (aplayer->team == pplayer->team && aplayer != pplayer) {
+          pteam->leader = aplayer;
+        }
+      } players_iterate_end;
+    }
+
     team_remove_player(pplayer);
+    send_team_info(pteam);
   }
 
   if (ntokens == 1) {
@@ -3196,6 +3216,7 @@
     team_add_player(pplayer, arg[1]);
     cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."),
              pplayer->name, team_get_by_id(pplayer->team)->name);
+    send_team_info(team_get_by_id(pplayer->team));
   }
   res = TRUE;
 
@@ -4449,19 +4470,56 @@
 }
 
 /**************************************************************************
-...
+  End the game and accord victory to the listed players, if any.
 **************************************************************************/
-static bool end_command(struct connection *caller, char *name, bool check)
+static bool end_command(struct connection *caller, char *str, bool check)
 {
   if (server_state == RUN_GAME_STATE) {
+    char *arg[MAX_NUM_PLAYERS];
+    int ntokens = 0, i;
+    enum m_pre_result plr_result;
+    bool result = TRUE;
+    char buf[MAX_LEN_CONSOLE_LINE];
+
+    if (str != NULL || strlen(str) > 0) {
+      sz_strlcpy(buf, str);
+      ntokens = get_tokens(buf, arg, MAX_NUM_PLAYERS, TOKEN_DELIMITERS);
+    }
+    /* Ensure players exist */
+    for (i = 0; i < ntokens; i++) {
+      struct player *pplayer = find_player_by_name_prefix(arg[i], &plr_result);
+
+      if (!pplayer) {
+        cmd_reply_no_such_player(CMD_TEAM, caller, arg[i], plr_result);
+        result = FALSE;
+        goto cleanup;
+      } else if (pplayer->is_alive == FALSE) {
+        cmd_reply(CMD_END_GAME, caller, C_FAIL, _("But %s is dead!"),
+                  pplayer->name);
+        result = FALSE;
+        goto cleanup;
+      }
+    }
     if (check) {
-      return TRUE;
+      goto cleanup;
+    }
+    if (ntokens > 0) {
+      /* Mark players for victory. */
+      for (i = 0; i < ntokens; i++) {
+        BV_SET(srvarg.draw, 
+               find_player_by_name_prefix(arg[i], &plr_result)->player_no);
+      }
     }
     server_state = GAME_OVER_STATE;
     force_end_of_sniff = TRUE;
     cmd_reply(CMD_END_GAME, caller, C_OK,
               _("Ending the game. The server will restart once all clients "
               "have disconnected."));
+
+    cleanup:
+    for (i = 0; i < ntokens; i++) {
+      free(arg[i]);
+    }
     return TRUE;
   } else {
     cmd_reply(CMD_END_GAME, caller, C_FAIL, 
@@ -4866,8 +4924,10 @@
                     get_nation_name_plural(pplayer->nation));
       }
       if (pplayer->team != TEAM_NONE) {
-        cat_snprintf(buf2, sizeof(buf2), (", team %s"),
-                     team_get_by_id(pplayer->team)->name);
+        struct team *pteam = team_get_by_id(pplayer->team);
+
+        cat_snprintf(buf2, sizeof(buf2), (", team %s%s"),
+                     pteam->name, pteam->leader == pplayer ? "*" : "");
       }
       my_snprintf(buf, sizeof(buf), "%s (%s)", pplayer->name, buf2);
       

[Prev in Thread] Current Thread [Next in Thread]