Index: ai/advdomestic.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v retrieving revision 1.90 diff -u -r1.90 advdomestic.c --- ai/advdomestic.c 2002/09/27 12:32:42 1.90 +++ ai/advdomestic.c 2002/10/30 18:08:52 @@ -403,6 +403,8 @@ int tprod, prod, sci, tax, t, val, wwtv; int j, k; int values[B_LAST]; + int nplayers = game.nplayers + - team_count_members_alive(pplayer->team); /* --- initialize control parameters --- */ t = pcity->ai.trade_want; /* trade_weighting */ @@ -677,7 +679,7 @@ case B_GREAT: /* basically (100 - freecost)% of a free tech per 2 turns */ values[id] = (total_bulbs_required(pplayer) * (100 - game.freecost) * t - * (game.nplayers - 2)) / (game.nplayers * 2 * 100); + * (nplayers - 2)) / (nplayers * 2 * 100); break; case B_WALL: if (!city_got_citywalls(pcity)) { Index: client/packhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v retrieving revision 1.256 diff -u -r1.256 packhand.c --- client/packhand.c 2002/09/23 22:47:09 1.256 +++ client/packhand.c 2002/10/30 18:08:53 @@ -1163,6 +1163,7 @@ pplayer->nation=pinfo->nation; pplayer->is_male=pinfo->is_male; + pplayer->team = pinfo->team; pplayer->economic.gold=pinfo->gold; pplayer->economic.tax=pinfo->tax; @@ -1671,6 +1672,11 @@ 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); + } } /************************************************************************** Index: client/gui-gtk/plrdlg.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/plrdlg.c,v retrieving revision 1.37 diff -u -r1.37 plrdlg.c --- client/gui-gtk/plrdlg.c 2002/08/08 22:10:18 1.37 +++ client/gui-gtk/plrdlg.c 2002/10/30 18:08:53 @@ -70,7 +70,7 @@ static void players_list_ucallback(GtkWidget *w, gint row, gint column); static void players_sship_callback(GtkWidget *w, gpointer data); -#define NUM_COLUMNS 11 /* number of columns in total */ +#define NUM_COLUMNS 12 /* number of columns in total */ #define DEF_SORT_COLUMN 2 /* default sort column (1 = nation) */ /**************************************************************** @@ -120,7 +120,7 @@ void create_players_dialog(void) { static gchar *titles_[NUM_COLUMNS] = - { N_("Name"), N_("Flag"), N_("Nation"), N_("Ai"), + { N_("Name"), N_("Flag"), N_("Nation"), N_("Team"), N_("Ai"), N_("Embassy"), N_("Dipl.State"), N_("Vision"), N_("Reputation"), N_("State"), N_("Host"), N_("Idle") }; @@ -250,7 +250,7 @@ repbuf[32], statebuf[32], idlebuf[32]; const struct player_diplstate *pds; - /* we cassume that neither name nor the nation of a player changes */ + /* we assume that neither name, team nor the nation of a player changes */ if (update == 0) { /* the playername */ my_snprintf(namebuf, sizeof(namebuf), "%-16s", game.players[i].name); @@ -262,6 +262,13 @@ /* the nation */ row[2] = (char *) get_nation_name(game.players[i].nation); + + /* the team */ + if (game.players[i].team != TEAM_NONE) { + row[3] = (char *) team_get_by_id(game.players[i].team)->name; + } else { + row[3] = (char *) ""; + } } /* text for name, plus AI marker */ @@ -311,14 +318,14 @@ reputation_text(game.players[i].reputation)); /* assemble the whole lot */ - row[3] = aibuf; - row[4] = get_embassy_status(game.player_ptr, &game.players[i]); - row[5] = dsbuf; - row[6] = get_vision_status(game.player_ptr, &game.players[i]); - row[7] = repbuf; - row[8] = statebuf; - row[9] = (char *) player_addr_hack(&game.players[i]); /* Fixme */ - row[10] = idlebuf; + row[4] = aibuf; + row[5] = get_embassy_status(game.player_ptr, &game.players[i]); + row[6] = dsbuf; + row[7] = get_vision_status(game.player_ptr, &game.players[i]); + row[8] = repbuf; + row[9] = statebuf; + row[10] = (char *) player_addr_hack(&game.players[i]); /* Fixme */ + row[11] = idlebuf; } #define MIN_DIMENSION 5 @@ -415,7 +422,7 @@ * The nation already had a row in the player report. In that * case we just update the row. */ - for (j = 3; j < NUM_COLUMNS; j++) { + for (j = 4; j < NUM_COLUMNS; j++) { gtk_clist_set_text(GTK_CLIST(players_list), row, j, row_texts[j]); } 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.6 diff -u -r1.6 plrdlg.c --- client/gui-gtk-2.0/plrdlg.c 2002/08/10 16:35:13 1.6 +++ client/gui-gtk-2.0/plrdlg.c 2002/10/30 18:08:53 @@ -70,7 +70,7 @@ static void players_list_ucallback(GtkWidget *w, gint row, gint column); static void players_sship_callback(GtkWidget *w, gpointer data); -#define NUM_COLUMNS 11 /* number of columns in total */ +#define NUM_COLUMNS 12 /* number of columns in total */ #define DEF_SORT_COLUMN 2 /* default sort column (1 = nation) */ /**************************************************************** @@ -120,7 +120,7 @@ void create_players_dialog(void) { static gchar *titles_[NUM_COLUMNS] = - { N_("Name"), N_("Flag"), N_("Nation"), N_("Ai"), + { N_("Name"), N_("Flag"), N_("Nation"), N_("Team"), N_("Ai"), N_("Embassy"), N_("Dipl.State"), N_("Vision"), N_("Reputation"), N_("State"), N_("Host"), N_("Idle") }; @@ -250,7 +250,7 @@ repbuf[32], statebuf[32], idlebuf[32]; const struct player_diplstate *pds; - /* we cassume that neither name nor the nation of a player changes */ + /* we assume that neither name, team nor the nation of a player changes */ if (update == 0) { /* the playername */ my_snprintf(namebuf, sizeof(namebuf), "%-16s", game.players[i].name); @@ -262,6 +262,13 @@ /* the nation */ row[2] = (char *) get_nation_name(game.players[i].nation); + + /* the team */ + if (game.players[i].team != TEAM_NONE) { + row[3] = (char *) team_get_by_id(game.players[i].team)->name; + } else { + row[3] = (char *) ""; + } } /* text for name, plus AI marker */ @@ -311,14 +318,14 @@ reputation_text(game.players[i].reputation)); /* assemble the whole lot */ - row[3] = aibuf; - row[4] = get_embassy_status(game.player_ptr, &game.players[i]); - row[5] = dsbuf; - row[6] = get_vision_status(game.player_ptr, &game.players[i]); - row[7] = repbuf; - row[8] = statebuf; - row[9] = (char *) player_addr_hack(&game.players[i]); /* Fixme */ - row[10] = idlebuf; + row[4] = aibuf; + row[5] = get_embassy_status(game.player_ptr, &game.players[i]); + row[6] = dsbuf; + row[7] = get_vision_status(game.player_ptr, &game.players[i]); + row[8] = repbuf; + row[9] = statebuf; + row[10] = (char *) player_addr_hack(&game.players[i]); /* Fixme */ + row[11] = idlebuf; } #define MIN_DIMENSION 5 @@ -415,7 +422,7 @@ * The nation already had a row in the player report. In that * case we just update the row. */ - for (j = 3; j < NUM_COLUMNS; j++) { + for (j = 4; j < NUM_COLUMNS; j++) { gtk_clist_set_text(GTK_CLIST(players_list), row, j, row_texts[j]); } Index: common/capstr.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v retrieving revision 1.112 diff -u -r1.112 capstr.c --- common/capstr.c 2002/10/07 17:26:21 1.112 +++ common/capstr.c 2002/10/30 18:08:53 @@ -70,12 +70,14 @@ * are not directly related to the capability strings discussed here.) */ -#define CAPABILITY "+1.14.0 conn_info" +#define CAPABILITY "+1.14.0 conn_info team" /* "+1.14.0" is protocol for 1.14.0 release. "conn_info" is sending the conn_id field. To preserve compatability with old clients trying to connect this should persist across releases. + + "team" is support for player teams */ void init_our_capability(void) Index: common/game.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/game.h,v retrieving revision 1.114 diff -u -r1.114 game.h --- common/game.h 2002/09/25 00:58:11 1.114 +++ common/game.h 2002/10/30 18:08:54 @@ -153,6 +153,8 @@ int playable_nation_count; int styles_count; + int team_count; + int watchtower_extra_vision; int watchtower_vision; int allowed_city_names; Index: common/nation.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/nation.c,v retrieving revision 1.25 diff -u -r1.25 nation.c --- common/nation.c 2002/08/07 11:21:47 1.25 +++ common/nation.c 2002/10/30 18:08:54 @@ -12,7 +12,7 @@ ***********************************************************************/ /********************************************************************** - Functions for handling the nations. + Functions for handling the nations and teams. ***********************************************************************/ #include @@ -26,10 +26,13 @@ #include "player.h" #include "support.h" #include "tech.h" +#include "config.h" +#include "fcintl.h" #include "nation.h" static struct nation_type *nations = NULL; +static struct team teams[MAX_NUM_TEAMS]; /*************************************************************** Returns 1 if nid is a valid nation id, else 0. @@ -151,6 +154,9 @@ return &nations[plr->nation]; } +/*************************************************************** + ... +***************************************************************/ struct nation_type *get_nation_by_idx(Nation_Type_id nation) { if (!bounds_check_nation_id(nation, LOG_FATAL, "get_nation_by_idx")) { @@ -233,4 +239,134 @@ exit(EXIT_FAILURE); } return nations[nation].city_style; +} + +/*************************************************************** + Returns the id of a team given its name, or TEAM_NONE if + not found. +***************************************************************/ +Team_Type_id team_find_by_name(const char *name) +{ + assert(name != NULL); + + team_iterate(pteam) { + if(mystrcasecmp(name, pteam->name) == 0) { + return pteam->id; + } + } team_iterate_end; + + return TEAM_NONE; +} + +/*************************************************************** + Returns pointer to a team given its id +***************************************************************/ +struct team *team_get_by_id(Team_Type_id id) +{ + assert(id == TEAM_NONE || (id < MAX_NUM_TEAMS && id >= 0)); + if (id == TEAM_NONE) { + return NULL; + } + return &teams[id]; +} + +/*************************************************************** + Count living members of given team +***************************************************************/ +int team_count_members_alive(Team_Type_id id) +{ + struct team *pteam = team_get_by_id(id); + int count = 0; + + if (pteam == NULL) { + return 0; + } + assert(pteam->id < game.team_count + && pteam->id != TEAM_NONE); + players_iterate(pplayer) { + if (pplayer->is_alive && pplayer->team == pteam->id) { + count++; + } + } players_iterate_end; + return count; +} + +/*************************************************************** + Set a player to a team. Removes previous team affiliation, + creates a new team if it does not exist. +***************************************************************/ +void team_add_player(struct player *pplayer, const char *team_name) +{ + Team_Type_id team_id, i; + + assert(pplayer != NULL && team_name != NULL); + + /* 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) { + team_id = i; + break; + } + } + /* check if too many teams */ + if (team_id == TEAM_NONE) { + freelog(LOG_ERROR, "Impossible: Too many teams!"); + assert(FALSE); + exit(EXIT_FAILURE); + } + /* add another team */ + teams[team_id].id = team_id; + sz_strlcpy(teams[team_id].name, team_name); + game.team_count++; + } + pplayer->team = team_id; +} + +/*************************************************************** + Removes a player from a team, and removes the team if empty of + players +***************************************************************/ +void team_remove_player(struct player *pplayer) +{ + bool others = FALSE; + + assert(pplayer != NULL && pplayer->team < MAX_NUM_TEAMS); + + if (pplayer->team == TEAM_NONE) { + return; + } + + /* anyone else using my team? */ + players_iterate(aplayer) { + if (aplayer->team == pplayer->team && aplayer != pplayer) { + others = TRUE; + break; + } + } players_iterate_end; + + /* no other team members left? remove team! */ + if (!others) { + teams[pplayer->team].id = TEAM_NONE; + game.team_count--; + } + pplayer->team = TEAM_NONE; +} + +/*************************************************************** + Initializes team structure +***************************************************************/ +void team_init() +{ + Team_Type_id i; + + assert(TEAM_NONE < 0 || TEAM_NONE >= MAX_NUM_TEAMS); + + for (i = 0; i < MAX_NUM_TEAMS; i++) { + /* mark as unused */ + teams[i].id = TEAM_NONE; + teams[i].name[0] = '\0'; + } } Index: common/nation.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/nation.h,v retrieving revision 1.16 diff -u -r1.16 nation.h --- common/nation.h 2002/08/07 11:21:47 1.16 +++ common/nation.h 2002/10/30 18:08:54 @@ -19,8 +19,11 @@ #define MAX_NUM_TECH_GOALS 10 #define MAX_NUM_NATIONS 63 #define MAX_NUM_LEADERS 16 +#define MAX_NUM_TEAMS MAX_NUM_PLAYERS +#define TEAM_NONE 255 typedef int Nation_Type_id; +typedef int Team_Type_id; struct Sprite; /* opaque; client-gui specific */ struct player; @@ -89,6 +92,11 @@ } goals; }; +struct team { + char name[MAX_LEN_NAME]; + Team_Type_id id; /* equal to array index if active, else TEAM_NONE */ +}; + Nation_Type_id find_nation_by_name(const char *name); const char *get_nation_name(Nation_Type_id nation); const char *get_nation_name_plural(Nation_Type_id nation); @@ -102,5 +110,23 @@ void nation_free(Nation_Type_id nation); void nation_city_names_free(struct city_name *city_names); int get_nation_city_style(Nation_Type_id nation); + +void team_init(void); +Team_Type_id team_find_by_name(const char *name); +struct team *team_get_by_id(Team_Type_id id); +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); + +#define team_iterate(PI_team) \ +{ \ + struct team *PI_team; \ + Team_Type_id PI_p_itr; \ + for (PI_p_itr = 0; PI_p_itr < game.team_count; PI_p_itr++) { \ + PI_team = team_get_by_id(PI_p_itr); + +#define team_iterate_end \ + } \ +} #endif /* FC__NATION_H */ Index: common/packets.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/packets.c,v retrieving revision 1.220 diff -u -r1.220 packets.c --- common/packets.c 2002/10/27 22:42:18 1.220 +++ common/packets.c 2002/10/30 18:08:54 @@ -818,6 +818,9 @@ dio_put_string(&dout, pinfo->name); dio_put_bool8(&dout, pinfo->is_male); + if (has_capability("team", pc->capability)) { + dio_put_uint8(&dout, pinfo->team); + } dio_put_uint8(&dout, pinfo->government); dio_put_uint32(&dout, pinfo->embassy); dio_put_uint8(&dout, pinfo->city_style); @@ -870,6 +873,11 @@ dio_get_string(&din, pinfo->name, sizeof(pinfo->name)); dio_get_bool8(&din, &pinfo->is_male); + if (has_capability("team", pc->capability)) { + dio_get_uint8(&din, &pinfo->team); + } else { + pinfo->team = TEAM_NONE; + } dio_get_uint8(&din, &pinfo->government); dio_get_uint32(&din, &pinfo->embassy); dio_get_uint8(&din, &pinfo->city_style); @@ -1797,6 +1805,13 @@ dio_put_tech_list(&dout, packet->rtech.partisan_req); + if (has_capability("team", pc->capability)) { + int i; + for (i = 0; i < MAX_NUM_TEAMS; i++) { + dio_put_string(&dout, packet->team_name[i]); + } + } + SEND_PACKET_END; } @@ -1806,6 +1821,8 @@ struct packet_ruleset_control * receive_packet_ruleset_control(struct connection *pc) { + int i; + RECEIVE_PACKET_START(packet_ruleset_control, packet); dio_get_uint8(&din, &packet->aqueduct_size); @@ -1832,6 +1849,15 @@ dio_get_uint8(&din, &packet->style_count); dio_get_tech_list(&din, packet->rtech.partisan_req); + + for (i = 0; i < MAX_NUM_TEAMS; i++) { + if (has_capability("team", pc->capability)) { + dio_get_string(&din, packet->team_name[i], + sizeof(packet->team_name[i])); + } else { + packet->team_name[i][0] = '\0'; + } + } RECEIVE_PACKET_END(packet); } Index: common/packets.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/packets.h,v retrieving revision 1.126 diff -u -r1.126 packets.h --- common/packets.h 2002/10/19 04:02:07 1.126 +++ common/packets.h 2002/10/30 18:08:54 @@ -460,6 +460,7 @@ int playerno; char name[MAX_LEN_NAME]; bool is_male; + int team; int government; int embassy; int city_style; @@ -573,6 +574,7 @@ int nation_count; int playable_nation_count; int style_count; + char team_name[MAX_NUM_TEAMS][MAX_LEN_NAME]; }; /********************************************************* Index: common/player.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/player.c,v retrieving revision 1.100 diff -u -r1.100 player.c --- common/player.c 2002/09/23 22:47:10 1.100 +++ common/player.c 2002/10/30 18:08:55 @@ -71,6 +71,7 @@ plr->is_male = TRUE; plr->government=game.default_government; plr->nation=MAX_NUM_NATIONS; + plr->team = TEAM_NONE; plr->capital = FALSE; unit_list_init(&plr->units); city_list_init(&plr->cities); Index: common/player.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/player.h,v retrieving revision 1.82 diff -u -r1.82 player.h --- common/player.h 2002/09/23 22:47:10 1.82 +++ common/player.h 2002/10/30 18:08:55 @@ -160,6 +160,7 @@ bool is_male; int government; Nation_Type_id nation; + Team_Type_id team; bool turn_done; int nturns_idle; bool is_alive; Index: server/gamelog.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/gamelog.c,v retrieving revision 1.20 diff -u -r1.20 gamelog.c --- server/gamelog.c 2002/09/29 18:33:12 1.20 +++ server/gamelog.c 2002/10/30 18:08:55 @@ -67,16 +67,20 @@ va_start(args, message); my_vsnprintf(buf, sizeof(buf), message, args); - if (level==GAMELOG_MAP){ - if (buf[0] == '(') /* KLUGE!! FIXME: remove when we fix the gamelog format --jjm */ + if (level == GAMELOG_MAP) { + if (buf[0] == '(') { + /* KLUGE!! FIXME: remove when we fix the gamelog format --jjm */ fprintf(fs, "%i %s\n", game.year, buf); - else + } else { fprintf(fs,"%s\n", buf); - } else if (level == GAMELOG_EOT){ + } + } else if (level == GAMELOG_EOT) { fprintf(fs,"*** %s\n", buf); - } else if (level == GAMELOG_RANK){ + } else if (level == GAMELOG_RANK) { fprintf(fs,"RANK %s\n", buf); - } else if (level == GAMELOG_STATUS){ + } else if (level == GAMELOG_TEAM) { + fprintf(fs,"%s\n", buf); + } else if (level == GAMELOG_STATUS) { fprintf(fs, "STATUS %s\n", buf); } else { fprintf(fs, "%i %s\n", game.year,buf); @@ -134,6 +138,38 @@ 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; + } + count++; + } players_iterate_end; + + /* average them */ + team_score = floor(team_score / team_members); + team_size = floor(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; qsort(size, count, sizeof(struct player_score_entry), secompare1); buffer[0] = 0; Index: server/gamelog.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/gamelog.h,v retrieving revision 1.7 diff -u -r1.7 gamelog.h --- server/gamelog.h 2002/10/09 20:54:20 1.7 +++ server/gamelog.h 2002/10/30 18:08:55 @@ -30,6 +30,7 @@ #define GAMELOG_REVOLT 11 #define GAMELOG_GENO 12 #define GAMELOG_TREATY 13 +#define GAMELOG_TEAM 15 #define GAMELOG_STATUS 16 #define GAMELOG_RANK 17 #define GAMELOG_LAST 18 Index: server/handchat.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/handchat.c,v retrieving revision 1.21 diff -u -r1.21 handchat.c --- server/handchat.c 2002/06/12 07:24:48 1.21 +++ server/handchat.c 2002/10/30 18:08:56 @@ -133,7 +133,8 @@ 2. Otherwise work out whether it is directed to a single player, or to a single connection, and send there. (For a player, send to all clients connected as that player, in multi-connect case); - 3. Else send to all connections (game.est_connections). + 3. Or it may be intended for all allied players. + 4. Else send to all connections (game.est_connections). In case 2, there can sometimes be ambiguity between player and connection names. By default this tries to match player name first, @@ -149,8 +150,8 @@ avoiding sending both original and echo if sender is in destination set. **************************************************************************/ -void handle_chat_msg(struct connection *pconn, - struct packet_generic_message *packet) +void handle_chat_msg(struct connection *pconn, + struct packet_generic_message *packet) { struct packet_generic_message genmsg; char sender_name[MAX_LEN_CHAT_NAME]; @@ -177,6 +178,28 @@ if (packet->message[0] == SERVER_COMMAND_PREFIX) { /* pass it to the command parser, which will chop the prefix off */ handle_stdin_input(pconn, packet->message); + return; + } + + /* Send to allies command */ + if (packet->message[0] == ALLIESCHAT_COMMAND_PREFIX) { + char sender_name[MAX_LEN_CHAT_NAME]; + struct packet_generic_message genmsg; + + genmsg.x = genmsg.y = genmsg.event = -1; + packet->message[0] = ' '; /* replace command prefix */ + form_chat_name(pconn, sender_name, sizeof(sender_name)); + my_snprintf(genmsg.message, sizeof(genmsg.message), + _("%s to allies: %s"), sender_name, + skip_leading_spaces(packet->message)); + players_iterate(aplayer) { + if (!pplayers_allied(pconn->player, aplayer)) { + continue; + } + conn_list_iterate(aplayer->connections, dest_conn) { + send_packet_generic_message(dest_conn, PACKET_CHAT_MSG, &genmsg); + } conn_list_iterate_end; + } players_iterate_end; return; } Index: server/handchat.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/handchat.h,v retrieving revision 1.3 diff -u -r1.3 handchat.h --- server/handchat.h 2000/08/07 13:08:50 1.3 +++ server/handchat.h 2002/10/30 18:08:56 @@ -16,7 +16,9 @@ struct connection; struct packet_generic_message; -void handle_chat_msg(struct connection *pconn, - struct packet_generic_message *packet); +#define ALLIESCHAT_COMMAND_PREFIX '.' + +void handle_chat_msg(struct connection *pconn, + struct packet_generic_message *packet); #endif /* FC__HANDCHAT_H */ Index: server/maphand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/maphand.c,v retrieving revision 1.108 diff -u -r1.108 maphand.c --- server/maphand.c 2002/09/28 01:36:24 1.108 +++ server/maphand.c 2002/10/30 18:08:56 @@ -20,6 +20,7 @@ #include "fcintl.h" #include "game.h" #include "log.h" +#include "nation.h" #include "map.h" #include "mem.h" #include "packets.h" @@ -1247,6 +1248,11 @@ pplayer2 = get_player(packet->value); if (pplayer == pplayer2 || !pplayer2->is_alive || !gives_shared_vision(pplayer, pplayer2)) { + return; + } + + /* Do not allow team mates to backstab */ + if (pplayer->team != TEAM_NONE && pplayer->team == pplayer2->team) { return; } Index: server/plrhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v retrieving revision 1.247 diff -u -r1.247 plrhand.c --- server/plrhand.c 2002/10/28 16:49:31 1.247 +++ server/plrhand.c 2002/10/30 18:08:56 @@ -811,6 +811,11 @@ if (pplayer == pplayer2) return; + /* can't break a pact with a team member */ + if (pplayer->team != TEAM_NONE && pplayer->team == pplayer2->team) { + return; + } + /* check what the new status will be, and what will happen to our reputation */ switch(old_type) { @@ -1112,6 +1117,7 @@ sz_strlcpy(packet->name, plr->name); packet->nation=plr->nation; packet->is_male=plr->is_male; + packet->team = plr->team; packet->city_style=plr->city_style; packet->is_alive=plr->is_alive; Index: server/ruleset.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v retrieving revision 1.120 diff -u -r1.120 ruleset.c --- server/ruleset.c 2002/09/27 12:32:46 1.120 +++ server/ruleset.c 2002/10/30 18:08:56 @@ -1746,6 +1746,10 @@ 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/savegame.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v retrieving revision 1.93 diff -u -r1.93 savegame.c --- server/savegame.c 2002/10/28 17:06:12 1.93 +++ server/savegame.c 2002/10/30 18:08:56 @@ -577,6 +577,16 @@ sz_strlcpy(plr->username, secfile_lookup_str_default(file, "", "player%d.username", plrno)); plr->nation=secfile_lookup_int(file, "player%d.race", plrno); + + /* not all players have teams */ + if (section_file_lookup(file, "player%d.team", plrno)) { + char tmp[MAX_LEN_NAME]; + sz_strlcpy(tmp, secfile_lookup_str(file, "player%d.team", plrno)); + team_add_player(plr, tmp); + plr->team = team_find_by_name(tmp); + } else { + plr->team = TEAM_NONE; + } if (is_barbarian(plr)) { plr->nation=game.nation_count-1; } @@ -1221,6 +1231,10 @@ secfile_insert_str(file, plr->name, "player%d.name", plrno); secfile_insert_str(file, plr->username, "player%d.username", plrno); secfile_insert_int(file, plr->nation, "player%d.race", plrno); + if (plr->team != TEAM_NONE) { + secfile_insert_str(file, (char *) team_get_by_id(plr->team)->name, + "player%d.team", plrno); + } secfile_insert_int(file, plr->government, "player%d.government", plrno); secfile_insert_int(file, plr->embassy, "player%d.embassy", plrno); Index: server/srv_main.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v retrieving revision 1.98 diff -u -r1.98 srv_main.c --- server/srv_main.c 2002/10/18 09:58:49 1.98 +++ server/srv_main.c 2002/10/30 18:08:56 @@ -176,6 +176,9 @@ srvarg.extra_metaserver_info[0] = '\0'; + /* initialize teams */ + team_init(); + /* mark as initialized */ has_been_srv_init = TRUE; @@ -184,32 +187,84 @@ } /************************************************************************** -... + Returns TRUE if any one game end condition is fulfilled, FALSE otherwise **************************************************************************/ static bool is_game_over(void) { - int barbs = 0; - int alive = 0; - - if (game.year > game.end_year) + int barbs = 0, alive = 0; + bool all_allied; + struct player *victor; + + /* quit if we are past the year limit */ + if (game.year > game.end_year) { + notify_conn(&game.est_connections, + "Game ended in a draw as end year exceeded"); + gamelog(GAMELOG_NORMAL, _("Game ended in a draw as end year exceeded")); return TRUE; + } + /* count barbarians */ players_iterate(pplayer) { if (is_barbarian(pplayer)) { barbs++; } } players_iterate_end; - if (game.nplayers == (barbs + 1)) + /* the game does not quit if we are playing solo */ + if (game.nplayers == (barbs + 1)) { return FALSE; + } + /* count the living */ players_iterate(pplayer) { if (pplayer->is_alive && !is_barbarian(pplayer)) { alive++; + victor = pplayer; + } + } players_iterate_end; + + /* quit if we have team victory */ + team_iterate(pteam) { + if (team_count_members_alive(pteam->id) == alive) { + notify_conn(&game.est_connections, + "Team victory to %s", pteam->name); + gamelog(GAMELOG_NORMAL, _("Team victory to %s"), pteam->name); + gamelog(GAMELOG_TEAM, "TEAMVICTORY %s", pteam->name); + return TRUE; + } + } team_iterate_end; + + /* quit if only one player is left alive */ + if (alive <= 1) { + notify_conn(&game.est_connections, + "Game ended in victory for %s", victor->name); + gamelog(GAMELOG_NORMAL, _("Game ended in victory for %s"), + victor->name); + gamelog(GAMELOG_TEAM, "SINGLEWINNER %s", victor->name); + return TRUE; + } + + /* quit if all players are allied to each other */ + all_allied = TRUE; + players_iterate(pplayer) { + players_iterate(aplayer) { + if (!pplayers_allied(pplayer, aplayer)) { + all_allied = FALSE; + break; + } + } players_iterate_end; + if (!all_allied) { + break; } } players_iterate_end; + if (all_allied) { + notify_conn(&game.est_connections, _("Game ended in allied victory")); + gamelog(GAMELOG_NORMAL, _("Game ended in allied victory")); + gamelog(GAMELOG_TEAM, "ALLIEDVICTORY"); + return TRUE; + } - return (alive <= 1); + return FALSE; } /************************************************************************** @@ -1976,6 +2031,20 @@ if(map.num_start_positions==0) { create_start_positions(); } + } + + /* Set up alliances based on team selections */ + if (game.is_new_game && game.team_count > 0) { + players_iterate(pplayer) { + players_iterate(pdest) { + if ((pplayer->team == pdest->team) + && (pplayer->player_no != pdest->player_no)) { + pplayer->diplstates[pdest->player_no].type = DS_ALLIANCE; + give_shared_vision(pplayer, pdest); + pplayer->embassy |= (1 << pdest->player_no); + } + } players_iterate_end; + } players_iterate_end; } initialize_move_costs(); /* this may be the wrong place to do this */ Index: server/stdinhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v retrieving revision 1.257 diff -u -r1.257 stdinhand.c --- server/stdinhand.c 2002/10/16 18:40:54 1.257 +++ server/stdinhand.c 2002/10/30 18:08:56 @@ -946,6 +946,7 @@ /* mostly non-harmful: */ CMD_SET, + CMD_TEAM, CMD_FIX, CMD_UNFIX, CMD_RULESETDIR, @@ -1072,6 +1073,15 @@ N_("set "), N_("Set server option."), NULL }, + {"team", ALLOW_CTRL, + N_("team [team]"), + N_("Change, add or remove a player's team affiliation."), + N_("Sets a player as member of a team. If no team specified, the " + "player is set teamless. Use \"\" if names contain whitespace. " + "A team is a group of players that start out allied, with shared " + "vision and embassies, and fight together to achieve team victory " + "with averaged individual scores.") + }, {"fix", ALLOW_CTRL, N_("fix "), N_("Make server option unchangeable during game."), NULL @@ -2658,6 +2668,59 @@ /****************************************************************** ... ******************************************************************/ +static void team_command(struct connection *caller, char *str) +{ + struct player *pplayer; + enum m_pre_result match_result; + char buf[MAX_LEN_CONSOLE_LINE]; + char *arg[2]; + int ntokens = 0; + + if (server_state != PRE_GAME_STATE) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, + _("Cannot change teams once game has begun.")); + return; + } + + if (str != NULL || strlen(str) > 0) { + sz_strlcpy(buf, str); + ntokens = get_tokens(buf, arg, 2, TOKEN_DELIMITERS); + } + if (ntokens > 2 || ntokens < 1) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, + _("Undefined argument. Usage: team [team].")); + return; + } + + pplayer = find_player_by_name_prefix(arg[0], &match_result); + if (pplayer == NULL) { + cmd_reply_no_such_player(CMD_TEAM, caller, arg[0], match_result); + return; + } + + if (pplayer->team != TEAM_NONE) { + team_remove_player(pplayer); + } + + if (ntokens == 1) { + /* Remove from team */ + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s is made teamless."), + pplayer->name); + return; + } + + if (get_sane_name(arg[1]) == NULL) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, _("Bad team name.")); + return; + } + team_add_player(pplayer, arg[1]); + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s."), + pplayer->name, arg[1]); +} + +/****************************************************************** + ... +******************************************************************/ static void set_command(struct connection *caller, char *str) { char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d; @@ -3152,12 +3215,15 @@ case CMD_SET: set_command(caller,arg); break; - case CMD_FIX: - fix_command(caller,arg, CMD_FIX); - break; - case CMD_UNFIX: - fix_command(caller, arg, CMD_UNFIX); - break; + case CMD_TEAM: + team_command(caller, arg); + break; + case CMD_FIX: + fix_command(caller,arg, CMD_FIX); + break; + case CMD_UNFIX: + fix_command(caller, arg, CMD_UNFIX); + break; case CMD_RULESETDIR: set_rulesetdir(caller, arg); break; @@ -3592,6 +3658,10 @@ if (!game.is_new_game) { cat_snprintf(buf2, sizeof(buf2), _(", nation %s"), 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); } my_snprintf(buf, sizeof(buf), "%s (%s)", pplayer->name, buf2);