]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Rewrite the QC port of droptofloor()
authorbones_was_here <bones_was_here@xonotic.au>
Mon, 18 Sep 2023 12:12:01 +0000 (22:12 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Thu, 14 Mar 2024 22:14:26 +0000 (08:14 +1000)
This uses DP_QC_NUDGEOUTOFSOLID to achieve better results (especially
noticeable with sv_legacy_bbox_expand 0) in less CPU time.

Documents the design.
Removes legacy code paths.
Adds warnings for badly placed items.
Implements Quake-compliant behaviour for each BSP format.

See also: https://gitlab.com/xonotic/darkplaces/-/merge_requests/144

Removes hacky workaround for #2774 and thus depends on !1245

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
qcsrc/server/world.qc
qcsrc/server/world.qh

index 7bbbfa0dd7d9152f8e096f3a778ee0639059bfde..1562d00a2c893d6c134edc37764dc95b8e8c066f 100644 (file)
@@ -2300,16 +2300,55 @@ void InitializeEntitiesRun()
        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)
+// originally ported from DP's droptofloor() builtin
+// TODO: make a common function for the client-side?
+// bones_was_here: when we have a use case for it, yes
+void DropToFloor_QC(entity this)
 {
+       int nudgeresult;
+
        if(!this || wasfreed(this))
        {
-               // no modifying free entities
+               LOG_WARN("DropToFloor_QC: can not modify free entity");
                return;
        }
 
+       /* Prior to sv_legacy_bbox_expand 0, both droptofloor and nudgeoutofsolid were done for items
+        * using box '-16 -16 0' '16 16 48' (without the FL_ITEM expansion applied),
+        * which often resulted in bboxes partially in solids once expansion was applied.
+        * We don't want bboxes in solids (bad for gameplay and culling),
+        * but we also don't want items to land on a "skirting board" or the base of a sloping wall.
+        * For initial nudgeoutofsolid and droptofloor stages we use a small box
+        * so they fall as far and in the same place as they traditionally would,
+        * then we nudge the full size box out of solid, in a direction appropriate for the plane(s).
+        */
+       vector saved_mins = this.mins; // gmqcc's used-uninitialized check doesn't handle
+       vector saved_maxs = this.maxs; // making these assignments FL_ITEM conditional.
+       if (this.flags & FL_ITEM)
+       {
+               // Using the Q3 bbox for best compatibility with all maps, except...
+               this.mins.x = -15;
+               this.mins.y = -15;
+               this.maxs.x = 15;
+               this.maxs.y = 15;
+               this.maxs.z = this.mins.z + 30; // ...Nex, Xon and Quake use a different vertical offset, see also: StartItem()
+       }
+
+       /* NOTE: sv_gameplayfix_droptofloorstartsolid_nudgetocorrect isn't checked, so it won't need to be networked to CSQC.
+        * It was enabled by default in all Xonotic releases and in Nexuiz, so now certain maps depend on it.
+        * Example: on erbium 0.8.6 the shards @ crylink are too low (in collision with the floor),
+        * so without this those fall through the floor.
+        * Q3, Q2 and Quake don't try to move items out of solid.
+        */
+       if(!Q3COMPAT_COMMON && autocvar_sv_mapformat_is_quake3) // Xonotic, Nexuiz
+       {
+               nudgeresult = nudgeoutofsolid(this);
+               if (!nudgeresult)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX badly placed entity \"%s\" before drop", this.origin, this.classname);
+               else if (nudgeresult > 0)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" before drop", this.origin, this.classname);
+       }
+
        vector end = this.origin;
        if (autocvar_sv_mapformat_is_quake3)
                end.z -= 4096;
@@ -2317,69 +2356,68 @@ void DropToFloor_Handler(entity this)
                end.z -= 128;
        else
                end.z -= 256; // Quake, QuakeWorld
+       tracebox(this.origin, this.mins, this.maxs, end, MOVE_NOMONSTERS, this);
 
