e.mins_y = -e.maxs.x;
}
if(e.scale)
- setsize(e, e.mins * e.scale, e.maxs * e.scale);
+ setsize(e, RoundPerfectVector(e.mins * e.scale), RoundPerfectVector(e.maxs * e.scale));
else
setsize(e, e.mins, e.maxs);
}
if((mon.spawnflags & MONSTER_SIZE_QUAKE) && autocvar_g_monsters_quake_resize && !(this.spawnflags & MONSTERFLAG_RESPAWNED))
this.scale *= 1.3;
- setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale);
+ setsize(this, RoundPerfectVector(mon.m_mins * this.scale), RoundPerfectVector(mon.m_maxs * this.scale));
this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);
{
e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
_setmodel(e, e.model); // reset mins and maxs based on mesh
- setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
+ // apply object scaling and prevent any float precision issues like #2742
+ setsize(e, RoundPerfectVector(e.mins * e.scale), RoundPerfectVector(e.maxs * e.scale));
}
}
bound(mi.z, org.z, ma.z)
);
}
+
+// bones_was_here: rounding bbox to nearest perfect floats prevents obscure collision bugs like #2742
+// FIXME: QC shouldn't need to work around tracebox potentially returning a tiny trace_fraction when the move should have been blocked.
+// Tiny values are valid in some situations and can't simply be ignored.
+#define PFLOAT (1/1024) // 1/32 1/64 etc also work
+#define RPFLOAT(a) (a=rint(a/PFLOAT)*PFLOAT)
+ERASEABLE
+vector RoundPerfectVector(vector v)
+{
+ RPFLOAT(v.x); RPFLOAT(v.y); RPFLOAT(v.z);
+ return v;
+}
+
#endif