2007-11-25 19:42:40 +00:00
|
|
|
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
2008-01-13 11:43:43 +00:00
|
|
|
#include <string.h>
|
2007-12-15 10:24:49 +00:00
|
|
|
#include <engine/e_config.h>
|
2008-01-19 10:57:25 +00:00
|
|
|
#include <engine/e_server_interface.h>
|
2008-06-12 12:09:34 +00:00
|
|
|
#include <game/g_mapitems.hpp>
|
2008-08-14 18:42:47 +00:00
|
|
|
|
|
|
|
#include <game/generated/g_protocol.hpp>
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-08-14 18:25:44 +00:00
|
|
|
#include "entities/pickup.hpp"
|
2008-08-14 18:42:47 +00:00
|
|
|
#include "gamecontroller.hpp"
|
|
|
|
#include "gamecontext.hpp"
|
2008-08-14 18:25:44 +00:00
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
GAMECONTROLLER::GAMECONTROLLER()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
// select gametype
|
2007-12-11 23:10:07 +00:00
|
|
|
if(strcmp(config.sv_gametype, "ctf") == 0)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
gametype = GAMETYPE_CTF;
|
|
|
|
dbg_msg("game", "-- Capture The Flag --");
|
|
|
|
}
|
2007-12-11 23:10:07 +00:00
|
|
|
else if(strcmp(config.sv_gametype, "tdm") == 0)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
gametype = GAMETYPE_TDM;
|
|
|
|
dbg_msg("game", "-- Team Death Match --");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gametype = GAMETYPE_DM;
|
|
|
|
dbg_msg("game", "-- Death Match --");
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2007-12-11 23:10:07 +00:00
|
|
|
do_warmup(config.sv_warmup);
|
2007-10-05 00:05:21 +00:00
|
|
|
game_over_tick = -1;
|
|
|
|
sudden_death = 0;
|
|
|
|
round_start_tick = server_tick();
|
|
|
|
round_count = 0;
|
2007-11-04 00:19:41 +00:00
|
|
|
is_teamplay = false;
|
2007-12-09 09:47:05 +00:00
|
|
|
teamscore[0] = 0;
|
|
|
|
teamscore[1] = 0;
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-01-13 11:43:43 +00:00
|
|
|
// UGLY!!!!
|
|
|
|
extern vec2 spawn_points[3][64];
|
|
|
|
extern int num_spawn_points[3];
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
bool GAMECONTROLLER::on_entity(int index, vec2 pos)
|
2008-01-13 11:43:43 +00:00
|
|
|
{
|
|
|
|
int type = -1;
|
|
|
|
int subtype = 0;
|
|
|
|
|
|
|
|
if(index == ENTITY_SPAWN)
|
|
|
|
spawn_points[0][num_spawn_points[0]++] = pos;
|
|
|
|
else if(index == ENTITY_SPAWN_RED)
|
|
|
|
spawn_points[1][num_spawn_points[1]++] = pos;
|
|
|
|
else if(index == ENTITY_SPAWN_BLUE)
|
|
|
|
spawn_points[2][num_spawn_points[2]++] = pos;
|
|
|
|
else if(index == ENTITY_ARMOR_1)
|
|
|
|
type = POWERUP_ARMOR;
|
|
|
|
else if(index == ENTITY_HEALTH_1)
|
|
|
|
type = POWERUP_HEALTH;
|
|
|
|
else if(index == ENTITY_WEAPON_SHOTGUN)
|
|
|
|
{
|
|
|
|
type = POWERUP_WEAPON;
|
|
|
|
subtype = WEAPON_SHOTGUN;
|
|
|
|
}
|
2008-02-02 18:05:16 +00:00
|
|
|
else if(index == ENTITY_WEAPON_GRENADE)
|
2008-01-13 11:43:43 +00:00
|
|
|
{
|
|
|
|
type = POWERUP_WEAPON;
|
2008-02-02 18:05:16 +00:00
|
|
|
subtype = WEAPON_GRENADE;
|
2008-01-13 11:43:43 +00:00
|
|
|
}
|
2008-03-21 19:05:35 +00:00
|
|
|
else if(index == ENTITY_WEAPON_RIFLE)
|
|
|
|
{
|
|
|
|
type = POWERUP_WEAPON;
|
|
|
|
subtype = WEAPON_RIFLE;
|
|
|
|
}
|
2008-03-29 12:04:14 +00:00
|
|
|
else if(index == ENTITY_POWERUP_NINJA && config.sv_powerups)
|
2008-01-13 11:43:43 +00:00
|
|
|
{
|
|
|
|
type = POWERUP_NINJA;
|
|
|
|
subtype = WEAPON_NINJA;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(type != -1)
|
|
|
|
{
|
2008-06-12 10:51:48 +00:00
|
|
|
PICKUP *pickup = new PICKUP(type, subtype);
|
|
|
|
pickup->pos = pos;
|
2008-01-13 11:43:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::endround()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2007-10-06 17:36:24 +00:00
|
|
|
if(warmup) // game can't end when we are running warmup
|
|
|
|
return;
|
|
|
|
|
2008-07-06 11:21:21 +00:00
|
|
|
game.world.paused = true;
|
2007-10-05 00:05:21 +00:00
|
|
|
game_over_tick = server_tick();
|
|
|
|
sudden_death = 0;
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::resetgame()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
game.world.reset_requested = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *GAMECONTROLLER::get_team_name(int team)
|
|
|
|
{
|
|
|
|
if(is_teamplay)
|
|
|
|
{
|
|
|
|
if(team == 0)
|
|
|
|
return "red team";
|
|
|
|
else if(team == 1)
|
|
|
|
return "blue team";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(team == 0)
|
|
|
|
return "game";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "spectators";
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; }
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::startround()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
resetgame();
|
|
|
|
|
|
|
|
round_start_tick = server_tick();
|
|
|
|
sudden_death = 0;
|
|
|
|
game_over_tick = -1;
|
2008-07-06 11:21:21 +00:00
|
|
|
game.world.paused = false;
|
2007-10-05 00:05:21 +00:00
|
|
|
teamscore[0] = 0;
|
|
|
|
teamscore[1] = 0;
|
|
|
|
round_count++;
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::cyclemap()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
if(!strlen(config.sv_maprotation))
|
|
|
|
return;
|
2008-03-01 20:21:34 +00:00
|
|
|
|
2008-03-29 21:46:38 +00:00
|
|
|
if(round_count < config.sv_rounds_per_map-1)
|
|
|
|
return;
|
|
|
|
|
2007-10-05 00:05:21 +00:00
|
|
|
// handle maprotation
|
2008-03-01 20:21:34 +00:00
|
|
|
const char *map_rotation = config.sv_maprotation;
|
|
|
|
const char *current_map = config.sv_map;
|
|
|
|
|
|
|
|
int current_map_len = strlen(current_map);
|
|
|
|
const char *next_map = map_rotation;
|
|
|
|
while(*next_map)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2008-03-01 20:21:34 +00:00
|
|
|
int wordlen = 0;
|
|
|
|
while(next_map[wordlen] && !is_separator(next_map[wordlen]))
|
|
|
|
wordlen++;
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-03-01 20:21:34 +00:00
|
|
|
if(wordlen == current_map_len && strncmp(next_map, current_map, current_map_len) == 0)
|
|
|
|
{
|
|
|
|
// map found
|
|
|
|
next_map += current_map_len;
|
|
|
|
while(*next_map && is_separator(*next_map))
|
|
|
|
next_map++;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
next_map++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// restart rotation
|
|
|
|
if(next_map[0] == 0)
|
|
|
|
next_map = map_rotation;
|
|
|
|
|
|
|
|
// cut out the next map
|
|
|
|
char buf[512];
|
|
|
|
for(int i = 0; i < 512; i++)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2008-03-01 20:21:34 +00:00
|
|
|
buf[i] = next_map[i];
|
|
|
|
if(is_separator(next_map[i]) || next_map[i] == 0)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
buf[i] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-01 20:21:34 +00:00
|
|
|
// skip spaces
|
|
|
|
int i = 0;
|
2007-10-05 00:05:21 +00:00
|
|
|
while(is_separator(buf[i]))
|
|
|
|
i++;
|
|
|
|
|
|
|
|
dbg_msg("game", "rotating map to %s", &buf[i]);
|
2008-02-11 21:49:26 +00:00
|
|
|
str_copy(config.sv_map, &buf[i], sizeof(config.sv_map));
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::post_reset()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].client_id != -1)
|
|
|
|
game.players[i].respawn();
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-18 14:24:34 +00:00
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::on_player_info_change(class PLAYER *p)
|
2007-11-18 14:24:34 +00:00
|
|
|
{
|
2007-11-26 22:26:33 +00:00
|
|
|
const int team_colors[2] = {65387, 10223467};
|
2007-11-18 14:24:34 +00:00
|
|
|
if(is_teamplay)
|
|
|
|
{
|
|
|
|
if(p->team >= 0 || p->team <= 1)
|
|
|
|
{
|
|
|
|
p->use_custom_color = 1;
|
|
|
|
p->color_body = team_colors[p->team];
|
|
|
|
p->color_feet = team_colors[p->team];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-06 11:21:21 +00:00
|
|
|
int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
// do scoreing
|
|
|
|
if(!killer)
|
2007-11-18 23:29:34 +00:00
|
|
|
return 0;
|
2008-07-06 11:21:21 +00:00
|
|
|
if(killer == victim->player)
|
|
|
|
victim->player->score--; // suicide
|
2007-10-05 00:05:21 +00:00
|
|
|
else
|
2007-12-11 21:19:52 +00:00
|
|
|
{
|
|
|
|
if(is_teamplay && victim->team == killer->team)
|
|
|
|
killer->score--; // teamkill
|
|
|
|
else
|
|
|
|
killer->score++; // normal kill
|
|
|
|
}
|
2007-11-18 23:29:34 +00:00
|
|
|
return 0;
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::do_warmup(int seconds)
|
2007-10-06 17:36:24 +00:00
|
|
|
{
|
2008-01-19 10:57:25 +00:00
|
|
|
warmup = seconds*server_tickspeed();
|
2007-10-06 17:36:24 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
bool GAMECONTROLLER::is_friendly_fire(int cid1, int cid2)
|
2007-12-10 20:53:37 +00:00
|
|
|
{
|
|
|
|
if(cid1 == cid2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(is_teamplay)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[cid1].team == game.players[cid2].team)
|
2007-12-10 20:53:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::tick()
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2007-10-06 17:36:24 +00:00
|
|
|
// do warmup
|
|
|
|
if(warmup)
|
|
|
|
{
|
|
|
|
warmup--;
|
|
|
|
if(!warmup)
|
2007-12-10 21:05:57 +00:00
|
|
|
startround();
|
2007-10-06 17:36:24 +00:00
|
|
|
}
|
|
|
|
|
2007-10-05 00:05:21 +00:00
|
|
|
if(game_over_tick != -1)
|
|
|
|
{
|
|
|
|
// game over.. wait for restart
|
|
|
|
if(server_tick() > game_over_tick+server_tickspeed()*10)
|
|
|
|
{
|
|
|
|
cyclemap();
|
|
|
|
startround();
|
|
|
|
}
|
|
|
|
}
|
2007-11-27 19:51:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
// update browse info
|
|
|
|
int prog = -1;
|
2007-12-11 23:10:07 +00:00
|
|
|
if(config.sv_timelimit > 0)
|
|
|
|
prog = max(prog, (server_tick()-round_start_tick) * 100 / (config.sv_timelimit*server_tickspeed()*60));
|
2007-11-27 19:51:48 +00:00
|
|
|
|
2007-12-11 23:10:07 +00:00
|
|
|
if(config.sv_scorelimit)
|
2007-11-27 19:51:48 +00:00
|
|
|
{
|
|
|
|
if(is_teamplay)
|
|
|
|
{
|
2007-12-11 23:10:07 +00:00
|
|
|
prog = max(prog, (teamscore[0]*100)/config.sv_scorelimit);
|
|
|
|
prog = max(prog, (teamscore[1]*100)/config.sv_scorelimit);
|
2007-11-27 19:51:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].client_id != -1)
|
|
|
|
prog = max(prog, (game.players[i].score*100)/config.sv_scorelimit);
|
2007-11-27 19:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(warmup)
|
|
|
|
prog = -1;
|
2007-12-18 23:21:57 +00:00
|
|
|
|
2007-11-27 19:51:48 +00:00
|
|
|
server_setbrowseinfo(gametype, prog);
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::snap(int snapping_client)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME));
|
|
|
|
gameobj->paused = game.world.paused;
|
|
|
|
gameobj->game_over = game_over_tick==-1?0:1;
|
|
|
|
gameobj->sudden_death = sudden_death;
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-07-06 11:21:21 +00:00
|
|
|
gameobj->score_limit = config.sv_scorelimit;
|
|
|
|
gameobj->time_limit = config.sv_timelimit;
|
|
|
|
gameobj->round_start_tick = round_start_tick;
|
|
|
|
gameobj->gametype = gametype;
|
2007-10-05 00:05:21 +00:00
|
|
|
|
2008-07-06 11:21:21 +00:00
|
|
|
gameobj->warmup = warmup;
|
2007-10-06 17:36:24 +00:00
|
|
|
|
2008-07-06 11:21:21 +00:00
|
|
|
gameobj->teamscore_red = teamscore[0];
|
|
|
|
gameobj->teamscore_blue = teamscore[1];
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
int GAMECONTROLLER::get_auto_team(int notthisid)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
|
|
|
int numplayers[2] = {0,0};
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid)
|
2007-10-05 00:05:21 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].team == 0 || game.players[i].team == 1)
|
|
|
|
numplayers[game.players[i].team]++;
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-23 14:59:58 +00:00
|
|
|
int team = 0;
|
|
|
|
if(is_teamplay)
|
|
|
|
team = numplayers[0] > numplayers[1] ? 1 : 0;
|
|
|
|
|
|
|
|
if(can_join_team(team, notthisid))
|
|
|
|
return team;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
bool GAMECONTROLLER::can_join_team(int team, int notthisid)
|
2008-03-23 14:59:58 +00:00
|
|
|
{
|
|
|
|
(void)team;
|
|
|
|
int numplayers[2] = {0,0};
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].client_id != -1 && game.players[i].client_id != notthisid)
|
2008-03-23 14:59:58 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].team >= 0 || game.players[i].team == 1)
|
|
|
|
numplayers[game.players[i].team]++;
|
2008-03-23 14:59:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (numplayers[0] + numplayers[1]) < config.sv_max_clients-config.sv_spectator_slots;
|
2007-10-05 00:05:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::do_player_score_wincheck()
|
2008-01-17 23:09:49 +00:00
|
|
|
{
|
|
|
|
if(game_over_tick == -1 && !warmup)
|
|
|
|
{
|
|
|
|
// gather some stats
|
|
|
|
int topscore = 0;
|
|
|
|
int topscore_count = 0;
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].client_id != -1)
|
2008-01-17 23:09:49 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
if(game.players[i].score > topscore)
|
2008-01-17 23:09:49 +00:00
|
|
|
{
|
2008-07-06 11:21:21 +00:00
|
|
|
topscore = game.players[i].score;
|
2008-01-17 23:09:49 +00:00
|
|
|
topscore_count = 1;
|
|
|
|
}
|
2008-07-06 11:21:21 +00:00
|
|
|
else if(game.players[i].score == topscore)
|
2008-01-17 23:09:49 +00:00
|
|
|
topscore_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check score win condition
|
|
|
|
if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) ||
|
|
|
|
(config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60))
|
|
|
|
{
|
|
|
|
if(topscore_count == 1)
|
|
|
|
endround();
|
|
|
|
else
|
|
|
|
sudden_death = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
void GAMECONTROLLER::do_team_score_wincheck()
|
2007-11-26 20:47:49 +00:00
|
|
|
{
|
|
|
|
if(game_over_tick == -1 && !warmup)
|
|
|
|
{
|
|
|
|
// check score win condition
|
2007-12-11 23:10:07 +00:00
|
|
|
if((config.sv_scorelimit > 0 && (teamscore[0] >= config.sv_scorelimit || teamscore[1] >= config.sv_scorelimit)) ||
|
|
|
|
(config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60))
|
2007-11-26 20:47:49 +00:00
|
|
|
{
|
|
|
|
if(teamscore[0] != teamscore[1])
|
|
|
|
endround();
|
|
|
|
else
|
|
|
|
sudden_death = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
int GAMECONTROLLER::clampteam(int team)
|
2007-12-18 23:21:57 +00:00
|
|
|
{
|
|
|
|
if(team < 0) // spectator
|
|
|
|
return -1;
|
|
|
|
if(is_teamplay)
|
|
|
|
return team&1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-12 10:51:48 +00:00
|
|
|
GAMECONTROLLER *gamecontroller = 0;
|