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
@@ -35,7 +35,7 @@ namespace Barotrauma
Cave = 0x4,
Ruin = 0x8,
Wreck = 0x10,
BeaconStation = 0x20,
BeaconStation = 0x20, // Not used anywhere
Abyss = 0x40,
AbyssCave = 0x80
}
@@ -325,9 +325,11 @@ namespace Barotrauma
get { return LevelData.Seed; }
}
public static float? ForcedDifficulty;
public float Difficulty
{
get { return LevelData.Difficulty; }
get { return ForcedDifficulty ?? LevelData.Difficulty; }
}
public LevelData.LevelType Type
@@ -387,7 +389,7 @@ namespace Barotrauma
private void Generate(bool mirror)
{
if (Loaded != null) { Loaded.Remove(); }
Loaded?.Remove();
Loaded = this;
Generating = true;
@@ -1152,7 +1154,7 @@ namespace Barotrauma
}
CreateWrecks();
CreateBeaconStation(cells);
CreateBeaconStation();
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
@@ -1892,26 +1894,45 @@ namespace Barotrauma
private void CalculateTunnelDistanceField(int density)
{
distanceField = new List<(Point point, double distance)>();
for (int x = 0; x < Size.X; x += density)
if (Mirrored)
{
for (int y = 0; y < Size.Y; y += density)
for (int x = Size.X - 1; x >= 0; x -= density)
{
Point point = new Point(x, y);
double shortestDistSqr = double.PositiveInfinity;
foreach (Tunnel tunnel in Tunnels)
for (int y = 0; y < Size.Y; y += density)
{
for (int i = 1; i < tunnel.Nodes.Count; i++)
{
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.LineSegmentToPointDistanceSquared(tunnel.Nodes[i - 1], tunnel.Nodes[i], point));
}
addPoint(x, y);
}
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)startPosition.X, (double)startPosition.Y));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)startExitPosition.X, (double)borders.Bottom));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)endPosition.X, (double)endPosition.Y));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)endExitPosition.X, (double)borders.Bottom));
distanceField.Add((point, Math.Sqrt(shortestDistSqr)));
}
}
else
{
for (int x = 0; x < Size.X; x += density)
{
for (int y = 0; y < Size.Y; y += density)
{
addPoint(x, y);
}
}
}
void addPoint(int x, int y)
{
Point point = new Point(x, y);
double shortestDistSqr = double.PositiveInfinity;
foreach (Tunnel tunnel in Tunnels)
{
for (int i = 1; i < tunnel.Nodes.Count; i++)
{
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.LineSegmentToPointDistanceSquared(tunnel.Nodes[i - 1], tunnel.Nodes[i], point));
}
}
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)startPosition.X, (double)startPosition.Y));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)startExitPosition.X, (double)borders.Bottom));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)endPosition.X, (double)endPosition.Y));
shortestDistSqr = Math.Min(shortestDistSqr, MathUtils.DistanceSquared((double)point.X, (double)point.Y, (double)endExitPosition.X, (double)borders.Bottom));
distanceField.Add((point, Math.Sqrt(shortestDistSqr)));
}
}
private double GetDistToTunnel(Vector2 position, Tunnel tunnel)
@@ -2710,14 +2731,21 @@ namespace Barotrauma
return position;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
public bool TryGetInterestingPositionAwayFromPoint(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Vector2 awayPoint, float minDistFromPoint, Func<InterestingPosition, bool> filter = null)
{
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, filter);
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, awayPoint, minDistFromPoint, filter);
position = pos.ToVector2();
return success;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Func<InterestingPosition, bool> filter = null)
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
{
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, Vector2.Zero, minDistFromPoint: 0, filter);
position = pos.ToVector2();
return success;
}
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Vector2 awayPoint, float minDistFromPoint = 0f, Func<InterestingPosition, bool> filter = null)
{
if (!PositionsOfInterest.Any())
{
@@ -2755,6 +2783,11 @@ namespace Barotrauma
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), sub.WorldPosition) < minDistFromSubs * minDistFromSubs);
}
}
if (minDistFromPoint > 0.0f)
{
farEnoughPositions.RemoveAll(p => Vector2.DistanceSquared(p.Position.ToVector2(), awayPoint) < minDistFromPoint * minDistFromPoint);
}
if (!farEnoughPositions.Any())
{
string errorMsg = "Could not find a position of interest far enough from the submarines. (PositionType: " + positionType + ", minDistFromSubs: " + minDistFromSubs + ")\n" + Environment.StackTrace.CleanupStackTrace();
@@ -2826,7 +2859,7 @@ namespace Barotrauma
if (index < 0 || index >= bottomPositions.Count - 1) { return new Vector2(xPosition, BottomPos); }
float t = (xPosition - bottomPositions[index].X) / (bottomPositions[index + 1].X - bottomPositions[index].X);
Debug.Assert(t < 1.0f);
Debug.Assert(t <= 1.0f);
t = MathHelper.Clamp(t, 0.0f, 1.0f);
float yPos = MathHelper.Lerp(bottomPositions[index].Y, bottomPositions[index + 1].Y, t);
@@ -2993,20 +3026,30 @@ namespace Barotrauma
return originalTag + "_" + shortSeed;
}
public bool IsCloseToStart(Vector2 position, float minDist) => IsCloseToStart(position.ToPoint(), minDist);
public bool IsCloseToEnd(Vector2 position, float minDist) => IsCloseToEnd(position.ToPoint(), minDist);
public bool IsCloseToStart(Point position, float minDist)
{
return MathUtils.LineSegmentToPointDistanceSquared(StartPosition.ToPoint(), StartExitPosition.ToPoint(), position) < minDist * minDist;
}
public bool IsCloseToEnd(Point position, float minDist)
{
return MathUtils.LineSegmentToPointDistanceSquared(EndPosition.ToPoint(), EndExitPosition.ToPoint(), position) < minDist * minDist;
}
private Submarine SpawnSubOnPath(string subName, ContentFile contentFile, SubmarineType type)
{
var tempSW = new Stopwatch();
// Min distance between a sub and the start/end/other sub.
float minDistance = Sonar.DefaultSonarRange;
float squaredMinDistance = minDistance * minDistance;
Vector2 start = startPosition.ToVector2();
Vector2 end = endPosition.ToVector2();
var waypoints = WayPoint.WayPointList.Where(wp =>
wp.Submarine == null &&
wp.SpawnType == SpawnType.Path &&
Vector2.DistanceSquared(wp.WorldPosition, start) > squaredMinDistance &&
Vector2.DistanceSquared(wp.WorldPosition, end) > squaredMinDistance).ToList();
!IsCloseToStart(wp.WorldPosition, minDistance) &&
!IsCloseToEnd(wp.WorldPosition, minDistance)).ToList();
var subDoc = SubmarineInfo.OpenFile(contentFile.Path);
Rectangle subBorders = Submarine.GetBorders(subDoc.Root);
@@ -3094,12 +3137,11 @@ namespace Barotrauma
sub.SetPosition(spawnPoint);
wreckPositions.Add(sub, positions);
blockedRects.Add(sub, rects);
return sub;
}
else
{
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds.ToString()} (ms).", Color.Red);
DebugConsole.NewMessage($"Failed to position wreck {subName}. Used {tempSW.ElapsedMilliseconds} (ms).", Color.Red);
return null;
}
@@ -3150,7 +3192,7 @@ namespace Barotrauma
else
{
var sp = spawnPoint;
if (Wrecks.Any(w => Vector2.DistanceSquared(w.WorldPosition, sp) < squaredMinDistance))
if (Wrecks.Any(w => Vector2.DistanceSquared(w.WorldPosition, sp) < minDistance * minDistance))
{
Debug.WriteLine($"Invalid position {spawnPoint}. Too close to other wreck(s).");
return false;
@@ -3306,18 +3348,25 @@ namespace Barotrauma
}
wreckFiles.Shuffle(Rand.RandSync.Server);
int wreckCount = Math.Min(Loaded.GenerationParams.WreckCount, wreckFiles.Count);
int minWreckCount = Math.Min(Loaded.GenerationParams.MinWreckCount, wreckFiles.Count);
int maxWreckCount = Math.Min(Loaded.GenerationParams.MaxWreckCount, wreckFiles.Count);
int wreckCount = Rand.Range(minWreckCount, maxWreckCount + 1, Rand.RandSync.Server);
if (GameMain.GameSession?.GameMode?.Missions.Any(m => m.Prefab.RequireWreck) ?? false)
{
wreckCount = Math.Max(wreckCount, 1);
}
Wrecks = new List<Submarine>(wreckCount);
for (int i = 0; i < wreckCount; i++)
{
ContentFile contentFile = wreckFiles[i];
if (contentFile == null) { continue; }
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
// For storing the translations. Used only for debugging.
SpawnSubOnPath(wreckName, contentFile, SubmarineType.Wreck);
}
totalSW.Stop();
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds.ToString()} (ms)");
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds} (ms)");
}
private bool HasStartOutpost()
@@ -3365,11 +3414,8 @@ namespace Barotrauma
for (int i = 0; i < 2; i++)
{
if (Submarine.MainSubs.Length > 1 && Submarine.MainSubs[0] != null && Submarine.MainSubs[1] != null)
{
continue;
}
if (GameMain.GameSession?.GameMode is PvPMode) { continue; }
bool isStart = (i == 0) == !Mirrored;
if (isStart)
{
@@ -3527,7 +3573,7 @@ namespace Barotrauma
{
spawnPos.Y = Math.Min(Size.Y - outpost.Borders.Height * 0.6f, spawnPos.Y + outpost.Borders.Height / 2);
}
outpost.SetPosition(spawnPos);
outpost.SetPosition(spawnPos, forceUndockFromStaticSubmarines: false);
if ((i == 0) == !Mirrored)
{
StartOutpost = outpost;
@@ -3550,7 +3596,7 @@ namespace Barotrauma
}
}
private void CreateBeaconStation(List<VoronoiCell> mainPath)
private void CreateBeaconStation()
{
if (!LevelData.HasBeaconStation) { return; }
var beaconStationFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.BeaconStation).ToList();
@@ -496,8 +496,11 @@ namespace Barotrauma
[Serialize(1, true, description: "The number of alien ruins in the level."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int RuinCount { get; set; }
[Serialize(1, true, description: "The minimum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int MinWreckCount { get; set; }
[Serialize(1, true, description: "The maximum number of wrecks in the level. Note that this value cannot be higher than the amount of wreck prefabs (subs)."), Editable(MinValueInt = 0, MaxValueInt = 10)]
public int WreckCount { get; set; }
public int MaxWreckCount { get; set; }
// TODO: Move the wreck parameters under a separate class?
#region Wreck parameters
@@ -8,7 +8,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
partial class LevelObject : ISpatialEntity
partial class LevelObject : ISpatialEntity, IDamageable, ISerializableEntity
{
public readonly LevelObjectPrefab Prefab;
public Vector3 Position;
@@ -21,6 +21,8 @@ namespace Barotrauma
private int spriteIndex;
protected bool tookDamage;
public LevelObjectPrefab ActivePrefab;
public PhysicsBody PhysicsBody
@@ -37,11 +39,15 @@ namespace Barotrauma
public bool NeedsNetworkSyncing
{
get { return Triggers != null && Triggers.Any(t => t.NeedsNetworkSyncing); }
get
{
return tookDamage || (Triggers != null && Triggers.Any(t => t.NeedsNetworkSyncing));
}
set
{
if (Triggers == null) { return; }
Triggers.ForEach(t => t.NeedsNetworkSyncing = false);
Triggers.ForEach(t => t.NeedsNetworkSyncing = false);
tookDamage = false;
}
}
@@ -50,6 +56,12 @@ namespace Barotrauma
get; private set;
}
public float Health
{
get;
private set;
}
public Sprite Sprite
{
get
@@ -67,6 +79,10 @@ namespace Barotrauma
public Submarine Submarine => null;
public string Name => Prefab?.Name ?? "LevelObject (null)";
public Dictionary<string, SerializableProperty> SerializableProperties { get; } = new Dictionary<string, SerializableProperty>();
public Level.Cave ParentCave;
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
@@ -75,6 +91,7 @@ namespace Barotrauma
Position = position;
Scale = scale;
Rotation = rotation;
Health = prefab.Health;
spriteIndex = ActivePrefab.Sprites.Any() ? Rand.Int(ActivePrefab.Sprites.Count, Rand.RandSync.Server) : -1;
@@ -89,10 +106,13 @@ namespace Barotrauma
if (PhysicsBody != null)
{
PhysicsBody.UserData = this;
PhysicsBody.SetTransformIgnoreContacts(PhysicsBody.SimPosition, -Rotation);
PhysicsBody.BodyType = BodyType.Static;
PhysicsBody.CollisionCategories = Physics.CollisionLevel;
PhysicsBody.CollidesWith = Physics.CollisionWall | Physics.CollisionCharacter;
PhysicsBody.CollidesWith = Prefab.TakeLevelWallDamage?
Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionProjectile :
Physics.CollisionWall | Physics.CollisionCharacter;
}
foreach (XElement triggerElement in prefab.LevelTriggerElements)
@@ -111,6 +131,10 @@ namespace Barotrauma
}
var newTrigger = new LevelTrigger(triggerElement, new Vector2(position.X, position.Y) + triggerPosition, -rotation, scale, prefab.Name);
if (newTrigger.PhysicsBody != null)
{
newTrigger.PhysicsBody.UserData = this;
}
int parentTriggerIndex = prefab.LevelTriggerElements.IndexOf(triggerElement.Parent);
if (parentTriggerIndex > -1) { newTrigger.ParentTrigger = Triggers[parentTriggerIndex]; }
Triggers.Add(newTrigger);
@@ -135,7 +159,54 @@ namespace Barotrauma
}
partial void InitProjSpecific();
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true)
{
if (Health <= 0.0f) { return new AttackResult(0.0f); }
float damage = 0.0f;
if (Prefab.TakeLevelWallDamage)
{
damage += attack.GetLevelWallDamage(deltaTime);
}
damage = Math.Max(Health, damage);
AddDamage(damage, deltaTime, attacker);
return new AttackResult(damage);
}
public void AddDamage(float damage, float deltaTime, Entity attacker, bool isNetworkEvent = false)
{
if (Health <= 0.0f) { return; }
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkEvent)
{
return;
}
tookDamage |= !MathUtils.NearlyEqual(damage, 0.0f);
Health -= damage;
if (Health <= 0.0f)
{
#if CLIENT
if (GameMain.GameSession?.Level?.LevelObjectManager != null)
{
GameMain.GameSession.Level.LevelObjectManager.ForceRefreshVisibleObjects = true;
}
#endif
if (PhysicsBody != null)
{
PhysicsBody.Enabled = false;
}
foreach (LevelTrigger trigger in Triggers)
{
trigger.PhysicsBody.Enabled = false;
foreach (StatusEffect effect in trigger.StatusEffects)
{
if (effect.type != ActionType.OnBroken) { continue; }
effect.Apply(effect.type, deltaTime, attacker, this, worldPosition: WorldPosition);
}
}
}
}
public Vector2 LocalToWorld(Vector2 localPosition, float swingState = 0.0f)
{
Vector2 emitterPos = localPosition * Scale;
@@ -169,6 +240,10 @@ namespace Barotrauma
public void ServerWrite(IWriteMessage msg, Client c)
{
if (Triggers == null) { return; }
if (Prefab.TakeLevelWallDamage)
{
msg.WriteRangedSingle(MathHelper.Clamp(Health, 0.0f, Prefab.Health), 0.0f, Prefab.Health, 8);
}
for (int j = 0; j < Triggers.Count; j++)
{
if (!Triggers[j].UseNetworkSyncing) { continue; }
@@ -21,6 +21,12 @@ namespace Barotrauma
private List<LevelObject> updateableObjects;
private List<LevelObject>[,] objectGrid;
public float GlobalForceDecreaseTimer
{
get;
private set;
}
public LevelObjectManager() : base(null, Entity.NullEntityID)
{
}
@@ -130,11 +136,16 @@ namespace Barotrauma
if (prefab == null) { continue; }
if (!suitableSpawnPositions.ContainsKey(prefab))
{
float minDistance = level.Size.X * 0.2f;
suitableSpawnPositions.Add(prefab,
availableSpawnPositions.Where(sp =>
sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
sp.Length >= prefab.MinSurfaceWidth &&
sp.Length >= prefab.MinSurfaceWidth &&
(prefab.AllowAtStart || !level.IsCloseToStart(sp.GraphEdge.Center, minDistance)) &&
(prefab.AllowAtEnd || !level.IsCloseToEnd(sp.GraphEdge.Center, minDistance)) &&
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
spawnPositionWeights.Add(prefab,
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
}
@@ -422,10 +433,10 @@ namespace Barotrauma
public IEnumerable<LevelObject> GetAllObjects(Vector2 worldPosition, float radius)
{
var minIndices = GetGridIndices(worldPosition - Vector2.One * radius);
if (minIndices.X >= objectGrid.GetLength(0) || minIndices.Y >= objectGrid.GetLength(1)) return Enumerable.Empty<LevelObject>();
if (minIndices.X >= objectGrid.GetLength(0) || minIndices.Y >= objectGrid.GetLength(1)) { return Enumerable.Empty<LevelObject>(); }
var maxIndices = GetGridIndices(worldPosition + Vector2.One * radius);
if (maxIndices.X < 0 || maxIndices.Y < 0) return Enumerable.Empty<LevelObject>();
if (maxIndices.X < 0 || maxIndices.Y < 0) { return Enumerable.Empty<LevelObject>(); }
minIndices.X = Math.Max(0, minIndices.X);
minIndices.Y = Math.Max(0, minIndices.Y);
@@ -440,6 +451,7 @@ namespace Barotrauma
if (objectGrid[x, y] == null) { continue; }
foreach (LevelObject obj in objectGrid[x, y])
{
if (obj.Prefab.HideWhenBroken && obj.Health <= 0.0f) { continue; }
objectsInRange.Add(obj);
}
}
@@ -484,6 +496,12 @@ namespace Barotrauma
public void Update(float deltaTime)
{
GlobalForceDecreaseTimer += deltaTime;
if (GlobalForceDecreaseTimer > 1000000.0f)
{
GlobalForceDecreaseTimer = 0.0f;
}
foreach (LevelObject obj in updateableObjects)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
@@ -496,6 +514,7 @@ namespace Barotrauma
obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval;
}
}
if (obj.Prefab.HideWhenBroken && obj.Health <= 0.0f) { continue; }
if (obj.Triggers != null)
{
@@ -176,6 +176,20 @@ namespace Barotrauma
private set;
}
[Editable, Serialize(true, true, description: "Can the object be placed near the start of the level.")]
public bool AllowAtStart
{
get;
private set;
}
[Editable, Serialize(true, true, description: "Can the object be placed near the end of the level.")]
public bool AllowAtEnd
{
get;
private set;
}
[Serialize(0.0f, true, description: "Minimum length of a graph edge the object can spawn on."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
/// <summary>
/// Minimum length of a graph edge the object can spawn on.
@@ -250,6 +264,27 @@ namespace Barotrauma
private set;
}
[Serialize(false, true, description: "Can the object take damage from weapons/attacks that damage level walls."), Editable]
public bool TakeLevelWallDamage
{
get;
private set;
}
[Serialize(false, true), Editable]
public bool HideWhenBroken
{
get;
private set;
}
[Serialize(100.0f, true), Editable]
public float Health
{
get;
private set;
}
public string Identifier
{
get;
@@ -38,6 +38,10 @@ namespace Barotrauma
/// Effects applied to entities that are inside the trigger
/// </summary>
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>();
public IEnumerable<StatusEffect> StatusEffects
{
get { return statusEffects; }
}
/// <summary>
/// Attacks applied to entities that are inside the trigger
@@ -140,6 +144,11 @@ namespace Barotrauma
get;
private set;
}
public float GlobalForceDecreaseInterval
{
get;
private set;
}
private readonly TriggerForceMode forceMode;
public TriggerForceMode ForceMode
@@ -200,7 +209,7 @@ namespace Barotrauma
PhysicsBody = new PhysicsBody(element, scale)
{
CollisionCategories = Physics.CollisionLevel,
CollidesWith = Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionProjectile | Physics.CollisionWall
CollidesWith = Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionProjectile | Physics.CollisionWall,
};
PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
@@ -234,6 +243,7 @@ namespace Barotrauma
ForceFluctuationInterval = element.GetAttributeFloat("forcefluctuationinterval", 0.01f);
ForceFluctuationStrength = Math.Max(element.GetAttributeFloat("forcefluctuationstrength", 0.0f), 0.0f);
ForceFalloff = element.GetAttributeBool("forcefalloff", true);
GlobalForceDecreaseInterval = element.GetAttributeFloat("globalforcedecreaseinterval", 0.0f);
ForceVelocityLimit = ConvertUnits.ToSimUnits(element.GetAttributeFloat("forcevelocitylimit", float.MaxValue));
string forceModeStr = element.GetAttributeString("forcemode", "Force");
@@ -434,6 +444,8 @@ namespace Barotrauma
}
}
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public void Update(float deltaTime)
{
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
@@ -457,7 +469,13 @@ namespace Barotrauma
if (!UseNetworkSyncing || isNotClient)
{
if (ForceFluctuationStrength > 0.0f)
if (GlobalForceDecreaseInterval > 0.0f && Level.Loaded?.LevelObjectManager != null &&
Level.Loaded.LevelObjectManager.GlobalForceDecreaseTimer % (GlobalForceDecreaseInterval * 2) < GlobalForceDecreaseInterval)
{
NeedsNetworkSyncing |= currentForceFluctuation > 0.0f;
currentForceFluctuation = 0.0f;
}
else if (ForceFluctuationStrength > 0.0f)
{
//no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers
if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any())
@@ -509,6 +527,7 @@ namespace Barotrauma
{
foreach (StatusEffect effect in statusEffects)
{
if (effect.type == ActionType.OnBroken) { continue; }
Vector2? position = null;
if (effect.HasTargetType(StatusEffect.TargetType.This)) { position = WorldPosition; }
if (triggerer is Character character)
@@ -533,8 +552,8 @@ namespace Barotrauma
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
{
var targets = new List<ISerializableEntity>();
effect.GetNearbyTargets(worldPosition, targets);
targets.Clear();
targets.AddRange(effect.GetNearbyTargets(worldPosition, targets));
effect.Apply(effect.type, deltaTime, triggerer, targets);
}
}