Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Game.Server/BaseAnimating.cs
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,11 @@ public virtual void HandleAnimEvent(ref AnimEvent animEvent) {
public TimeUnit_t LastEventCheck;
public TimeUnit_t GetCycle() => Cycle;
public void SetCycle(TimeUnit_t cycle) => Cycle = cycle;

public void SetFadeDistance(float minFadeDist, float maxFadeDist) {
FadeMinDist = minFadeDist;
FadeMaxDist = maxFadeDist;
}
public float GetSequenceMoveDist(StudioHdr? studioHdr, int sequence) {
Animation.GetSequenceLinearMotion(studioHdr, sequence, GetPoseParameterArray(), out Vector3 ret);

Expand Down
107 changes: 107 additions & 0 deletions Game.Server/Physics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,79 @@ public static float PhysGetEntityMass(BaseEntity entity) {
return otherMass;
}

class SkipKeys : IVPhysicsKeyHandler
{
public void ParseKeyValue<T>(T data, ReadOnlySpan<char> key, ReadOnlySpan<char> value) { }
public void SetDefaults<T>(T data) { }
}

public static void PhysSolidOverride(ref Solid solid, string? overrideScript) {
if (overrideScript != null) {
bool collisions = solid.Params.EnableCollisions;

StringBuilder tmpString = new("solid { ");

nexttoken(out ReadOnlySpan<char> token, overrideScript, ',', out ReadOnlySpan<char> str);
while (!token.IsEmpty) {
tmpString.Append('"');
tmpString.Append(token);
tmpString.Append("\" ");
nexttoken(out token, str, ',', out str);
}
tmpString.Append('}');

IVPhysicsKeyParser parse = physcollision.VPhysicsKeyParserCreate(Encoding.ASCII.GetBytes(tmpString.ToString()));
parse.ParseSolid(ref solid, new SkipKeys());
physcollision.VPhysicsKeyParserDestroy(parse);

solid.Params.EnableCollisions = collisions;
}
}

public static void PhysSetMassCenterOverride(in MassCenterOverride massOverride) {
if (massOverride.EntityName != null)
g_PhysicsHook.MassCenterOverrides.Add(massOverride);
}

public static int PhysGetMassCenterOverrideIndex(string? name) {
if (name != null && g_PhysicsHook.MassCenterOverrides.Count != 0) {
for (int i = 0; i < g_PhysicsHook.MassCenterOverrides.Count; i++) {
if (g_PhysicsHook.MassCenterOverrides[i].EntityName == name)
return i;
}
}
return -1;
}

public static void PhysGetMassCenterOverride(BaseEntity entity, VCollide? collide, ref Solid solidOut) {
int index = PhysGetMassCenterOverrideIndex(entity.GetEntityName());

if (index >= 0) {
MassCenterOverride massOverride = g_PhysicsHook.MassCenterOverrides[index];
Vector3 massCenterWS = massOverride.Center;
switch (massOverride.AlignType) {
case MassCenterOverride.Align.Point:
MathLib.VectorITransform(massCenterWS, entity.EntityToWorldTransform(), out solidOut.MassCenterOverride);
break;
case MassCenterOverride.Align.Axis: {
physcollision.CollideGetMassCenter(collide!.Solids![solidOut.Index], out Vector3 massCenterLocal);
MathLib.VectorTransform(massCenterLocal, entity.EntityToWorldTransform(), out Vector3 defaultMassCenterWS);
massCenterWS += massOverride.Axis *
(Vector3.Dot(defaultMassCenterWS, massOverride.Axis) - Vector3.Dot(massOverride.Axis, massOverride.Center));
MathLib.VectorITransform(massCenterWS, entity.EntityToWorldTransform(), out solidOut.MassCenterOverride);
}
break;
}
g_PhysicsHook.MassCenterOverrides[index] = g_PhysicsHook.MassCenterOverrides[^1];
g_PhysicsHook.MassCenterOverrides.RemoveAt(g_PhysicsHook.MassCenterOverrides.Count - 1);

if (solidOut.MassCenterOverride.Length() > DIST_EPSILON) {
Vector3[] massCenter = [solidOut.MassCenterOverride];
solidOut.Params.MassCenterOverrideFn = () => ref massCenter[0];
}
}
}

public static void PhysicsImpactSound(BaseEntity entity, IPhysicsObject physObject, SoundEntityChannel channel, int surfaceProps, int surfacePropsHit, float volume, float impactSpeed) {
PhysicsSound.AddImpactSound(g_PhysicsHook.ImpactSounds, entity, entity.EntIndex(), channel, physObject, surfaceProps, surfacePropsHit, volume, impactSpeed);
}
Expand Down Expand Up @@ -202,6 +275,38 @@ public static void PhysCallbackDamage(BaseEntity entity, in TakeDamageInfo info)
}
}