-       // NOTE: SV_NudgeOutOfSolid is used in the engine here
-       if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+       if (!autocvar_sv_mapformat_is_quake3 && !autocvar_sv_mapformat_is_quake2 && (trace_allsolid || trace_fraction == 1)) // Quake
        {
-               _Movetype_UnstickEntity(this);
-               move_out_of_solid(this);
+               // Quake games just delete badly placed entities...
+               LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): DELETING badly placed entity \"%s\"", this.origin, this.classname);
+               delete(this);
+               return;
        }
+       else if ((Q3COMPAT_COMMON || autocvar_sv_mapformat_is_quake2) && trace_startsolid) // Q3, Q2
+       {
+               // ...but we can't do that on Q3 maps like jamdm1
+               // because our tracebox hits things Q3's trace doesn't (patches?).
+               LOG_WARNF("DropToFloor_QC at \"%v\" (Quake 3 compat): badly placed entity \"%s\"", this.origin, this.classname);
+       }
+
+       /* NOTE: sv_gameplayfix_droptofloorstartsolid (fallback from tracebox to traceline) isn't implemented.
+        * It was disabled by default in all Xonotic releases and in Nexuiz.
+        * Q3 doesn't support it (always uses its '-15 -15 -15' '15 15 15' box when dropping items), neither does Quake or Q2.
+        */
 
-       tracebox(this.origin, this.mins, this.maxs, end, MOVE_NORMAL, this);
+       if (!autocvar_sv_mapformat_is_quake2) // Quake, Q3, Nexuiz, Xonotic
+               // allow to ride movers (or unset if in freefall)
+               this.groundentity = trace_ent;
 
-       if(trace_startsolid && autocvar_sv_gameplayfix_droptofloorstartsolid)
+       if (!autocvar_sv_mapformat_is_quake3)
+               // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+               // bones_was_here: is this for Q1BSP only? Which maps use it? Do we need it at all? Intentions unclear in DP...
+               this.move_suspendedinair = true;
+
+       if (trace_fraction)
+               this.origin = trace_endpos;
+
+       if (this.flags & FL_ITEM)
        {
-               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);
-                       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)
-                       this.move_suspendedinair = true;
-               }
+               this.mins = saved_mins;
+               this.maxs = saved_maxs;
+
+               // A side effect of using a small box to drop items (and do the initial nudge) is
+               // the full size box can end up in collision with a sloping floor or terrain model.
+               nudgeresult = nudgeoutofsolid(this);
+               // No warns for successful nudge because it would spam about items on slopes/terrain.
        }
-       else
+       else if (trace_allsolid && trace_fraction) // dropped using "proper" bbox but never left solid
        {
-               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;
-               }
-               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);
-               }
+               nudgeresult = nudgeoutofsolid(this);
+               if (nudgeresult > 0)
+                       LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" after drop", this.origin, this.classname);
        }
-       this.dropped_origin = this.origin;
+       else
+               nudgeresult = -1;
+
+       if (!nudgeresult)
+       if (!Q3COMPAT_COMMON) // to be expected on Q3 maps like gu3-pewter because we use bigger final bboxes
+               LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX stuck entity \"%s\" after drop", this.origin, this.classname);
+
+       setorigin(this, this.dropped_origin = this.origin);
 }
 
 void droptofloor(entity this)
 {
-       InitializeEntity(this, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
+       InitializeEntity(this, DropToFloor_QC, INITPRIO_DROPTOFLOOR);
 }
 
 bool autocvar_sv_gameplayfix_multiplethinksperframe = true;
index ff799e64cc529d7d406aebd5d929d12cb25b4766..67adb0fab77201a230537eff216c9790175ff656 100644 (file)
@@ -28,8 +28,6 @@ float autocvar_timelimit_max;
 float autocvar_timelimit_overtime;
 int autocvar_timelimit_overtimes;
 float autocvar_timelimit_suddendeath;
-bool autocvar_sv_gameplayfix_droptofloorstartsolid;
-bool autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect;
 
 bool autocvar_sv_mapformat_is_quake3;
 bool autocvar_sv_mapformat_is_quake2;