#include "world.qh"
-#include "anticheat.qh"
-#include "antilag.qh"
-#include "bot/api.qh"
-#include "campaign.qh"
-#include "cheats.qh"
-#include "client.qh"
-#include "command/common.qh"
-#include "command/getreplies.qh"
-#include "command/sv_cmd.qh"
-#include "command/vote.qh"
-#include "hook.qh"
-#include <server/gamelog.qh>
+#include <common/constants.qh>
+#include <common/deathtypes/all.qh>
+#include <common/gamemodes/_mod.qh>
+#include <common/gamemodes/sv_rules.qh>
+#include <common/items/_mod.qh>
+#include <common/mapinfo.qh>
+#include <common/mapobjects/target/music.qh>
+#include <common/mapobjects/trigger/hurt.qh>
+#include <common/mapobjects/trigger/secret.qh>
+#include <common/mapobjects/triggers.qh>
+#include <common/monsters/_mod.qh>
+#include <common/monsters/sv_monsters.qh>
+#include <common/net_linked.qh>
+#include <common/notifications/all.qh>
+#include <common/physics/player.qh>
+#include <common/playerstats.qh>
+#include <common/state.qh>
+#include <common/stats.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+#include <common/vehicles/all.qh>
+#include <common/weapons/_all.qh>
+#include <server/anticheat.qh>
+#include <server/antilag.qh>
+#include <server/bot/api.qh>
+#include <server/campaign.qh>
+#include <server/cheats.qh>
+#include <server/client.qh>
+#include <server/command/common.qh>
+#include <server/command/getreplies.qh>
+#include <server/command/sv_cmd.qh>
+#include <server/command/vote.qh>
#include <server/damage.qh>
-#include "ipban.qh"
+#include <server/gamelog.qh>
+#include <server/hook.qh>
#include <server/intermission.qh>
+#include <server/ipban.qh>
+#include <server/items/items.qh>
#include <server/main.qh>
-#include "mapvoting.qh"
+#include <server/mapvoting.qh>
#include <server/mutators/_mod.qh>
-#include "race.qh"
-#include "scores.qh"
-#include "scores_rules.qh"
-#include "spawnpoints.qh"
-#include "teamplay.qh"
-#include "weapons/weaponstats.qh"
+#include <server/race.qh>
+#include <server/scores.qh>
+#include <server/scores_rules.qh>
+#include <server/spawnpoints.qh>
+#include <server/teamplay.qh>
#include <server/weapons/common.qh>
-#include "../common/constants.qh"
-#include <common/net_linked.qh>
-#include "../common/deathtypes/all.qh"
-#include <common/gamemodes/_mod.qh>
-#include "../common/gamemodes/sv_rules.qh"
-#include "../common/mapinfo.qh"
-#include "../common/monsters/_mod.qh"
-#include "../common/monsters/sv_monsters.qh"
-#include "../common/vehicles/all.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/mapobjects/triggers.qh>
-#include "../common/mapobjects/trigger/secret.qh"
-#include "../common/mapobjects/target/music.qh"
-#include "../common/util.qh"
-#include "../common/items/_mod.qh"
-#include <common/weapons/_all.qh>
-#include "../common/state.qh"
+#include <server/weapons/weaponstats.qh>
const float LATENCY_THINKRATE = 10;
.float latency_sum;
{
WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
WriteByte(MSG_BROADCAST, this.cnt);
- WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 65535));
+ WriteShort(MSG_BROADCAST, bound(1, rint(CS(e).ping), 32767));
WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_packetloss * 255), 255));
WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_movementloss * 255), 255));
}
const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
-float world_initialized;
void SetDefaultAlpha()
{
#define BADPREFIX(p) if(substring(k, 0, strlen(p)) == p) continue
#define BADPRESUFFIX(p,s) if(substring(k, 0, strlen(p)) == p && substring(k, -strlen(s), -1) == s) continue
#define BADCVAR(p) if(k == p) continue
+#define BADVALUE(p, val) if (k == p && v == val) continue
// general excludes and namespaces for server admin used cvars
BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
BADCVAR("g_chatsounds");
BADCVAR("g_ca_point_leadlimit");
BADCVAR("g_ca_point_limit");
+ BADCVAR("g_ca_spectate_enemies");
BADCVAR("g_ctf_captimerecord_always");
BADCVAR("g_ctf_flag_glowtrails");
BADCVAR("g_ctf_dynamiclights");
BADCVAR("g_ctf_flag_pickup_verbosename");
+ BADCVAR("g_ctf_flagcarrier_auto_helpme_damage");
BADPRESUFFIX("g_ctf_flag_", "_model");
BADPRESUFFIX("g_ctf_flag_", "_skin");
BADCVAR("g_domination_point_leadlimit");
BADCVAR("sv_minigames");
BADCVAR("sv_namechangetimer");
BADCVAR("sv_precacheplayermodels");
+ BADCVAR("sv_qcphysics");
BADCVAR("sv_radio");
BADCVAR("sv_stepheight");
BADCVAR("sv_timeout");
BADPREFIX("skill_");
BADPREFIX("sv_allow_");
BADPREFIX("sv_cullentities_");
- BADPREFIX("sv_maxidle_");
+ BADPREFIX("sv_maxidle");
BADPREFIX("sv_minigames_");
BADPREFIX("sv_radio_");
BADPREFIX("sv_timeout_");
BADCVAR("g_ctf_leaderboard");
BADCVAR("g_domination_point_limit");
BADCVAR("g_domination_teams_override");
+ BADCVAR("g_freezetag_revive_spawnshield");
BADCVAR("g_freezetag_teams_override");
BADCVAR("g_friendlyfire");
BADCVAR("g_fullbrightitems");
BADCVAR("g_physics_clientselect");
BADCVAR("g_pinata");
BADCVAR("g_powerups");
+ BADCVAR("g_powerups_drop_ondeath");
BADCVAR("g_player_brightness");
BADCVAR("g_rocket_flying");
BADCVAR("g_rocket_flying_disabledelays");
- BADCVAR("g_spawnshieldtime");
+ BADPREFIX("g_spawnshield");
BADCVAR("g_start_delay");
BADCVAR("g_superspectate");
BADCVAR("g_tdm_teams_override");
BADCVAR("sv_defaultplayercolors");
BADCVAR("sv_defaultplayermodel");
BADCVAR("sv_defaultplayerskin");
- BADCVAR("sv_maxidle");
BADCVAR("sv_maxrate");
BADCVAR("sv_motd");
BADCVAR("sv_public");
- BADCVAR("sv_ready_restart");
+ BADCVAR("sv_showfps");
BADCVAR("sv_status_privacy");
BADCVAR("sv_taunt");
BADCVAR("sv_vote_call");
BADCVAR("sv_vote_master_commands");
BADCVAR("sv_vote_master_password");
BADCVAR("sv_vote_simple_majority_factor");
+ BADVALUE("sys_ticrate", "0.0166667");
+ BADVALUE("sys_ticrate", "0.0333333");
BADCVAR("teamplay_mode");
BADCVAR("timelimit_override");
BADPREFIX("g_warmup_");
BADCVAR("g_lms_weaponarena");
BADCVAR("g_ctf_stalemate_time");
- if(cvar_string("g_mod_balance") == "Testing")
- {
- // (temporary) while using the Testing balance, any weapon balance cvars are allowed to be changed
- BADPREFIX("g_balance_");
- }
-
#undef BADPRESUFFIX
#undef BADPREFIX
#undef BADCVAR
+#undef BADVALUE
if(pureadding)
{
delete_fn = remove_unsafely;
- entity e = spawn();
+ entity e = new(GotoFirstMap);
setthink(e, GotoFirstMap);
e.nextthink = time; // this is usually 1 at this point
cvar_changes_init(); // do this very early now so it REALLY matches the server config
+ // default to RACE_RECORD, can be overwritten by gamemodes
+ record_type = RACE_RECORD;
+
// needs to be done so early because of the constants they create
static_init();
// character set: ASCII 33-126 without the following characters: : ; ' " \ $
if(autocvar_sv_eventlog)
{
- string s = sprintf("%s.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000));
+ string num = strftime_s(); // strftime(false, "%s") isn't reliable, see strftime_s description
+ string s = sprintf("%s.%s.%06d", itos(autocvar_sv_eventlog_files_counter), num, floor(random() * 1000000));
matchid = strzone(s);
GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s));
if(autocvar_g_norecoil)
s = strcat(s, ":norecoil");
- // TODO to mutator system
- if(autocvar_g_powerups == 0)
- s = strcat(s, ":no_powerups");
- if(autocvar_g_powerups > 0)
- s = strcat(s, ":powerups");
-
GameLogEcho(s);
GameLogEcho(":gameinfo:end");
}
maplist_reply = strzone(getmaplist());
lsmaps_reply = strzone(getlsmaps());
monsterlist_reply = strzone(getmonsterlist());
+ bool records_available = false;
for(int i = 0; i < 10; ++i)
{
string s = getrecords(i);
- if (s)
+ if (s != "")
+ {
records_reply[i] = strzone(s);
+ records_available = true;
+ }
}
+ if (!records_available)
+ records_reply[0] = "No records available for the current game mode.\n";
ladder_reply = strzone(getladder());
rankings_reply = strzone(getrankings());
// fill sv_curl_serverpackages from .serverpackage files
if (autocvar_sv_curl_serverpackages_auto)
{
- string s = "csprogs-" WATERMARK ".txt";
+ string s = "csprogs-" WATERMARK ".dat";
// 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)
{
delete(this);
}
+bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance)
+{
+ float m = e.dphitcontentsmask;
+ e.dphitcontentsmask = goodcontents | badcontents;
+
+ vector org = boundmin;
+ vector delta = boundmax - boundmin;
+
+ vector start, end;
+ start = end = org;
+ int j; // used after the loop
+ for(j = 0; j < attempts; ++j)
+ {
+ start.x = org.x + random() * delta.x;
+ start.y = org.y + random() * delta.y;
+ start.z = org.z + random() * delta.z;
+
+ // rule 1: start inside world bounds, and outside
+ // solid, and don't start from somewhere where you can
+ // fall down to evil
+ tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
+ if (trace_fraction >= 1)
+ continue;
+ if (trace_startsolid)
+ continue;
+ if (trace_dphitcontents & badcontents)
+ continue;
+ if (trace_dphitq3surfaceflags & badsurfaceflags)
+ continue;
+
+ // rule 2: if we are too high, lower the point
+ if (trace_fraction * delta.z > maxaboveground)
+ start = trace_endpos + '0 0 1' * maxaboveground;
+ vector enddown = trace_endpos;
+
+ // rule 3: make sure we aren't outside the map. This only works
+ // for somewhat well formed maps. A good rule of thumb is that
+ // the map should have a convex outside hull.
+ // these can be traceLINES as we already verified the starting box
+ vector mstart = start + 0.5 * (e.mins + e.maxs);
+ traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+
+ // rule 4: we must "see" some spawnpoint or item
+ entity sp = NULL;
+ IL_EACH(g_spawnpoints, checkpvs(mstart, it),
+ {
+ if((traceline(mstart, it.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
+ {
+ sp = it;
+ break;
+ }
+ });
+ if(!sp)
+ {
+ int items_checked = 0;
+ IL_EACH(g_items, checkpvs(mstart, it),
+ {
+ if((traceline(mstart, it.origin + (it.mins + it.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
+ {
+ sp = it;
+ break;
+ }
+
+ ++items_checked;
+ if(items_checked >= attempts)
+ break; // sanity
+ });
+
+ if(!sp)
+ continue;
+ }
+
+ // find a random vector to "look at"
+ end.x = org.x + random() * delta.x;
+ end.y = org.y + random() * delta.y;
+ end.z = org.z + random() * delta.z;
+ end = start + normalize(end - start) * vlen(delta);
+
+ // rule 4: start TO end must not be too short
+ tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
+ if(trace_startsolid)
+ continue;
+ if(trace_fraction < minviewdistance / vlen(delta))
+ continue;
+
+ // rule 5: don't want to look at sky
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
+ continue;
+
+ // rule 6: we must not end up in trigger_hurt
+ if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
+ continue;
+
+ break;
+ }
+
+ e.dphitcontentsmask = m;
+
+ if(j < attempts)
+ {
+ setorigin(e, start);
+ e.angles = vectoangles(end - start);
+ LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
+ return true;
+ }
+ return false;
+}
+
+float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
+{
+ return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance);
+}
+
/*
===============================================================================
s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
if(to_console)
- LOG_INFO(s);
+ LOG_HELP(s);
if(to_eventlog)
GameLogEcho(s);
s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
if(to_console)
- LOG_INFO(s);
+ LOG_HELP(s);
if(to_eventlog)
GameLogEcho(s);
if(to_file)
FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), {
s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
- if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it))
+ if(IS_PLAYER(it) || INGAME_JOINED(it))
s = strcat(s, ftos(it.team), ":");
else
s = strcat(s, "spectator:");
if(to_console)
- LOG_INFO(s, playername(it.netname, it.team, false));
+ LOG_HELP(s, playername(it.netname, it.team, false));
if(to_eventlog)
GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false)));
if(to_file)
{
s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
if(to_console)
- LOG_INFO(s);
+ LOG_HELP(s);
if(to_eventlog)
GameLogEcho(s);
if(to_file)
s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
s = strcat(s, ":", ftos(i));
if(to_console)
- LOG_INFO(s);
+ LOG_HELP(s);
if(to_eventlog)
GameLogEcho(s);
if(to_file)
}
if(to_console)
- LOG_INFO(":end");
+ LOG_HELP(":end");
if(to_eventlog)
GameLogEcho(":end");
if(to_file)
void NextLevel()
{
game_stopped = true;
- intermission_running = 1; // game over
+ intermission_running = true; // game over
// enforce a wait time before allowing changelevel
if(player_count > 0)
GameLogClose();
- FOREACH_CLIENT(IS_PLAYER(it), {
+ int winner_team = 0;
+ FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
FixIntermissionClient(it);
if(it.winning)
- bprint(playername(it.netname, it.team, false), " ^7wins.\n");
+ {
+ if (teamplay && !winner_team)
+ {
+ winner_team = it.team;
+ bprint(Team_ColorCode(winner_team), Team_ColorName_Upper(winner_team), "^7 team wins the match\n");
+ }
+ bprint(playername(it.netname, it.team, false), " ^7wins\n");
+ }
});
target_music_kill();
// set the .winning flag for exactly those players with a given field value
void SetWinners(.float field, float value)
{
- FOREACH_CLIENT(IS_PLAYER(it), { it.winning = (it.(field) == value); });
+ FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = (it.(field) == value); });
}
// set the .winning flag for those players with a given field value
void AddWinners(.float field, float value)
{
- FOREACH_CLIENT(IS_PLAYER(it), {
+ FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
if(it.(field) == value)
it.winning = 1;
});
// clear the .winning flags
void ClearWinners()
{
- FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
+ FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
}
int fragsleft_last;
if(readyplayers || playerswithlaps >= 2)
{
checkrules_suddendeathend = 0;
- ReadyRestart(); // go to race
+ ReadyRestart(true); // go to race
return;
}
else
start_ammo_plasma = 0;
if (random_start_ammo == NULL)
{
- random_start_ammo = spawn();
+ random_start_ammo = new_pure(random_start_ammo);
}
start_health = cvar("g_balance_health_start");
start_armorvalue = cvar("g_balance_armor_start");
random_start_weapons_count = cvar("g_random_start_weapons_count");
SetResource(random_start_ammo, RES_SHELLS, cvar("g_random_start_shells"));
SetResource(random_start_ammo, RES_BULLETS, cvar("g_random_start_bullets"));
- SetResource(random_start_ammo, RES_ROCKETS,cvar("g_random_start_rockets"));
+ SetResource(random_start_ammo, RES_ROCKETS, cvar("g_random_start_rockets"));
SetResource(random_start_ammo, RES_CELLS, cvar("g_random_start_cells"));
SetResource(random_start_ammo, RES_PLASMA, cvar("g_random_start_plasma"));
}
start_ammo_cells = max(0, start_ammo_cells);
start_ammo_plasma = max(0, start_ammo_plasma);
start_ammo_fuel = max(0, start_ammo_fuel);
- SetResource(random_start_ammo, RES_SHELLS,
- max(0, GetResource(random_start_ammo, RES_SHELLS)));
- SetResource(random_start_ammo, RES_BULLETS,
- max(0, GetResource(random_start_ammo, RES_BULLETS)));
- SetResource(random_start_ammo, RES_ROCKETS,
- max(0, GetResource(random_start_ammo, RES_ROCKETS)));
- SetResource(random_start_ammo, RES_CELLS,
- max(0, GetResource(random_start_ammo, RES_CELLS)));
- SetResource(random_start_ammo, RES_PLASMA,
- max(0, GetResource(random_start_ammo, RES_PLASMA)));
+ SetResource(random_start_ammo, RES_SHELLS, max(0, GetResource(random_start_ammo, RES_SHELLS)));
+ SetResource(random_start_ammo, RES_BULLETS, max(0, GetResource(random_start_ammo, RES_BULLETS)));
+ SetResource(random_start_ammo, RES_ROCKETS, max(0, GetResource(random_start_ammo, RES_ROCKETS)));
+ SetResource(random_start_ammo, RES_CELLS, max(0, GetResource(random_start_ammo, RES_CELLS)));
+ SetResource(random_start_ammo, RES_PLASMA, max(0, GetResource(random_start_ammo, RES_PLASMA)));
warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
}
bool autocvar_sv_gameplayfix_multiplethinksperframe = true;
-void RunThink(entity this)
+void RunThink(entity this, float dt)
{
// don't let things stay in the past.
// it is possible to start that way by a trigger with a local time.
- if(this.nextthink <= 0 || this.nextthink > time + frametime)
+ if(this.nextthink <= 0 || this.nextthink > time + dt)
return;
float oldtime = time; // do we need to save this?
// we don't want to loop in that case, so exit if the new nextthink is
// <= the time the qc was told, also exit if it is past the end of the
// frame
- if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe)
+ if(this.nextthink <= time || this.nextthink > oldtime + dt || !autocvar_sv_gameplayfix_multiplethinksperframe)
break;
}
if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
continue; // these movetypes have no regular think function
// handle thinking here
- if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime)
- RunThink(it);
+ if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + PHYS_INPUT_TIMELENGTH)
+ RunThink(it, PHYS_INPUT_TIMELENGTH);
}
});
STAT(TYPEHIT_TIME, it) = time;
} else if (e.killsound) {
STAT(KILL_TIME, it) = time;
- } else if (e.damage_dealt) {
+ } else if (e.hitsound_damage_dealt) {
STAT(HIT_TIME, it) = time;
- STAT(DAMAGE_DEALT_TOTAL, it) += ceil(e.damage_dealt);
+ // NOTE: this is not accurate as client code doesn't need so much accuracy for its purposes
+ STAT(HITSOUND_DAMAGE_DEALT_TOTAL, it) += ceil(e.hitsound_damage_dealt);
}
});
// add 1 frametime because after this, engine SV_Physics
float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
FOREACH_CLIENT(true, {
it.typehitsound = false;
- it.damage_dealt = 0;
+ it.hitsound_damage_dealt = 0;
it.killsound = false;
antilag_record(it, CS(it), altime);
});