v0.14.6.0
This commit is contained in:
@@ -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; }
|
||||
|
||||
+22
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user