diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/Makefile.am freeciv-phased/common/Makefile.am --- freeciv/common/Makefile.am Wed Jan 9 21:48:08 2002 +++ freeciv-phased/common/Makefile.am Tue Feb 26 22:16:40 2002 @@ -48,6 +48,8 @@ map.h \ nation.c \ nation.h \ + team.c \ + team.h \ netintf.c \ netintf.h \ packets.c \ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/game.h freeciv-phased/common/game.h --- freeciv/common/game.h Tue Feb 26 16:06:07 2002 +++ freeciv-phased/common/game.h Sun Mar 3 01:47:13 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 Sun Feb 24 11:50:35 2002 +++ freeciv-phased/common/nation.c Tue Feb 26 22:16:40 2002 @@ -151,6 +151,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 +170,9 @@ nations = (struct nation_type *)fc_calloc(num, sizeof(struct nation_type)); } +/*************************************************************** + ... +***************************************************************/ void free_nations(int num) { int i, j; diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/player.c freeciv-phased/common/player.c --- freeciv/common/player.c Tue Feb 26 19:33:24 2002 +++ freeciv-phased/common/player.c Sun Mar 3 01:47:21 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 Feb 26 19:33:24 2002 +++ freeciv-phased/common/player.h Tue Feb 26 22:16:40 2002 @@ -17,6 +17,7 @@ #include "connection.h" /* struct conn_list */ #include "improvement.h" /* Impr_Status */ #include "nation.h" +#include "team.h" #include "shared.h" #include "spaceship.h" #include "tech.h" @@ -155,6 +156,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/common/team.c freeciv-phased/common/team.c --- freeciv/common/team.c Thu Jan 1 00:00:00 1970 +++ freeciv-phased/common/team.c Sun Mar 3 02:04:21 2002 @@ -0,0 +1,257 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +/********************************************************************** + Functions for handling teams +***********************************************************************/ + +#include +#include +#include + +#include "game.h" +#include "government.h" +#include "log.h" +#include "mem.h" +#include "player.h" +#include "support.h" +#include "tech.h" + +#include "team.h" + +static struct team_type *teams = NULL; + +/* used for next_team(..) only */ +static int team_counter = 0; + +/*************************************************************** + Returns 1 if nid is a valid team id, else 0. + If returning 0, prints log message with given loglevel + quoting given func name, explaining problem. +***************************************************************/ +static bool bounds_check_team_id(Team_Type_id nid, int loglevel, + const char *func_name) +{ + if (game.team_count==0) { + freelog(loglevel, "%s before teams setup", func_name); + return FALSE; + } + if (nid < 0 || nid >= game.team_count) { + freelog(loglevel, "Bad team id %d (count %d) in %s", + nid, game.team_count, func_name); + return FALSE; + } + return TRUE; +} + +/*************************************************************** + 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; + + /* if no teams, punt */ + if (game.team_count == 0) { + return -1; + } + + for(i = 0; i < game.team_count; i++) + if(mystrcasecmp(name, get_team_name (i)) == 0) + return i; + + return -1; +} + +/*************************************************************** + Returns name of a team given its id +***************************************************************/ +char *get_team_name(Team_Type_id team) +{ + /* if no teams, punt */ + if (game.team_count == 0) { + return NULL; + } + + if (!bounds_check_team_id(team, LOG_ERROR, "get_team_name")) { + return ""; + } + 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); + /* if no teams, punt */ + if (game.team_count == 0) { + return NULL; + } + /* if no team for this player, punt */ + if (plr->team == TEAM_NONE) { + return NULL; + } + if (!bounds_check_team_id(plr->team, LOG_FATAL, "get_team_by_plr")) { + exit(EXIT_FAILURE); + } + return &teams[plr->team]; +} + +/*************************************************************** + Returns point to a team given its id +***************************************************************/ +struct team_type *get_team_by_idx(Team_Type_id team) +{ + /* if invalid team index, punt */ + if (team >= game.team_count) { + return NULL; + } + if (!bounds_check_team_id(team, LOG_FATAL, "get_team_by_idx")) { + exit(EXIT_FAILURE); + } + return &teams[team]; +} + +/*************************************************************** + (re)allocate space for the given number of teams. You can + never reduce the number of teams. +***************************************************************/ +void alloc_teams(int num) +{ + int a,i; + struct team_type *teams_new = NULL; + + assert((num > 0) && (num >= game.team_count)); + + teams_new = (struct team_type *)fc_calloc(num, sizeof(struct team_type)); + /* set up team_no and sane values */ + for (i = 0; i < num; i++) { + teams_new[i].team_no = i; + teams_new[i].name[0] = '\0'; + teams_new[i].nation[0] = MAX_NUM_NATIONS; + teams_new[i].init_techs[0] = A_LAST; + } + /* copy old teams */ + if (teams) { + for (a = 0; a < game.team_count; a++) { + sz_strlcpy(teams_new[a].name, teams[a].name); + for (i = 0; i < MAX_NUM_NATIONS; i++) { + teams_new[a].nation[i] = teams[a].nation[i]; + } + for (i = 0; i < MAX_NUM_TECH_LIST; i++) { + teams_new[a].init_techs[i] = teams[a].init_techs[i]; + } + } /* for */ + free(teams); + } /* if */ + teams = teams_new; + game.team_count = num; +} + +/*************************************************************** + Free teams info +***************************************************************/ +void free_teams(int num) +{ + if (!teams) return; + free(teams); +} + +/*************************************************************** + Try to distribute new players evenly among the teams. The + present algorithm is truly bad, but functional. + TODO: fill up teams with less players instead - Per +***************************************************************/ +int next_team() +{ + if (game.team_count == 0) return TEAM_NONE; + team_counter = (team_counter + 1) % game.team_count; + return team_counter; +} + +/*************************************************************** + Check if given nation is available to given team +***************************************************************/ +bool team_can_use_nation(struct team_type *team, + Nation_Type_id nation) +{ + int i; + + /* if we're not in a team, punt */ + if (team == NULL) return TRUE; + + /* check if a nation is available to our team */ + for (i = 0; i < MAX_NUM_NATIONS; i++) { + /* if we don't have a nations list, then default to no limits */ + if (team->nation[0] == MAX_NUM_NATIONS) { + return TRUE; + } + /* yes */ + if (team->nation[i] == nation) { + return TRUE; + } + /* well obviuosly not */ + if (team->nation[i] == MAX_NUM_NATIONS) { + return FALSE; + } + } + assert(0); /* a nation array wasn't terminated properly */ + return FALSE; +} + +/*************************************************************** + Count living members of given team +***************************************************************/ +int count_members_of_team(struct team_type *team) +{ + int count = 0; + + assert(team && team->team_no >= 0); + 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 a new + team was created, false otherwise. +***************************************************************/ +bool player_set_to_team(struct player *plr, const char *name) +{ + Team_Type_id team_id; + + assert(plr && name); + + /* find or create team */ + team_id = find_team_by_name(name); + if (team_id == -1) { + /* we do this mostly to catch runaway code or rouge players */ + if (game.team_count == MAX_NUM_TEAMS) { + assert(1); + return FALSE; + } + /* add another team */ + team_id = game.team_count; + alloc_teams(game.team_count + 1); + sz_strlcpy(teams[team_id].name, name); + plr->team = team_id; + return TRUE; + } else { + plr->team = team_id; + return FALSE; + } +} diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/common/team.h freeciv-phased/common/team.h --- freeciv/common/team.h Thu Jan 1 00:00:00 1970 +++ freeciv-phased/common/team.h Sun Mar 3 02:04:57 2002 @@ -0,0 +1,55 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ +#ifndef FC__TEAM_H +#define FC__TEAM_H + +#include "shared.h" /* MAX_LEN_NAME */ +#include "nation.h" /* Nation_Type_id and MAX_NUM_NATIONS */ + +/* each player his own team */ +#define MAX_NUM_TEAMS 32 +#define TEAM_NONE -1 + +typedef int Team_Type_id; + +struct team_type { + char name[MAX_LEN_NAME]; + Nation_Type_id nation[MAX_NUM_NATIONS]; + int init_techs[MAX_NUM_TECH_LIST]; + Team_Type_id team_no; +}; + +#define teams_iterate(PI_team,PI_p_itr) \ +{ \ + struct team_type *PI_team; \ + int 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 \ + } \ +} + +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); +void alloc_teams(int num); +void free_teams(int num); +int next_team(); +bool team_can_use_nation(struct team_type *team, + Nation_Type_id nation); +bool player_set_to_team(struct player *plr, const char *name); +int count_members_of_team(struct team_type *team); + +#endif /* FC__TEAM_H */ diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/plrhand.c freeciv-phased/server/plrhand.c --- freeciv/server/plrhand.c Wed Feb 27 15:19:33 2002 +++ freeciv-phased/server/plrhand.c Sun Mar 3 01:47:21 2002 @@ -16,6 +16,7 @@ #include +#include "game.h" #include "events.h" #include "fcintl.h" #include "government.h" @@ -491,6 +492,7 @@ { int i; struct nation_type *nation = get_nation_by_plr(plr); + struct team_type *team = get_team_by_plr(plr); for (i = 0; i < game.num_tech_types; i++) { set_invention(plr, i, TECH_UNKNOWN); @@ -517,6 +519,18 @@ break; } set_invention(plr, nation->init_techs[i], TECH_KNOWN); + } + + /* + * Give team specific initial techs, if teams activated + */ + if ((game.team_count > 0) && (team != NULL)) { + for (i = 0; i < MAX_NUM_TECH_LIST; i++) { + if (team->init_techs[i] == A_LAST) { + break; + } + set_invention(plr, team->init_techs[i], TECH_KNOWN); + } } update_research(plr); diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/ruleset.c freeciv-phased/server/ruleset.c --- freeciv/server/ruleset.c Wed Feb 27 15:19:33 2002 +++ freeciv-phased/server/ruleset.c Sun Mar 3 01:51:52 2002 @@ -29,6 +29,7 @@ #include "map.h" #include "mem.h" #include "nation.h" +#include "team.h" #include "packets.h" #include "registry.h" #include "support.h" @@ -1759,6 +1760,47 @@ /************************************************************************** ... **************************************************************************/ +static void load_team_names(struct section_file *file) +{ + char **sec; + int i, j; + + section_file_lookup(file, "datafile.description"); /* unused */ + + sec = secfile_get_secnames_prefix(file, "team", &game.team_count); + freelog(LOG_VERBOSE, "There are %d teams defined", game.team_count); + + if (game.team_count == 0) { + freelog(LOG_VERBOSE, "Team mode not activated."); + return; + } else if (game.team_count >= MAX_NUM_NATIONS) { + freelog(LOG_FATAL, "There are more teams than nations! Impossible!"); + exit(EXIT_FAILURE); + } + alloc_teams(game.team_count); + + for( i=0; iname, name); + + /* Check if team name is already defined. */ + for(j = 0; j < i; j++) { + if (0 == strcmp(get_team_name(j), pl->name)) { + freelog(LOG_FATAL, + "Team %s defined twice; in section team%d and section team%d", + pl->name, j, i); + exit(EXIT_FAILURE); + } + } + } + free(sec); +} + +/************************************************************************** + ... +**************************************************************************/ static void load_nation_names(struct section_file *file) { char **sec; @@ -1816,10 +1858,12 @@ struct government *gov; int *res, dim, val, i, j, nval; char temp_name[MAX_LEN_NAME]; - char **cities, **techs, **leaders, **sec; + char **slist, **cities, **techs, **leaders, **sec; const char *filename = secfile_filename(file); enum tile_terrain_type type; + load_team_names(file); + datafile_options = check_ruleset_capabilities(file, "+1.9", filename); sec = secfile_get_secnames_prefix(file, "nation", &nval); @@ -2094,6 +2138,24 @@ #undef BASE_READ_CITY_NAME_LIST free(sec); + + /* read team info */ + sec = secfile_get_secnames_prefix(file, "team", &nval); + teams_iterate(pteam, tcount) { + /* Load team specific initial techs */ + lookup_tech_list(file, sec[tcount], "init_techs", pteam->init_techs, filename); + /* Load a team's nations */ + slist = secfile_lookup_str_vec(file, &nval, "%s.nations", sec[tcount]); + for(j=0; j < MAX_NUM_NATIONS; j++) { + if ((j < nval) && (strcmp(slist[j], "") != 0)) { + pteam->nation[j] = find_nation_by_name(slist[j]); + } else { + pteam->nation[j] = MAX_NUM_NATIONS; + } + } + } teams_iterate_end; + free(sec); + section_file_check_unused(file, filename); section_file_free(file); } @@ -2676,4 +2738,16 @@ send_ruleset_cities(dest); conn_list_do_unbuffer(dest); +} + +/************************************************************************** + Deallocate and clean up. For use when freeciv quits, or in case we + later want to be able to reload rulesets. +**************************************************************************/ +void free_rulesets() +{ + free_nations(game.nation_count); + free_teams(game.team_count); + /* TODO: take a deep look at the other rulesets too, probably lots + of free()s left to call there as well - Per */ } diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/ruleset.h freeciv-phased/server/ruleset.h --- freeciv/server/ruleset.h Mon Aug 13 15:13:36 2001 +++ freeciv-phased/server/ruleset.h Tue Feb 26 22:16:40 2002 @@ -16,6 +16,7 @@ struct conn_list; void load_rulesets(void); +void free_rulesets(void); void send_rulesets(struct conn_list *dest); char *valid_ruleset_filename(char *subdir, char *whichset); diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/srv_main.c freeciv-phased/server/srv_main.c --- freeciv/server/srv_main.c Wed Feb 27 15:19:33 2002 +++ freeciv-phased/server/srv_main.c Sun Mar 3 01:54:23 2002 @@ -183,32 +183,77 @@ } /************************************************************************** -... + 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 there are only barbarians left */ + 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; - return (alive <= 1); + /* quit if we have team victory */ + teams_iterate(team,tcount) { + 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); + 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 FALSE; } /************************************************************************** @@ -997,13 +1042,17 @@ static void send_select_nation(struct player *pplayer) { struct packet_generic_values select_nation; - + struct team_type *team; + select_nation.value1=0; /* assume int is 32 bit, safe */ select_nation.value2=0; + team = get_team_by_plr(pplayer); + /* set bits in mask corresponding to nations already selected by others */ players_iterate(other_player) { - if (other_player->nation != MAX_NUM_NATIONS) { + if ((other_player->nation != MAX_NUM_NATIONS) && + (team_can_use_nation(team, other_player->nation))) { if (other_player->nation < 32) select_nation.value1 |= 1 << other_player->nation; else @@ -1130,6 +1179,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); send_server_info_to_metaserver(TRUE, FALSE); } @@ -1946,6 +2002,20 @@ } } + /* 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 */ generate_minimap(); /* for city_desire; saves a lot of calculations */ @@ -1983,4 +2053,5 @@ } close_connections_and_socket(); + free_rulesets(); } diff -u3NrX /home/perrin/freeciv/diff_ignore freeciv/server/stdinhand.c freeciv-phased/server/stdinhand.c --- freeciv/server/stdinhand.c Wed Feb 27 15:19:33 2002 +++ freeciv-phased/server/stdinhand.c Sun Mar 3 01:54:35 2002 @@ -51,6 +51,7 @@ #include "rulesout.h" #include "sernet.h" #include "srv_main.h" +#include "team.h" #include "advmilitary.h" /* assess_danger_player() */ @@ -1000,6 +1001,7 @@ /* mostly non-harmful: */ CMD_SET, + CMD_TEAM, CMD_RENAME, CMD_METAINFO, CMD_METACONN, @@ -1121,6 +1123,10 @@ N_("set "), N_("Set server options."), NULL }, + {"team", ALLOW_CTRL, + N_("team "), + N_("If team mode activated, change a player's team affiliation."), NULL + }, {"rename", ALLOW_CTRL, NULL, N_("This command is not currently implemented."), NULL @@ -1768,7 +1774,7 @@ { struct player *pplayer; PlayerNameStatus PNameStatus; - + if (server_state!=PRE_GAME_STATE) { cmd_reply(CMD_CREATE, caller, C_SYNTAX, @@ -2667,6 +2673,41 @@ /****************************************************************** ... ******************************************************************/ +static void team_command(struct connection *caller, char *str) +{ + char *arg1, *arg2; + struct player *pplayer; + bool is_new_team_created; + + if (str == NULL) { + cmd_reply(CMD_SET, caller, C_SYNTAX, + _("Undefined argument. Usage: team .")); + return; + } + arg1 = str; + arg2 = strchr(str,' '); + if (arg2 != NULL) *arg2++ = '\0'; + pplayer = find_player_by_name(arg1); + if (pplayer == NULL) { + cmd_reply(CMD_TEAM, caller, C_SYNTAX, + _("Invalid player")); + return; + } + if (arg2 == NULL) { + pplayer->team = TEAM_NONE; + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s made teamless"), arg1); + return; + } + is_new_team_created = player_set_to_team(pplayer, arg2); + if (is_new_team_created) { + cmd_reply(CMD_TEAM, caller, C_OK, _("Team %s created"), arg2); + } + cmd_reply(CMD_TEAM, caller, C_OK, _("Player %s set to team %s"), arg1, arg2); +} + +/****************************************************************** + ... +******************************************************************/ static void set_command(struct connection *caller, char *str) { char command[MAX_LEN_CONSOLE_LINE], arg[MAX_LEN_CONSOLE_LINE], *cptr_s, *cptr_d; @@ -2928,6 +2969,9 @@ case CMD_SET: set_command(caller,arg); break; + case CMD_TEAM: + team_command(caller, arg); + break; case CMD_SCORE: if(server_state==RUN_GAME_STATE) { report_scores(FALSE); @@ -3353,6 +3397,10 @@ if (!game.is_new_game) { cat_snprintf(buf2, sizeof(buf2), _(", nation %s"), get_nation_name_plural(pplayer->nation)); + } + if ((game.team_count>0) && (pplayer->team != -1)) { + cat_snprintf(buf2, sizeof(buf2), (", team %s"), + get_team_name(pplayer->team)); } my_snprintf(buf, sizeof(buf), "%s (%s)", pplayer->name, buf2);