public struct MassCenterOverride
{
public enum Align
{
Point = 0,
Axis = 1,
}

public void Defaults() {
EntityName = null;
}

public void SnapToPoint(string? name, in Vector3 pointWS) {
EntityName = name;
Center = pointWS;
Axis = default;
AlignType = Align.Point;
}

public void SnapToAxis(string? name, in Vector3 axisStartWS, in Vector3 unitAxisDirWS) {
EntityName = name;
Center = axisStartWS;
Axis = unitAxisDirWS;
AlignType = Align.Axis;
}

public Vector3 Center;
public Vector3 Axis;
public Align AlignType;
public string? EntityName;
}

public class PhysicsHook : BaseGameSystemPerFrame
{
public static TimeUnit_t g_PhysAverageSimTime;
Expand Down Expand Up @@ -341,6 +446,7 @@ public override void LevelShutdownPreEntity() {
}
public override void LevelShutdownPostEntity() {
base.LevelShutdownPostEntity();
MassCenterOverrides.Clear();
}
public override void FrameUpdatePostEntityThink() {
base.FrameUpdatePostEntityThink();
Expand All @@ -358,4 +464,5 @@ public override void PreClientUpdate() {
TimeUnit_t ImpactSoundTime;
public readonly List<ImpactSound> ImpactSounds = [];
public readonly List<BreakSound> BreakSounds = [];
public readonly List<MassCenterOverride> MassCenterOverrides = [];
}
99 changes: 93 additions & 6 deletions Game.Server/Props.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Game.Server;
using System.Diagnostics;
using System.Numerics;

using DEFINE = Source.DEFINE<Game.Server.PhysicsProp>;
using FIELD_BPD = Source.FIELD<BasePropDoor>;
using FIELD_DP = Source.FIELD<DynamicProp>;
using FIELD_PBM = Source.FIELD<PhysBoxMultiplayer>;
Expand Down Expand Up @@ -62,6 +63,13 @@ public class BreakableProp : BaseProp, IBreakableWithPropData
public static readonly SendTable DT_BreakableProp = new(DT_BaseAnimating, []);
public static readonly new ServerClass ServerClass = new ServerClass(DT_BreakableProp);

public bool PropDataOverrodeBlockLOS() => BlockLOSSetByPropData;

public void DisableAutoFade() {
FadeScale = 0;
DefaultFadeScale = 0;
}

public void SetDmgModBullet(float dmgMod) => DmgModBullet = dmgMod;
public void SetDmgModClub(float dmgMod) => DmgModClub = dmgMod;
public void SetDmgModExplosive(float dmgMod) => DmgModExplosive = dmgMod;
Expand All @@ -82,7 +90,7 @@ public class BreakableProp : BaseProp, IBreakableWithPropData
public int GetBreakableCount() => BreakableCount;
public void SetMaxBreakableSize(int size) => MaxBreakableSize = size;
public int GetMaxBreakableSize() => MaxBreakableSize;
public void SetPropDataBlocksLOS(bool blocksLOS) => SetBlocksLOS(blocksLOS);
public void SetPropDataBlocksLOS(bool blocksLOS) { BlockLOSSetByPropData = true; SetBlocksLOS(blocksLOS); }
public void SetPropDataIsAIWalkable(bool blocksLOS) => SetAIWalkable(blocksLOS);
public void SetBasePropData(ReadOnlySpan<char> propdata) => BasePropData = propdata.IsEmpty ? null : new(propdata);
public ReadOnlySpan<char> GetBasePropData() => BasePropData;
Expand All @@ -107,6 +115,8 @@ public class BreakableProp : BaseProp, IBreakableWithPropData
protected float ExplodeRadius;
protected PhysicsMultiplayerMode m_iPhysicsMode;
protected MultiplayerBreak MpBreakMode;
bool BlockLOSSetByPropData;
float DefaultFadeScale;
}

[LinkEntityToClass("func_physbox_multiplayer")]
Expand Down Expand Up @@ -146,7 +156,36 @@ public class PhysicsProp : BreakableProp
[NetworkName("m_bAwake")]
public bool Awake;

public static readonly new DataMap DataDesc = new(typeof(PhysicsProp), BaseEntity.DataDesc, [
DEFINE.KEYFIELD(nameof(MassScale), FieldType.Float, "massscale"),
DEFINE.KEYFIELD(nameof(InertiaScale), FieldType.Float, "inertiascale"),
DEFINE.KEYFIELD(nameof(DamageType), FieldType.Integer, "Damagetype"),
DEFINE.KEYFIELD(nameof(OverrideScript), FieldType.String, "overridescript"),
DEFINE.KEYFIELD(nameof(DamageToEnableMotion), FieldType.Integer, "damagetoenablemotion"),
DEFINE.KEYFIELD(nameof(ForceToEnableMotion), FieldType.Float, "forcetoenablemotion"),
]);
public override DataMap? GetDataDescMap() => DataDesc;

public static int g_ActiveGibCount = 0;

float MassScale;
float InertiaScale;
int DamageType;
string? OverrideScript;
int DamageToEnableMotion;
float ForceToEnableMotion;

public override void Term() {
if (HasSpawnFlags(SF_PHYSPROP_IS_GIB))
g_ActiveGibCount--;

base.Term();
}

public override void Spawn() {
if (HasSpawnFlags(SF_PHYSPROP_IS_GIB))
g_ActiveGibCount++;

// Condense classnames to one, except for "prop_physics_override"
if (FClassnameIs(this, "physics_prop"))
SetClassname("prop_physics");
Expand All @@ -160,7 +199,21 @@ public override void Spawn() {
if (FClassnameIs(this, "prop_physics_override"))
SetClassname("prop_physics");

if (HasSpawnFlags(SF_PHYSPROP_DEBRIS) || HasInteraction(PropDataInteractions.PhysgunCreateFlare))
SetCollisionGroup(HasSpawnFlags(SF_PHYSPROP_FORCE_TOUCH_TRIGGERS) ? Source.CollisionGroup.DebrisTrigger : Source.CollisionGroup.Debris);

if (HasSpawnFlags(SF_PHYSPROP_NO_ROTORWASH_PUSH))
AddEFlags(EFL.NoRotorWashPush);

CreateVPhysics();

if (!PropDataOverrodeBlockLOS())
CalculateBlockLOS();

if (((ReadOnlySpan<char>)GetModelName()).Equals("models/props_c17/door01_left.mdl", StringComparison.Ordinal)) {
SetFadeDistance(-1, 0);
DisableAutoFade();
}
}

public static bool PropIsGib(BaseEntity entity) {
Expand All @@ -174,18 +227,52 @@ public static bool PropIsGib(BaseEntity entity) {
public bool IsGib() => (SpawnFlags & SF_PHYSPROP_IS_GIB) != 0;

public virtual bool CreateVPhysics() {
SetSolid(SolidType.VPhysics);
bool asleep = HasSpawnFlags(SF_PHYSPROP_START_ASLEEP);

if (m_takedamage == (byte)Damage.No)
SetMoveType(Source.MoveType.None);
Solid tmpSolid = default;
PhysModelParseSolid(ref tmpSolid, this, GetModelIndex());

if (MassScale > 0)
tmpSolid.Params.Mass *= MassScale;

if (InertiaScale > 0) {
tmpSolid.Params.Inertia *= InertiaScale;
if (tmpSolid.Params.Inertia < 0.5)
tmpSolid.Params.Inertia = 0.5f;
}

PhysGetMassCenterOverride(this, modelinfo.GetVCollide(GetModelIndex()), ref tmpSolid);
if (HasSpawnFlags(SF_PHYSPROP_NO_COLLISIONS))
tmpSolid.Params.EnableCollisions = false;

PhysSolidOverride(ref tmpSolid, OverrideScript);

IPhysicsObject? physObj = VPhysicsInitNormal(SolidType.VPhysics, GetSolidFlags(), false);
if (physObj == null) {
IPhysicsObject? physicsObject = VPhysicsInitNormal(SolidType.VPhysics, 0, asleep, ref tmpSolid);

if (physicsObject == null) {
SetSolid(SolidType.None);
SetMoveType(Source.MoveType.None);
Warning($"ERROR!: Can't create physics object for {GetModelName()}\n");
return false;
}
else {
if (DamageType == 1)
PhysSetGameFlags(physicsObject, PhysicsFlags.DamageSlice);

if (HasSpawnFlags(SF_PHYSPROP_MOTIONDISABLED) || DamageToEnableMotion > 0 || ForceToEnableMotion > 0)
physicsObject.EnableMotion(false);
}

if (HasInteraction(PropDataInteractions.PhysgunLaunchSpinZ)) {
if ((VPhysicsGetObject()!.GetGameFlags() & PhysicsFlags.DamageSlice) == 0)
PhysSetGameFlags(physicsObject, PhysicsFlags.DamageSlice);
}

if (HasInteraction(PropDataInteractions.PhysgunDamageNone))
PhysSetGameFlags(physicsObject, PhysicsFlags.NoImpactDamage);

if (HasSpawnFlags(SF_PHYSPROP_PREVENT_PICKUP))
PhysSetGameFlags(physicsObject, PhysicsFlags.NoPlayerPickup);

return true;
}
Expand Down
12 changes: 12 additions & 0 deletions Game.Shared/PhysicsShared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,18 @@ public static void PhysFrictionEffect(in Vector3 pos, Vector3 vel, float energy,
}
#endif

public static bool PhysModelParseSolidByIndex(ref Solid solid, BaseEntity entity, int modelIndex, int solidIndex) {
VCollide? collide = modelinfo.GetVCollide(modelIndex);
if (collide == null)
return false;

return PhysModelParseSolidByIndex(ref solid, entity, collide, solidIndex);
}

public static bool PhysModelParseSolid(ref Solid solid, BaseEntity entity, int modelIndex) {
return PhysModelParseSolidByIndex(ref solid, entity, modelIndex, -1);
}

public static bool PhysModelParseSolidByIndex(ref Solid solid, BaseEntity entity, VCollide? collide, int solidIndex) {
if (collide == null || collide.KeyValues == null)
return false;
Expand Down
Loading