BADCVAR("g_duel_not_dm_maps");
BADCVAR("g_freezetag");
BADCVAR("g_freezetag_teams");
- BADCVAR("g_invasion_teams");
BADCVAR("g_invasion_type");
BADCVAR("g_jailbreak");
BADCVAR("g_jailbreak_teams");
BADCVAR("nextmap");
BADCVAR("teamplay");
BADCVAR("timelimit");
+ BADCVAR("g_mapinfo_q3compat");
BADCVAR("g_mapinfo_settemp_acl");
BADCVAR("g_mapinfo_ignore_warnings");
BADCVAR("g_maplist_ignore_sizes");
BADCVAR("g_maplist");
BADCVAR("g_maplist_mostrecent");
BADCVAR("sv_motd");
+ BADCVAR("sv_termsofservice_url");
v = cvar_string(k);
d = cvar_defstring(k);
// does nothing gameplay relevant
BADCVAR("captureleadlimit_override");
BADCVAR("condump_stripcolors");
- BADCVAR("gameversion");
BADCVAR("fs_gamedir");
BADCVAR("g_allow_oldvortexbeam");
BADCVAR("g_balance_kill_delay");
BADCVAR("w_prop_interval");
BADPREFIX("chat_");
BADPREFIX("crypto_");
- BADPREFIX("gameversion_");
+ BADPREFIX("gameversion");
BADPREFIX("g_chat_");
BADPREFIX("g_ctf_captimerecord_");
BADPREFIX("g_hats_");
BADCVAR("g_start_delay");
BADCVAR("g_superspectate");
BADCVAR("g_tdm_teams_override");
- BADCVAR("g_warmup");
BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
BADCVAR("hostname");
BADCVAR("log_file");
BADVALUE("sys_ticrate", "0.0333333");
BADCVAR("teamplay_mode");
BADCVAR("timelimit_override");
- BADPREFIX("g_warmup_");
+ BADPREFIX("g_warmup");
BADPREFIX("sv_info_");
BADPREFIX("sv_ready_restart_");
BADCVAR("g_grappling_hook");
BADCVAR("g_jetpack");
- // temporary for testing
- // TODO remove before 0.8.3 release
- BADCVAR("g_ca_weaponarena");
- BADCVAR("g_freezetag_weaponarena");
- BADCVAR("g_lms_weaponarena");
- BADCVAR("g_ctf_stalemate_time");
-
#undef BADPRESUFFIX
#undef BADPREFIX
#undef BADCVAR
e = new(info_player_deathmatch); // safeguard against player joining
- // assign reflectively to avoid "assignment to world" warning
- for (int i = 0, n = numentityfields(); i < n; ++i) {
- string k = entityfieldname(i);
- if (k == "classname") {
- // safeguard against various stuff ;)
- putentityfieldstring(i, this, "worldspawn");
- break;
- }
- }
+ // assign reflectively to avoid "assignment to world" warning
+ for (int i = 0, n = numentityfields(); i < n; ++i)
+ {
+ string k = entityfieldname(i);
+ if (k == "classname")
+ {
+ // safeguard against various stuff ;)
+ putentityfieldstring(i, this, "worldspawn");
+ break;
+ }
+ }
// needs to be done so early because of the constants they create
static_init();
void GameplayMode_DelayedInit(entity this)
{
+ // at this stage team entities are spawned, teamplay contains the number of them
+
if(!scores_initialized)
ScoreRules_generic();
+
+ if (warmup_stage >= 0 && autocvar_g_maxplayers >= 0)
+ return;
+ if (!g_duel)
+ MapReadSizes(mapname);
+
+ if (autocvar_g_maxplayers < 0 && teamplay)
+ {
+ // automatic maxplayers should be a multiple of team count
+ if (map_maxplayers == 0 || map_maxplayers > maxclients)
+ map_maxplayers = maxclients; // unlimited, but may need rounding
+ int d = map_maxplayers % AVAILABLE_TEAMS;
+ int u = AVAILABLE_TEAMS - d;
+ map_maxplayers += (u <= d && u + map_maxplayers <= maxclients) ? u : -d;
+ }
+
+ if (warmup_stage < 0)
+ {
+ int m = GetPlayerLimit();
+ if (m <= 0) m = maxclients;
+ map_minplayers = bound(max(2, AVAILABLE_TEAMS * 2), map_minplayers, m);
+ if (teamplay)
+ {
+ // automatic minplayers should be a multiple of team count
+ int d = map_minplayers % AVAILABLE_TEAMS;
+ int u = AVAILABLE_TEAMS - d;
+ map_minplayers += (u < d && u + map_minplayers <= m) ? u : -d;
+ }
+ warmup_limit = -1;
+ }
+ else
+ map_minplayers = 0; // don't display a minimum if it's not used
}
void InitGameplayMode()
// find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
get_mi_min_max(1);
// assign reflectively to avoid "assignment to world" warning
- int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
- string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
- if (v) {
- putentityfieldstring(i, world, sprintf("%v", v));
- if (++done == 2) break;
- }
+ for (int i = 0, done = 0, n = numentityfields(); i < n; ++i)
+ {
+ string k = entityfieldname(i);
+ vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
+ if (v)
+ {
+ putentityfieldstring(i, world, sprintf("%v", v));
+ if (++done == 2) break;
+ }
}
// currently, NetRadiant's limit is 131072 qu for each side
// distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
bool world_already_spawned;
spawnfunc(worldspawn)
{
+ cvar_set("_endmatch", "0");
server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
if (autocvar_sv_termsofservice_url && autocvar_sv_termsofservice_url != "")
GameRules_limit_fallbacks();
if(warmup_limit == 0)
- warmup_limit = (autocvar_timelimit > 0) ? autocvar_timelimit * 60 : autocvar_timelimit;
+ warmup_limit = autocvar_timelimit * 60;
player_count = 0;
bot_waypoints_for_items = autocvar_g_waypoints_for_items;
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
- q3compat = BITSET(q3compat, Q3COMPAT_ARENA, fexists(strcat("scripts/", mapname, ".arena")));
- q3compat = BITSET(q3compat, Q3COMPAT_DEFI, fexists(strcat("scripts/", mapname, ".defi")));
+ q3compat = BITSET(q3compat, Q3COMPAT_ARENA, _MapInfo_FindArenaFile(mapname, ".arena") != "");
+ q3compat = BITSET(q3compat, Q3COMPAT_DEFI, _MapInfo_FindArenaFile(mapname, ".defi") != "");
+
+ // quake 3 music support
+ if(world.music || world.noise)
+ {
+ // prefer .music over .noise
+ string chosen_music;
+ if(world.music)
+ chosen_music = world.music;
+ else
+ chosen_music = world.noise;
+
+ string newstuff = strcat(clientstuff, "cd loop \"", chosen_music, "\"\n");
+ strcpy(clientstuff, newstuff);
+ }
if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
{
delete(this);
}
-bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance)
+bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
{
- 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;
+ 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;
- }
- });
+ entity sp = NULL;
+ if(frompos)
+ {
+ if((traceline(mstart, e.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
+ sp = e;
+ }
+ if(!sp)
+ {
+ 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;
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;
+ // 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);
+ return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance, false);
}
/*
void readlevelcvars()
{
+ serverflags &= ~SERVERFLAG_ALLOW_FULLBRIGHT;
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
- g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
- if(!g_weapon_stay)
- g_weapon_stay = cvar("g_weapon_stay");
+ g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
+ if(!g_weapon_stay)
+ g_weapon_stay = cvar("g_weapon_stay");
- MUTATOR_CALLHOOK(ReadLevelCvars);
+ MUTATOR_CALLHOOK(ReadLevelCvars);
if (!warmup_stage && !autocvar_g_campaign)
game_starttime = time + cvar("g_start_delay");
void InitializeEntity(entity e, void(entity this) func, int order)
{
- entity prev, cur;
-
- if (!e || e.initialize_entity)
- {
- // make a proxy initializer entity
- entity e_old = e;
- e = new(initialize_entity);
- e.enemy = e_old;
- }
-
- e.initialize_entity = func;
- e.initialize_entity_order = order;
-
- cur = initialize_entity_first;
- prev = NULL;
- for (;;)
- {
- if (!cur || cur.initialize_entity_order > order)
- {
- // insert between prev and cur
- if (prev)
- prev.initialize_entity_next = e;
- else
- initialize_entity_first = e;
- e.initialize_entity_next = cur;
- return;
- }
- prev = cur;
- cur = cur.initialize_entity_next;
- }
+ entity prev, cur;
+
+ if (!e || e.initialize_entity)
+ {
+ // make a proxy initializer entity
+ entity e_old = e;
+ e = new(initialize_entity);
+ e.enemy = e_old;
+ }
+
+ e.initialize_entity = func;
+ e.initialize_entity_order = order;
+
+ cur = initialize_entity_first;
+ prev = NULL;
+ for (;;)
+ {
+ if (!cur || cur.initialize_entity_order > order)
+ {
+ // insert between prev and cur
+ if (prev)
+ prev.initialize_entity_next = e;
+ else
+ initialize_entity_first = e;
+ e.initialize_entity_next = cur;
+ return;
+ }
+ prev = cur;
+ cur = cur.initialize_entity_next;
+ }
}
void InitializeEntitiesRun()
{
- entity startoflist = initialize_entity_first;
- initialize_entity_first = NULL;
- delete_fn = remove_except_protected;
- for (entity e = startoflist; e; e = e.initialize_entity_next)
- {
+ entity startoflist = initialize_entity_first;
+ initialize_entity_first = NULL;
+ delete_fn = remove_except_protected;
+ for (entity e = startoflist; e; e = e.initialize_entity_next)
+ {
e.remove_except_protected_forbidden = 1;
- }
- for (entity e = startoflist; e; )
- {
+ }
+ for (entity e = startoflist; e; )
+ {
e.remove_except_protected_forbidden = 0;
- e.initialize_entity_order = 0;
- entity next = e.initialize_entity_next;
- e.initialize_entity_next = NULL;
- var void(entity this) func = e.initialize_entity;
- e.initialize_entity = func_null;
- if (e.classname == "initialize_entity")
- {
- entity wrappee = e.enemy;
- builtin_remove(e);
- e = wrappee;
- }
- //dprint("Delayed initialization: ", e.classname, "\n");
- if (func)
- {
- func(e);
- }
- else
- {
- eprint(e);
- backtrace(strcat("Null function in: ", e.classname, "\n"));
- }
- e = next;
- }
- delete_fn = remove_unsafely;
+ e.initialize_entity_order = 0;
+ entity next = e.initialize_entity_next;
+ e.initialize_entity_next = NULL;
+ var void(entity this) func = e.initialize_entity;
+ e.initialize_entity = func_null;
+ if (e.classname == "initialize_entity")
+ {
+ entity wrappee = e.enemy;
+ builtin_remove(e);
+ e = wrappee;
+ }
+ //dprint("Delayed initialization: ", e.classname, "\n");
+ if (func)
+ {
+ func(e);
+ }
+ else
+ {
+ eprint(e);
+ backtrace(strcat("Null function in: ", e.classname, "\n"));
+ }
+ e = next;
+ }
+ delete_fn = remove_unsafely;
}
// deferred dropping
+// ported from VM_SV_droptofloor TODO: make a common function for the client-side?
void DropToFloor_Handler(entity this)
{
- WITHSELF(this, builtin_droptofloor());
+ if(!this || wasfreed(this))
+ {
+ // no modifying free entities
+ return;
+ }
+
+ vector end = this.origin - '0 0 256';
+
+ // NOTE: NudgeOutOfSolid support is not added as Xonotic's physics do not use it!
+ //if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+ //SV_NudgeOutOfSolid(this);
+
+ tracebox(this.origin, this.mins, this.maxs, end, MOVE_NORMAL, this);
+
+ if(trace_startsolid && autocvar_sv_gameplayfix_droptofloorstartsolid)
+ {
+ vector offset, org;
+ offset = 0.5 * (this.mins + this.maxs);
+ offset.z = this.mins.z;
+ org = this.origin + offset;
+ traceline(org, end, MOVE_NORMAL, this);
+ trace_endpos = trace_endpos - offset;
+ if(trace_startsolid)
+ {
+ LOG_DEBUGF("DropToFloor_Handler: %v could not fix badly placed entity", this.origin);
+ _Movetype_LinkEdict(this, false);
+ SET_ONGROUND(this);
+ this.groundentity = NULL;
+ }
+ else if(trace_fraction < 1)
+ {
+ LOG_DEBUGF("DropToFloor_Handler: %v fixed badly placed entity", this.origin);
+ //if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+ //SV_NudgeOutOfSolid(this);
+ setorigin(this, trace_endpos);
+ SET_ONGROUND(this);
+ this.groundentity = trace_ent;
+ // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+ this.move_suspendedinair = true;
+ }
+ }
+ else
+ {
+ if(!trace_allsolid && trace_fraction < 1)
+ {
+ setorigin(this, trace_endpos);
+ SET_ONGROUND(this);
+ this.groundentity = trace_ent;
+ // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+ this.move_suspendedinair = true;
+ }
+ }
this.dropped_origin = this.origin;
}