v0.11.0.9

This commit is contained in:
Joonas Rikkonen
2020-12-09 16:34:16 +02:00
parent bbf06f0984
commit f433a7ba10
325 changed files with 13947 additions and 3652 deletions
@@ -37,18 +37,23 @@ namespace Barotrauma
public bool NeedsNetworkSyncing
{
get { return Triggers.Any(t => t.NeedsNetworkSyncing); }
set { Triggers.ForEach(t => t.NeedsNetworkSyncing = false); }
get { return Triggers != null && Triggers.Any(t => t.NeedsNetworkSyncing); }
set
{
if (Triggers == null) { return; }
Triggers.ForEach(t => t.NeedsNetworkSyncing = false);
}
}
public bool NeedsUpdate
{
get; private set;
}
public Sprite Sprite
{
get { return spriteIndex < 0 || Prefab.Sprites.Count == 0 ? null : Prefab.Sprites[spriteIndex % Prefab.Sprites.Count]; }
}
public Sprite SpecularSprite
{
get { return spriteIndex < 0 || Prefab.SpecularSprites.Count == 0 ? null : Prefab.SpecularSprites[spriteIndex % Prefab.SpecularSprites.Count]; }
}
Vector2 ISpatialEntity.Position => new Vector2(Position.X, Position.Y);
@@ -60,8 +65,6 @@ namespace Barotrauma
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
{
Triggers = new List<LevelTrigger>();
ActivePrefab = Prefab = prefab;
Position = position;
Scale = scale;
@@ -69,13 +72,26 @@ namespace Barotrauma
spriteIndex = ActivePrefab.Sprites.Any() ? Rand.Int(ActivePrefab.Sprites.Count, Rand.RandSync.Server) : -1;
if (prefab.PhysicsBodyElement != null)
if (Sprite != null && prefab.SpriteSpecificPhysicsBodyElements.ContainsKey(Sprite))
{
PhysicsBody = new PhysicsBody(prefab.SpriteSpecificPhysicsBodyElements[Sprite], ConvertUnits.ToSimUnits(new Vector2(position.X, position.Y)), Scale);
}
else if (prefab.PhysicsBodyElement != null)
{
PhysicsBody = new PhysicsBody(prefab.PhysicsBodyElement, ConvertUnits.ToSimUnits(new Vector2(position.X, position.Y)), Scale);
}
if (PhysicsBody != null)
{
PhysicsBody.SetTransformIgnoreContacts(PhysicsBody.SimPosition, -Rotation);
PhysicsBody.BodyType = BodyType.Static;
PhysicsBody.CollisionCategories = Physics.CollisionLevel;
PhysicsBody.CollidesWith = Physics.CollisionWall | Physics.CollisionCharacter;
}
foreach (XElement triggerElement in prefab.LevelTriggerElements)
{
Triggers ??= new List<LevelTrigger>();
Vector2 triggerPosition = triggerElement.GetAttributeVector2("position", Vector2.Zero) * scale;
if (rotation != 0.0f)
@@ -90,10 +106,12 @@ namespace Barotrauma
var newTrigger = new LevelTrigger(triggerElement, new Vector2(position.X, position.Y) + triggerPosition, -rotation, scale, prefab.Name);
int parentTriggerIndex = prefab.LevelTriggerElements.IndexOf(triggerElement.Parent);
if (parentTriggerIndex > -1) newTrigger.ParentTrigger = Triggers[parentTriggerIndex];
if (parentTriggerIndex > -1) { newTrigger.ParentTrigger = Triggers[parentTriggerIndex]; }
Triggers.Add(newTrigger);
}
NeedsUpdate = NeedsNetworkSyncing || (Triggers != null && Triggers.Any()) || Prefab.PhysicsBodyTriggerIndex > -1;
InitProjSpecific();
}
@@ -131,9 +149,10 @@ namespace Barotrauma
public void ServerWrite(IWriteMessage msg, Client c)
{
if (Triggers == null) { return; }
for (int j = 0; j < Triggers.Count; j++)
{
if (!Triggers[j].UseNetworkSyncing) continue;
if (!Triggers[j].UseNetworkSyncing) { continue; }
Triggers[j].ServerWrite(msg, c);
}
}
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Voronoi2;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -17,9 +18,10 @@ namespace Barotrauma
const int GridSize = 2000;
private List<LevelObject> objects;
private List<LevelObject> updateableObjects;
private List<LevelObject>[,] objectGrid;
public LevelObjectManager() : base(null)
public LevelObjectManager() : base(null, Entity.NullEntityID)
{
}
@@ -27,20 +29,36 @@ namespace Barotrauma
{
public readonly GraphEdge GraphEdge;
public readonly Vector2 Normal;
public readonly LevelObjectPrefab.SpawnPosType SpawnPosType;
public readonly List<LevelObjectPrefab.SpawnPosType> SpawnPosTypes = new List<LevelObjectPrefab.SpawnPosType>();
public readonly Alignment Alignment;
public readonly float Length;
private readonly float noiseVal;
public SpawnPosition(GraphEdge graphEdge, Vector2 normal, LevelObjectPrefab.SpawnPosType spawnPosType, Alignment alignment)
: this(graphEdge, normal, spawnPosType.ToEnumerable(), alignment)
{ }
public SpawnPosition(GraphEdge graphEdge, Vector2 normal, IEnumerable<LevelObjectPrefab.SpawnPosType> spawnPosTypes, Alignment alignment)
{
GraphEdge = graphEdge;
Normal = normal;
SpawnPosType = spawnPosType;
Alignment = alignment;
Length = Vector2.Distance(graphEdge.Point1, graphEdge.Point2);
Normal = normal.NearlyEquals(Vector2.Zero) ? Vector2.UnitY : Vector2.Normalize(normal);
SpawnPosTypes.AddRange(spawnPosTypes);
if (spawnPosTypes.Contains(LevelObjectPrefab.SpawnPosType.MainPath) ||
spawnPosTypes.Contains(LevelObjectPrefab.SpawnPosType.LevelStart) ||
spawnPosTypes.Contains(LevelObjectPrefab.SpawnPosType.LevelEnd))
{
Length = 1000.0f;
Normal = Vector2.Zero;
Alignment = Alignment.Any;
}
else
{
Alignment = alignment;
Length = Vector2.Distance(graphEdge.Point1, graphEdge.Point2);
}
noiseVal =
(float)(PerlinNoise.CalculatePerlin(GraphEdge.Point1.X / 10000.0f, GraphEdge.Point1.Y / 10000.0f, 0.5f) +
@@ -83,7 +101,7 @@ namespace Barotrauma
foreach (var posOfInterest in level.PositionsOfInterest)
{
if (posOfInterest.PositionType != Level.PositionType.MainPath) continue;
if (posOfInterest.PositionType != Level.PositionType.MainPath && posOfInterest.PositionType != Level.PositionType.SidePath) { continue; }
availableSpawnPositions.Add(new SpawnPosition(
new GraphEdge(posOfInterest.Position.ToVector2(), posOfInterest.Position.ToVector2() + Vector2.UnitX),
@@ -101,49 +119,29 @@ namespace Barotrauma
var availablePrefabs = new List<LevelObjectPrefab>(LevelObjectPrefab.List);
objects = new List<LevelObject>();
updateableObjects = new List<LevelObject>();
Dictionary<LevelObjectPrefab, List<SpawnPosition>> suitableSpawnPositions = new Dictionary<LevelObjectPrefab, List<SpawnPosition>>();
Dictionary<LevelObjectPrefab, List<float>> spawnPositionWeights = new Dictionary<LevelObjectPrefab, List<float>>();
for (int i = 0; i < amount; i++)
{
//get a random prefab and find a place to spawn it
LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams.Identifier, availablePrefabs);
LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams, availablePrefabs);
if (prefab == null) { continue; }
if (!suitableSpawnPositions.ContainsKey(prefab))
{
suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp =>
prefab.SpawnPos.HasFlag(sp.SpawnPosType) && (sp.Length >= prefab.MinSurfaceWidth && prefab.Alignment.HasFlag(sp.Alignment) || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelEnd || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelStart)).ToList());
suitableSpawnPositions.Add(prefab,
availableSpawnPositions.Where(sp =>
sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
sp.Length >= prefab.MinSurfaceWidth &&
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
spawnPositionWeights.Add(prefab,
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
}
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
float rotation = 0.0f;
if (prefab.AlignWithSurface && spawnPosition != null)
{
rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
}
rotation += Rand.Range(prefab.RandomRotationRad.X, prefab.RandomRotationRad.Y, Rand.RandSync.Server);
Vector2 position = Vector2.Zero;
Vector2 edgeDir = Vector2.UnitX;
if (spawnPosition == null)
{
position = new Vector2(
Rand.Range(0.0f, level.Size.X, Rand.RandSync.Server),
Rand.Range(0.0f, level.Size.Y, Rand.RandSync.Server));
}
else
{
edgeDir = (spawnPosition.GraphEdge.Point1 - spawnPosition.GraphEdge.Point2) / spawnPosition.Length;
position = spawnPosition.GraphEdge.Point2 + edgeDir * Rand.Range(prefab.MinSurfaceWidth / 2.0f, spawnPosition.Length - prefab.MinSurfaceWidth / 2.0f, Rand.RandSync.Server);
}
var newObject = new LevelObject(prefab,
new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
AddObject(newObject, level);
PlaceObject(prefab, spawnPosition, level);
if (prefab.MaxCount < amount)
{
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
@@ -151,38 +149,119 @@ namespace Barotrauma
availablePrefabs.Remove(prefab);
}
}
}
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
foreach (Level.Cave cave in level.Caves)
{
availablePrefabs = new List<LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.CaveWall)));
availableSpawnPositions.Clear();
suitableSpawnPositions.Clear();
spawnPositionWeights.Clear();
var caveCells = cave.Tunnels.SelectMany(t => t.Cells);
List<VoronoiCell> caveWallCells = new List<VoronoiCell>();
foreach (var edge in caveCells.SelectMany(c => c.Edges))
{
int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
for (int j = 0; j < childCount; j++)
{
var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
int prefabCount = matchingPrefabs.Count();
var childPrefab = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
if (childPrefab == null) continue;
Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;
var childObject = new LevelObject(childPrefab,
new Vector3(childPos, Rand.Range(childPrefab.DepthRange.X, childPrefab.DepthRange.Y, Rand.RandSync.Server)),
Rand.Range(childPrefab.MinSize, childPrefab.MaxSize, Rand.RandSync.Server),
rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));
AddObject(childObject, level);
}
if (!edge.NextToCave) { continue; }
if (edge.Cell1?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell1); }
if (edge.Cell2?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell2); }
}
}
availableSpawnPositions.AddRange(GetAvailableSpawnPositions(caveWallCells.Distinct(), LevelObjectPrefab.SpawnPosType.CaveWall));
for (int i = 0; i < cave.CaveGenerationParams.LevelObjectAmount; i++)
{
//get a random prefab and find a place to spawn it
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs);
if (prefab == null) { continue; }
if (!suitableSpawnPositions.ContainsKey(prefab))
{
suitableSpawnPositions.Add(prefab,
availableSpawnPositions.Where(sp =>
sp.Length >= prefab.MinSurfaceWidth &&
(sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList());
spawnPositionWeights.Add(prefab,
suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList());
}
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
PlaceObject(prefab, spawnPosition, level);
if (prefab.MaxCount < amount)
{
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
}
}
}
}
}
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level)
{
float rotation = 0.0f;
if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null)
{
rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
}
rotation += Rand.Range(prefab.RandomRotationRad.X, prefab.RandomRotationRad.Y, Rand.RandSync.Server);
Vector2 position = Vector2.Zero;
Vector2 edgeDir = Vector2.UnitX;
if (spawnPosition == null)
{
position = new Vector2(
Rand.Range(0.0f, level.Size.X, Rand.RandSync.Server),
Rand.Range(0.0f, level.Size.Y, Rand.RandSync.Server));
}
else
{
edgeDir = (spawnPosition.GraphEdge.Point1 - spawnPosition.GraphEdge.Point2) / spawnPosition.Length;
position = spawnPosition.GraphEdge.Point2 + edgeDir * Rand.Range(prefab.MinSurfaceWidth / 2.0f, spawnPosition.Length - prefab.MinSurfaceWidth / 2.0f, Rand.RandSync.Server);
}
if (!MathUtils.NearlyEqual(prefab.RandomOffset.X, 0.0f) || !MathUtils.NearlyEqual(prefab.RandomOffset.Y, 0.0f))
{
Vector2 offsetDir = spawnPosition.Normal.LengthSquared() > 0.001f ? spawnPosition.Normal : Rand.Vector(1.0f, Rand.RandSync.Server);
position += offsetDir * Rand.Range(prefab.RandomOffset.X, prefab.RandomOffset.Y, Rand.RandSync.Server);
}
var newObject = new LevelObject(prefab,
new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
AddObject(newObject, level);
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
{
int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
for (int j = 0; j < childCount; j++)
{
var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
int prefabCount = matchingPrefabs.Count();
var childPrefab = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
if (childPrefab == null) continue;
Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;
var childObject = new LevelObject(childPrefab,
new Vector3(childPos, Rand.Range(childPrefab.DepthRange.X, childPrefab.DepthRange.Y, Rand.RandSync.Server)),
Rand.Range(childPrefab.MinSize, childPrefab.MaxSize, Rand.RandSync.Server),
rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));
AddObject(childObject, level);
}
}
}
private void AddObject(LevelObject newObject, Level level)
{
foreach (LevelTrigger trigger in newObject.Triggers)
{
trigger.OnTriggered += (levelTrigger, obj) =>
if (newObject.Triggers != null)
{
foreach (LevelTrigger trigger in newObject.Triggers)
{
OnObjectTriggered(newObject, levelTrigger, obj);
};
trigger.OnTriggered += (levelTrigger, obj) =>
{
OnObjectTriggered(newObject, levelTrigger, obj);
};
}
}
var spriteCorners = new List<Vector2>
@@ -223,22 +302,24 @@ namespace Barotrauma
float minY = spriteCorners.Min(c => c.Y) - newObject.Position.Z - level.BottomPos;
float maxY = spriteCorners.Max(c => c.Y) + newObject.Position.Z - level.BottomPos;
foreach (LevelTrigger trigger in newObject.Triggers)
if (newObject.Triggers != null)
{
if (trigger.PhysicsBody == null) continue;
for (int i = 0; i < trigger.PhysicsBody.FarseerBody.FixtureList.Count; i++)
foreach (LevelTrigger trigger in newObject.Triggers)
{
trigger.PhysicsBody.FarseerBody.GetTransform(out FarseerPhysics.Common.Transform transform);
trigger.PhysicsBody.FarseerBody.FixtureList[i].Shape.ComputeAABB(out FarseerPhysics.Collision.AABB aabb, ref transform, i);
if (trigger.PhysicsBody == null) { continue; }
for (int i = 0; i < trigger.PhysicsBody.FarseerBody.FixtureList.Count; i++)
{
trigger.PhysicsBody.FarseerBody.GetTransform(out FarseerPhysics.Common.Transform transform);
trigger.PhysicsBody.FarseerBody.FixtureList[i].Shape.ComputeAABB(out FarseerPhysics.Collision.AABB aabb, ref transform, i);
minX = Math.Min(minX, ConvertUnits.ToDisplayUnits(aabb.LowerBound.X));
maxX = Math.Max(maxX, ConvertUnits.ToDisplayUnits(aabb.UpperBound.X));
minY = Math.Min(minY, ConvertUnits.ToDisplayUnits(aabb.LowerBound.Y) - level.BottomPos);
maxY = Math.Max(maxY, ConvertUnits.ToDisplayUnits(aabb.UpperBound.Y) - level.BottomPos);
minX = Math.Min(minX, ConvertUnits.ToDisplayUnits(aabb.LowerBound.X));
maxX = Math.Max(maxX, ConvertUnits.ToDisplayUnits(aabb.UpperBound.X));
minY = Math.Min(minY, ConvertUnits.ToDisplayUnits(aabb.LowerBound.Y) - level.BottomPos);
maxY = Math.Max(maxY, ConvertUnits.ToDisplayUnits(aabb.UpperBound.Y) - level.BottomPos);
}
}
}
#if CLIENT
if (newObject.ParticleEmitters != null)
{
@@ -253,6 +334,7 @@ namespace Barotrauma
}
#endif
objects.Add(newObject);
if (newObject.NeedsUpdate) { updateableObjects.Add(newObject); }
newObject.Position.Z += (minX + minY) % 100.0f * 0.00001f;
int xStart = (int)Math.Floor(minX / GridSize);
@@ -290,7 +372,7 @@ namespace Barotrauma
return objects;
}
private readonly static List<LevelObject> objectsInRange = new List<LevelObject>();
private readonly static HashSet<LevelObject> objectsInRange = new HashSet<LevelObject>();
public IEnumerable<LevelObject> GetAllObjects(Vector2 worldPosition, float radius)
{
var minIndices = GetGridIndices(worldPosition - Vector2.One * radius);
@@ -309,10 +391,10 @@ namespace Barotrauma
{
for (int y = minIndices.Y; y <= maxIndices.Y; y++)
{
if (objectGrid[x, y] == null) continue;
if (objectGrid[x, y] == null) { continue; }
foreach (LevelObject obj in objectGrid[x, y])
{
if (!objectsInRange.Contains(obj)) objectsInRange.Add(obj);
objectsInRange.Add(obj);
}
}
}
@@ -322,12 +404,14 @@ namespace Barotrauma
private List<SpawnPosition> GetAvailableSpawnPositions(IEnumerable<VoronoiCell> cells, LevelObjectPrefab.SpawnPosType spawnPosType)
{
List<LevelObjectPrefab.SpawnPosType> spawnPosTypes = new List<LevelObjectPrefab.SpawnPosType>(4);
List<SpawnPosition> availableSpawnPositions = new List<SpawnPosition>();
foreach (var cell in cells)
{
foreach (var edge in cell.Edges)
{
if (!edge.IsSolid || edge.OutsideLevel) continue;
if (!edge.IsSolid || edge.OutsideLevel) { continue; }
if (spawnPosType != LevelObjectPrefab.SpawnPosType.CaveWall && edge.NextToCave) { continue; }
Vector2 normal = edge.GetNormal(cell);
Alignment edgeAlignment = 0;
@@ -340,7 +424,13 @@ namespace Barotrauma
else if(normal.X > 0.5f)
edgeAlignment |= Alignment.Right;
availableSpawnPositions.Add(new SpawnPosition(edge, normal, spawnPosType, edgeAlignment));
spawnPosTypes.Clear();
spawnPosTypes.Add(spawnPosType);
if (spawnPosType.HasFlag(LevelObjectPrefab.SpawnPosType.MainPathWall) && edge.NextToMainPath) { spawnPosTypes.Add(LevelObjectPrefab.SpawnPosType.MainPathWall); }
if (spawnPosType.HasFlag(LevelObjectPrefab.SpawnPosType.SidePathWall) && edge.NextToSidePath) { spawnPosTypes.Add(LevelObjectPrefab.SpawnPosType.SidePathWall); }
if (spawnPosType.HasFlag(LevelObjectPrefab.SpawnPosType.CaveWall) && edge.NextToCave) { spawnPosTypes.Add(LevelObjectPrefab.SpawnPosType.CaveWall); }
availableSpawnPositions.Add(new SpawnPosition(edge, normal, spawnPosTypes, edgeAlignment));
}
}
return availableSpawnPositions;
@@ -348,7 +438,7 @@ namespace Barotrauma
public void Update(float deltaTime)
{
foreach (LevelObject obj in objects)
foreach (LevelObject obj in updateableObjects)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
@@ -361,21 +451,24 @@ namespace Barotrauma
}
}
obj.ActivePrefab = obj.Prefab;
for (int i = 0; i < obj.Triggers.Count; i++)
if (obj.Triggers != null)
{
obj.Triggers[i].Update(deltaTime);
if (obj.Triggers[i].IsTriggered && obj.Prefab.OverrideProperties[i] != null)
obj.ActivePrefab = obj.Prefab;
for (int i = 0; i < obj.Triggers.Count; i++)
{
obj.ActivePrefab = obj.Prefab.OverrideProperties[i];
obj.Triggers[i].Update(deltaTime);
if (obj.Triggers[i].IsTriggered && obj.Prefab.OverrideProperties[i] != null)
{
obj.ActivePrefab = obj.Prefab.OverrideProperties[i];
}
}
}
if (obj.PhysicsBody != null)
{
if (obj.Prefab.PhysicsBodyTriggerIndex > -1) obj.PhysicsBody.Enabled = obj.Triggers[obj.Prefab.PhysicsBodyTriggerIndex].IsTriggered;
obj.Position = new Vector3(obj.PhysicsBody.Position, obj.Position.Z);
obj.Rotation = obj.PhysicsBody.Rotation;
if (obj.Prefab.PhysicsBodyTriggerIndex > -1) { obj.PhysicsBody.Enabled = obj.Triggers[obj.Prefab.PhysicsBodyTriggerIndex].IsTriggered; }
/*obj.Position = new Vector3(obj.PhysicsBody.Position, obj.Position.Z);
obj.Rotation = -obj.PhysicsBody.Rotation;*/
}
}
@@ -386,10 +479,10 @@ namespace Barotrauma
private void OnObjectTriggered(LevelObject triggeredObject, LevelTrigger trigger, Entity triggerer)
{
if (trigger.TriggerOthersDistance <= 0.0f) return;
if (trigger.TriggerOthersDistance <= 0.0f) { return; }
foreach (LevelObject obj in objects)
{
if (obj == triggeredObject) continue;
if (obj == triggeredObject || obj.Triggers == null) { continue; }
foreach (LevelTrigger otherTrigger in obj.Triggers)
{
otherTrigger.OtherTriggered(triggeredObject, trigger);
@@ -397,16 +490,20 @@ namespace Barotrauma
}
}
private LevelObjectPrefab GetRandomPrefab(string levelType)
{
return GetRandomPrefab(levelType, LevelObjectPrefab.List);
}
private LevelObjectPrefab GetRandomPrefab(string levelType, IList<LevelObjectPrefab> availablePrefabs)
private LevelObjectPrefab GetRandomPrefab(LevelGenerationParams generationParams, IList<LevelObjectPrefab> availablePrefabs)
{
if (availablePrefabs.Sum(p => p.GetCommonness(generationParams)) <= 0.0f) { return null; }
return ToolBox.SelectWeightedRandom(
availablePrefabs,
availablePrefabs.Select(p => p.GetCommonness(levelType)).ToList(), Rand.RandSync.Server);
availablePrefabs.Select(p => p.GetCommonness(generationParams)).ToList(), Rand.RandSync.Server);
}
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs)
{
if (availablePrefabs.Sum(p => p.GetCommonness(caveParams)) <= 0.0f) { return null; }
return ToolBox.SelectWeightedRandom(
availablePrefabs,
availablePrefabs.Select(p => p.GetCommonness(caveParams)).ToList(), Rand.RandSync.Server);
}
public override void Remove()
@@ -418,6 +515,7 @@ namespace Barotrauma
obj.Remove();
}
objects.Clear();
updateableObjects.Clear();
}
RemoveProjSpecific();
@@ -8,11 +8,7 @@ namespace Barotrauma
{
partial class LevelObjectPrefab : ISerializableEntity
{
private static List<LevelObjectPrefab> list = new List<LevelObjectPrefab>();
public static List<LevelObjectPrefab> List
{
get { return list; }
}
public static List<LevelObjectPrefab> List { get; } = new List<LevelObjectPrefab>();
public class ChildObject
{
@@ -38,12 +34,15 @@ namespace Barotrauma
public enum SpawnPosType
{
None = 0,
Wall = 1,
RuinWall = 2,
SeaFloor = 4,
MainPath = 8,
LevelStart = 16,
LevelEnd = 32,
MainPathWall = 1,
SidePathWall = 2,
CaveWall = 4,
RuinWall = 8,
SeaFloor = 16,
MainPath = 32,
LevelStart = 64,
LevelEnd = 128,
Wall = MainPathWall | SidePathWall | CaveWall,
}
public List<Sprite> Sprites
@@ -52,12 +51,6 @@ namespace Barotrauma
private set;
} = new List<Sprite>();
public List<Sprite> SpecularSprites
{
get;
private set;
} = new List<Sprite>();
public DeformableSprite DeformableSprite
{
get;
@@ -117,7 +110,13 @@ namespace Barotrauma
{
get;
private set;
}
} = -1;
public Dictionary<Sprite, XElement> SpriteSpecificPhysicsBodyElements
{
get;
private set;
} = new Dictionary<Sprite, XElement>();
[Serialize(10000, false, description: "Maximum number of this specific object per level."), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
@@ -162,6 +161,13 @@ namespace Barotrauma
private set;
}
[Editable, Serialize("0,0", true, description: "Random offset from the surface the object spawns on.")]
public Vector2 RandomOffset
{
get;
private set;
}
[Editable, Serialize(false, true, description: "Should the object be rotated to align it with the wall surface it spawns on.")]
public bool AlignWithSurface
{
@@ -243,12 +249,18 @@ namespace Barotrauma
private set;
}
public string Name
public string Identifier
{
get;
set;
}
public string Name
{
get { return Identifier; }
}
public List<ChildObject> ChildObjects
{
get;
@@ -272,12 +284,12 @@ namespace Barotrauma
public override string ToString()
{
return "LevelObjectPrefab (" + Name + ")";
return "LevelObjectPrefab (" + Identifier + ")";
}
public static void LoadAll()
{
list.Clear();
List.Clear();
var files = GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs);
if (files.Count() > 0)
{
@@ -303,35 +315,62 @@ namespace Barotrauma
{
mainElement = doc.Root.FirstElement();
DebugConsole.NewMessage($"Overriding all level object prefabs with '{configPath}'", Color.Yellow);
list.Clear();
List.Clear();
}
else if (list.Any())
else if (List.Any())
{
DebugConsole.NewMessage($"Loading additional level object prefabs from file '{configPath}'");
DebugConsole.Log($"Loading additional level object prefabs from file '{configPath}'");
}
foreach (XElement element in mainElement.Elements())
foreach (XElement subElement in mainElement.Elements())
{
list.Add(new LevelObjectPrefab(element));
var element = subElement.IsOverride() ? subElement.FirstElement() : subElement;
string identifier = element.GetAttributeString("identifier", "");
var existingPrefab = List.Find(p => p.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
if (existingPrefab != null)
{
if (subElement.IsOverride())
{
DebugConsole.NewMessage($"Overriding the existing level object prefab '{identifier}' using the file '{configPath}'", Color.Yellow);
List.Remove(existingPrefab);
}
else
{
DebugConsole.ThrowError($"Error in '{configPath}': Duplicate level object prefab '{identifier}' found in '{configPath}'! Each level object prefab must have a unique identifier. " +
"Use <override></override> tags to override prefabs.");
continue;
}
}
List.Add(new LevelObjectPrefab(element));
}
}
catch (Exception e)
{
DebugConsole.ThrowError(String.Format("Failed to load LevelObject prefabs from {0}", configPath), e);
DebugConsole.ThrowError(string.Format("Failed to load LevelObject prefabs from {0}", configPath), e);
}
}
public LevelObjectPrefab(XElement element)
public LevelObjectPrefab(XElement element, string identifier = null)
{
ChildObjects = new List<ChildObject>();
LevelTriggerElements = new List<XElement>();
OverrideProperties = new List<LevelObjectPrefab>();
OverrideCommonness = new Dictionary<string, float>();
Identifier = null;
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
if (element != null)
{
Config = element;
Name = element.Name.ToString();
Identifier = element.GetAttributeString("identifier", null) ?? identifier;
if (string.IsNullOrEmpty(Identifier))
{
#if DEBUG
DebugConsole.ThrowError($"Level object prefab \"{element.Name}\" has no identifier! Using the name as the identifier instead...");
#else
DebugConsole.AddWarning($"Level object prefab \"{element.Name}\" has no identifier! Using the name as the identifier instead...");
#endif
Identifier = element.Name.ToString();
}
LoadElements(element, -1);
InitProjSpecific(element);
}
@@ -346,21 +385,27 @@ namespace Barotrauma
private void LoadElements(XElement element, int parentTriggerIndex)
{
int propertyOverrideCount = 0;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprites.Add( new Sprite(subElement, lazyLoad: true));
break;
case "specularsprite":
SpecularSprites.Add(new Sprite(subElement, lazyLoad: true));
var newSprite = new Sprite(subElement, lazyLoad: true);
Sprites.Add(newSprite);
var spriteSpecificPhysicsBodyElement =
subElement.Element("PhysicsBody") ?? subElement.Element("Body") ??
subElement.Element("physicsbody") ?? subElement.Element("body");
if (spriteSpecificPhysicsBodyElement != null)
{
SpriteSpecificPhysicsBodyElements.Add(newSprite, spriteSpecificPhysicsBodyElement);
}
break;
case "deformablesprite":
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "overridecommonness":
string levelType = subElement.GetAttributeString("leveltype", "");
string levelType = subElement.GetAttributeString("leveltype", "").ToLowerInvariant();
if (!OverrideCommonness.ContainsKey(levelType))
{
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
@@ -376,13 +421,14 @@ namespace Barotrauma
ChildObjects.Add(new ChildObject(subElement));
break;
case "overrideproperties":
var propertyOverride = new LevelObjectPrefab(subElement);
var propertyOverride = new LevelObjectPrefab(subElement, identifier: Identifier + "-" + propertyOverrideCount);
OverrideProperties[OverrideProperties.Count - 1] = propertyOverride;
if (!propertyOverride.Sprites.Any() && propertyOverride.DeformableSprite == null)
{
propertyOverride.Sprites = Sprites;
propertyOverride.DeformableSprite = DeformableSprite;
}
propertyOverrideCount++;
break;
case "body":
case "physicsbody":
@@ -395,13 +441,26 @@ namespace Barotrauma
partial void InitProjSpecific(XElement element);
public float GetCommonness(string levelType)
public float GetCommonness(CaveGenerationParams generationParams)
{
if (!OverrideCommonness.TryGetValue(levelType, out float commonness))
if (generationParams?.Identifier != null &&
OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness))
{
return Commonness;
return commonness;
}
return commonness;
return 0.0f;
}
public float GetCommonness(LevelGenerationParams generationParams)
{
if (generationParams?.Identifier != null &&
(OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) ||
(generationParams.OldIdentifier != null && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness))))
{
return commonness;
}
return Commonness;
}
}
}
@@ -186,6 +186,18 @@ namespace Barotrauma
get;
set;
}
public string InfectIdentifier
{
get;
set;
}
public float InfectionChance
{
get;
set;
}
public LevelTrigger(XElement element, Vector2 position, float rotation, float scale = 1.0f, string parentDebugName = "")
{
@@ -211,6 +223,9 @@ namespace Barotrauma
}
cameraShake = element.GetAttributeFloat("camerashake", 0.0f);
InfectIdentifier = element.GetAttributeString("infectidentifier", null);
InfectionChance = element.GetAttributeFloat("infectionchance", 0.05f);
stayTriggeredDelay = element.GetAttributeFloat("staytriggereddelay", 0.0f);
randomTriggerInterval = element.GetAttributeFloat("randomtriggerinterval", 0.0f);
@@ -513,9 +528,14 @@ namespace Barotrauma
float structureDamage = attack.GetStructureDamage(deltaTime);
if (structureDamage > 0.0f)
{
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage);
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, levelWallDamage: 0.0f);
}
}
if (!string.IsNullOrWhiteSpace(InfectIdentifier))
{
submarine.AttemptBallastFloraInfection(InfectIdentifier, deltaTime, InfectionChance);
}
}
if (Force.LengthSquared() > 0.01f)
@@ -598,7 +618,7 @@ namespace Barotrauma
public Vector2 GetWaterFlowVelocity()
{
if (Force == Vector2.Zero) return Vector2.Zero;
if (Force == Vector2.Zero || ForceMode == TriggerForceMode.LimitVelocity) { return Vector2.Zero; }
Vector2 vel = Force;
if (ForceMode == TriggerForceMode.Acceleration)