X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fworld.qc;h=cfaec58e38cd5dc17413cfa28b1d5d497d5c2d34;hb=4225b1769a3a1328f9564719a6f363700d6fcbfc;hp=4334cb422e3281c450a08034d14557046a80eac3;hpb=942859a1be813eba450a0fb18ef6b1d0d2bbdccc;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 4334cb422..cfaec58e3 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -590,15 +591,17 @@ spawnfunc(__init_dedicated_server) 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(); @@ -670,12 +673,15 @@ 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 @@ -1044,59 +1050,59 @@ spawnfunc(light) 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; @@ -1136,40 +1142,40 @@ bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax 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) @@ -2094,6 +2100,10 @@ void readlevelcvars() if(cvar("sv_allow_fullbright")) serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; + serverflags &= ~SERVERFLAG_FORBID_PICKUPTIMER; + if(cvar("sv_forbid_pickuptimer")) + serverflags |= SERVERFLAG_FORBID_PICKUPTIMER; + sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown"); warmup_stage = cvar("g_warmup"); @@ -2158,11 +2168,11 @@ void readlevelcvars() 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"); @@ -2174,73 +2184,73 @@ void readlevelcvars() 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 @@ -2253,11 +2263,20 @@ void DropToFloor_Handler(entity this) return; } - vector end = this.origin - '0 0 256'; + vector end = this.origin; + if (autocvar_sv_mapformat_is_quake3) + end.z -= 4096; + else if (autocvar_sv_mapformat_is_quake2) + end.z -= 128; + else + end.z -= 256; // Quake, QuakeWorld - // NOTE: NudgeOutOfSolid support is not added as Xonotic's physics do not use it! - //if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect) - //SV_NudgeOutOfSolid(this); + // NOTE: SV_NudgeOutOfSolid is used in the engine here + if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect) + { + _Movetype_UnstickEntity(this); + move_out_of_solid(this); + } tracebox(this.origin, this.mins, this.maxs, end, MOVE_NORMAL, this); @@ -2279,9 +2298,12 @@ void DropToFloor_Handler(entity this) 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); + if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect) + { + _Movetype_UnstickEntity(this); + move_out_of_solid(this); + } SET_ONGROUND(this); this.groundentity = trace_ent; // if support is destroyed, keep suspended (gross hack for floating items in various maps) @@ -2298,6 +2320,12 @@ void DropToFloor_Handler(entity this) // if support is destroyed, keep suspended (gross hack for floating items in various maps) this.move_suspendedinair = true; } + else + { + // if we can't get the entity out of solid, mark it as on ground so physics doesn't attempt to drop it + // hacky workaround for #2774 + SET_ONGROUND(this); + } } this.dropped_origin = this.origin; }