v0.14.6.0

This commit is contained in:
Joonas Rikkonen
2021-06-17 17:54:52 +03:00
parent 3f324b14e8
commit c27e2ea5ab
348 changed files with 13156 additions and 4266 deletions
@@ -20,7 +20,6 @@ namespace Barotrauma.Items.Components
public Vector2 Point;
public Vector2 Normal;
public float Fraction;
public HitscanResult(Fixture fixture, Vector2 point, Vector2 normal, float fraction)
{
Fixture = fixture;
@@ -45,6 +44,8 @@ namespace Barotrauma.Items.Components
private readonly Queue<Impact> impactQueue = new Queue<Impact>();
private bool removePending;
//continuous collision detection is used while the projectile is moving faster than this
const float ContinuousCollisionThreshold = 5.0f;
@@ -71,6 +72,8 @@ namespace Barotrauma.Items.Components
}
}
public Character Attacker { get; set; }
public IEnumerable<Body> Hits
{
get { return hits; }
@@ -81,7 +84,11 @@ namespace Barotrauma.Items.Components
[Serialize(10.0f, false, description: "The impulse applied to the physics body of the item when it's launched. Higher values make the projectile faster.")]
public float LaunchImpulse { get; set; }
[Serialize(0.0f, false, description: "The random percentage modifier used to add variance to the launch impulse.")]
public float ImpulseSpread { get; set; }
[Serialize(0.0f, false, description: "The rotation of the item relative to the rotation of the weapon when launched (in degrees).")]
public float LaunchRotation
{
get { return MathHelper.ToDegrees(LaunchRotationRadians); }
@@ -168,6 +175,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false, description: "Override random spread with static spread; hitscan are launched with an equal amount of angle between them. Only applies when firing multiple hitscan.")]
public bool StaticSpread
{
get;
set;
}
public Body StickTarget
{
get;
@@ -189,7 +203,9 @@ namespace Barotrauma.Items.Components
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
Attack = new Attack(subElement, item.Name + ", Projectile");
}
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
public override void OnItemLoaded()
{
@@ -264,11 +280,26 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < HitScanCount; i++)
{
float launchAngle = item.body.Rotation + MathHelper.ToRadians(Spread * Rand.Range(-0.5f, 0.5f));
float launchAngle;
if (StaticSpread)
{
float staticSpread = Spread / (HitScanCount - 1);
// because the position of the item changes as hitscan are fired, we will set an
// initial offset on the first hitscan and then increase the item's angle by a set amount as hitscan are fired
float offset = i == 0 ? -staticSpread * (HitScanCount -1) : 0f;
launchAngle = item.body.Rotation + MathHelper.ToRadians(staticSpread + offset);
}
else
{
launchAngle = item.body.Rotation + MathHelper.ToRadians(Spread * Rand.Range(-0.5f, 0.5f));
}
Vector2 launchDir = new Vector2((float)Math.Cos(launchAngle), (float)Math.Sin(launchAngle));
if (Hitscan)
{
Vector2 prevSimpos = item.SimPosition;
item.body.SetTransformIgnoreContacts(item.body.SimPosition, launchAngle);
DoHitscan(launchDir);
if (i < HitScanCount - 1)
{
@@ -277,7 +308,9 @@ namespace Barotrauma.Items.Components
}
else
{
DoLaunch(launchDir * LaunchImpulse * item.body.Mass);
item.body.SetTransform(item.body.SimPosition, launchAngle);
float modifiedLaunchImpulse = LaunchImpulse * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
DoLaunch(launchDir * modifiedLaunchImpulse * item.body.Mass);
}
}
User = character;
@@ -331,7 +364,14 @@ namespace Barotrauma.Items.Components
IsActive = true;
Vector2 rayStart = simPositon;
Vector2 rayEnd = simPositon + dir * 500.0f;
Vector2 rayEnd = rayStart + dir * 500.0f;
Vector2 rayStartWorld = item.WorldPosition;
float worldDist = 1000.0f;
#if CLIENT
worldDist = Screen.Selected?.Cam?.WorldView.Width ?? GameMain.GraphicsWidth;
#endif
Vector2 rayEndWorld = rayStartWorld + dir * worldDist;
List<HitscanResult> hits = new List<HitscanResult>();
@@ -375,6 +415,7 @@ namespace Barotrauma.Items.Components
item.SetTransform(h.Point, rotation);
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
{
LaunchProjSpecific(rayStartWorld, item.WorldPosition);
hitSomething = true;
break;
}
@@ -383,6 +424,8 @@ namespace Barotrauma.Items.Components
//the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething)
{
item.body.SetTransformIgnoreContacts(item.body.SimPosition, rotation);
LaunchProjSpecific(rayStartWorld, rayEndWorld);
if (Entity.Spawner == null)
{
item.Remove();
@@ -413,8 +456,15 @@ namespace Barotrauma.Items.Components
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) =>
{
//ignore sensors and items
if (fixture?.Body == null || fixture.IsSensor) { return true; }
if (fixture?.Body.UserData is LevelObject levelObj)
{
if (!levelObj.Prefab.TakeLevelWallDamage) { return true; }
}
else if (fixture?.Body == null || fixture.IsSensor)
{
//ignore sensors and items
return true;
}
if (fixture.Body.UserData is VineTile) { return true; }
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return true; }
if (fixture.Body.UserData as string == "ruinroom") { return true; }
@@ -422,6 +472,7 @@ namespace Barotrauma.Items.Components
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
if (submarine != null)
{
if (fixture.Body.UserData is VoronoiCell) { return true; }
if (fixture.Body.UserData is Entity entity && entity.Submarine != submarine) { return true; }
}
@@ -442,7 +493,15 @@ namespace Barotrauma.Items.Components
GameMain.World.RayCast((fixture, point, normal, fraction) =>
{
//ignore sensors and items
if (fixture?.Body == null || fixture.IsSensor) { return -1; }
if (fixture?.Body.UserData is LevelObject levelObj)
{
if (!levelObj.Prefab.TakeLevelWallDamage) { return -1; }
}
else if (fixture?.Body == null || fixture.IsSensor)
{
//ignore sensors and items
return -1;
}
if (fixture.Body.UserData is VineTile) { return -1; }
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return -1; }
@@ -456,21 +515,40 @@ namespace Barotrauma.Items.Components
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
if (submarine != null)
{
if (fixture.Body.UserData is VoronoiCell) { return -1; }
if (fixture.Body.UserData is Entity entity && entity.Submarine != submarine) { return -1; }
}
//ignore level cells if the item and the point of impact are inside a sub
if (fixture.Body.UserData is VoronoiCell && this.item.Submarine != null)
if (fixture.Body.UserData is VoronoiCell)
{
if (Hull.FindHull(ConvertUnits.ToDisplayUnits(point), this.item.CurrentHull) != null)
if (Hull.FindHull(ConvertUnits.ToDisplayUnits(point), this.item.CurrentHull) != null && this.item.Submarine != null)
{
return -1;
}
}
if (hits.Count > 50)
{
float furthestHit = 0.0f;
int furthestHitIndex = -1;
for (int i = 0; i < hits.Count; i++)
{
if (hits[i].Fraction > furthestHit)
{
furthestHitIndex = i;
furthestHit = hits[i].Fraction;
}
}
if (furthestHitIndex > -1)
{
hits.RemoveAt(furthestHitIndex);
}
}
hits.Add(new HitscanResult(fixture, point, normal, fraction));
return hits.Count < 25 ? 1 : 0;
return 1;
}, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel);
return hits;
@@ -488,14 +566,17 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
while (impactQueue.Count > 0)
{
var impact = impactQueue.Dequeue();
HandleProjectileCollision(impact.Fixture, impact.Normal, impact.LinearVelocity);
}
if (!removePending)
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
}
if (item.body != null && item.body.FarseerBody.IsBullet)
{
if (item.body.LinearVelocity.LengthSquared() < ContinuousCollisionThreshold * ContinuousCollisionThreshold)
@@ -573,11 +654,11 @@ namespace Barotrauma.Items.Components
}
else if (target.Body.UserData is Limb limb)
{
//severed limbs don't deactivate the projectile (but may still slow it down enough to make it inactive)
if (limb.IsSevered)
{
target.Body.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass);
return true;
//push the severed limb around a bit, but let the projectile pass through it
limb.body?.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass * 0.1f, item.SimPosition);
return false;
}
}
else if (target.Body.UserData is Item item)
@@ -594,6 +675,10 @@ namespace Barotrauma.Items.Components
hits.Add(target.Body);
impactQueue.Enqueue(new Impact(target, contact.Manifold.LocalNormal, item.body.LinearVelocity));
IsActive = true;
if (RemoveOnHit)
{
item.body.FarseerBody.ResetDynamics();
}
if (hits.Count() >= MaxTargetsToHit || target.Body.UserData is VoronoiCell)
{
Deactivate();
@@ -605,6 +690,8 @@ namespace Barotrauma.Items.Components
}
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
private bool HandleProjectileCollision(Fixture target, Vector2 collisionNormal, Vector2 velocity)
{
if (User != null && User.Removed) { User = null; }
@@ -615,6 +702,9 @@ namespace Barotrauma.Items.Components
return false;
}
float projectileNewSpeed = 0.5f;
float projectileDeflectedNewSpeed = 0.1f;
AttackResult attackResult = new AttackResult();
Character character = null;
if (target.Body.UserData is Submarine submarine)
@@ -626,12 +716,16 @@ namespace Barotrauma.Items.Components
}
else if (target.Body.UserData is Limb limb)
{
//severed limbs don't deactivate the projectile (but may still slow it down enough to make it inactive)
if (limb.IsSevered) { return true; }
if (limb.character == null || limb.character.Removed) { return false; }
// when hitting limbs with piercing ammo, don't lose as much speed
if (MaxTargetsToHit > 1)
{
projectileNewSpeed = 1f;
projectileDeflectedNewSpeed = 0.8f;
}
if (limb.IsSevered || limb.character == null || limb.character.Removed) { return false; }
limb.character.LastDamageSource = item;
if (Attack != null) { attackResult = Attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); }
if (Attack != null) { attackResult = Attack.DoDamageToLimb(User ?? Attacker, limb, item.WorldPosition, 1.0f); }
if (limb.character != null) { character = limb.character; }
}
else if (target.Body.UserData is Item targetItem)
@@ -639,18 +733,18 @@ namespace Barotrauma.Items.Components
if (targetItem.Removed) { return false; }
if (Attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
{
attackResult = Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
}
}
else if (target.Body.UserData is IDamageable damageable)
{
if (Attack != null) { attackResult = Attack.DoDamage(User, damageable, item.WorldPosition, 1.0f); }
if (Attack != null) { attackResult = Attack.DoDamage(User ?? Attacker, damageable, item.WorldPosition, 1.0f); }
}
else if (target.Body.UserData is VoronoiCell voronoiCell && voronoiCell.IsDestructible && Attack != null && Math.Abs(Attack.StructureDamage) > 0.0f)
else if (target.Body.UserData is VoronoiCell voronoiCell && voronoiCell.IsDestructible && Attack != null && Math.Abs(Attack.LevelWallDamage) > 0.0f)
{
if (Level.Loaded?.ExtraWalls.Find(w => w.Body == target.Body) is DestructibleLevelWall destructibleWall)
{
attackResult = Attack.DoDamage(User, destructibleWall, item.WorldPosition, 1.0f);
attackResult = Attack.DoDamage(User ?? Attacker, destructibleWall, item.WorldPosition, 1.0f);
}
}
@@ -690,8 +784,8 @@ namespace Barotrauma.Items.Components
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
{
var targets = new List<ISerializableEntity>();
effect.GetNearbyTargets(targetLimb.WorldPosition, targets);
targets.Clear();
targets.AddRange(effect.GetNearbyTargets(targetLimb.WorldPosition, targets));
effect.Apply(ActionType.OnActive, 1.0f, targetLimb.character, targets);
}
@@ -731,7 +825,7 @@ namespace Barotrauma.Items.Components
if (attackResult.AppliedDamageModifiers != null &&
attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles))
{
item.body.LinearVelocity *= 0.1f;
item.body.LinearVelocity *= projectileDeflectedNewSpeed;
}
else if (Vector2.Dot(velocity, collisionNormal) < 0.0f && hits.Count() >= MaxTargetsToHit &&
target.Body.Mass > item.body.Mass * 0.5f &&
@@ -761,13 +855,13 @@ namespace Barotrauma.Items.Components
item.CreateServerEvent(this);
}
#endif
item.body.LinearVelocity *= 0.5f;
item.body.LinearVelocity *= projectileNewSpeed;
return Hitscan;
}
else
{
item.body.LinearVelocity *= 0.5f;
item.body.LinearVelocity *= projectileNewSpeed;
}
var containedItems = item.OwnInventory?.AllItems;
@@ -784,15 +878,10 @@ namespace Barotrauma.Items.Components
if (RemoveOnHit)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
//clients aren't allowed to remove items by themselves, so lets hide the projectile until the server tells us to remove it
item.HiddenInGame = Hitscan;
}
else
{
Entity.Spawner?.AddToRemoveQueue(item);
}
removePending = true;
item.HiddenInGame = true;
item.body.FarseerBody.Enabled = false;
Entity.Spawner?.AddToRemoveQueue(item);
}
return true;
@@ -876,5 +965,6 @@ namespace Barotrauma.Items.Components
}
}
partial void LaunchProjSpecific(Vector2 startLocation, Vector2 endLocation);
}
}