diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/game.h freeciv-phased/common/game.h --- freeciv/common/game.h Tue Mar 26 01:34:22 2002 +++ freeciv-phased/common/game.h Fri Apr 5 22:58:14 2002 @@ -145,6 +145,8 @@ int playable_nation_count; int styles_count; + int team_count; + int watchtower_extra_vision; int watchtower_vision; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/nation.c freeciv-phased/common/nation.c --- freeciv/common/nation.c Tue Mar 26 01:34:23 2002 +++ freeciv-phased/common/nation.c Fri Apr 5 22:58:14 2002 @@ -12,7 +12,7 @@ ***********************************************************************/ /********************************************************************** - Functions for handling the nations. + Functions for handling the nations and teams. ***********************************************************************/ #include @@ -26,10 +26,16 @@ #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_type teams[MAX_NUM_TEAMS]; + +/* used for next_team(..) only */ +static int team_counter = 0; /*************************************************************** Returns 1 if nid is a valid nation id, else 0. @@ -151,6 +157,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")) { @@ -167,6 +176,9 @@ nations = (struct nation_type *)fc_calloc(num, sizeof(struct nation_type)); } +/*************************************************************** + ... +***************************************************************/ void free_nations(int num) { int i, j; @@ -191,3 +203,156 @@ } return nations[nation].city_style; } + +/*************************************************************** + Returns the id of a team given its name, or -1 if not found +***************************************************************/ +Team_Type_id find_team_by_name(const char *name) +{ + int i; + + assert(name != NULL); + for(i = 0; i < game.team_count; i++) + if(mystrcasecmp(name, get_team_name(i)) == 0) + return i; + + return TEAM_NONE; +} + +/*************************************************************** + Returns name of a team given its id +***************************************************************/ +char *get_team_name(Team_Type_id team) +{ + assert((team < MAX_NUM_TEAMS) && (team > TEAM_NONE)); + if (game.team_count < team) { + return NULL; + } + + return teams[team].name; +} + +/*************************************************************** + Returns pointer to a team given a player, returns null if none +***************************************************************/ +struct team_type *get_team_by_plr(struct player *plr) +{ + assert((plr != NULL) && (plr->team < game.team_count)); + if ((game.team_count == 0) || (plr->team == TEAM_NONE)) { + return NULL; + } + return &teams[plr->team]; +} + +/*************************************************************** + Returns point to a team given its id +***************************************************************/ +struct team_type *get_team_by_idx(Team_Type_id team) +{ + assert((team < game.team_count) && (team > TEAM_NONE)); + return &teams[team]; +} + +/*************************************************************** + Try to distribute new players evenly among the teams. The + present algorithm is not optimal, but functional. +***************************************************************/ +int next_team() +{ + if (game.team_count == 0) return TEAM_NONE; + team_counter = (team_counter + 1) % game.team_count; + return team_counter; +} + +/*************************************************************** + Count living members of given team +***************************************************************/ +int count_members_of_team(struct team_type *team) +{ + int count = 0; + + assert((team != NULL) && (team->team_no < game.team_count) + && (team->team_no > TEAM_NONE)); + players_iterate(pplayer) { + if ((pplayer->is_alive) && (pplayer->team == team->team_no)) 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. Returns true if + action successful, false if teams list full. +***************************************************************/ +bool player_set_to_team(struct player *plr, const char *name) +{ + Team_Type_id team_id, i; + + assert((plr != NULL) && (name != NULL)); + + /* find or create team */ + team_id = find_team_by_name(name); + if (team_id == TEAM_NONE) { + /* see if we have another team available */ + for (i = 0; i < MAX_NUM_TEAMS; i++) { + if (teams[i].team_no == TEAM_NONE) { + team_id = i; + break; + } + } + /* check if too many teams */ + if (team_id == TEAM_NONE) { + return FALSE; + } + /* add another team */ + teams[team_id].team_no = team_id; + sz_strlcpy(teams[team_id].name, name); + plr->team = team_id; + game.team_count++; + return TRUE; + } else { + plr->team = team_id; + return TRUE; + } +} + +/*************************************************************** + Removes a player from a team, and removes the team if empty of + players +***************************************************************/ +void player_remove_from_team(struct player *plr) +{ + Team_Type_id i; + int count = 0; + + assert((plr != NULL) && (plr->team < MAX_NUM_TEAMS)); + + if (plr->team == TEAM_NONE) { + return; + } + /* anyone else using my team? */ + for (i = 0; i < MAX_NUM_TEAMS; i++) { + if ((teams[i].team_no == plr->team) + && (plr->team != i)) count++; + } + /* no other team members left? remove team */ + if (count == 0) { + teams[plr->team].team_no = TEAM_NONE; + game.team_count--; + } + plr->team = TEAM_NONE; +} + +/*************************************************************** + Initializes team structure +***************************************************************/ +void alloc_teams() +{ + Team_Type_id i; + + for (i = 0; i < MAX_NUM_TEAMS; i++) { + /* mark as unused */ + teams[i].team_no = TEAM_NONE; + } +} + diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/nation.h freeciv-phased/common/nation.h --- freeciv/common/nation.h Tue Mar 26 01:34:23 2002 +++ freeciv-phased/common/nation.h Fri Apr 5 22:58:14 2002 @@ -19,8 +19,11 @@ #define MAX_NUM_TECH_GOALS 10 #define MAX_NUM_NATIONS 63 #define MAX_NUM_LEADERS 16 +#define MAX_NUM_TEAMS 12 +#define TEAM_NONE -1 typedef int Nation_Type_id; +typedef int Team_Type_id; struct Sprite; /* opaque; client-gui specific */ struct player; @@ -72,6 +75,11 @@ } goals; }; +struct team_type { + char name[MAX_LEN_NAME]; + Team_Type_id team_no; +}; + Nation_Type_id find_nation_by_name(char *name); char *get_nation_name(Nation_Type_id nation); char *get_nation_name_plural(Nation_Type_id nation); @@ -83,5 +91,26 @@ void alloc_nations(int num); void free_nations(int num); int get_nation_city_style(Nation_Type_id nation); + +Team_Type_id find_team_by_name(const char *name); +char *get_team_name(Team_Type_id team); +struct team_type *get_team_by_plr(struct player *plr); +struct team_type *get_team_by_idx(Team_Type_id team); +int next_team(); +bool player_set_to_team(struct player *plr, const char *name); +void player_remove_from_team(struct player *plr); +int count_members_of_team(struct team_type *team); +void alloc_teams(); + +#define teams_iterate(PI_team) \ +{ \ + struct team_type *PI_team; \ + Team_Type_id PI_p_itr; \ + for (PI_p_itr = 0; PI_p_itr < game.team_count; PI_p_itr++) { \ + PI_team = get_team_by_idx(PI_p_itr); + +#define teams_iterate_end \ + } \ +} #endif /* FC__NATION_H */ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/player.c freeciv-phased/common/player.c --- freeciv/common/player.c Fri Apr 5 16:12:27 2002 +++ freeciv-phased/common/player.c Fri Apr 5 22:58:14 2002 @@ -70,6 +70,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); diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/player.h freeciv-phased/common/player.h --- freeciv/common/player.h Tue Mar 26 01:34:23 2002 +++ freeciv-phased/common/player.h Fri Apr 5 22:58:14 2002 @@ -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; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/gamelog.c freeciv-phased/server/gamelog.c --- freeciv/server/gamelog.c Tue Mar 26 01:34:28 2002 +++ freeciv-phased/server/gamelog.c Fri Apr 5 22:58:14 2002 @@ -76,6 +76,8 @@ fprintf(fs,"*** %s\n",buf); } else if (level==GAMELOG_RANK){ fprintf(fs,"RANK %s\n",buf); + } else if (level == GAMELOG_TEAM){ + fprintf(fs,"TEAMVICTORY %s\n", buf); } else { fprintf(fs,"%i %s\n", game.year,buf); } @@ -132,6 +134,35 @@ count++; } } players_iterate_end; + + /* average game scores for teams */ + teams_iterate(team) { + int numplayers = 0; + int count = 0; + int teamscore = 0; + int teamsize = 0; + /* sum team score */ + players_iterate(pplayer) { + if (pplayer->team == team->team_no) { + numplayers++; + teamscore += rank[count].value; + teamsize += size[count].value; + } + count++; + } players_iterate_end; + /* average them */ + teamscore = floor(teamscore / numplayers); + teamsize = floor(teamsize / numplayers); + /* set scores to average */ + count = 0; + players_iterate(pplayer) { + if (pplayer->team == team->team_no) { + rank[count].value = teamscore; + size[count].value = teamsize; + } + count++; + } players_iterate_end; + } teams_iterate_end; qsort(size, count, sizeof(struct player_score_entry), secompare1); buffer[0]=0; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/gamelog.h freeciv-phased/server/gamelog.h --- freeciv/server/gamelog.h Tue Mar 26 01:34:28 2002 +++ freeciv-phased/server/gamelog.h Fri Apr 5 22:58:14 2002 @@ -30,6 +30,7 @@ #define GAMELOG_REVOLT 11 #define GAMELOG_GENO 12 #define GAMELOG_TREATY 13 +#define GAMELOG_TEAM 16 #define GAMELOG_RANK 17 #define GAMELOG_LAST 18 #define GAMELOG_EOT 19 diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/savegame.c freeciv-phased/server/savegame.c --- freeciv/server/savegame.c Fri Apr 5 16:12:30 2002 +++ freeciv-phased/server/savegame.c Fri Apr 5 22:58:14 2002 @@ -565,6 +565,15 @@ 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)); + player_set_to_team(plr, tmp); + plr->team = find_team_by_name(tmp); + } else { + plr->team = TEAM_NONE; + } if (is_barbarian(plr)) { plr->nation=game.nation_count-1; } @@ -1200,6 +1209,9 @@ 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 (get_team_by_plr(plr) != NULL) { + secfile_insert_str(file, get_team_name(plr->team), "player%d.team", plrno); + } secfile_insert_int(file, plr->government, "player%d.government", plrno); secfile_insert_int(file, plr->embassy, "player%d.embassy", plrno); diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/srv_main.c freeciv-phased/server/srv_main.c --- freeciv/server/srv_main.c Fri Apr 5 16:12:30 2002 +++ freeciv-phased/server/srv_main.c Fri Apr 5 23:23:35 2002 @@ -175,6 +175,9 @@ *(srvarg.metaserver_servername) = '\0'; + /* initialize teams */ + alloc_teams(); + /* mark as initialized */ has_been_srv_init = TRUE; @@ -183,32 +186,78 @@ } /************************************************************************** -... + 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 year limit exceeded"); + gamelog(GAMELOG_NORMAL, _("Game ended in a draw as year limit 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 */ + teams_iterate(team) { + if (count_members_of_team(team) == alive) { + notify_conn(&game.est_connections, + "Team victory to %s", team->name); + gamelog(GAMELOG_NORMAL, _("Team victory to %s"), team->name); + gamelog(GAMELOG_TEAM, "%s", team->name); + return TRUE; } + } teams_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); + 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; + } + } players_iterate_end; } players_iterate_end; + if (all_allied) { + notify_conn(&game.est_connections, _("Game ended in allied victory")); + gamelog(GAMELOG_NORMAL, _("Game ended in allied victory")); + return TRUE; + } - return (alive <= 1); + return FALSE; } /************************************************************************** @@ -1120,6 +1169,13 @@ game.nplayers++; + /* if teams exist, put player on a team */ + if (game.team_count > 0) { + pplayer->team = next_team(); + notify_conn(&game.est_connections, _("%s added to team %s"), + pplayer->name, get_team_name(pplayer->team)); + } + introduce_game_to_connection(pconn); (void) send_server_info_to_metaserver(TRUE, FALSE); } @@ -1936,6 +1992,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 */ Binary files freeciv/server/stMuVF4F and freeciv-phased/server/stMuVF4F differ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/stdinhand.c freeciv-phased/server/stdinhand.c --- freeciv/server/stdinhand.c Fri Apr 5 16:12:30 2002 +++ freeciv-phased/server/stdinhand.c Fri Apr 5 23:11:07 2002 @@ -882,6 +882,7 @@ /* mostly non-harmful: */ CMD_SET, + CMD_TEAM, CMD_RULESETDIR, CMD_RENAME, CMD_METAINFO, @@ -1004,6 +1005,12 @@ N_("set "), N_("Set server options."), NULL }, + {"team", ALLOW_CTRL, + N_("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. Team names cannot contain whitespace.") + }, {"rulesetdir", ALLOW_CTRL, N_("rulesetdir "), N_("Choose new ruleset directory or modpack. Calling this\n " @@ -1655,7 +1662,7 @@ { struct player *pplayer; PlayerNameStatus PNameStatus; - + if (server_state!=PRE_GAME_STATE) { cmd_reply(CMD_CREATE, caller, C_SYNTAX, @@ -2604,6 +2611,55 @@ /****************************************************************** ... ******************************************************************/ +static void team_command(struct connection *caller, char *str) +{ + char *arg1, *arg2; + struct player *pplayer; + + 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)) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, + _("Undefined argument. Usage: team .")); + return; + } + /* if all of str is a player, don't check for teams */ + pplayer = find_player_by_name(str); + if (pplayer == NULL) { + /* all but last word is player name */ + arg1 = str; + /* find team name */ + arg2 = strrchr(str, ' '); + if (arg2 != NULL) *arg2++ = '\0'; + /* what is left is player name */ + pplayer = find_player_by_name(arg1); + } + if (pplayer == NULL) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, + _("Invalid player")); + return; + } + if (arg2 == NULL) { + player_remove_from_team(pplayer); + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s is made teamless"), + pplayer->name); + return; + } + if (player_set_to_team(pplayer, arg2)) { + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s"), + pplayer->name, arg2); + } else { + cmd_reply(CMD_TEAM, caller, C_FAIL, _("Sorry! Too many teams!")); + } +} + +/****************************************************************** + ... +******************************************************************/ static void set_command(struct connection *caller, char *str) { char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d; @@ -2936,6 +2992,9 @@ case CMD_SET: set_command(caller,arg); break; + case CMD_TEAM: + team_command(caller, arg); + break; case CMD_RULESETDIR: set_rulesetdir(caller, arg); break; @@ -3364,6 +3423,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"), + get_team_name(pplayer->team)); } my_snprintf(buf, sizeof(buf), "%s (%s)", pplayer->name, buf2);