[Freeciv-Dev] New alliances v3 (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 v3 (PR#8394) |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Sun, 18 Apr 2004 14:28:17 -0700 |
Reply-to: |
rt@xxxxxxxxxxx |
<URL: http://rt.freeciv.org/Ticket/Display.html?id=8394 >
CHANGES
- the letter '*' has been forbidden from player, team and nation names
- the AI will no longer be blocked from creating alliances if a human
player creates a "Pax <AI nation>" alliance
- if we cancel an alliance, then earlier we would automatically drop
out of it; now, if we are the alliance leader, we will instead throw
the one we cancel the alliance to out of our alliance
- fix bug in cvs: if we make teams with team research, the AI may
declare war on its allies (not forward-porting since code changed here)
- fix create alliance when you are in alliance crash (new in v2)
- added pteam->active to remove old ugly kludge of setting pteam->id to
TEAM_NONE to indicate that the team is not in use; fixed new bug in v2
- added even more insane sanity checks (should disable before commit)
ODDITIES
- if you make contact with an alliance, you get to know all members
of an alliance; but, if someone you have contact with joins an
alliance, you will not automatically know the members of this alliance;
this is slightly inconsistent, but seems right nonetheless
- you lose reputation if you are kicked out of an alliance; this also
may seem odd, but I think it is correct
I think it is at or near a committable state. Please speak up now if you
think the patch needs to be changed in some way.
- 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 18 Apr 2004 21:24:38 -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,12 @@
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_TEAM) {
continue;
}
war_desire[aplayer->player_no] = ai_war_desire(pplayer, aplayer, ai);
@@ -824,8 +788,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 +824,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 */
@@ -894,12 +872,21 @@
return;
}
+ /* Dissolve a one-member alliance which we may have created earlier
+ * to woo a player into alliance with us. */
+ if (pplayer->team != TEAM_NONE
+ && team_count_members_alive(pplayer->team) == 1) {
+ handle_team_remove_player(pplayer, pplayer->player_no);
+ }
+
/*** If we are greviously insulted, go to war immediately. ***/
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 +915,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,12 +941,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 (pplayers_allied(pplayer, target)) {
- PLAYER_LOG(LOG_ERROR, pplayer, ai, "Went to war against %s, who is "
- "an ally!", target->name); /* Oh, my. */
- }
+ if (target
+ && !pplayers_at_war(pplayer, target)
+ && ai->diplomacy.countdown <= 0
+ && (pplayer->team == TEAM_NONE
+ || team_get_by_id(pplayer->team)->leader == pplayer)) {
if (pplayer->diplstates[target->player_no].has_reason_to_cancel > 0) {
/* We have good reason */
notify(target, _("*%s (AI)* Your despicable actions will not go "
@@ -978,21 +963,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 +1058,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 +1075,28 @@
|| !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));
+ /* Fallback in case a human player tries to be clever
+ * on us. */
+ while (team_find_by_name(teamname) != TEAM_NONE) {
+ snprintf(teamname, sizeof(teamname), _("Alliance %d"), myrand(50));
+ }
+ handle_team_add_player(pplayer, pplayer->player_no, teamname);
+ team_consistency_check;
+ }
+ 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 18 Apr 2004 21:24:38 -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 18 Apr 2004 21:24:38 -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 18 Apr 2004 21:24:38 -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 18 Apr 2004 21:24:39 -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 18 Apr 2004 21:24:39 -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 18 Apr 2004 21:24:40 -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,22 @@
void handle_server_shutdown(void)
{
freelog(LOG_VERBOSE, "server shutdown");
+}
+
+/**************************************************************************
+ Receive team info packet.
+**************************************************************************/
+void handle_team_info(int team_no, bool active, char *name, int leader)
+{
+ struct team *pteam = team_get_by_id(team_no);
+
+ if (name != NULL && name[0] != '\0') {
+ sz_strlcpy(pteam->name, name);
+ } else {
+ pteam->name[0] = '\0';
+ }
+ pteam->leader = get_player(leader);
+ pteam->id = team_no;
+ pteam->active = active;
+ update_players_dialog();
}
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 18 Apr 2004 21:24:42 -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 18 Apr 2004 21:24:44 -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 18 Apr 2004 21:24:44 -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 18 Apr 2004 21:24:44 -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 18 Apr 2004 21:24:44 -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 18 Apr 2004 21:24:44 -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 18 Apr 2004 21:24:45 -0000
@@ -326,7 +326,7 @@
}
/***************************************************************
- Count living members of given team
+ Count living members of given team.
***************************************************************/
int team_count_members_alive(Team_Type_id id)
{
@@ -355,12 +355,17 @@
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) {
/* see if we have another team available */
for (i = 0; i < MAX_NUM_TEAMS; i++) {
- if (teams[i].id == TEAM_NONE) {
+ if (teams[i].active == FALSE) {
team_id = i;
break;
}
@@ -371,7 +376,9 @@
}
/* add another team */
teams[team_id].id = team_id;
+ teams[team_id].active = TRUE;
sz_strlcpy(teams[team_id].name, team_name);
+ teams[team_id].leader = pplayer;
}
pplayer->team = team_id;
}
@@ -382,7 +389,7 @@
***************************************************************/
void team_remove_player(struct player *pplayer)
{
- bool others = FALSE;
+ struct player *others = NULL;
if (pplayer->team == TEAM_NONE) {
return;
@@ -393,14 +400,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) {
- teams[pplayer->team].id = TEAM_NONE;
+ if (others == NULL) {
+ teams[pplayer->team].leader = NULL;
+ teams[pplayer->team].name[0] = '\0';
+ teams[pplayer->team].active = FALSE;
}
pplayer->team = TEAM_NONE;
}
@@ -416,7 +425,9 @@
for (i = 0; i < MAX_NUM_TEAMS; i++) {
/* mark as unused */
- teams[i].id = TEAM_NONE;
+ teams[i].id = i;
teams[i].name[0] = '\0';
+ teams[i].leader = NULL;
+ teams[i].active = FALSE;
}
}
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 18 Apr 2004 21:24:45 -0000
@@ -104,6 +104,8 @@
struct team {
char name[MAX_LEN_NAME];
Team_Type_id id; /* equal to array index if active, else TEAM_NONE */
+ struct player *leader;
+ bool active;
};
Nation_Type_id find_nation_by_name(const char *name);
@@ -128,6 +130,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) \
{ \
@@ -135,12 +138,65 @@
Team_Type_id PI_p_itr; \
for (PI_p_itr = 0; PI_p_itr < MAX_NUM_TEAMS; PI_p_itr++) { \
PI_team = team_get_by_id(PI_p_itr); \
- if (PI_team->id == TEAM_NONE) { \
+ if (PI_team->active == FALSE) { \
continue; \
}
#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->diplstates[plr2->player_no].type == DS_ALLIANCE \
+ && (plr1->team != TEAM_NONE || plr2->team != TEAM_NONE) \
+ && plr1->team != plr2->team) { \
+ die("%s and %s are allied but in different teams (%s and %s)!", \
+ plr1->name, plr2->name, plr1->team != TEAM_NONE \
+ ? team_get_by_id(plr1->team)->name : "None", \
+ plr2->team != TEAM_NONE \
+ ? team_get_by_id(plr2->team)->name : "None"); \
+ } \
+ 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 18 Apr 2004 21:24:45 -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,25 @@
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;
+ BOOL active;
+ 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 18 Apr 2004 21:24:46 -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: common/shared.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.c,v
retrieving revision 1.109
diff -u -r1.109 shared.c
--- common/shared.c 10 Apr 2004 01:40:17 -0000 1.109
+++ common/shared.c 18 Apr 2004 21:24:47 -0000
@@ -393,7 +393,7 @@
/* must be composed entirely of printable ISO 8859-1 characters,
* and no illegal characters which can break ranking scripts */
for (cp = name; is_iso_latin1(*cp) && *cp != '|' && *cp != '%'
- && *cp != '"' && *cp != ','; cp++) {
+ && *cp != '"' && *cp != ',' && *cp != '*'; cp++) {
/* nothing */
}
if (*cp != '\0') {
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 18 Apr 2004 21:24:50 -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,116 @@
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);
+ team_remove_player(pplayer);
+ 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 +630,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 18 Apr 2004 21:24:50 -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 18 Apr 2004 21:24:50 -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 18 Apr 2004 21:24:52 -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"
@@ -202,10 +203,14 @@
**************************************************************************/
void kill_player(struct player *pplayer) {
bool palace;
+ int team_no = pplayer->team;
pplayer->is_dying = FALSE; /* Can't get more dead than this. */
pplayer->is_alive = FALSE;
+ team_remove_player(pplayer);
+ player_join_team(pplayer, TEAM_NONE, team_no);
+
/* 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;
}
/**************************************************************************
@@ -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,21 @@
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;
+ /* If we are the team leader, remove offending player. If not,
+ * the team leader will remove us. */
+ assert(pplayer->team != TEAM_NONE);
+ if (team_get_by_id(pplayer->team)->leader == pplayer) {
+ handle_team_remove_player(pplayer, pplayer2->player_no);
+ } else {
+ handle_team_remove_player(team_get_by_id(pplayer->team)->leader,
+ pplayer->player_no);
+ }
break;
case DS_TEAM:
new_type = DS_ALLIANCE;
@@ -1020,28 +1075,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 +1090,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 +1108,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 +1129,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 +1572,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 +1595,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 +1858,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 +2100,166 @@
}
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 (!alone && 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);
+ }
+ }
+ pplayer->team = new_team;
+ 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_remove_player(pplayer);
+ 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_team_info(team_get_by_id(old_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."));
+ team_remove_player(booted);
+ 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. team_no should be the team number, not
+ pteam->id, which may have been reset to TEAM_NONE to mark it as unused.
+**************************************************************************/
+void send_team_info(struct team *pteam)
+{
+ if (pteam == NULL) {
+ return;
+ }
+ assert(pteam->id != 255);
+ dlsend_packet_team_info(&game.game_connections, pteam->id, pteam->active,
+ 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 18 Apr 2004 21:24:52 -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 18 Apr 2004 21:24:54 -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 18 Apr 2004 21:24:54 -0000
@@ -339,11 +339,22 @@
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 || pplayer2->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 18 Apr 2004 21:24:56 -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 18 Apr 2004 21:24:57 -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 18 Apr 2004 21:24:57 -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.314
diff -u -r1.314 stdinhand.c
--- server/stdinhand.c 17 Apr 2004 04:48:20 -0000 1.314
+++ server/stdinhand.c 18 Apr 2004 21:25:01 -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;
@@ -4442,19 +4463,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,
@@ -4859,8 +4917,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);
- [Freeciv-Dev] Re: New alliances (PR#8394), (continued)
- [Freeciv-Dev] Re: New alliances (PR#8394), Per I. Mathisen, 2004/04/13
- [Freeciv-Dev] Re: New alliances (PR#8394), LoboGris, 2004/04/13
- [Freeciv-Dev] Re: New alliances (PR#8394), Per I. Mathisen, 2004/04/14
- [Freeciv-Dev] Re: New alliances (PR#8394), LoboGris, 2004/04/14
- [Freeciv-Dev] Re: New alliances (PR#8394), rwetmore@xxxxxxxxxxxx, 2004/04/14
- [Freeciv-Dev] Re: New alliances (PR#8394), Per I. Mathisen, 2004/04/14
- [Freeciv-Dev] Re: New alliances (PR#8394), imbaczek@xxxxxxxxxxxxxx, 2004/04/14
- [Freeciv-Dev] New alliances v2 (PR#8394), Per I. Mathisen, 2004/04/15
- [Freeciv-Dev] Re: New alliances v2 (PR#8394), Mateusz Stefek, 2004/04/16
- [Freeciv-Dev] Re: New alliances v2 (PR#8394), Per I. Mathisen, 2004/04/18
- [Freeciv-Dev] New alliances v3 (PR#8394),
Per I. Mathisen <=
- [Freeciv-Dev] Re: New alliances v3 (PR#8394), Per I. Mathisen, 2004/04/18
- [Freeciv-Dev] Re: New alliances (PR#8394), Jason Short, 2004/04/20
- [Freeciv-Dev] Re: New alliances (PR#8394), Mike Kaufman, 2004/04/20
- [Freeciv-Dev] Re: New alliances (PR#8394), Per I. Mathisen, 2004/04/20
- [Freeciv-Dev] Re: New alliances (PR#8394), Jason Short, 2004/04/20
- [Freeciv-Dev] Re: New alliances (PR#8394), Per I. Mathisen, 2004/04/20
- [Freeciv-Dev] Re: New alliances (PR#8394), LoboGris, 2004/04/20
- [Freeciv-Dev] Re: New alliances v3 (PR#8394), Mateusz Stefek, 2004/04/26
- [Freeciv-Dev] Re: New alliances v3 (PR#8394), Per I. Mathisen, 2004/04/26
|
|