public static void Break(Model model, Vector3 pos, Rotation rot, Result result = null, PhysicsBody sourcePhysics = null) { if (model == null || model.IsError) { return; } var breakList = model.GetBreakPieces(); if (breakList == null || breakList.Length <= 0) { return; } foreach (var piece in breakList) { var mdl = Model.Load(piece.Model); var offset = mdl.GetAttachment("placementOrigin"); var gib = new Prop { WorldPos = pos + rot * (piece.Offset - offset.Pos), WorldRot = rot, CollisionGroup = piece.GetCollisionGroup(), }; gib.SetModel(mdl); var phys = gib.PhysicsBody; if (phys != null) { // Apply the velocity at the parent object's position if (sourcePhysics != null) { phys.Velocity = sourcePhysics.GetVelocityAtPoint(phys.Pos); phys.AngularVelocity = sourcePhysics.AngularVelocity; } } if (piece.FadeTime > 0) { _ = FadeAsync(gib, piece.FadeTime); } result?.AddProp(gib); } }
private void UpdateBody(Entity ent, PhysicsBody body) { var waterDensity = 1000; var oldLevel = body.WaterLevel; var density = body.Density; var waterSurface = WaterEntity.WorldPos; var bounds = body.GetBounds(); var velocity = body.Velocity; var pos = bounds.Center; pos.z = waterSurface.z; var densityDiff = density - waterDensity; var volume = bounds.Volume; var level = waterSurface.z.LerpInverse(bounds.Mins.z, bounds.Maxs.z, true); body.WaterLevel = level; if (ent.IsClientOnly == Host.IsClient) { var bouyancy = densityDiff.LerpInverse(0.0f, -300f); bouyancy = MathF.Pow(bouyancy, 0.1f); // DebugOverlay.Text( pos, $"{bouyancy}", Host.Color, 0.1f, 10000 ); if (bouyancy <= 0) { body.GravityScale = 1.0f - body.WaterLevel * 0.8f; } else { var point = bounds.Center; if (level < 1.0f) { point.z = bounds.Mins.z - 100; } var closestpoint = body.FindClosestPoint(point); float depth = (waterSurface.z - bounds.Maxs.z) / 100.0f; depth = depth.Clamp(1.0f, 10.0f); //DebugOverlay.Text( point, $"{depth}", Host.Color, 0.1f, 10000 ); //DebugOverlay.Line( point, closestpoint, 1.0f ); //body.ApplyImpulseAt( closestpoint, (Vector3.Up * volume * level * bouyancy * 0.0001f) ); body.ApplyForceAt(closestpoint, (Vector3.Up * volume * level * bouyancy * 0.05f * depth)); //body.ApplyImpulseAt( ) body.GravityScale = 1.0f - MathF.Pow(body.WaterLevel.Clamp(0, 0.5f) * 2.0f, 0.5f); } body.LinearDrag = body.WaterLevel * WaterThickness; body.AngularDrag = body.WaterLevel * WaterThickness * 0.5f; } if (Host.IsClient) { if (oldLevel == 0) { return; } var change = MathF.Abs(oldLevel - level); //Log.Info( $"{change}" ); if (change > 0.001f && body.LastWaterEffect > 0.2f) { if (oldLevel < 0.3f && level >= 0.35f) { var particle = Particles.Create("particles/water_splash.vpcf", pos); particle.SetForward(0, Vector3.Up); body.LastWaterEffect = 0; Sound.FromWorld("water_splash_medium", pos); } if (velocity.Length > 2f && velocity.z > -10 && velocity.z < 10) { var particle = Particles.Create("particles/water_bob.vpcf", pos); particle.SetForward(0, Vector3.Up); body.LastWaterEffect = 0; } } } }
public void Weld(Prop prop) { Host.AssertServer(); if (!prop.IsValid()) { return; } // Don't weld myself to myself, stupid if (prop == this) { return; } // Don't weld myself twice, stupid if (prop.weldParent == this || weldParent == prop) { return; } // Only allow welding of two root props if (Parent != null || prop.Parent != null) { return; } // Only allow welding of props that have physics bodies if (!PhysicsBody.IsValid() || !prop.PhysicsBody.IsValid()) { return; } // Only allow welding of props that have a single body if (PhysicsGroup.BodyCount > 1 || prop.PhysicsGroup.BodyCount > 1) { return; } if (prop.childrenProps.Count > 0) { // Reweld everything to this prop.Unweld(true, this); } var thisBody = PhysicsBody; var otherBody = prop.PhysicsBody; // We want traces to now think the other body is now this body otherBody.Parent = thisBody; // Disable solid collisions on other prop so things attached with constraints wont collide with it prop.EnableSolidCollisions = false; // Merge all other shapes from other body into this body for (int shapeIndex = 0; shapeIndex < otherBody.ShapeCount; ++shapeIndex) { var clonedShape = thisBody.AddCloneShape(otherBody.GetShape(shapeIndex)); // We don't want to be able to trace this cloned shape (but we want it to generate contacts) clonedShape.DisableTraceQuery(); // Keep track of the cloned shape so they can be removed when unwelded prop.clonedShapes.Add(clonedShape); } // Visually parent other prop to this prop prop.Parent = this; prop.weldParent = this; // Keep track of welded children childrenProps.Add(prop); }
public static bool IsValid(this PhysicsBody body) { throw new NotImplementedException(); }
public void Unweld(bool reweldChildren = false, Prop reweldProp = null) { if (!weldParent.IsValid()) { // Unweld our group, reweld them to the first child or something else if (childrenProps.Count > 0) { // Create a copy of the child list so it doesn't get invalidated var chilrenPropsCopy = childrenProps.ToList(); // Unweld all children foreach (var childProp in chilrenPropsCopy) { childProp.Unweld(); } if (!reweldChildren) { return; } if (!reweldProp.IsValid()) { // If no reweld prop has been specified, reweld everything to the first child // So that the child becomes the new weld parent reweldProp = chilrenPropsCopy.First(); } // For whatever reason, we don't have a prop to reweld to, bail if (!reweldProp.IsValid()) { return; } // Reweld all children to the new parent foreach (var childProp in chilrenPropsCopy) { if (childProp != reweldProp) { reweldProp.Weld(childProp); } } } return; } // Remove us from the weld parent weldParent.childrenProps.Remove(this); // If weld parent has no physics body, it's probably pending destroy if (weldParent.PhysicsBody.IsValid()) { // Remove all of our cloned shapes from the weld parent foreach (var shape in clonedShapes) { weldParent.PhysicsBody.RemoveShape(shape); } } clonedShapes.Clear(); // If there's no physics body, we're probably pending destroy if (PhysicsBody.IsValid()) { Parent = null; PhysicsBody.Parent = null; EnableSolidCollisions = true; } weldParent = null; }