(6eeea9b7c) v0.9.10.0.0

This commit is contained in:
Joonas Rikkonen
2020-06-04 16:41:07 +03:00
parent ce4ccd99ac
commit eeac247a8e
366 changed files with 7772 additions and 3692 deletions
@@ -1,7 +1,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Text;
@@ -146,7 +146,7 @@ namespace Barotrauma
{
id += 1;
IDfound = dictionary.ContainsKey(id);
} while (IDfound);
} while (IDfound || id == NullEntityID || id == EntitySpawnerID);
return id;
}
@@ -136,7 +136,7 @@ namespace Barotrauma
if (powered == null || !powered.VulnerableToEMP) continue;
if (item.Repairables.Any())
{
item.Condition -= 100 * EmpStrength * distFactor;
item.Condition -= item.MaxCondition * EmpStrength * distFactor;
}
//discharge batteries
@@ -157,42 +157,47 @@ namespace Barotrauma
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
if (flames)
foreach (Item item in Item.ItemList)
{
foreach (Item item in Item.ItemList)
if (item.Condition <= 0.0f) { continue; }
if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; }
if (flames && !item.FireProof)
{
if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f) { continue; }
//don't apply OnFire effects if the item is inside a fireproof container
//(or if it's inside a container that's inside a fireproof container, etc)
Item container = item.Container;
bool fireProof = false;
while (container != null)
{
if (container.FireProof) { fireProof = true; break; }
if (container.FireProof)
{
fireProof = true;
break;
}
container = container.Container;
}
if (fireProof || Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; }
item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
if (!fireProof)
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
}
}
}
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
{
float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
if (dist > attack.Range)
{
float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
if (dist > attack.Range) { continue; }
float distFactor = 1.0f - dist / attack.Range;
float damageAmount = attack.GetItemDamage(1.0f);
item.Condition -= damageAmount * distFactor;
continue;
}
float distFactor = 1.0f - dist / attack.Range;
float damageAmount = attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
item.Condition -= damageAmount * distFactor;
}
}
}
@@ -200,10 +205,9 @@ namespace Barotrauma
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull);
public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
{
if (attack.Range <= 0.0f) return;
if (attack.Range <= 0.0f) { return; }
//long range for the broad distance check, because large characters may still be in range even if their collider isn't
float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);
@@ -226,13 +230,14 @@ namespace Barotrauma
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
Dictionary<Limb, float> damages = new Dictionary<Limb, float>();
foreach (Limb limb in c.AnimController.Limbs)
{
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
//calculate distance from the "outer surface" of the physics body
//doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
float limbRadius = limb.body.GetMaxExtent();
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
if (dist > attack.Range) { continue; }
@@ -240,14 +245,18 @@ namespace Barotrauma
float distFactor = 1.0f - dist / attack.Range;
//solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null) distFactor *= 0.1f;
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
{
distFactor *= 0.1f;
}
distFactors.Add(limb, distFactor);
List<Affliction> modifiedAfflictions = new List<Affliction>();
int limbCount = c.AnimController.Limbs.Count(l => !l.IsSevered && !l.ignoreCollisions);
foreach (Affliction affliction in attack.Afflictions.Keys)
{
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / limbCount));
}
c.LastDamageSource = damageSource;
if (attacker == null)
@@ -255,14 +264,18 @@ namespace Barotrauma
if (damageSource is Item item)
{
attacker = item.GetComponent<Projectile>()?.User;
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
if (attacker == null)
{
attacker = item.GetComponent<MeleeWeapon>()?.User;
}
}
}
//use a position slightly from the limb's position towards the explosion
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
damages.Add(limb, attackResult.Damage);
if (attack.StatusEffects != null && attack.StatusEffects.Any())
{
@@ -279,22 +292,27 @@ namespace Barotrauma
if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
{
Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
if (!MathUtils.IsValid(limbDiff)) limbDiff = Rand.Vector(1.0f);
if (!MathUtils.IsValid(limbDiff)) { limbDiff = Rand.Vector(1.0f); }
Vector2 impulse = limbDiff * distFactor * force;
Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.2f);
}
}
//sever joints
if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
if (attack.SeverLimbsProbability > 0.0f)
{
foreach (Limb limb in c.AnimController.Limbs)
{
if (!distFactors.ContainsKey(limb)) { continue; }
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
if (limb.character.Removed || limb.Removed) { continue; }
if (limb.IsSevered) { continue; }
if (!c.IsDead && !limb.CanBeSeveredAlive) { continue; }
if (distFactors.TryGetValue(limb, out float distFactor))
{
c.TrySeverLimbJoints(limb, 1.0f);
if (damages.TryGetValue(limb, out float damage))
{
c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
}
}
}
}
@@ -2,12 +2,14 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.Extensions;
#if CLIENT
using Barotrauma.Sounds;
using Barotrauma.Lights;
using Barotrauma.Particles;
#endif
using FarseerPhysics;
using System.Linq;
namespace Barotrauma
{
@@ -15,6 +17,7 @@ namespace Barotrauma
{
const float OxygenConsumption = 50.0f;
const float GrowSpeed = 20.0f;
const float MaxDamageRange = 250.0f;
protected Hull hull;
@@ -65,7 +68,7 @@ namespace Barotrauma
public virtual float DamageRange
{
get { return (float)Math.Sqrt(size.X) * 20.0f; }
get { return Math.Min((float)Math.Sqrt(size.X) * 10.0f, MaxDamageRange); }
}
public Hull Hull
@@ -123,8 +126,8 @@ namespace Barotrauma
{
i = Math.Min(i, fireSources.Count - 1);
j = Math.Min(j, i - 1);
if (!fireSources[i].CheckOverLap(fireSources[j])) continue;
if (!fireSources[i].CheckOverLap(fireSources[j])) { continue; }
float leftEdge = Math.Min(fireSources[i].position.X, fireSources[j].position.X);
@@ -133,12 +136,10 @@ namespace Barotrauma
- leftEdge;
fireSources[j].position.X = leftEdge;
#if CLIENT
fireSources[j].burnDecals.AddRange(fireSources[i].burnDecals);
fireSources[j].burnDecals.Sort((d1, d2) => { return Math.Sign(d1.WorldPosition.X - d2.WorldPosition.X); });
#endif
fireSources[i].Remove();
}
}
@@ -210,21 +211,32 @@ namespace Barotrauma
private void DamageCharacters(float deltaTime)
{
if (size.X <= 0.0f) return;
if (size.X <= 0.0f) { return; }
for (int i = 0; i < Character.CharacterList.Count; i++)
{
Character c = Character.CharacterList[i];
if (c.AnimController.CurrentHull == null || c.IsDead) continue;
if (c.CurrentHull == null || c.IsDead) { continue; }
if (!IsInDamageRange(c, DamageRange)) continue;
if (!IsInDamageRange(c, DamageRange)) { continue; }
float dmg = (float)Math.Sqrt(size.X) * deltaTime / c.AnimController.Limbs.Length;
//GetApproximateDistance returns float.MaxValue if there's no path through open gaps between the hulls (e.g. if there's a door/wall in between)
if (hull.GetApproximateDistance(Position, c.Position, c.CurrentHull, 10000.0f) > size.X + DamageRange)
{
return;
}
float dmg = (float)Math.Sqrt(Math.Min(500, size.X)) * deltaTime / c.AnimController.Limbs.Count(l => !l.IsSevered);
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.IsSevered) { continue; }
c.LastDamageSource = null;
c.DamageLimb(WorldPosition, limb, new List<Affliction>() { AfflictionPrefab.Burn.Instantiate(dmg) }, 0.0f, false, 0.0f);
c.DamageLimb(WorldPosition, limb, AfflictionPrefab.Burn.Instantiate(dmg).ToEnumerable(), 0.0f, false, 0.0f);
}
#if CLIENT
//let clients display the client-side damage immediately, otherwise they may not be able to react to the damage fast enough
c.CharacterHealth.DisplayedVitality = c.Vitality;
#endif
c.ApplyStatusEffects(ActionType.OnFire, deltaTime);
}
}
@@ -33,6 +33,8 @@ namespace Barotrauma
//the force of the water flow which is exerted on physics bodies
private Vector2 flowForce;
private Hull flowTargetHull;
private float openedTimer = 1.0f;
private float higherSurface;
private float lowerSurface;
@@ -54,7 +56,11 @@ namespace Barotrauma
public float Open
{
get { return open; }
set { open = MathHelper.Clamp(value, 0.0f, 1.0f); }
set
{
if (value > open) { openedTimer = 1.0f; }
open = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
public float Size => IsHorizontal ? Rect.Height : Rect.Width;
@@ -124,6 +130,7 @@ namespace Barotrauma
InsertToList();
outsideCollisionBlocker = GameMain.World.CreateEdge(-Vector2.UnitX * 2.0f, Vector2.UnitX * 2.0f);
outsideCollisionBlocker.UserData = $"CollisionBlocker (Gap {ID})";
outsideCollisionBlocker.BodyType = BodyType.Static;
outsideCollisionBlocker.CollisionCategories = Physics.CollisionWall;
outsideCollisionBlocker.CollidesWith = Physics.CollisionCharacter;
@@ -277,57 +284,18 @@ namespace Barotrauma
flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);
if (openedTimer > 0.0f && flowForce.Length() > lerpedFlowForce.Length())
{
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
lerpedFlowForce = flowForce;
}
else
{
lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);
}
openedTimer -= deltaTime;
EmitParticles(deltaTime);
if (flowTargetHull != null && lerpedFlowForce.LengthSquared() > 0.0001f)
{
foreach (Character character in Character.CharacterList)
{
if (character.CurrentHull == null) continue;
if (character.CurrentHull != linkedTo[0] as Hull &&
(linkedTo.Count < 2 || character.CurrentHull != linkedTo[1] as Hull))
{
continue;
}
foreach (Limb limb in character.AnimController.Limbs)
{
if (!limb.inWater) continue;
float dist = Vector2.Distance(limb.WorldPosition, WorldPosition);
if (dist > lerpedFlowForce.Length()) continue;
Vector2 force = lerpedFlowForce / (float)Math.Max(Math.Sqrt(dist), 20.0f) * 0.025f;
//vertical gaps only apply forces if the character is roughly above/below the gap
if (!IsHorizontal)
{
float xDist = Math.Abs(limb.WorldPosition.X - WorldPosition.X);
if (xDist > rect.Width || rect.Width == 0) break;
force *= 1.0f - xDist / rect.Width;
}
if (!MathUtils.IsValid(force))
{
string errorMsg = "Attempted to apply invalid flow force to the character \"" + character.Name +
"\", gap pos: " + WorldPosition +
", limb pos: " + limb.WorldPosition +
", flowforce: " + flowForce + ", lerpedFlowForce:" + lerpedFlowForce +
", dist: " + dist;
DebugConsole.Log(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Gap.Update:InvalidFlowForce:" + character.Name,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
continue;
}
character.AnimController.Collider.ApplyForce(force * limb.body.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
}
}
}
partial void EmitParticles(float deltaTime);
@@ -22,9 +22,9 @@ namespace Barotrauma
public const float OxygenConsumptionSpeed = 700.0f;
public const int WaveWidth = 32;
public static float WaveStiffness = 0.02f;
public static float WaveSpread = 0.05f;
public static float WaveDampening = 0.05f;
public static float WaveStiffness = 0.01f;
public static float WaveSpread = 0.02f;
public static float WaveDampening = 0.02f;
//how much excess water the room can contain, relative to the volume of the room.
//needed to make it possible for pressure to "push" water up through U-shaped hull configurations
@@ -83,6 +83,21 @@ namespace Barotrauma
}
}
private Color ambientLight;
[Editable, Serialize("0,0,0,0", true)]
public Color AmbientLight
{
get { return ambientLight; }
set
{
ambientLight = value;
#if CLIENT
lastAmbientLightEditTime = Timing.TotalTime;
#endif
}
}
public override Rectangle Rect
{
get
@@ -501,54 +516,34 @@ namespace Barotrauma
rightDelta[i] = WaveSpread * (waveY[i] - waveY[i + 1]);
waveVel[i + 1] += rightDelta[i];
}
for (int i = 1; i < waveY.Length - 1; i++)
{
waveY[i - 1] += leftDelta[i];
waveY[i + 1] += rightDelta[i];
}
}
//make waves propagate through horizontal gaps
foreach (Gap gap in ConnectedGaps)
{
if (!gap.IsRoomToRoom || !gap.IsHorizontal || gap.Open <= 0.0f) continue;
if (surface > gap.Rect.Y || surface < gap.Rect.Y - gap.Rect.Height) continue;
Hull hull2 = this == gap.linkedTo[0] as Hull ? (Hull)gap.linkedTo[1] : (Hull)gap.linkedTo[0];
float otherSurfaceY = hull2.surface;
if (otherSurfaceY > gap.Rect.Y || otherSurfaceY < gap.Rect.Y - gap.Rect.Height) continue;
float surfaceDiff = (surface - otherSurfaceY) * gap.Open;
if (this != gap.linkedTo[0] as Hull)
{
//the first hull linked to the gap handles the wave propagation,
//the second just updates the surfaces to the same level
if (surfaceDiff < 32.0f)
{
hull2.waveY[hull2.waveY.Length - 1] = surfaceDiff * 0.5f;
waveY[0] = -surfaceDiff * 0.5f;
}
//let the first linked hull handle the water propagation
continue;
}
if (!gap.IsRoomToRoom || !gap.IsHorizontal || gap.Open <= 0.0f) { continue; }
if (surface > gap.Rect.Y || surface < gap.Rect.Y - gap.Rect.Height) { continue; }
Hull hull2 = this == gap.linkedTo[0] as Hull ? (Hull)gap.linkedTo[1] : (Hull)gap.linkedTo[0];
float otherSurfaceY = hull2.surface;
if (otherSurfaceY > gap.Rect.Y || otherSurfaceY < gap.Rect.Y - gap.Rect.Height) { continue; }
float surfaceDiff = (surface - otherSurfaceY) * gap.Open;
for (int j = 0; j < 2; j++)
{
int i = waveY.Length - 1;
rightDelta[waveY.Length - 1] = WaveSpread * (hull2.waveY[0] - waveY[waveY.Length - 1] - surfaceDiff) * 0.5f;
waveVel[waveY.Length - 1] += rightDelta[waveY.Length - 1];
waveY[waveY.Length - 1] += rightDelta[waveY.Length - 1];
leftDelta[i] = WaveSpread * (waveY[i] - waveY[i - 1]);
waveVel[i - 1] += leftDelta[i];
rightDelta[i] = WaveSpread * (waveY[i] - hull2.waveY[0] + surfaceDiff);
hull2.waveVel[0] += rightDelta[i];
i = 0;
hull2.leftDelta[i] = WaveSpread * (hull2.waveY[i] - waveY[waveY.Length - 1] - surfaceDiff);
waveVel[waveVel.Length - 1] += hull2.leftDelta[i];
hull2.rightDelta[i] = WaveSpread * (hull2.waveY[i] - hull2.waveY[i + 1]);
hull2.waveVel[i + 1] += hull2.rightDelta[i];
hull2.leftDelta[0] = WaveSpread * (waveY[waveY.Length - 1] - hull2.waveY[0] + surfaceDiff) * 0.5f;
hull2.waveVel[0] += hull2.leftDelta[0];
hull2.waveY[0] += hull2.leftDelta[0];
}
if (surfaceDiff < 32.0f)
@@ -557,13 +552,19 @@ namespace Barotrauma
hull2.waveY[0] = surfaceDiff * 0.5f;
waveY[waveY.Length - 1] = -surfaceDiff * 0.5f;
}
else
}
//apply spread (two iterations)
for (int j = 0; j < 2; j++)
{
for (int i = 1; i < waveY.Length - 1; i++)
{
hull2.waveY[0] += rightDelta[waveY.Length - 1];
waveY[waveY.Length - 1] += hull2.leftDelta[0];
waveY[i - 1] += leftDelta[i];
waveY[i + 1] += rightDelta[i];
}
}
if (waterVolume < Volume)
{
LethalPressure -= 10.0f * deltaTime;
@@ -609,37 +610,33 @@ namespace Barotrauma
FireSources.Remove(fire);
}
private HashSet<Hull> adjacentHulls = new HashSet<Hull>();
public IEnumerable<Hull> GetConnectedHulls(bool includingThis, int? searchDepth = null)
private readonly HashSet<Hull> adjacentHulls = new HashSet<Hull>();
public IEnumerable<Hull> GetConnectedHulls(bool includingThis, int? searchDepth = null, bool ignoreClosedGaps = false)
{
adjacentHulls.Clear();
int startStep = 0;
searchDepth = searchDepth ?? 100;
return GetAdjacentHulls(includingThis, adjacentHulls, ref startStep, searchDepth.Value);
searchDepth ??= 100;
GetAdjacentHulls(adjacentHulls, ref startStep, searchDepth.Value, ignoreClosedGaps);
if (!includingThis) { adjacentHulls.Remove(this); }
return adjacentHulls;
}
private HashSet<Hull> GetAdjacentHulls(bool includingThis, HashSet<Hull> connectedHulls, ref int step, int searchDepth)
private void GetAdjacentHulls(HashSet<Hull> connectedHulls, ref int step, int searchDepth, bool ignoreClosedGaps = false)
{
if (includingThis)
{
connectedHulls.Add(this);
}
if (step > searchDepth)
{
return connectedHulls;
}
connectedHulls.Add(this);
if (step > searchDepth) { return; }
foreach (Gap g in ConnectedGaps)
{
if (ignoreClosedGaps && g.Open <= 0.0f) { continue; }
for (int i = 0; i < 2 && i < g.linkedTo.Count; i++)
{
if (g.linkedTo[i] is Hull hull && !connectedHulls.Contains(hull))
{
step++;
hull.GetAdjacentHulls(true, connectedHulls, ref step, searchDepth);
hull.GetAdjacentHulls(connectedHulls, ref step, searchDepth, ignoreClosedGaps);
}
}
}
return connectedHulls;
}
/// <summary>
@@ -666,7 +663,7 @@ namespace Barotrauma
if (g.ConnectedDoor != null && !g.ConnectedDoor.IsBroken)
{
//gap blocked if the door is not open or the predicted state is not open
if (!g.ConnectedDoor.IsOpen || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
if ((!g.ConnectedDoor.IsOpen && !g.ConnectedDoor.IsBroken) || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
{
if (g.ConnectedDoor.OpenState < 0.1f) continue;
}
@@ -2,7 +2,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
@@ -10,11 +10,14 @@ namespace Barotrauma
{
partial class ItemAssemblyPrefab : MapEntityPrefab
{
private string name;
private readonly string name;
public override string Name { get { return name; } }
public static readonly PrefabCollection<ItemAssemblyPrefab> Prefabs = new PrefabCollection<ItemAssemblyPrefab>();
public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies");
public static readonly string SaveFolder = "ItemAssemblies";
private bool disposed = false;
public override void Dispose()
{
@@ -50,12 +53,25 @@ namespace Barotrauma
name = TextManager.Get("EntityName." + identifier, returnNull: true) ?? originalName;
Description = TextManager.Get("EntityDescription." + identifier, returnNull: true) ?? Description;
List<ushort> containedItemIDs = new List<ushort>();
foreach (XElement entityElement in doc.Root.Elements())
{
var containerElement = entityElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("itemcontainer", StringComparison.OrdinalIgnoreCase));
if (containerElement == null) { continue; }
var itemIds = containerElement.GetAttributeIntArray("contained", new int[0]);
containedItemIDs.AddRange(itemIds.Select(id => (ushort)id));
}
int minX = int.MaxValue, minY = int.MaxValue;
int maxX = int.MinValue, maxY = int.MinValue;
DisplayEntities = new List<Pair<MapEntityPrefab, Rectangle>>();
foreach (XElement entityElement in doc.Root.Elements())
{
string identifier = entityElement.GetAttributeString("identifier", "");
ushort id = (ushort)entityElement.GetAttributeInt("ID", 0);
if (id > 0 && containedItemIDs.Contains(id)) { continue; }
string identifier = entityElement.GetAttributeString("identifier", entityElement.Name.ToString().ToLowerInvariant());
MapEntityPrefab mapEntity = List.FirstOrDefault(p => p.Identifier == identifier);
if (mapEntity == null)
{
@@ -64,9 +80,9 @@ namespace Barotrauma
}
Rectangle rect = entityElement.GetAttributeRect("rect", Rectangle.Empty);
if (mapEntity != null && !entityElement.GetAttributeBool("hideinassemblypreview", false))
if (mapEntity != null && !entityElement.Elements().Any(e => e.Name.LocalName.Equals("wire", StringComparison.OrdinalIgnoreCase)))
{
DisplayEntities.Add(new Pair<MapEntityPrefab, Rectangle>(mapEntity, rect));
if (!entityElement.GetAttributeBool("hideinassemblypreview", false)) { DisplayEntities.Add(new Pair<MapEntityPrefab, Rectangle>(mapEntity, rect)); }
minX = Math.Min(minX, rect.X);
minY = Math.Min(minY, rect.Y - rect.Height);
maxX = Math.Max(maxX, rect.Right);
@@ -74,7 +90,9 @@ namespace Barotrauma
}
}
Bounds = new Rectangle(minX, minY, maxX - minX, maxY - minY);
Bounds = minX == int.MaxValue ?
new Rectangle(0, 0, 1, 1) :
new Rectangle(minX, minY, maxX - minX, maxY - minY);
Prefabs.Add(this, false);
}
@@ -142,11 +160,14 @@ namespace Barotrauma
List<string> itemAssemblyFiles = new List<string>();
//find assembly files in the item assembly folder
string directoryPath = Path.Combine("Content", "Items", "Assemblies");
if (Directory.Exists(directoryPath))
//find assembly files in the item assembly folders
if (Directory.Exists(VanillaSaveFolder))
{
itemAssemblyFiles.AddRange(Directory.GetFiles(directoryPath));
itemAssemblyFiles.AddRange(Directory.GetFiles(VanillaSaveFolder));
}
if (Directory.Exists(SaveFolder))
{
itemAssemblyFiles.AddRange(Directory.GetFiles(SaveFolder));
}
//find assembly files in selected content packages
@@ -27,19 +27,19 @@ namespace Barotrauma
foreach (GraphEdge ge in graphEdges)
{
if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f) continue;
if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f) { continue; }
for (int i = 0; i < 2; i++)
{
Site site = (i == 0) ? ge.Site1 : ge.Site2;
int x = (int)(Math.Floor((site.Coord.X-borders.X) / gridCellSize));
int y = (int)(Math.Floor((site.Coord.Y-borders.Y) / gridCellSize));
int x = (int)(Math.Floor((site.Coord.X - borders.X) / gridCellSize));
int y = (int)(Math.Floor((site.Coord.Y - borders.Y) / gridCellSize));
x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0)-1);
y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1)-1);
VoronoiCell cell = cellGrid[x,y].Find(c => c.Site == site);
x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0) - 1);
y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1) - 1);
VoronoiCell cell = cellGrid[x, y].Find(c => c.Site == site);
if (cell == null)
{
@@ -60,14 +60,62 @@ namespace Barotrauma
}
}
//add edges to the borders of the graph
foreach (var cell in cells)
{
Vector2? point1 = null, point2 = null;
foreach (GraphEdge ge in cell.Edges)
{
if (MathUtils.NearlyEqual(ge.Point1.X, borders.X) || MathUtils.NearlyEqual(ge.Point1.X, borders.Right) ||
MathUtils.NearlyEqual(ge.Point1.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point1.Y, borders.Bottom))
{
if (point1 == null)
{
point1 = ge.Point1;
}
else if (point2 == null)
{
if (MathUtils.NearlyEqual(point1.Value, ge.Point1)) { continue; }
point2 = ge.Point1;
}
}
if (MathUtils.NearlyEqual(ge.Point2.X, borders.X) || MathUtils.NearlyEqual(ge.Point2.X, borders.Right) ||
MathUtils.NearlyEqual(ge.Point2.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point2.Y, borders.Bottom))
{
if (point1 == null)
{
point1 = ge.Point2;
}
else
{
if (MathUtils.NearlyEqual(point1.Value, ge.Point2)) { continue; }
point2 = ge.Point2;
}
}
if (point1.HasValue && point2.HasValue)
{
Debug.Assert(point1 != point2);
var newEdge = new GraphEdge(point1.Value, point2.Value)
{
Cell1 = cell,
IsSolid = true,
Site1 = cell.Site,
OutsideLevel = true
};
cell.Edges.Add(newEdge);
break;
}
}
}
return cells;
}
private static Vector2 GetEdgeNormal(GraphEdge edge, VoronoiCell cell = null)
{
if (cell == null) cell = edge.AdjacentCell(null);
if (cell == null) return Vector2.UnitX;
if (cell == null) { cell = edge.AdjacentCell(null); }
if (cell == null) { return Vector2.UnitX; }
CompareCCW compare = new CompareCCW(cell.Center);
if (compare.Compare(edge.Point1, edge.Point2) == -1)
@@ -77,9 +125,7 @@ namespace Barotrauma
edge.Point2 = temp;
}
Vector2 normal = Vector2.Zero;
normal = Vector2.Normalize(edge.Point2 - edge.Point1);
Vector2 normal = Vector2.Normalize(edge.Point2 - edge.Point1);
Vector2 diffToCell = Vector2.Normalize(cell.Center - edge.Point2);
normal = new Vector2(-normal.Y, normal.X);
@@ -136,7 +182,7 @@ namespace Barotrauma
currentCell.CellType = CellType.Path;
pathCells.Add(currentCell);
int currentTargetIndex = 1;
int currentTargetIndex = 0;
int iterationsLeft = cells.Count;
@@ -148,7 +194,7 @@ namespace Barotrauma
foreach (GraphEdge edge in currentCell.Edges)
{
var adjacentCell = edge.AdjacentCell(currentCell);
if (limits.Contains(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y))
if (adjacentCell != null && limits.Contains(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y))
{
allowedEdges.Add(edge);
}
@@ -161,6 +207,7 @@ namespace Barotrauma
for (int i = 0; i < currentCell.Edges.Count; i++)
{
var adjacentCell = currentCell.Edges[i].AdjacentCell(currentCell);
if (adjacentCell == null) { continue; }
double dist = MathUtils.Distance(
adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y,
targetCells[currentTargetIndex].Site.Coord.X, targetCells[currentTargetIndex].Site.Coord.Y);
@@ -18,6 +18,10 @@ namespace Barotrauma
//all entities are disabled after they reach this depth
public const int MaxEntityDepth = -300000;
public const float ShaftHeight = 1000.0f;
/// <summary>
/// The level generator won't try to adjust the width of the main path above this limit.
/// </summary>
public const int MaxSubmarineWidth = 16000;
public static Level Loaded
{
@@ -141,8 +145,6 @@ namespace Barotrauma
get { return positionsOfInterest; }
}
public readonly List<InterestingPosition> UsedPositions = new List<InterestingPosition>();
public Submarine StartOutpost { get; private set; }
public Submarine EndOutpost { get; private set; }
@@ -324,26 +326,25 @@ namespace Barotrauma
SeaFloorTopPos = generationParams.SeaFloorDepth + generationParams.MountainHeightMax + generationParams.SeaFloorVariance;
int minWidth = 6500;
int maxWidth = 50000;
if (Submarine.MainSub != null)
{
Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders();
dockedSubBorders.Inflate(dockedSubBorders.Size.ToVector2() * 0.15f);
minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height));
minWidth = Math.Min(minWidth, maxWidth);
minWidth = Math.Min(minWidth, MaxSubmarineWidth);
}
Rectangle pathBorders = borders;
pathBorders.Inflate(-minWidth * 2, -minWidth);
pathBorders.Inflate(-Math.Min(minWidth * 2, MaxSubmarineWidth), -minWidth);
Debug.Assert(pathBorders.Width > 0 && pathBorders.Height > 0, "The size of the level's path area was negative.");
startPosition = new Point(
Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
minWidth,
Rand.Range(borders.Height / 2, borders.Height - minWidth * 2, Rand.RandSync.Server));
endPosition = new Point(
borders.Width - Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
borders.Width - minWidth,
Rand.Range(borders.Height / 2, borders.Height - minWidth * 2, Rand.RandSync.Server));
//----------------------------------------------------------------------------------
@@ -356,19 +357,25 @@ namespace Barotrauma
Point nodeInterval = generationParams.MainPathNodeIntervalRange;
for (int x = startPosition.X + nodeInterval.X;
x < endPosition.X - nodeInterval.X;
x < endPosition.X - nodeInterval.X;
x += Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server))
{
pathNodes.Add(new Point(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, Rand.RandSync.Server)));
}
pathNodes.Add(new Point(endPosition.X, borders.Height));
if (pathNodes.Count <= 2)
if (pathNodes.Count == 1)
{
pathNodes.Insert(1, borders.Center);
pathNodes.Add(new Point(pathBorders.Center.X, pathBorders.Y));
}
//if all nodes ended up high up in the level, move one down to make sure we utilize the full height of the level
else if (pathNodes.GetRange(1, pathNodes.Count - 1).All(p => p.Y > pathBorders.Y + pathBorders.Height * 0.25f))
{
int nodeIndex = Rand.Range(1, pathNodes.Count, Rand.RandSync.Server);
pathNodes[nodeIndex] = new Point(pathNodes[nodeIndex].X, pathBorders.Y);
}
pathNodes.Add(new Point(endPosition.X, borders.Height));
GenerateTunnels(pathNodes, minWidth);
//----------------------------------------------------------------------------------
@@ -547,21 +554,21 @@ namespace Barotrauma
{
foreach (GraphEdge edge in cell.Edges)
{
if (mirroredEdges.Contains(edge)) continue;
if (mirroredEdges.Contains(edge)) { continue; }
edge.Point1.X = borders.Width - edge.Point1.X;
edge.Point2.X = borders.Width - edge.Point2.X;
if (!mirroredSites.Contains(edge.Site1))
if (edge.Site1 != null && !mirroredSites.Contains(edge.Site1))
{
//make sure that sites right at the edge of a grid cell end up in the same cell as in the non-mirrored level
if (edge.Site1.Coord.X % GridCellSize < 1.0f &&
edge.Site1.Coord.X % GridCellSize >= 0.0f) edge.Site1.Coord.X += 1.0f;
edge.Site1.Coord.X % GridCellSize >= 0.0f) { edge.Site1.Coord.X += 1.0f; }
edge.Site1.Coord.X = borders.Width - edge.Site1.Coord.X;
mirroredSites.Add(edge.Site1);
}
if (!mirroredSites.Contains(edge.Site2))
if (edge.Site2 != null && !mirroredSites.Contains(edge.Site2))
{
if (edge.Site2.Coord.X % GridCellSize < 1.0f &&
edge.Site2.Coord.X % GridCellSize >= 0.0f) edge.Site2.Coord.X += 1.0f;
edge.Site2.Coord.X % GridCellSize >= 0.0f) { edge.Site2.Coord.X += 1.0f; }
edge.Site2.Coord.X = borders.Width - edge.Site2.Coord.X;
mirroredSites.Add(edge.Site2);
}
@@ -1583,8 +1590,8 @@ namespace Barotrauma
var subDoc = SubmarineInfo.OpenFile(contentFile.Path);
Rectangle borders = Submarine.GetBorders(subDoc.Root);
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
// Add some vertical margin so that the wreck doesn't block the path entirely. It's still possible that some larger subs can't pass by.
Point paddedDimensions = new Point(borders.Width, borders.Height + 3000);
// Add some margin so that the wreck doesn't block the path entirely. It's still possible that some larger subs can't pass by.
Point paddedDimensions = new Point(borders.Width + 3000, borders.Height + 3000);
tempSW.Restart();
// For storing the translations. Used only for debugging.
var positions = new List<Vector2>();
@@ -1593,6 +1600,7 @@ namespace Barotrauma
int attemptsLeft = maxAttempts;
bool success = false;
Vector2 spawnPoint = Vector2.Zero;
var allCells = Loaded.GetAllCells();
while (attemptsLeft > 0)
{
if (attemptsLeft < maxAttempts)
@@ -1621,7 +1629,7 @@ namespace Barotrauma
tempSW.Stop();
if (success)
{
Debug.WriteLine($"Wreck {wreckName} successfully positioned to {spawnPoint} in {tempSW.ElapsedMilliseconds.ToString()} (ms)");
Debug.WriteLine($"Wreck {wreckName} successfully positioned to {spawnPoint} in {tempSW.ElapsedMilliseconds} (ms)");
tempSW.Restart();
SubmarineInfo info = new SubmarineInfo(contentFile.Path)
{
@@ -1630,7 +1638,7 @@ namespace Barotrauma
Submarine wreck = new Submarine(info);
wreck.MakeWreck();
tempSW.Stop();
Debug.WriteLine($"Wreck {wreck.Info.Name} loaded in { tempSW.ElapsedMilliseconds.ToString()} (ms)");
Debug.WriteLine($"Wreck {wreck.Info.Name} loaded in { tempSW.ElapsedMilliseconds} (ms)");
wrecks.Add(wreck);
wreck.SetPosition(spawnPoint);
wreckPositions.Add(wreck, positions);
@@ -1643,7 +1651,8 @@ namespace Barotrauma
hull.WaterVolume = hull.Volume * Rand.Range(Loaded.GenerationParams.WreckFloodingHullMinWaterPercentage, Loaded.GenerationParams.WreckFloodingHullMaxWaterPercentage, Rand.RandSync.Server);
}
}
if (Rand.Value(Rand.RandSync.Server) <= Loaded.GenerationParams.ThalamusProbability)
// Only spawn thalamus when the wreck has some thalamus items defined.
if (Rand.Value(Rand.RandSync.Server) <= Loaded.GenerationParams.ThalamusProbability && wreck.GetItems(false).Any(i => i.Prefab.Category == MapEntityCategory.Thalamus))
{
if (!wreck.CreateWreckAI())
{
@@ -1839,8 +1848,7 @@ namespace Barotrauma
{
return true;
}
var cells = Loaded.GetAllCells().Where(c => c.Body != null && Vector2.DistanceSquared(pos, c.Center) <= maxDistance);
return cells.Any(c => c.BodyVertices.Any(v => bounds.ContainsWorld(v)));
return cells.Any(c => c.Body != null && Vector2.DistanceSquared(pos, c.Center) <= maxDistance && c.BodyVertices.Any(v => bounds.ContainsWorld(v)));
}
}
totalSW.Stop();
@@ -238,7 +238,8 @@ namespace Barotrauma
}
[Editable, Serialize("5000, 10000", true, description: "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
Serialize("5000, 10000", true, description: "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
public Point MainPathNodeIntervalRange
{
get { return mainPathNodeIntervalRange; }
@@ -256,7 +257,8 @@ namespace Barotrauma
set { smallTunnelCount = MathHelper.Clamp(value, 0, 100); }
}
[Editable, Serialize("5000, 10000", true, description: "The minimum and maximum length of small tunnels placed along the main path.")]
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
Serialize("5000, 10000", true, description: "The minimum and maximum length of small tunnels placed along the main path.")]
public Point SmallTunnelLengthRange
{
get { return smallTunnelLengthRange; }
@@ -358,19 +358,23 @@ namespace Barotrauma
return;
}
//check if there are any other contacts with the entity
//check if there are contacts with any other fixture of the trigger
//(the OnSeparation callback happens when two fixtures separate,
//e.g. if a body stops touching the circular fixture at the end of a capsule-shaped body)
ContactEdge contactEdge = fixtureA.Body.ContactList;
while (contactEdge != null)
{
if (contactEdge.Contact != null &&
contactEdge.Contact.Enabled &&
contactEdge.Contact.IsTouching)
{
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
contactEdge.Contact.FixtureB :
contactEdge.Contact.FixtureA);
if (otherEntity == entity) return;
if (contactEdge.Contact.FixtureA != fixtureA && contactEdge.Contact.FixtureB != fixtureA)
{
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
contactEdge.Contact.FixtureB :
contactEdge.Contact.FixtureA);
if (otherEntity == entity) { return; }
}
}
contactEdge = contactEdge.Next;
}
@@ -418,10 +422,20 @@ namespace Barotrauma
public void Update(float deltaTime)
{
if (ParentTrigger != null && !ParentTrigger.IsTriggered) return;
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
triggerers.RemoveWhere(t => t.Removed);
if (physicsBody != null)
{
//failsafe to ensure triggerers get removed when they're far from the trigger
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(physicsBody.GetMaxExtent() * 5), 5000.0f);
triggerers.RemoveWhere(t =>
{
return Vector2.Distance(t.WorldPosition, WorldPosition) > maxExtent;
});
}
bool isNotClient = true;
#if CLIENT
isNotClient = GameMain.Client == null;
@@ -511,6 +525,7 @@ namespace Barotrauma
ApplyForce(character.AnimController.Collider, deltaTime);
foreach (Limb limb in character.AnimController.Limbs)
{
if (limb.IsSevered) { continue; }
ApplyForce(limb.body, deltaTime);
}
}
@@ -1,8 +1,8 @@
using Microsoft.Xna.Framework;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace Barotrauma.RuinGeneration
@@ -174,7 +174,7 @@ namespace Barotrauma.RuinGeneration
public static void SaveAll()
{
XmlWriterSettings settings = new XmlWriterSettings
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = true
@@ -654,13 +654,14 @@ namespace Barotrauma.RuinGeneration
{
connectionPanel.Locked = true;
connectionPanel.CanBeSelected = false;
connectionPanel.Item.ShouldBeSaved = false;
}
// Hide wires for now
// Hide wires
if (ic is Wire wire)
{
wire.Hidden = true;
wire.CanBeSelected = false;
wire.Item.ShouldBeSaved = false;
}
}
}
@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Barotrauma.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@@ -174,7 +174,11 @@ namespace Barotrauma
int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale);
int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale);
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
if (Sections != null)
if (StairDirection != Direction.None)
{
CreateStairBodies();
}
else if (Sections != null)
{
UpdateSections();
}
@@ -283,6 +287,13 @@ namespace Barotrauma
}
}
[Serialize(false, true), Editable]
public bool NoAITarget
{
get;
private set;
}
public Dictionary<string, SerializableProperty> SerializableProperties
{
get;
@@ -354,6 +365,7 @@ namespace Barotrauma
}
StairDirection = Prefab.StairDirection;
NoAITarget = Prefab.NoAITarget;
SerializableProperties = SerializableProperty.GetProperties(this);
InitProjSpecific();
@@ -381,7 +393,7 @@ namespace Barotrauma
}
// Only add ai targets automatically to submarine/outpost walls
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !submarine.Info.IsWreck && !Prefab.NoAITarget)
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !submarine.Info.IsWreck && !NoAITarget)
{
aiTarget = new AITarget(this)
{
@@ -423,6 +435,7 @@ namespace Barotrauma
private void CreateStairBodies()
{
Bodies = new List<Body>();
bodyDebugDimensions.Clear();
float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f));
@@ -440,7 +453,7 @@ namespace Barotrauma
newBody.Friction = 0.8f;
newBody.UserData = this;
newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset;
newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset * Scale;
bodyDebugDimensions.Add(new Vector2(bodyWidth, bodyHeight));
@@ -567,12 +580,12 @@ namespace Barotrauma
{
foreach (MapEntity mapEntity in mapEntityList)
{
if (!(mapEntity is Structure structure)) continue;
if (!structure.Prefab.AllowAttachItems) continue;
if (structure.Bodies != null && structure.Bodies.Count > 0) continue;
if (!(mapEntity is Structure structure)) { continue; }
if (!structure.Prefab.AllowAttachItems) { continue; }
if (structure.Bodies != null && structure.Bodies.Count > 0) { continue; }
Rectangle worldRect = mapEntity.WorldRect;
if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) continue;
if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) continue;
if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) { continue; }
if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) { continue; }
return structure;
}
return null;
@@ -818,7 +831,10 @@ namespace Barotrauma
Vector2 sectionPos = new Vector2(
Sections[sectionIndex].rect.X + Sections[sectionIndex].rect.Width / 2.0f,
Sections[sectionIndex].rect.Y - Sections[sectionIndex].rect.Height / 2.0f);
if (world && Submarine != null) sectionPos += Submarine.Position;
if (world && Submarine != null)
{
sectionPos += Submarine.Position;
}
return sectionPos;
}
else
@@ -839,7 +855,10 @@ namespace Barotrauma
(float)Math.Cos(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation),
(float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation)) * diffFromCenter;
if (world && Submarine != null) sectionPos += Submarine.Position;
if (world && Submarine != null)
{
sectionPos += Submarine.Position;
}
return sectionPos;
}
@@ -1255,6 +1274,11 @@ namespace Barotrauma
s.UseDropShadow = prefab.Body;
}
if (element.Attribute("noaitarget") == null)
{
s.NoAITarget = prefab.NoAITarget;
}
return s;
}
@@ -1327,6 +1351,7 @@ namespace Barotrauma
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
Sprite.ReloadXML();
SpriteDepth = Sprite.Depth;
NoAITarget = Prefab.NoAITarget;
}
public override void Update(float deltaTime, Camera cam)
@@ -4,7 +4,7 @@ using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using System.IO;
using Barotrauma.IO;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
#endif
@@ -7,7 +7,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -740,7 +740,7 @@ namespace Barotrauma
public void FlipX(List<Submarine> parents = null)
{
if (parents == null) parents = new List<Submarine>();
if (parents == null) { parents = new List<Submarine>(); }
parents.Add(this);
flippedX = !flippedX;
@@ -748,20 +748,25 @@ namespace Barotrauma
Item.UpdateHulls();
List<Item> bodyItems = Item.ItemList.FindAll(it => it.Submarine == this && it.body != null);
List<MapEntity> subEntities = MapEntity.mapEntityList.FindAll(me => me.Submarine == this);
foreach (MapEntity e in subEntities)
{
if (e is Item) continue;
if (e is LinkedSubmarine)
if (e is LinkedSubmarine linkedSub)
{
Submarine sub = ((LinkedSubmarine)e).Sub;
if (!parents.Contains(sub))
Submarine sub = linkedSub.Sub;
if (sub == null)
{
Vector2 relative1 = linkedSub.Position - SubBody.Position;
relative1.X = -relative1.X;
linkedSub.Rect = new Rectangle((relative1 + SubBody.Position).ToPoint(), linkedSub.Rect.Size);
}
else if (!parents.Contains(sub))
{
Vector2 relative1 = sub.SubBody.Position - SubBody.Position;
relative1.X = -relative1.X;
sub.SetPosition(relative1 + SubBody.Position);
sub.SetPosition(relative1 + SubBody.Position, new List<Submarine>(parents));
sub.FlipX(parents);
}
}
@@ -779,7 +784,7 @@ namespace Barotrauma
Vector2 pos = new Vector2(subBody.Position.X, subBody.Position.Y);
subBody.Body.Remove();
subBody = new SubmarineBody(this);
SetPosition(pos);
SetPosition(pos, new List<Submarine>(parents.Where(p => p != this)));
if (entityGrid != null)
{
@@ -812,20 +817,24 @@ namespace Barotrauma
Item.UpdateHulls();
Gap.UpdateHulls();
#if CLIENT
Lights.ConvexHull.RecalculateAll(this);
#endif
}
public void Update(float deltaTime)
{
//if (PlayerInput.KeyHit(InputType.Crouch) && (this == MainSub)) FlipX();
if (Level.Loaded == null || subBody == null) { return; }
if (Info.IsWreck)
{
WreckAI?.Update(deltaTime);
}
if (WorldPosition.Y < Level.MaxEntityDepth &&
if (subBody?.Body == null) { return; }
if (Level.Loaded != null &&
WorldPosition.Y < Level.MaxEntityDepth &&
subBody.Body.Enabled &&
(GameMain.NetworkMember?.RespawnManager == null || this != GameMain.NetworkMember.RespawnManager.RespawnShuttle))
{
@@ -852,17 +861,17 @@ namespace Barotrauma
return;
}
subBody.Body.LinearVelocity = new Vector2(
LockX ? 0.0f : subBody.Body.LinearVelocity.X,
LockY ? 0.0f : subBody.Body.LinearVelocity.Y);
subBody.Update(deltaTime);
for (int i = 0; i < 2; i++)
{
if (MainSubs[i] == null) continue;
if (this != MainSubs[i] && MainSubs[i].DockedTo.Contains(this)) return;
if (MainSubs[i] == null) { continue; }
if (this != MainSubs[i] && MainSubs[i].DockedTo.Contains(this)) { return; }
}
//send updates more frequently if moving fast
@@ -884,9 +893,9 @@ namespace Barotrauma
prevPosition = position;
}
public void SetPosition(Vector2 position, List<Submarine> checkd=null)
public void SetPosition(Vector2 position, List<Submarine> checkd = null)
{
if (!MathUtils.IsValid(position)) return;
if (!MathUtils.IsValid(position)) { return; }
if (checkd == null) { checkd = new List<Submarine>(); }
if (checkd.Contains(this)) { return; }
@@ -898,7 +907,7 @@ namespace Barotrauma
foreach (Submarine dockedSub in DockedTo)
{
if (dockedSub.Info.IsOutpost)
if (dockedSub.PhysicsBody.BodyType == BodyType.Static)
{
if (ConnectedDockingPorts.TryGetValue(dockedSub, out DockingPort port))
{
@@ -1169,7 +1178,7 @@ namespace Barotrauma
foreach (Hull hull in matchingHulls)
{
if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.Contains("roomname.", StringComparison.OrdinalIgnoreCase))
if (string.IsNullOrEmpty(hull.RoomName))// || !hull.RoomName.Contains("roomname.", StringComparison.OrdinalIgnoreCase))
{
hull.RoomName = hull.CreateRoomName();
}
@@ -1229,7 +1238,7 @@ namespace Barotrauma
Info.CheckSubsLeftBehind(element);
}
public bool SaveAs(string filePath, MemoryStream previewImage = null)
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
{
var newInfo = new SubmarineInfo(this)
{
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Common;
@@ -302,7 +303,7 @@ namespace Barotrauma
//-------------------------
//if outside left or right edge of the level
if (Position.X < 0 || Position.X > Level.Loaded.Size.X)
if (Level.Loaded != null && (Position.X < 0 || Position.X > Level.Loaded.Size.X))
{
Rectangle worldBorders = Borders;
worldBorders.Location += MathUtils.ToPoint(Position);
@@ -386,12 +387,13 @@ namespace Barotrauma
foreach (Character c in Character.CharacterList)
{
if (c.AnimController.CurrentHull != null && c.AnimController.CanEnterSubmarine) continue;
if (c.AnimController.CurrentHull != null && c.AnimController.CanEnterSubmarine) { continue; }
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.IsSevered) { continue; }
//if the character isn't inside the bounding box, continue
if (!Submarine.RectContains(worldBorders, limb.WorldPosition)) continue;
if (!Submarine.RectContains(worldBorders, limb.WorldPosition)) { continue; }
//cast a line from the position of the character to the same direction as the translation of the sub
//and see where it intersects with the bounding box
@@ -450,16 +452,18 @@ namespace Barotrauma
private void UpdateDepthDamage(float deltaTime)
{
if (Position.Y > DamageDepth) { return; }
#if CLIENT
if (GameMain.GameSession.GameMode is SubTestMode) { return; }
#endif
float depth = DamageDepth - Position.Y;
depthDamageTimer -= deltaTime;
if (depthDamageTimer > 0.0f) return;
if (depthDamageTimer > 0.0f) { return; }
foreach (Structure wall in Structure.WallList)
{
if (wall.Submarine != submarine) continue;
if (wall.Submarine != submarine) { continue; }
if (wall.Health < depth * 0.01f)
{
@@ -499,10 +503,14 @@ namespace Barotrauma
}
return collision;
}
if (f2.Body.UserData is Character character)
else if (f2.Body.UserData is Character character)
{
return CheckCharacterCollision(contact, character);
}
else if (f2.UserData is Items.Components.DockingPort)
{
return false;
}
lock (impactQueue)
{
@@ -634,7 +642,7 @@ namespace Barotrauma
float damageAmount = contactDot * Body.Mass / limb.character.Mass;
limb.character.LastDamageSource = submarine;
limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(collision.ImpactPos), limb,
new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(damageAmount) }, 0.0f, true, 0.0f);
AfflictionPrefab.ImpactDamage.Instantiate(damageAmount).ToEnumerable(), 0.0f, true, 0.0f);
if (limb.character.IsDead)
{
@@ -693,8 +701,8 @@ namespace Barotrauma
//find all contacts between this sub and level walls
List<Contact> levelContacts = new List<Contact>();
ContactEdge contactEdge = Body.FarseerBody.ContactList;
while (contactEdge.Next != null)
ContactEdge contactEdge = Body?.FarseerBody?.ContactList;
while (contactEdge?.Next != null)
{
if (contactEdge.Contact.Enabled &&
contactEdge.Other.UserData is VoronoiCell &&
@@ -706,7 +714,7 @@ namespace Barotrauma
contactEdge = contactEdge.Next;
}
if (levelContacts.Count == 0) return;
if (levelContacts.Count == 0) { return; }
//if this sub is in contact with the level, apply artifical impacts
//to both subs to prevent the other sub from bouncing on top of this one
@@ -795,6 +803,7 @@ namespace Barotrauma
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.IsSevered) { continue; }
limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f);
}
c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);
@@ -2,7 +2,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using Barotrauma.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -233,7 +233,7 @@ namespace Barotrauma
for (int i = 0; i <= maxLoadRetries; i++)
{
doc = OpenFile(FilePath, out Exception e);
if (e != null && !(e is IOException)) { break; }
if (e != null && !(e is System.IO.IOException)) { break; }
if (doc != null || i == maxLoadRetries || !File.Exists(FilePath)) { break; }
DebugConsole.NewMessage("Opening submarine file \"" + FilePath + "\" failed, retrying in 250 ms...");
Thread.Sleep(250);
@@ -369,13 +369,16 @@ namespace Barotrauma
//saving/loading ----------------------------------------------------
public bool SaveAs(string filePath, MemoryStream previewImage=null)
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage=null)
{
var newElement = new XElement(SubmarineElement.Name,
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase)),
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) &&
!string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)),
SubmarineElement.Elements());
XDocument doc = new XDocument(newElement);
doc.Root.Add(new XAttribute("name", Name));
if (previewImage != null)
{
doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray())));
@@ -459,7 +462,7 @@ namespace Barotrauma
subDirectories = Directory.GetDirectories(SavePath).Where(s =>
{
DirectoryInfo dir = new DirectoryInfo(s);
return (dir.Attributes & FileAttributes.Hidden) == 0;
return (dir.Attributes & System.IO.FileAttributes.Hidden) == 0;
}).ToArray();
}
catch (Exception e)
@@ -559,12 +562,12 @@ namespace Barotrauma
if (extension == ".sub")
{
Stream stream = null;
System.IO.Stream stream = null;
try
{
stream = SaveUtil.DecompressFiletoStream(file);
}
catch (FileNotFoundException e)
catch (System.IO.FileNotFoundException e)
{
exception = e;
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (File not found) " + Environment.StackTrace, e);
@@ -124,13 +124,15 @@ namespace Barotrauma
#if CLIENT
if (iconSprites == null)
{
iconSprites = new Dictionary<SpawnType, Sprite>()
iconSprites = new Dictionary<string, Sprite>()
{
{ SpawnType.Path, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,0,128,128)) },
{ SpawnType.Human, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,0,128,128)) },
{ SpawnType.Enemy, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256,0,128,128)) },
{ SpawnType.Cargo, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384,0,128,128)) },
{ SpawnType.Corpse, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512,0,128,128)) }
{ "Path", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,0,128,128)) },
{ "Human", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,0,128,128)) },
{ "Enemy", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256,0,128,128)) },
{ "Cargo", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384,0,128,128)) },
{ "Corpse", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512,0,128,128)) },
{ "Ladder", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,128,128,128)) },
{ "Door", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,128,128,128)) }
};
}
#endif
@@ -592,6 +594,11 @@ namespace Barotrauma
return assignedWayPoints;
}
public void FindHull()
{
currentHull = Hull.FindHull(WorldPosition, CurrentHull);
}
public override void OnMapLoaded()
{
currentHull = Hull.FindHull(WorldPosition, currentHull);