#include "scores.qh"
#include "teamplay.qh"
#include "weapons/weaponstats.qh"
-#include "../common/buffs/all.qh"
#include "../common/constants.qh"
#include "../common/deathtypes/all.qh"
#include "../common/mapinfo.qh"
#include "../common/monsters/all.qh"
#include "../common/monsters/sv_monsters.qh"
#include "../common/vehicles/all.qh"
-#include "../common/notifications.qh"
-#include "../common/physics.qh"
+#include "../common/notifications/all.qh"
+#include "../common/physics/player.qh"
#include "../common/playerstats.qh"
#include "../common/stats.qh"
#include "../common/teams.qh"
#include "../common/util.qh"
#include "../common/items/all.qh"
#include "../common/weapons/all.qh"
+#include "../common/state.qh"
const float LATENCY_THINKRATE = 10;
.float latency_sum;
delta = 3 / maxclients;
if(delta < sys_frametime)
delta = 0;
- self.nextthink = time + delta;
+ this.nextthink = time + delta;
- e = edict_num(self.cnt + 1);
+ e = edict_num(this.cnt + 1);
if(IS_REAL_CLIENT(e))
{
WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
- WriteByte(MSG_BROADCAST, self.cnt);
+ WriteByte(MSG_BROADCAST, this.cnt);
WriteShort(MSG_BROADCAST, max(1, e.ping));
WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255));
WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255));
else
{
WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
- WriteByte(MSG_BROADCAST, self.cnt);
+ WriteByte(MSG_BROADCAST, this.cnt);
WriteShort(MSG_BROADCAST, 0);
WriteByte(MSG_BROADCAST, 0);
WriteByte(MSG_BROADCAST, 0);
}
- self.cnt = (self.cnt + 1) % maxclients;
+ this.cnt = (this.cnt + 1) % maxclients;
}
void PingPLReport_Spawn()
{
- pingplreport = new(pingplreport);
- make_pure(pingplreport);
+ pingplreport = new_pure(pingplreport);
pingplreport.think = PingPLReport_Think;
pingplreport.nextthink = time;
}
BADPREFIX("g_chat_flood_");
BADPREFIX("g_ghost_items");
BADPREFIX("g_playerstats_");
- BADPREFIX("g_respawn_ghosts");
BADPREFIX("g_voice_flood_");
BADPREFIX("log_file");
BADPREFIX("rcon_");
BADCVAR("g_nexball");
BADCVAR("g_onslaught");
BADCVAR("g_race");
+ BADCVAR("g_race_laps_limit");
BADCVAR("g_race_qualifying_timelimit");
+ BADCVAR("g_race_qualifying_timelimit_override");
BADCVAR("g_tdm");
BADCVAR("g_tdm_teams");
BADCVAR("leadlimit");
// now check if the changes are actually gameplay relevant
- // does nothing visible
+ // does nothing gameplay relevant
BADCVAR("captureleadlimit_override");
+ BADCVAR("gameversion");
+ BADCVAR("g_allow_oldvortexbeam");
BADCVAR("g_balance_kill_delay");
- BADCVAR("g_ca_point_limit");
+ BADCVAR("g_campcheck_distance");
BADCVAR("g_ca_point_leadlimit");
+ BADCVAR("g_ca_point_limit");
BADCVAR("g_ctf_captimerecord_always");
BADCVAR("g_ctf_flag_glowtrails");
BADCVAR("g_ctf_flag_pickup_verbosename");
BADCVAR("g_domination_point_leadlimit");
BADCVAR("g_forced_respawn");
- BADCVAR("g_freezetag_point_limit");
BADCVAR("g_freezetag_point_leadlimit");
- BADCVAR("g_keyhunt_point_leadlimit");
- BADPREFIX("g_mod_");
+ BADCVAR("g_freezetag_point_limit");
+ BADCVAR("g_hats");
BADCVAR("g_invasion_point_limit");
+ BADCVAR("g_keyhunt_point_leadlimit");
BADCVAR("g_nexball_goalleadlimit");
- BADCVAR("g_tdm_point_limit");
BADCVAR("g_tdm_point_leadlimit");
+ BADCVAR("g_tdm_point_limit");
BADCVAR("leadlimit_and_fraglimit");
BADCVAR("leadlimit_override");
BADCVAR("pausable");
BADCVAR("sv_allow_fullbright");
BADCVAR("sv_checkforpacketsduringsleep");
+ BADCVAR("sv_intermission_cdtrack");
+ BADCVAR("sv_minigames");
+ BADCVAR("sv_namechangetimer");
+ BADCVAR("sv_precacheplayermodels");
BADCVAR("sv_timeout");
- BADPREFIX("sv_timeout_");
BADPREFIX("crypto_");
+ BADPREFIX("gameversion_");
BADPREFIX("g_chat_");
BADPREFIX("g_ctf_captimerecord_");
BADPREFIX("g_maplist_votable_");
+ BADPREFIX("g_mod_");
+ BADPREFIX("g_respawn_");
BADPREFIX("net_");
BADPREFIX("prvm_");
BADPREFIX("skill_");
BADPREFIX("sv_cullentities_");
BADPREFIX("sv_maxidle_");
+ BADPREFIX("sv_minigames_");
+ BADPREFIX("sv_timeout_");
BADPREFIX("sv_vote_");
BADPREFIX("timelimit_");
- BADCVAR("gameversion");
- BADPREFIX("gameversion_");
- BADCVAR("sv_minigames");
- BADPREFIX("sv_minigames_");
- BADCVAR("sv_namechangetimer");
// allowed changes to server admins (please sync this to server.cfg)
// vi commands:
BADCVAR("g_mirrordamage");
BADCVAR("g_nexball_goallimit");
BADCVAR("g_powerups");
+ BADCVAR("g_spawnshieldtime");
BADCVAR("g_start_delay");
+ BADCVAR("g_superspectate");
BADCVAR("g_tdm_teams_override");
BADCVAR("g_warmup");
BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
BADCVAR("sv_vote_simple_majority_factor");
BADCVAR("teamplay_mode");
BADCVAR("timelimit_override");
- BADCVAR("g_spawnshieldtime");
BADPREFIX("g_warmup_");
BADPREFIX("sv_ready_restart_");
o.y += random() * (world.maxs.y - world.mins.y);
o.z += random() * (world.maxs.z - world.mins.z);
- tracebox(o, PL_MIN, PL_MAX, o - '0 0 32768', MOVE_WORLDONLY, world);
+ tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 32768', MOVE_WORLDONLY, world);
if(trace_fraction == 1)
continue;
bool RandomSeed_Send(entity this, entity to, int sf)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
- WriteShort(MSG_ENTITY, self.cnt);
+ WriteShort(MSG_ENTITY, this.cnt);
return true;
}
void RandomSeed_Think()
{SELFPARAM();
- self.cnt = bound(0, floor(random() * 65536), 65535);
- self.nextthink = time + 5;
+ this.cnt = bound(0, floor(random() * 65536), 65535);
+ this.nextthink = time + 5;
- self.SendFlags |= 1;
+ this.SendFlags |= 1;
}
void RandomSeed_Spawn()
{SELFPARAM();
- randomseed = new(randomseed);
- make_pure(randomseed);
+ randomseed = new_pure(randomseed);
randomseed.think = RandomSeed_Think;
Net_LinkEntity(randomseed, false, 0, RandomSeed_Send);
- WITH(entity, self, randomseed, randomseed.think()); // sets random seed and nextthink
+ WITHSELF(randomseed, randomseed.think()); // sets random seed and nextthink
}
spawnfunc(__init_dedicated_server)
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
}
+void __init_dedicated_server_shutdown() {
+ MapInfo_Shutdown();
+}
+
+void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override)
+{
+ if(!autocvar_g_campaign)
+ {
+ if(fraglimit_override >= 0) cvar_set("fraglimit", ftos(fraglimit_override));
+ if(timelimit_override >= 0) cvar_set("timelimit", ftos(timelimit_override));
+ if(leadlimit_override >= 0) cvar_set("leadlimit", ftos(leadlimit_override));
+ if(qualifying_override >= 0) cvar_set("g_race_qualifying_timelimit", ftos(qualifying_override));
+ }
+ limits_are_set = true;
+}
+
void Map_MarkAsRecent(string m);
float world_already_spawned;
void Nagger_Init();
void ClientInit_Spawn();
void WeaponStats_Init();
void WeaponStats_Shutdown();
-void Physics_AddStats();
spawnfunc(worldspawn)
{
- float fd, l, j, n;
+ server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
+
+ {
+ bool wantrestart = false;
+
+ if (!server_is_dedicated)
+ {
+ // force unloading of server pk3 files when starting a listen server
+ // localcmd("\nfs_rescan\n"); // FIXME: does more harm than good, has unintended side effects. What we really want is to unload temporary pk3s only
+ // restore csqc_progname too
+ string expect = "csprogs.dat";
+ wantrestart = cvar_string_normal("csqc_progname") != expect;
+ cvar_set_normal("csqc_progname", expect);
+ }
+ else
+ {
+ // Try to use versioned csprogs from pk3
+ // Only ever use versioned csprogs.dat files on dedicated servers;
+ // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
+ string pk3csprogs = "csprogs-" WATERMARK ".dat";
+ // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
+ string select = "csprogs.dat";
+ if (fexists(pk3csprogs)) select = pk3csprogs;
+ if (cvar_string_normal("csqc_progname") != select)
+ {
+ cvar_set_normal("csqc_progname", select);
+ wantrestart = true;
+ }
+ // Check for updates on startup
+ // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
+ int sentinel = fopen("progs.txt", FILE_READ);
+ if (sentinel >= 0)
+ {
+ string switchversion = fgets(sentinel);
+ fclose(sentinel);
+ if (switchversion != "" && switchversion != WATERMARK)
+ {
+ LOG_INFOF("Switching progs: " WATERMARK " -> %s\n", switchversion);
+ // if it doesn't exist, assume either:
+ // a) the current program was overwritten
+ // b) this is a client only update
+ string newprogs = sprintf("progs-%s.dat", switchversion);
+ if (fexists(newprogs))
+ {
+ cvar_set_normal("sv_progs", newprogs);
+ wantrestart = true;
+ }
+ string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
+ if (fexists(newcsprogs))
+ {
+ cvar_set_normal("csqc_progname", newcsprogs);
+ wantrestart = true;
+ }
+ }
+ }
+ }
+ if (wantrestart)
+ {
+ LOG_INFOF("Restart requested\n");
+ changelevel(mapname);
+ // let initialization continue, shutdown depends on it
+ }
+ }
+
+ float fd, l;
string s;
cvar = cvar_normal;
compressShortVector_init();
- entity head;
- head = nextent(world);
maxclients = 0;
- while(head)
+ for (entity head = nextent(world); head; head = nextent(head))
{
++maxclients;
- head = nextent(head);
}
- server_is_dedicated = (stof(cvar_defstring("is_dedicated")) ? true : false);
-
// needs to be done so early because of the constants they create
static_init();
readlevelcvars();
GrappleHookInit();
+ if(!limits_are_set)
+ SetLimits(autocvar_fraglimit_override, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
+
player_count = 0;
bot_waypoints_for_items = autocvar_g_waypoints_for_items;
if(bot_waypoints_for_items == 1)
WeaponStats_Init();
- WepSet_AddStat();
- WepSet_AddStat_InMap();
Nagger_Init();
- // freeze attacks
- addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
-
- // physics
- Physics_AddStats();
-
- // new properties
- addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_sv_jumpvelocity);
- addstat(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, AS_FLOAT, stat_sv_airaccel_qw_stretchfactor);
- addstat(STAT_MOVEVARS_MAXAIRSTRAFESPEED, AS_FLOAT, stat_sv_maxairstrafespeed);
- addstat(STAT_MOVEVARS_MAXAIRSPEED, AS_FLOAT, stat_sv_maxairspeed);
- addstat(STAT_MOVEVARS_AIRSTRAFEACCELERATE, AS_FLOAT, stat_sv_airstrafeaccelerate);
- addstat(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL, AS_FLOAT, stat_sv_warsowbunny_turnaccel);
- addstat(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, AS_FLOAT, stat_sv_airaccel_sideways_friction);
- addstat(STAT_MOVEVARS_AIRCONTROL, AS_FLOAT, stat_sv_aircontrol);
- addstat(STAT_MOVEVARS_AIRCONTROL_POWER, AS_FLOAT, stat_sv_aircontrol_power);
- addstat(STAT_MOVEVARS_AIRCONTROL_PENALTY, AS_FLOAT, stat_sv_aircontrol_penalty);
- addstat(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, AS_FLOAT, stat_sv_warsowbunny_airforwardaccel);
- addstat(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED, AS_FLOAT, stat_sv_warsowbunny_topspeed);
- addstat(STAT_MOVEVARS_WARSOWBUNNY_ACCEL, AS_FLOAT, stat_sv_warsowbunny_accel);
- addstat(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, AS_FLOAT, stat_sv_warsowbunny_backtosideratio);
- addstat(STAT_MOVEVARS_FRICTION, AS_FLOAT, stat_sv_friction);
- addstat(STAT_MOVEVARS_ACCELERATE, AS_FLOAT, stat_sv_accelerate);
- addstat(STAT_MOVEVARS_STOPSPEED, AS_FLOAT, stat_sv_stopspeed);
- addstat(STAT_MOVEVARS_AIRACCELERATE, AS_FLOAT, stat_sv_airaccelerate);
- addstat(STAT_MOVEVARS_AIRSTOPACCELERATE, AS_FLOAT, stat_sv_airstopaccelerate);
-
next_pingtime = time + 5;
detect_maptype();
localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
// fill sv_curl_serverpackages from .serverpackage files
- if(autocvar_sv_curl_serverpackages_auto)
+ if (autocvar_sv_curl_serverpackages_auto)
{
- s = "";
- n = tokenize_console(cvar_string("sv_curl_serverpackages"));
- for(int i = 0; i < n; ++i)
- if(substring(argv(i), -18, -1) != "-serverpackage.txt")
- if(substring(argv(i), -14, -1) != ".serverpackage") // OLD legacy
- s = strcat(s, " ", argv(i));
- fd = search_begin("*-serverpackage.txt", true, false);
- if(fd >= 0)
- {
- j = search_getsize(fd);
- for(int i = 0; i < j; ++i)
- s = strcat(s, " ", search_getfilename(fd, i));
- search_end(fd);
- }
- fd = search_begin("*.serverpackage", true, false);
- if(fd >= 0)
+ s = "csprogs-" WATERMARK ".txt";
+ // remove automatically managed files from the list to prevent duplicates
+ for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
{
- j = search_getsize(fd);
- for(int i = 0; i < j; ++i)
- s = strcat(s, " ", search_getfilename(fd, i));
- search_end(fd);
+ string pkg = argv(i);
+ if (startsWith(pkg, "csprogs-")) continue;
+ if (endsWith(pkg, "-serverpackage.txt")) continue;
+ if (endsWith(pkg, ".serverpackage")) continue; // OLD legacy
+ s = cons(s, pkg);
}
- cvar_set("sv_curl_serverpackages", substring(s, 1, -1));
+ // add automatically managed files to the list
+ #define X(match) MACRO_BEGIN { \
+ fd = search_begin(match, true, false); \
+ if (fd >= 0) \
+ { \
+ for (int i = 0, j = search_getsize(fd); i < j; ++i) \
+ { \
+ s = cons(s, search_getfilename(fd, i)); \
+ } \
+ search_end(fd); \
+ } \
+ } MACRO_END
+ X("*-serverpackage.txt");
+ X("*.serverpackage");
+ #undef X
+ cvar_set("sv_curl_serverpackages", s);
}
// MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
return;
if(!mapvote_initialized)
- if (time < intermission_exittime + 10 && !(self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE))
+ if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(self) || PHYS_INPUT_BUTTON_JUMP(self) || PHYS_INPUT_BUTTON_ATCK2(self) || PHYS_INPUT_BUTTON_HOOK(self) || PHYS_INPUT_BUTTON_USE(self)))
return;
MapVote_Start();
if(to_file)
fputs(file, strcat(s, "\n"));
- FOR_EACH_CLIENT(other)
- {
- if ((IS_REAL_CLIENT(other)) || (IS_BOT_CLIENT(other) && autocvar_sv_logscores_bots))
- {
- s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
- s = strcat(s, ftos(rint(time - other.jointime)), ":");
- if(IS_PLAYER(other) || MUTATOR_CALLHOOK(GetPlayerStatus, other, s))
- s = strcat(s, ftos(other.team), ":");
- else
- s = strcat(s, "spectator:");
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), LAMBDA(
+ s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
+ s = strcat(s, ftos(rint(time - it.jointime)), ":");
+ if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it, s))
+ s = strcat(s, ftos(it.team), ":");
+ else
+ s = strcat(s, "spectator:");
- if(to_console)
- LOG_INFO(s, other.netname, "\n");
- if(to_eventlog)
- GameLogEcho(strcat(s, ftos(other.playerid), ":", other.netname));
- if(to_file)
- fputs(file, strcat(s, other.netname, "\n"));
- }
- }
+ if(to_console)
+ LOG_INFO(s, it.netname, "\n");
+ if(to_eventlog)
+ GameLogEcho(strcat(s, ftos(it.playerid), ":", it.netname));
+ if(to_file)
+ fputs(file, strcat(s, it.netname, "\n"));
+ ));
if(teamplay)
{
void FixIntermissionClient(entity e)
{
- string s;
if(!e.autoscreenshot) // initial call
{
e.autoscreenshot = time + 0.8; // used for autoscreenshot
if(e.(weaponentity))
{
e.(weaponentity).effects = EF_NODRAW;
- if (e.(weaponentity).(weaponentity))
- e.(weaponentity).(weaponentity).effects = EF_NODRAW;
+ if (e.(weaponentity).weaponchild)
+ e.(weaponentity).weaponchild.effects = EF_NODRAW;
}
}
if(IS_REAL_CLIENT(e))
{
stuffcmd(e, "\nscr_printspeed 1000000\n");
- s = autocvar_sv_intermission_cdtrack;
- if(s != "")
- stuffcmd(e, strcat("\ncd loop ", s, "\n"));
+ RandomSelection_Init();
+ FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, LAMBDA(
+ RandomSelection_Add(NULL, 0, it, 1, 1);
+ ));
+ if (RandomSelection_chosen_string != "")
+ {
+ stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
+ }
msg_entity = e;
WriteByte(MSG_ONE, SVC_INTERMISSION);
}
*/
void NextLevel()
{
+ SELFPARAM();
gameover = true;
intermission_running = 1;
PlayerStats_GameReport(true);
WeaponStats_Shutdown();
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER, CPID_Null); // kill all centerprints now
if(autocvar_sv_eventlog)
GameLogEcho(":gameover");
GameLogClose();
- FOR_EACH_PLAYER(other) {
- FixIntermissionClient(other);
- if(other.winning)
- bprint(other.netname, " ^7wins.\n");
- }
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+ FixIntermissionClient(it);
+ if(it.winning)
+ bprint(it.netname, " ^7wins.\n");
+ ));
- entity oldself = self;
- target_music_kill();
- self = oldself;
+ WITHSELF(NULL, target_music_kill());
if(autocvar_g_campaign)
CampaignPreIntermission();
if (gameover) // someone else quit the game already
return;
- if(self.deadflag == DEAD_NO)
- self.play_time += frametime;
+ if(!IS_DEAD(this))
+ this.play_time += frametime;
// fixme: don't check players; instead check spawnfunc_dom_team and spawnfunc_ctf_team entities
// (div0: and that in CheckRules_World please)
// set the .winning flag for exactly those players with a given field value
void SetWinners(.float field, float value)
{
- entity head;
- FOR_EACH_PLAYER(head)
- head.winning = (head.(field) == value);
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = (it.(field) == value)));
}
// set the .winning flag for those players with a given field value
void AddWinners(.float field, float value)
{
- entity head;
- FOR_EACH_PLAYER(head)
- if (head.(field) == value)
- head.winning = 1;
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+ if(it.(field) == value)
+ it.winning = 1;
+ ));
}
// clear the .winning flags
void ClearWinners()
{
- entity head;
- FOR_EACH_PLAYER(head)
- head.winning = 0;
-}
-
-// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
-// they win. Otherwise the defending team wins once the timelimit passes.
-void assault_new_round();
-float WinningCondition_Assault()
-{SELFPARAM();
- float status;
-
- WinningConditionHelper(); // set worldstatus
-
- status = WINNING_NO;
- // as the timelimit has not yet passed just assume the defending team will win
- if(assault_attacker_team == NUM_TEAM_1)
- {
- SetWinners(team, NUM_TEAM_2);
- }
- else
- {
- SetWinners(team, NUM_TEAM_1);
- }
-
- entity ent;
- ent = find(world, classname, "target_assault_roundend");
- if(ent)
- {
- if(ent.winning) // round end has been triggered by attacking team
- {
- bprint("ASSAULT: round completed...\n");
- SetWinners(team, assault_attacker_team);
-
- TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
-
- if(ent.cnt == 1 || autocvar_g_campaign) // this was the second round
- {
- status = WINNING_YES;
- }
- else
- {
- WITH(entity, self, ent, assault_new_round());
- }
- }
- }
-
- return status;
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = 0));
}
void ShuffleMaplist()
);
}
-float WinningCondition_Race(float fraglimit)
-{
- float wc;
- entity p;
- float n, c;
-
- n = 0;
- c = 0;
- FOR_EACH_PLAYER(p)
- {
- ++n;
- if(p.race_completed)
- ++c;
- }
- if(n && (n == c))
- return WINNING_YES;
- wc = WinningCondition_Scores(fraglimit, 0);
-
- // ALWAYS initiate overtime, unless EVERYONE has finished the race!
- if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME)
- // do NOT support equality when the laps are all raced!
- return WINNING_STARTSUDDENDEATHOVERTIME;
- else
- return WINNING_NEVER;
-}
-
-float WinningCondition_QualifyingThenRace(float limit)
-{
- float wc;
- wc = WinningCondition_Scores(limit, 0);
-
- // NEVER initiate overtime
- if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME)
- {
- return WINNING_YES;
- }
-
- return wc;
-}
-
float WinningCondition_RanOutOfSpawns()
{
- entity head;
-
if(have_team_spawns <= 0)
return WINNING_NO;
team1_score = team2_score = team3_score = team4_score = 0;
- FOR_EACH_PLAYER(head) if(head.deadflag == DEAD_NO)
- {
- if(head.team == NUM_TEAM_1)
- team1_score = 1;
- else if(head.team == NUM_TEAM_2)
- team2_score = 1;
- else if(head.team == NUM_TEAM_3)
- team3_score = 1;
- else if(head.team == NUM_TEAM_4)
- team4_score = 1;
- }
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), LAMBDA(
+ switch(it.team)
+ {
+ case NUM_TEAM_1: team1_score = 1; break;
+ case NUM_TEAM_2: team2_score = 1; break;
+ case NUM_TEAM_3: team3_score = 1; break;
+ case NUM_TEAM_4: team4_score = 1; break;
+ }
+ ));
- for(head = world; (head = find(head, classname, "info_player_deathmatch")) != world; )
- {
- if(head.team == NUM_TEAM_1)
- team1_score = 1;
- else if(head.team == NUM_TEAM_2)
- team2_score = 1;
- else if(head.team == NUM_TEAM_3)
- team3_score = 1;
- else if(head.team == NUM_TEAM_4)
- team4_score = 1;
- }
+ FOREACH_ENTITY_CLASS("info_player_deathmatch", true, LAMBDA(
+ switch(it.team)
+ {
+ case NUM_TEAM_1: team1_score = 1; break;
+ case NUM_TEAM_2: team2_score = 1; break;
+ case NUM_TEAM_3: team3_score = 1; break;
+ case NUM_TEAM_4: team4_score = 1; break;
+ }
+ ));
ClearWinners();
if(team1_score + team2_score + team3_score + team4_score == 0)
float totalplayers;
float playerswithlaps;
float readyplayers;
- entity head;
totalplayers = playerswithlaps = readyplayers = 0;
- FOR_EACH_PLAYER(head)
- {
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
++totalplayers;
- if(PlayerScore_Add(head, SP_RACE_FASTEST, 0))
+ if(PlayerScore_Add(it, SP_RACE_FASTEST, 0))
++playerswithlaps;
- if(head.ready)
+ if(it.ready)
++readyplayers;
- }
+ ));
// at least 2 of the players have completed a lap: start the RACE
// otherwise, the players should end the qualifying on their own
void EndFrame()
-{SELFPARAM();
+{
anticheat_endframe();
- float altime;
- entity e_;
- FOR_EACH_REALCLIENT(e_)
- {
- entity e = IS_SPEC(e_) ? e_.enemy : e_;
- if(e.typehitsound)
- e_.typehit_time = time;
- else if(e.damage_dealt)
- {
- e_.hit_time = time;
- e_.damage_dealt_total += ceil(e.damage_dealt);
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+ entity e = IS_SPEC(it) ? it.enemy : it;
+ if (e.typehitsound) {
+ it.typehit_time = time;
+ } else if (e.damage_dealt) {
+ it.hit_time = time;
+ it.damage_dealt_total += ceil(e.damage_dealt);
}
- }
- altime = time + frametime * (1 + autocvar_g_antilag_nudge);
+ });
// add 1 frametime because after this, engine SV_Physics
// increases time by a frametime and then networks the frame
// add another frametime because client shows everything with
// 1 frame of lag (cl_nolerp 0). The last +1 however should not be
// needed!
- FOR_EACH_CLIENT(e_)
- {
- e_.typehitsound = false;
- e_.damage_dealt = 0;
- setself(e_);
- antilag_record(e_, altime);
- }
- FOR_EACH_MONSTER(e_)
- {
- setself(e_);
- antilag_record(e_, altime);
- }
+ float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
+ FOREACH_CLIENT(true, {
+ it.typehitsound = false;
+ it.damage_dealt = 0;
+ antilag_record(it, CS(it), altime);
+ });
+ FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
+ antilag_record(it, it, altime);
+ });
+ FOREACH_CLIENT(PS(it), {
+ PlayerState s = PS(it);
+ s.ps_push(s, it);
+ });
}
redirection_nextthink = time + 1;
clients_found = 0;
- entity e;
- FOR_EACH_REALCLIENT(e)
- {
- setself(e);
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+ setself(it);
// TODO add timer
LOG_INFO("Redirecting: sending connect command to ", self.netname, "\n");
if(redirection_target == "self")
else
stuffcmd(self, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
++clients_found;
- }
+ ));
LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.\n");
if(world_initialized > 0)
{
world_initialized = 0;
- LOG_INFO("Saving persistent data...\n");
+ LOG_TRACE("Saving persistent data...\n");
Ban_SaveBans();
// playerstats with unfinished match
CheatShutdown(); // must be after cheatcount check
db_close(ServerProgsDB);
db_close(TemporaryDB);
- LOG_INFO("done!\n");
+ LOG_TRACE("Saving persistent data... done!\n");
// tell the bot system the game is ending now
bot_endgame();
{
LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data\n");
}
+ else
+ {
+ __init_dedicated_server_shutdown();
+ }
}