v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
@@ -11,8 +11,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private ISpatialEntity source;
|
||||
private Item target;
|
||||
|
||||
private Vector2? launchDir;
|
||||
private float currentRopeLength;
|
||||
|
||||
private void SetSource(ISpatialEntity source)
|
||||
{
|
||||
@@ -73,6 +73,13 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(200.0f, IsPropertySaveable.No, description: "At which distance the user stops pulling the target?")]
|
||||
public float MinPullDistance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(360.0f, IsPropertySaveable.No, description: "The maximum angle from the source to the target until the rope breaks.")]
|
||||
public float MaxAngle
|
||||
@@ -108,7 +115,8 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
private bool isReelingIn;
|
||||
private bool snapped;
|
||||
public bool Snapped
|
||||
{
|
||||
@@ -134,6 +142,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
snapTimer = 0;
|
||||
}
|
||||
else if (target != null && source != null && target != source)
|
||||
{
|
||||
#if CLIENT
|
||||
// Play a sound at both ends. Initially tested playing the sound in the middle when the rope snaps in the middle,
|
||||
// but I think it's more important to ensure that the players hear the sound.
|
||||
PlaySound(snapSound, source.WorldPosition);
|
||||
PlaySound(snapSound, target.WorldPosition);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +173,17 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, worldPosition: item.WorldPosition);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
var user = item.GetComponent<Projectile>()?.User;
|
||||
UpdateProjSpecific();
|
||||
isReelingIn = false;
|
||||
Character user = item.GetComponent<Projectile>()?.User;
|
||||
if (source == null || target == null || target.Removed ||
|
||||
(source is Entity sourceEntity && sourceEntity.Removed) ||
|
||||
(source is Limb limb && limb.Removed) ||
|
||||
(user != null && user.Removed))
|
||||
source is Entity { Removed: true } ||
|
||||
source is Limb { Removed: true } ||
|
||||
user is null ||
|
||||
user is { Removed: true })
|
||||
{
|
||||
ResetSource();
|
||||
target = null;
|
||||
@@ -191,11 +211,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (MaxAngle < 180 && lengthSqr > 2500)
|
||||
{
|
||||
if (launchDir == null)
|
||||
{
|
||||
launchDir = diff;
|
||||
}
|
||||
float angle = MathHelper.ToDegrees(VectorExtensions.Angle(launchDir.Value, diff));
|
||||
launchDir ??= diff;
|
||||
float angle = MathHelper.ToDegrees(launchDir.Value.Angle(diff));
|
||||
if (angle > MaxAngle)
|
||||
{
|
||||
Snap();
|
||||
@@ -262,8 +279,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Vector2 forceDir = diff;
|
||||
float distance = diff.Length();
|
||||
if (distance > 0.001f)
|
||||
currentRopeLength = diff.Length();
|
||||
if (currentRopeLength > 0.001f)
|
||||
{
|
||||
forceDir = Vector2.Normalize(forceDir);
|
||||
}
|
||||
@@ -277,87 +294,162 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float targetMass = float.MaxValue;
|
||||
Character targetCharacter = null;
|
||||
if (projectile.StickTarget.UserData is Limb targetLimb)
|
||||
switch (projectile.StickTarget.UserData)
|
||||
{
|
||||
targetCharacter = targetLimb.character;
|
||||
targetMass = targetLimb.ragdoll.Mass;
|
||||
}
|
||||
else if (projectile.StickTarget.UserData is Character character)
|
||||
{
|
||||
targetCharacter = character;
|
||||
targetMass = character.Mass;
|
||||
}
|
||||
else if (projectile.StickTarget.UserData is Item item)
|
||||
{
|
||||
targetMass = projectile.StickTarget.Mass;
|
||||
case Limb targetLimb:
|
||||
targetCharacter = targetLimb.character;
|
||||
targetMass = targetLimb.ragdoll.Mass;
|
||||
break;
|
||||
case Character character:
|
||||
targetCharacter = character;
|
||||
targetMass = character.Mass;
|
||||
break;
|
||||
case Item _:
|
||||
targetMass = projectile.StickTarget.Mass;
|
||||
break;
|
||||
}
|
||||
if (projectile.StickTarget.BodyType != BodyType.Dynamic)
|
||||
{
|
||||
targetMass = float.MaxValue;
|
||||
}
|
||||
if (targetMass > TargetMinMass)
|
||||
if (!snapped)
|
||||
{
|
||||
if (Math.Abs(SourcePullForce) > 0.001f)
|
||||
user.AnimController.HoldToRope();
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
var sourceBody = GetBodyToPull(source);
|
||||
if (sourceBody != null)
|
||||
targetCharacter.AnimController.DragWithRope();
|
||||
}
|
||||
if (user.InWater)
|
||||
{
|
||||
user.AnimController.HangWithRope();
|
||||
}
|
||||
}
|
||||
if (Math.Abs(SourcePullForce) > 0.001f && targetMass > TargetMinMass)
|
||||
{
|
||||
// This should be the main collider.
|
||||
var sourceBody = GetBodyToPull(source);
|
||||
if (sourceBody != null)
|
||||
{
|
||||
isReelingIn = user.InWater && user.IsRagdolled || !user.InWater && targetCharacter is { IsIncapacitated: false };
|
||||
if (isReelingIn)
|
||||
{
|
||||
if (user != null && user.InWater)
|
||||
float pullForce = SourcePullForce;
|
||||
if (!user.InWater)
|
||||
{
|
||||
// Apply a tiny amount to the character holding the rope, so that the connection "feels" more real.
|
||||
pullForce *= 0.1f;
|
||||
}
|
||||
float lengthFactor = MathUtils.InverseLerp(0, MaxLength / 2, currentRopeLength);
|
||||
float force = LerpForces ? MathHelper.Lerp(0, pullForce, lengthFactor) : pullForce;
|
||||
sourceBody.ApplyForce(forceDir * force);
|
||||
// Take the target velocity into account.
|
||||
PhysicsBody targetBody = GetBodyToPull(target);
|
||||
if (targetBody != null)
|
||||
{
|
||||
if (user.IsRagdolled)
|
||||
{
|
||||
// Reel in towards the target.
|
||||
user.AnimController.Hang();
|
||||
float force = LerpForces ? MathHelper.Lerp(0, SourcePullForce, MathUtils.InverseLerp(0, MaxLength / 2, distance)) : SourcePullForce;
|
||||
sourceBody.ApplyForce(forceDir * force);
|
||||
}
|
||||
// Take the target velocity into account.
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
var myCollider = user.AnimController.Collider;
|
||||
var targetCollider = targetCharacter.AnimController.Collider;
|
||||
if (myCollider.LinearVelocity != Vector2.Zero && targetCollider.LinearVelocity != Vector2.Zero)
|
||||
if (targetBody.LinearVelocity != Vector2.Zero && sourceBody.LinearVelocity != Vector2.Zero)
|
||||
{
|
||||
if (Vector2.Dot(Vector2.Normalize(myCollider.LinearVelocity), Vector2.Normalize(targetCollider.LinearVelocity)) < 0)
|
||||
Vector2 targetDir = Vector2.Normalize(targetBody.LinearVelocity);
|
||||
float movementDot = Vector2.Dot(Vector2.Normalize(sourceBody.LinearVelocity), targetDir);
|
||||
if (movementDot < 0)
|
||||
{
|
||||
myCollider.ApplyForce(targetCollider.LinearVelocity * targetCollider.Mass);
|
||||
// Pushing to a different dir -> add some counter force
|
||||
const float multiplier = 5;
|
||||
float inverseLengthFactor = MathHelper.Lerp(1, 0, lengthFactor);
|
||||
sourceBody.ApplyForce(targetBody.LinearVelocity * Math.Min(targetBody.Mass * multiplier, 250) * sourceBody.Mass * -movementDot * inverseLengthFactor);
|
||||
}
|
||||
float forceDot = Vector2.Dot(forceDir, targetDir);
|
||||
if (forceDot > 0)
|
||||
{
|
||||
// Pulling to the same dir -> add extra force
|
||||
float targetSpeed = targetBody.LinearVelocity.Length();
|
||||
const float multiplier = 25;
|
||||
sourceBody.ApplyForce(forceDir * targetSpeed * sourceBody.Mass * multiplier * forceDot * lengthFactor);
|
||||
}
|
||||
float colliderMainLimbDistance = Vector2.Distance(sourceBody.SimPosition, user.AnimController.MainLimb.SimPosition);
|
||||
const float minDist = 1;
|
||||
const float maxDist = 10;
|
||||
if (colliderMainLimbDistance > minDist)
|
||||
{
|
||||
// Move the ragdoll closer to the collider, if it's too far (the correction force in HumanAnimController is not enough -> the ragdoll would lag behind and get teleported).
|
||||
float correctionForce = MathHelper.Lerp(10.0f, NetConfig.MaxPhysicsBodyVelocity, MathUtils.InverseLerp(minDist, maxDist, colliderMainLimbDistance));
|
||||
Vector2 targetPos = sourceBody.SimPosition + new Vector2((float)Math.Sin(-sourceBody.Rotation), (float)Math.Cos(-sourceBody.Rotation)) * 0.4f;
|
||||
user.AnimController.MainLimb.MoveToPos(targetPos, correctionForce);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetBody = GetBodyToPull(target);
|
||||
if (targetBody != null)
|
||||
{
|
||||
sourceBody.ApplyForce(targetBody.LinearVelocity * sourceBody.Mass);
|
||||
}
|
||||
sourceBody.ApplyForce(targetBody.LinearVelocity * sourceBody.Mass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Math.Abs(TargetPullForce) > 0.001f)
|
||||
if (Math.Abs(TargetPullForce) > 0.001f && !user.IsRagdolled)
|
||||
{
|
||||
var targetBody = GetBodyToPull(target);
|
||||
PhysicsBody targetBody = GetBodyToPull(target);
|
||||
if (targetBody == null) { return; }
|
||||
bool lerpForces = LerpForces;
|
||||
if (!lerpForces && user != null && targetCharacter != null && !user.AnimController.InWater)
|
||||
float maxVelocity = NetConfig.MaxPhysicsBodyVelocity * 0.25f;
|
||||
// The distance where we start pulling with max force.
|
||||
float maxPullDistance = MaxLength / 3;
|
||||
float minPullDistance = MinPullDistance;
|
||||
const float absoluteMinPullDistance = 50;
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
if ((forceDir.X < 0) != (user.AnimController.Dir < 0))
|
||||
if (targetCharacter.IsRagdolled || targetCharacter.IsUnconscious)
|
||||
{
|
||||
// Prevents rubberbanding horizontally when dragging a corpse.
|
||||
lerpForces = true;
|
||||
if (!targetCharacter.InWater)
|
||||
{
|
||||
// Limits the velocity of ragdolled characters on ground/air, because otherwise they tend to move with too high forces.
|
||||
maxVelocity = NetConfig.MaxPhysicsBodyVelocity * 0.075f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Target alive and kicking -> Use the absolute min pull distance and full forces to pull.
|
||||
// Keep some lerping, because it results into smoothing when the target is close by.
|
||||
minPullDistance = absoluteMinPullDistance;
|
||||
maxPullDistance = 200;
|
||||
}
|
||||
}
|
||||
float force = lerpForces ? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(0, MaxLength / 3, distance - 50)) : TargetPullForce;
|
||||
targetBody?.ApplyForce(-forceDir * force);
|
||||
var targetRagdoll = targetCharacter?.AnimController;
|
||||
if (targetRagdoll?.Collider != null && (targetRagdoll.InWater || targetRagdoll.OnGround))
|
||||
minPullDistance = MathHelper.Max(minPullDistance, absoluteMinPullDistance);
|
||||
if (currentRopeLength < minPullDistance) { return; }
|
||||
maxPullDistance = MathHelper.Max(minPullDistance * 2, maxPullDistance);
|
||||
float force = lerpForces
|
||||
? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(minPullDistance, maxPullDistance, currentRopeLength))
|
||||
: TargetPullForce;
|
||||
targetBody.ApplyForce(-forceDir * force, maxVelocity);
|
||||
AnimController targetRagdoll = targetCharacter?.AnimController;
|
||||
if (targetRagdoll?.Collider != null)
|
||||
{
|
||||
targetRagdoll.Collider.ApplyForce(-forceDir * force * 3);
|
||||
isReelingIn = true;
|
||||
if (targetRagdoll.InWater || targetRagdoll.OnGround)
|
||||
{
|
||||
float forceMultiplier = 1;
|
||||
if (!targetCharacter.IsRagdolled && !targetCharacter.IsIncapacitated)
|
||||
{
|
||||
// Pulling the main collider requires higher forces when the target is trying to move away.
|
||||
Vector2 targetMovement = targetCharacter.AnimController.TargetMovement;
|
||||
float dot = Vector2.Dot(Vector2.Normalize(targetMovement), forceDir);
|
||||
if (dot > 0)
|
||||
{
|
||||
const float constMultiplier = 2.5f;
|
||||
float targetVelocity = targetMovement.Length();
|
||||
float massFactor = Math.Max((float)Math.Log(targetCharacter.Mass / 10), 1);
|
||||
forceMultiplier = Math.Max(targetVelocity * massFactor * constMultiplier * dot, 1);
|
||||
}
|
||||
}
|
||||
targetRagdoll.Collider.ApplyForce(-forceDir * force * forceMultiplier, maxVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific();
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
@@ -409,32 +501,22 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (target is Item targetItem)
|
||||
{
|
||||
if (targetItem.ParentInventory is CharacterInventory characterInventory &&
|
||||
characterInventory.Owner is Character ownerCharacter)
|
||||
if (targetItem.ParentInventory is CharacterInventory { Owner: Character ownerCharacter })
|
||||
{
|
||||
if (ownerCharacter.Removed) { return null; }
|
||||
return ownerCharacter.AnimController.Collider;
|
||||
}
|
||||
var projectile = targetItem.GetComponent<Projectile>();
|
||||
if (projectile != null && projectile.StickTarget != null)
|
||||
if (projectile is { StickTarget: not null })
|
||||
{
|
||||
if (projectile.StickTarget.UserData is Structure structure)
|
||||
return projectile.StickTarget.UserData switch
|
||||
{
|
||||
return structure.Submarine?.PhysicsBody;
|
||||
}
|
||||
else if (projectile.StickTarget.UserData is Submarine sub)
|
||||
{
|
||||
return sub.PhysicsBody;
|
||||
}
|
||||
else if (projectile.StickTarget.UserData is Item item)
|
||||
{
|
||||
return item.body;
|
||||
}
|
||||
else if (projectile.StickTarget.UserData is Limb limb)
|
||||
{
|
||||
return limb.body;
|
||||
}
|
||||
return null;
|
||||
Structure structure => structure.Submarine?.PhysicsBody,
|
||||
Submarine sub => sub.PhysicsBody,
|
||||
Item item => item.body,
|
||||
Limb limb => limb.body,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
if (targetItem.body != null) { return targetItem.body; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user