Unstable v0.1300.0.0 (February 19th 2021)

This commit is contained in:
Joonas Rikkonen
2021-02-25 13:44:23 +02:00
parent b772654326
commit 24cbef485a
441 changed files with 21343 additions and 8562 deletions
@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -100,19 +101,19 @@ namespace Barotrauma
public Sprite WallSprite { get; private set; }
public Sprite WallEdgeSprite { get; private set; }
public static CaveGenerationParams GetRandom(LevelGenerationParams generationParams, Rand.RandSync rand)
public static CaveGenerationParams GetRandom(LevelGenerationParams generationParams, bool abyss, Rand.RandSync rand)
{
if (CaveParams.All(p => p.GetCommonness(generationParams) <= 0.0f))
if (CaveParams.All(p => p.GetCommonness(generationParams, abyss) <= 0.0f))
{
return CaveParams.First();
}
return ToolBox.SelectWeightedRandom(CaveParams, CaveParams.Select(p => p.GetCommonness(generationParams)).ToList(), rand);
return ToolBox.SelectWeightedRandom(CaveParams, CaveParams.Select(p => p.GetCommonness(generationParams, abyss)).ToList(), rand);
}
public float GetCommonness(LevelGenerationParams generationParams)
public float GetCommonness(LevelGenerationParams generationParams, bool abyss)
{
if (generationParams?.Identifier != null &&
OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness))
OverrideCommonness.TryGetValue(abyss ? "abyss" : generationParams.Identifier, out float commonness))
{
return commonness;
}
@@ -135,6 +136,13 @@ namespace Barotrauma
case "walledge":
WallEdgeSprite = new Sprite(subElement);
break;
case "overridecommonness":
string levelType = subElement.GetAttributeString("leveltype", "").ToLowerInvariant();
if (!OverrideCommonness.ContainsKey(levelType))
{
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
}
break;
}
}
}
@@ -193,5 +201,30 @@ namespace Barotrauma
}
}
}
public void Save(XElement element)
{
SerializableProperty.SerializeProperties(this, element, true);
foreach (KeyValuePair<string, float> overrideCommonness in OverrideCommonness)
{
bool elementFound = false;
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("overridecommonness", StringComparison.OrdinalIgnoreCase)
&& subElement.GetAttributeString("leveltype", "").Equals(overrideCommonness.Key, StringComparison.OrdinalIgnoreCase))
{
subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture);
elementFound = true;
break;
}
}
if (!elementFound)
{
element.Add(new XElement("overridecommonness",
new XAttribute("leveltype", overrideCommonness.Key),
new XAttribute("commonness", overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture))));
}
}
}
}
}
@@ -141,69 +141,36 @@ namespace Barotrauma
return cells;
}
private static Vector2 GetEdgeNormal(GraphEdge edge, VoronoiCell cell = null)
{
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)
{
var temp = edge.Point1;
edge.Point1 = edge.Point2;
edge.Point2 = temp;
}
Vector2 normal = Vector2.Normalize(edge.Point2 - edge.Point1);
Vector2 diffToCell = Vector2.Normalize(cell.Center - edge.Point2);
normal = new Vector2(-normal.Y, normal.X);
if (Vector2.Dot(normal, diffToCell) < 0)
{
normal = -normal;
}
return normal;
}
public static void GeneratePath(Level.Tunnel tunnel, List<VoronoiCell> cells, List<VoronoiCell>[,] cellGrid, int gridCellSize, Rectangle limits)
public static void GeneratePath(Level.Tunnel tunnel, Level level)
{
var targetCells = new List<VoronoiCell>();
for (int i = 0; i < tunnel.Nodes.Count; i++)
{
//a search depth of 2 is large enough to find a cell in almost all maps, but in case it fails, we increase the depth
int searchDepth = 2;
while (searchDepth < 5)
var closestCell = level.GetClosestCell(tunnel.Nodes[i].ToVector2());
if (closestCell != null && !targetCells.Contains(closestCell))
{
int cellIndex = FindCellIndex(tunnel.Nodes[i], cells, cellGrid, gridCellSize, searchDepth);
if (cellIndex > -1)
{
targetCells.Add(cells[cellIndex]);
break;
}
searchDepth++;
targetCells.Add(closestCell);
}
}
tunnel.Cells.AddRange(GeneratePath(targetCells, cells, limits));
tunnel.Cells.AddRange(GeneratePath(targetCells, level.GetAllCells()));
}
public static List<VoronoiCell> GeneratePath(List<VoronoiCell> targetCells, List<VoronoiCell> cells, Rectangle limits)
public static List<VoronoiCell> GeneratePath(List<VoronoiCell> targetCells, List<VoronoiCell> cells)
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
List<VoronoiCell> pathCells = new List<VoronoiCell>();
if (targetCells.Count == 0) { return pathCells; }
VoronoiCell currentCell = targetCells[0];
currentCell.CellType = CellType.Path;
pathCells.Add(currentCell);
int currentTargetIndex = 0;
int iterationsLeft = cells.Count;
int iterationsLeft = cells.Count / 2;
do
{
@@ -216,10 +183,14 @@ namespace Barotrauma
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);
dist += MathUtils.Distance(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y, currentCell.Site.Coord.X, currentCell.Site.Coord.Y) * 0.5f;
//disfavor small edges to prevent generating a very small passage
if (Vector2.Distance(currentCell.Edges[i].Point1, currentCell.Edges[i].Point2) < 200.0f)
//disfavor short edges to prevent generating a very small passage
if (Vector2.DistanceSquared(currentCell.Edges[i].Point1, currentCell.Edges[i].Point2) < 150.0f * 150.0f)
{
dist += 1000000;
//divide by the number of times the current cell has been used
// prevents the path from getting "stuck" (jumping back and forth between adjacent cells)
// if there's no other way to the destination than going through a short edge
dist *= 10.0f / Math.Max(pathCells.Count(c => c == currentCell), 1.0f);
}
if (dist < smallestDist)
{
@@ -258,7 +229,7 @@ namespace Barotrauma
List<GraphEdge> tempEdges = new List<GraphEdge>();
foreach (GraphEdge edge in cell.Edges)
{
if (!edge.IsSolid)
if (!edge.IsSolid || edge.OutsideLevel)
{
tempEdges.Add(edge);
continue;
@@ -289,7 +260,8 @@ namespace Barotrauma
}
List<Vector2> edgePoints = new List<Vector2>();
Vector2 edgeNormal = GetEdgeNormal(edge, cell);
Vector2 edgeNormal = edge.GetNormal(cell);
float edgeLength = Vector2.Distance(edge.Point1, edge.Point2);
int pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1);
Vector2 edgeDir = edge.Point2 - edge.Point1;
@@ -310,12 +282,39 @@ namespace Barotrauma
float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.Server);
Vector2 extrudedPoint =
edge.Point1 +
edgeDir * (i / (float)pointCount) -
edgeDir * (i / (float)pointCount) +
edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;
//check if extruding the edge causes it to go inside another one
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 1);
if (!nearbyCells.Any(c => c.CellType == CellType.Solid && c != cell && c.IsPointInside(extrudedPoint))) { edgePoints.Add(extrudedPoint); }
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
bool isInside = false;
foreach (var nearbyCell in nearbyCells)
{
if (nearbyCell == cell || nearbyCell.CellType != CellType.Solid) { continue; }
//check if extruding the edge causes it to go inside another one
if (nearbyCell.IsPointInside(extrudedPoint))
{
isInside = true;
break;
}
//check if another edge will be inside this cell after the extrusion
Vector2 triangleCenter = (edge.Point1 + edge.Point2 + extrudedPoint) / 3;
foreach (GraphEdge nearbyEdge in nearbyCell.Edges)
{
if (!MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, extrudedPoint) &&
!MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point2, extrudedPoint) &&
!MathUtils.LinesIntersect(nearbyEdge.Point1, triangleCenter, edge.Point1, edge.Point2))
{
isInside = true;
break;
}
}
if (isInside) { break; }
}
if (!isInside)
{
edgePoints.Add(extrudedPoint);
}
}
}
@@ -381,7 +380,19 @@ namespace Barotrauma
continue;
}
renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, cell.Center));
Vector2 minVert = tempVertices[0];
Vector2 maxVert = tempVertices[0];
foreach (var vert in tempVertices)
{
minVert = new Vector2(
Math.Min(minVert.X, vert.X),
Math.Min(minVert.Y, vert.Y));
maxVert = new Vector2(
Math.Max(maxVert.X, vert.X),
Math.Max(maxVert.Y, vert.Y));
}
Vector2 center = (minVert + maxVert) / 2;
renderTriangles.AddRange(MathUtils.TriangulateConvexHull(tempVertices, center));
if (bodyPoints.Count < 2) { continue; }
@@ -404,7 +415,7 @@ namespace Barotrauma
if (cell.CellType == CellType.Empty) { continue; }
cellBody.UserData = cell;
var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(cell.Center));
var triangles = MathUtils.TriangulateConvexHull(bodyPoints, ConvertUnits.ToSimUnits(center));
for (int i = 0; i < triangles.Count; i++)
{
@@ -435,14 +446,21 @@ namespace Barotrauma
}
cell.Body = cellBody;
}
cellBody.CollisionCategories = Physics.CollisionLevel;
cellBody.ResetMassData();
return cellBody;
}
public static List<Vector2> CreateRandomChunk(float radius, int vertexCount, float radiusVariance)
{
Debug.Assert(radiusVariance < radius);
return CreateRandomChunk(radius * 2, radius * 2, vertexCount, radiusVariance);
}
public static List<Vector2> CreateRandomChunk(float width, float height, int vertexCount, float radiusVariance)
{
Debug.Assert(radiusVariance < Math.Min(width, height));
Debug.Assert(vertexCount >= 3);
List<Vector2> verts = new List<Vector2>();
@@ -450,72 +468,12 @@ namespace Barotrauma
float angle = 0.0f;
for (int i = 0; i < vertexCount; i++)
{
verts.Add(new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) *
(radius + Rand.Range(-radiusVariance, radiusVariance, Rand.RandSync.Server)));
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
verts.Add(new Vector2(dir.X * width / 2, dir.Y * height / 2) + dir * Rand.Range(-radiusVariance, radiusVariance, Rand.RandSync.Server));
angle += angleStep;
}
return verts;
}
/// <summary>
/// find the index of the cell which the point is inside
/// (actually finds the cell whose center is closest, but it's always the correct cell assuming the point is inside the borders of the diagram)
/// </summary>
public static int FindCellIndex(Vector2 position,List<VoronoiCell> cells, List<VoronoiCell>[,] cellGrid, int gridCellSize, int searchDepth = 1, Vector2? offset = null)
{
float closestDist = float.PositiveInfinity;
VoronoiCell closestCell = null;
Vector2 gridOffset = offset == null ? Vector2.Zero : (Vector2)offset;
position -= gridOffset;
int gridPosX = (int)Math.Floor(position.X / gridCellSize);
int gridPosY = (int)Math.Floor(position.Y / gridCellSize);
for (int x = Math.Max(gridPosX - searchDepth, 0); x <= Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); x++)
{
for (int y = Math.Max(gridPosY - searchDepth, 0); y <= Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1); y++)
{
for (int i = 0; i < cellGrid[x, y].Count; i++)
{
float dist = Vector2.DistanceSquared(cellGrid[x, y][i].Center, position);
if (dist > closestDist) continue;
closestDist = dist;
closestCell = cellGrid[x, y][i];
}
}
}
return cells.IndexOf(closestCell);
}
public static int FindCellIndex(Point position, List<VoronoiCell> cells, List<VoronoiCell>[,] cellGrid, int gridCellSize, int searchDepth = 1)
{
int closestDist = int.MaxValue;
VoronoiCell closestCell = null;
int gridPosX = position.X / gridCellSize;
int gridPosY = position.Y / gridCellSize;
for (int x = Math.Max(gridPosX - searchDepth, 0); x <= Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); x++)
{
for (int y = Math.Max(gridPosY - searchDepth, 0); y <= Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1); y++)
{
for (int i = 0; i < cellGrid[x, y].Count; i++)
{
int dist = MathUtils.DistanceSquared(
(int)cellGrid[x, y][i].Site.Coord.X, (int)cellGrid[x, y][i].Site.Coord.Y,
position.X, position.Y);
if (dist > closestDist) continue;
closestDist = dist;
closestCell = cellGrid[x, y][i];
}
}
}
return cells.IndexOf(closestCell);
}
}
}
@@ -58,7 +58,8 @@ namespace Barotrauma
public DestructibleLevelWall(List<Vector2> vertices, Color color, Level level, float? health = null, bool giftWrap = false)
: base (vertices, color, level, giftWrap)
{
MaxHealth = health ?? MathHelper.Clamp(Body.Mass, 100.0f, 1000.0f);
MaxHealth = health ?? MathHelper.Clamp(Body.Mass * 0.5f, 50.0f, 1000.0f);
Cells.ForEach(c => c.IsDestructible = true);
}
public override void Update(float deltaTime)
@@ -201,6 +202,10 @@ namespace Barotrauma
if (Destroyed) { return; }
Destroyed = true;
level?.UnsyncedExtraWalls?.Remove(this);
foreach (var cell in Cells)
{
cell.CellType = CellType.Removed;
}
GameMain.World.Remove(Body);
Dispose();
}
File diff suppressed because it is too large Load Diff
@@ -29,12 +29,19 @@ namespace Barotrauma
public bool HasBeaconStation;
public bool IsBeaconActive;
public bool HasHuntingGrounds;
public OutpostGenerationParams ForceOutpostGenerationParams;
public readonly Point Size;
public readonly int InitialDepth;
/// <summary>
/// Determined during level generation based on the size of the submarine. Null if the level hasn't been generated.
/// </summary>
public int? MinMainPathWidth;
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
@@ -81,6 +88,8 @@ namespace Barotrauma
HasBeaconStation = element.GetAttributeBool("hasbeaconstation", false);
IsBeaconActive = element.GetAttributeBool("isbeaconactive", false);
HasHuntingGrounds = element.GetAttributeBool("hashuntinggrounds", false);
string generationParamsId = element.GetAttributeString("generationparams", "");
GenerationParams = LevelGenerationParams.LevelParams.Find(l => l.Identifier == generationParamsId || l.OldIdentifier == generationParamsId);
if (GenerationParams == null)
@@ -96,7 +105,7 @@ namespace Barotrauma
InitialDepth = element.GetAttributeInt("initialdepth", GenerationParams.InitialDepthMin);
string biomeIdentifier = element.GetAttributeString("biome", "");
Biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.Identifier == biomeIdentifier);
Biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.Identifier == biomeIdentifier || b.OldIdentifier == biomeIdentifier);
if (Biome == null)
{
DebugConsole.ThrowError($"Error in level data: could not find the biome \"{biomeIdentifier}\".");
@@ -135,7 +144,10 @@ namespace Barotrauma
var rand = new MTRandom(ToolBox.StringToInt(Seed));
InitialDepth = (int)MathHelper.Lerp(GenerationParams.InitialDepthMin, GenerationParams.InitialDepthMax, (float)rand.NextDouble());
HasBeaconStation = rand.NextDouble() < locationConnection.Locations.Select(l => l.Type.BeaconStationChance).Max();
float maxHuntingGroundsProbability = 0.3f;
HasHuntingGrounds = rand.NextDouble() < Difficulty / 100.0f * maxHuntingGroundsProbability;
HasBeaconStation = !HasHuntingGrounds && rand.NextDouble() < locationConnection.Locations.Select(l => l.Type.BeaconStationChance).Max();
IsBeaconActive = false;
}
@@ -158,7 +170,7 @@ namespace Barotrauma
(int)MathUtils.Round(GenerationParams.Height, Level.GridCellSize));
}
public static LevelData CreateRandom(string seed = "", float? difficulty = null, LevelGenerationParams generationParams = null)
public static LevelData CreateRandom(string seed = "", float? difficulty = null, LevelGenerationParams generationParams = null, bool requireOutpost = false)
{
if (string.IsNullOrEmpty(seed))
{
@@ -167,25 +179,34 @@ namespace Barotrauma
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
LevelType type = generationParams == null ? LevelData.LevelType.LocationConnection : generationParams.Type;
LevelType type = generationParams == null ?
(requireOutpost ? LevelType.Outpost : LevelType.LocationConnection) :
generationParams.Type;
if (generationParams == null) { generationParams = LevelGenerationParams.GetRandom(seed, type); }
var biome =
LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ??
LevelGenerationParams.GetBiomes().GetRandom(Rand.RandSync.Server);
float beaconRng = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
var levelData = new LevelData(
seed,
difficulty ?? Rand.Range(30.0f, 80.0f, Rand.RandSync.Server),
Rand.Range(0.0f, 1.0f, Rand.RandSync.Server),
generationParams,
biome)
biome);
if (type == LevelType.LocationConnection)
{
HasBeaconStation = beaconRng < 0.5f,
IsBeaconActive = beaconRng > 0.25f
};
GameMain.GameSession?.GameMode?.Mission?.AdjustLevelData(levelData);
float beaconRng = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
levelData.HasBeaconStation = beaconRng < 0.5f;
levelData.IsBeaconActive = beaconRng > 0.25f;
}
if (GameMain.GameSession?.GameMode != null)
{
foreach (Mission mission in GameMain.GameSession.GameMode.Missions)
{
mission.AdjustLevelData(levelData);
}
}
return levelData;
}
@@ -207,6 +228,13 @@ namespace Barotrauma
new XAttribute("isbeaconactive", IsBeaconActive.ToString()));
}
if (HasHuntingGrounds)
{
newElement.Add(
new XAttribute("hashuntinggrounds", HasHuntingGrounds.ToString()));
}
if (Type == LevelType.Outpost)
{
if (EventHistory.Any())
@@ -195,25 +195,25 @@ namespace Barotrauma
set;
}
[Serialize(100000, true), Editable(MinValueInt = 10000, MaxValueInt = 1000000)]
[Serialize(100000, true), Editable]
public int MinWidth
{
get { return minWidth; }
set { minWidth = Math.Max(value, 2000); }
set { minWidth = MathHelper.Clamp(value, 2000, 1000000); }
}
[Serialize(100000, true), Editable(MinValueInt = 10000, MaxValueInt = 1000000)]
[Serialize(100000, true), Editable]
public int MaxWidth
{
get { return maxWidth; }
set { maxWidth = Math.Max(value, 2000); }
set { maxWidth = MathHelper.Clamp(value, 2000, 1000000); }
}
[Serialize(50000, true), Editable(MinValueInt = 10000, MaxValueInt = 1000000)]
[Serialize(50000, true), Editable]
public int Height
{
get { return height; }
set { height = Math.Max(value, 2000); }
set { height = MathHelper.Clamp(value, 2000, 1000000); }
}
[Serialize(80000, true), Editable(MinValueInt = 0, MaxValueInt = 1000000)]
@@ -404,7 +404,35 @@ namespace Barotrauma
set;
}
[Serialize(300000, true, description: "How far below the level the sea floor is placed."), Editable(MinValueFloat = Level.MaxEntityDepth, MaxValueFloat = 0.0f)]
[Serialize(5, true), Editable(MinValueInt = 0, MaxValueInt = 20)]
public int AbyssIslandCount
{
get;
set;
}
[Serialize("4000,7000", true), Editable]
public Point AbyssIslandSizeMin
{
get;
set;
}
[Serialize("8000,10000", true), Editable]
public Point AbyssIslandSizeMax
{
get;
set;
}
[Serialize(0.5f, true), Editable()]
public float AbyssIslandCaveProbability
{
get;
set;
}
[Serialize(-300000, true, description: "How far below the level the sea floor is placed."), Editable(MinValueFloat = Level.MaxEntityDepth, MaxValueFloat = 0.0f)]
public int SeaFloorDepth
{
get { return seaFloorBaseDepth; }
@@ -554,7 +582,7 @@ namespace Barotrauma
var matchingLevelParams = LevelParams.FindAll(lp => lp.Type == type && lp.allowedBiomes.Any());
if (biome == null)
{
matchingLevelParams = matchingLevelParams.FindAll(lp => !lp.allowedBiomes.Any(b => b.IsEndBiome));
matchingLevelParams = matchingLevelParams.FindAll(lp => !lp.allowedBiomes.All(b => b.IsEndBiome));
}
else
{
@@ -52,7 +52,11 @@ namespace Barotrauma
public Sprite Sprite
{
get { return spriteIndex < 0 || Prefab.Sprites.Count == 0 ? null : Prefab.Sprites[spriteIndex % Prefab.Sprites.Count]; }
get
{
var prefab = ActivePrefab?.Sprites.Count > 0 ? ActivePrefab : Prefab;
return spriteIndex < 0 || prefab.Sprites.Count == 0 ? null : prefab.Sprites[spriteIndex % prefab.Sprites.Count];
}
}
Vector2 ISpatialEntity.Position => new Vector2(Position.X, Position.Y);
@@ -63,6 +67,8 @@ namespace Barotrauma
public Submarine Submarine => null;
public Level.Cave ParentCave;
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
{
ActivePrefab = Prefab = prefab;
@@ -110,6 +116,19 @@ namespace Barotrauma
Triggers.Add(newTrigger);
}
if (spriteIndex == -1)
{
foreach (var overrideProperties in prefab.OverrideProperties)
{
if (overrideProperties == null) { continue; }
if (overrideProperties.Sprites.Count > 0)
{
spriteIndex = Rand.Int(overrideProperties.Sprites.Count, Rand.RandSync.Server);
break;
}
}
}
NeedsUpdate = NeedsNetworkSyncing || (Triggers != null && Triggers.Any()) || Prefab.PhysicsBodyTriggerIndex > -1;
InitProjSpecific();
@@ -151,10 +151,10 @@ namespace Barotrauma
}
}
availableSpawnPositions.Clear();
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();
@@ -171,7 +171,7 @@ namespace Barotrauma
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);
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: true);
if (prefab == null) { continue; }
if (!suitableSpawnPositions.ContainsKey(prefab))
{
@@ -184,19 +184,63 @@ namespace Barotrauma
}
SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server);
if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; }
PlaceObject(prefab, spawnPosition, level);
PlaceObject(prefab, spawnPosition, level, cave);
if (prefab.MaxCount < amount)
{
if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
if (objects.Count(o => o.Prefab == prefab && o.ParentCave == cave) >= prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
}
}
}
}
}
}
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level)
public void PlaceNestObjects(Level level, Level.Cave cave, Vector2 nestPosition, float nestRadius, int objectAmount)
{
Rand.SetSyncedSeed(ToolBox.StringToInt(level.Seed));
var availablePrefabs = new List<LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.NestWall)));
Dictionary<LevelObjectPrefab, List<SpawnPosition>> suitableSpawnPositions = new Dictionary<LevelObjectPrefab, List<SpawnPosition>>();
Dictionary<LevelObjectPrefab, List<float>> spawnPositionWeights = new Dictionary<LevelObjectPrefab, List<float>>();
List<SpawnPosition> availableSpawnPositions = new List<SpawnPosition>();
var caveCells = cave.Tunnels.SelectMany(t => t.Cells);
List<VoronoiCell> caveWallCells = new List<VoronoiCell>();
foreach (var edge in caveCells.SelectMany(c => c.Edges))
{
if (!edge.NextToCave) { continue; }
if (MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), nestPosition.ToPoint()) > nestRadius * nestRadius) { 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 < objectAmount; i++)
{
//get a random prefab and find a place to spawn it
LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: false);
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 (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
}
}
}
private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level, Level.Cave parentCave = null)
{
float rotation = 0.0f;
if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null)
@@ -228,6 +272,7 @@ namespace Barotrauma
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);
newObject.ParentCave = parentCave;
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
{
@@ -237,7 +282,7 @@ namespace Barotrauma
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;
if (childPrefab == null) { continue; }
Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;
@@ -247,6 +292,7 @@ namespace Barotrauma
rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));
AddObject(childObject, level);
childObject.ParentCave = parentCave;
}
}
}
@@ -372,7 +418,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);
@@ -391,10 +437,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);
}
}
}
@@ -402,7 +448,7 @@ namespace Barotrauma
return objectsInRange;
}
private List<SpawnPosition> GetAvailableSpawnPositions(IEnumerable<VoronoiCell> cells, LevelObjectPrefab.SpawnPosType spawnPosType)
private List<SpawnPosition> GetAvailableSpawnPositions(IEnumerable<VoronoiCell> cells, LevelObjectPrefab.SpawnPosType spawnPosType, bool checkFlags = true)
{
List<LevelObjectPrefab.SpawnPosType> spawnPosTypes = new List<LevelObjectPrefab.SpawnPosType>(4);
List<SpawnPosition> availableSpawnPositions = new List<SpawnPosition>();
@@ -498,12 +544,12 @@ namespace Barotrauma
availablePrefabs.Select(p => p.GetCommonness(generationParams)).ToList(), Rand.RandSync.Server);
}
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs)
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs, bool requireCaveSpecificOverride)
{
if (availablePrefabs.Sum(p => p.GetCommonness(caveParams)) <= 0.0f) { return null; }
if (availablePrefabs.Sum(p => p.GetCommonness(caveParams, requireCaveSpecificOverride)) <= 0.0f) { return null; }
return ToolBox.SelectWeightedRandom(
availablePrefabs,
availablePrefabs.Select(p => p.GetCommonness(caveParams)).ToList(), Rand.RandSync.Server);
availablePrefabs.Select(p => p.GetCommonness(caveParams, requireCaveSpecificOverride)).ToList(), Rand.RandSync.Server);
}
public override void Remove()
@@ -37,11 +37,12 @@ namespace Barotrauma
MainPathWall = 1,
SidePathWall = 2,
CaveWall = 4,
RuinWall = 8,
SeaFloor = 16,
MainPath = 32,
LevelStart = 64,
LevelEnd = 128,
NestWall = 8,
RuinWall = 16,
SeaFloor = 32,
MainPath = 64,
LevelStart = 128,
LevelEnd = 256,
Wall = MainPathWall | SidePathWall | CaveWall,
}
@@ -319,7 +320,7 @@ namespace Barotrauma
}
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 subElement in mainElement.Elements())
{
@@ -442,14 +443,14 @@ namespace Barotrauma
partial void InitProjSpecific(XElement element);
public float GetCommonness(CaveGenerationParams generationParams)
public float GetCommonness(CaveGenerationParams generationParams, bool requireCaveSpecificOverride = true)
{
if (generationParams?.Identifier != null &&
OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness))
{
return commonness;
}
return 0.0f;
return requireCaveSpecificOverride ? 0.0f : Commonness;
}
public float GetCommonness(LevelGenerationParams generationParams)
@@ -34,44 +34,38 @@ namespace Barotrauma
public Action<LevelTrigger, Entity> OnTriggered;
private PhysicsBody physicsBody;
/// <summary>
/// Effects applied to entities that are inside the trigger
/// </summary>
private List<StatusEffect> statusEffects = new List<StatusEffect>();
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>();
/// <summary>
/// Attacks applied to entities that are inside the trigger
/// </summary>
private List<Attack> attacks = new List<Attack>();
private readonly List<Attack> attacks = new List<Attack>();
private float cameraShake;
private readonly float cameraShake;
private Vector2 unrotatedForce;
private float forceFluctuationTimer, currentForceFluctuation = 1.0f;
private HashSet<Entity> triggerers = new HashSet<Entity>();
private readonly HashSet<Entity> triggerers = new HashSet<Entity>();
private TriggererType triggeredBy;
private readonly TriggererType triggeredBy;
private float randomTriggerInterval;
private float randomTriggerProbability;
private readonly float randomTriggerInterval;
private readonly float randomTriggerProbability;
private float randomTriggerTimer;
private float triggeredTimer;
//how far away this trigger can activate other triggers from
private float triggerOthersDistance;
private HashSet<string> tags = new HashSet<string>();
private readonly HashSet<string> tags = new HashSet<string>();
//other triggers have to have at least one of these tags to trigger this one
private HashSet<string> allowedOtherTriggerTags = new HashSet<string>();
private readonly HashSet<string> allowedOtherTriggerTags = new HashSet<string>();
/// <summary>
/// How long the trigger stays in the triggered state after triggerers have left
/// </summary>
private float stayTriggeredDelay;
private readonly float stayTriggeredDelay;
public LevelTrigger ParentTrigger;
@@ -88,30 +82,24 @@ namespace Barotrauma
set
{
worldPosition = value;
physicsBody?.SetTransform(ConvertUnits.ToSimUnits(value), physicsBody.Rotation);
PhysicsBody?.SetTransform(ConvertUnits.ToSimUnits(value), PhysicsBody.Rotation);
}
}
public float Rotation
{
get { return physicsBody == null ? 0.0f : physicsBody.Rotation; }
get { return PhysicsBody == null ? 0.0f : PhysicsBody.Rotation; }
set
{
if (physicsBody == null) return;
physicsBody.SetTransform(physicsBody.Position, value);
if (PhysicsBody == null) return;
PhysicsBody.SetTransform(PhysicsBody.Position, value);
CalculateDirectionalForce();
}
}
public PhysicsBody PhysicsBody
{
get { return physicsBody; }
}
public PhysicsBody PhysicsBody { get; private set; }
public float TriggerOthersDistance
{
get { return triggerOthersDistance; }
}
public float TriggerOthersDistance { get; private set; }
public IEnumerable<Entity> Triggerers
{
@@ -153,7 +141,7 @@ namespace Barotrauma
private set;
}
private TriggerForceMode forceMode;
private readonly TriggerForceMode forceMode;
public TriggerForceMode ForceMode
{
get { return forceMode; }
@@ -198,6 +186,9 @@ namespace Barotrauma
get;
set;
}
private bool triggeredOnce;
private readonly bool triggerOnce;
public LevelTrigger(XElement element, Vector2 position, float rotation, float scale = 1.0f, string parentDebugName = "")
{
@@ -206,20 +197,20 @@ namespace Barotrauma
worldPosition = position;
if (element.Attributes("radius").Any() || element.Attributes("width").Any() || element.Attributes("height").Any())
{
physicsBody = new PhysicsBody(element, scale)
PhysicsBody = new PhysicsBody(element, scale)
{
CollisionCategories = Physics.CollisionLevel,
CollidesWith = Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionProjectile | Physics.CollisionWall
};
physicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
physicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
physicsBody.FarseerBody.SetIsSensor(true);
physicsBody.FarseerBody.BodyType = BodyType.Static;
physicsBody.FarseerBody.BodyType = BodyType.Kinematic;
PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
PhysicsBody.FarseerBody.SetIsSensor(true);
PhysicsBody.FarseerBody.BodyType = BodyType.Static;
PhysicsBody.FarseerBody.BodyType = BodyType.Kinematic;
ColliderRadius = ConvertUnits.ToDisplayUnits(Math.Max(Math.Max(PhysicsBody.radius, PhysicsBody.width / 2.0f), PhysicsBody.height / 2.0f));
physicsBody.SetTransform(ConvertUnits.ToSimUnits(position), rotation);
PhysicsBody.SetTransform(ConvertUnits.ToSimUnits(position), rotation);
}
cameraShake = element.GetAttributeFloat("camerashake", 0.0f);
@@ -227,6 +218,8 @@ namespace Barotrauma
InfectIdentifier = element.GetAttributeString("infectidentifier", null);
InfectionChance = element.GetAttributeFloat("infectionchance", 0.05f);
triggerOnce = element.GetAttributeBool("triggeronce", false);
stayTriggeredDelay = element.GetAttributeFloat("staytriggereddelay", 0.0f);
randomTriggerInterval = element.GetAttributeFloat("randomtriggerinterval", 0.0f);
randomTriggerProbability = element.GetAttributeFloat("randomtriggerprobability", 0.0f);
@@ -256,7 +249,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Error in LevelTrigger config: \"" + triggeredByStr + "\" is not a valid triggerer type.");
}
UpdateCollisionCategories();
triggerOthersDistance = element.GetAttributeFloat("triggerothersdistance", 0.0f);
TriggerOthersDistance = element.GetAttributeFloat("triggerothersdistance", 0.0f);
var tagsArray = element.GetAttributeStringArray("tags", new string[0]);
foreach (string tag in tagsArray)
@@ -283,11 +276,14 @@ namespace Barotrauma
case "attack":
case "damage":
var attack = new Attack(subElement, string.IsNullOrEmpty(parentDebugName) ? "LevelTrigger" : "LevelTrigger in " + parentDebugName);
var multipliedAfflictions = attack.GetMultipliedAfflictions((float)Timing.Step);
attack.Afflictions.Clear();
foreach (Affliction affliction in multipliedAfflictions)
if (!triggerOnce)
{
attack.Afflictions.Add(affliction, null);
var multipliedAfflictions = attack.GetMultipliedAfflictions((float)Timing.Step);
attack.Afflictions.Clear();
foreach (Affliction affliction in multipliedAfflictions)
{
attack.Afflictions.Add(affliction, null);
}
}
attacks.Add(attack);
break;
@@ -300,14 +296,14 @@ namespace Barotrauma
private void UpdateCollisionCategories()
{
if (physicsBody == null) return;
if (PhysicsBody == null) return;
var collidesWith = Physics.CollisionNone;
if (triggeredBy.HasFlag(TriggererType.Character) || triggeredBy.HasFlag(TriggererType.Creature)) collidesWith |= Physics.CollisionCharacter;
if (triggeredBy.HasFlag(TriggererType.Item)) collidesWith |= Physics.CollisionItem | Physics.CollisionProjectile;
if (triggeredBy.HasFlag(TriggererType.Submarine)) collidesWith |= Physics.CollisionWall;
if (triggeredBy.HasFlag(TriggererType.Human) || triggeredBy.HasFlag(TriggererType.Creature)) { collidesWith |= Physics.CollisionCharacter; }
if (triggeredBy.HasFlag(TriggererType.Item)) { collidesWith |= Physics.CollisionItem | Physics.CollisionProjectile; }
if (triggeredBy.HasFlag(TriggererType.Submarine)) { collidesWith |= Physics.CollisionWall; }
physicsBody.CollidesWith = collidesWith;
PhysicsBody.CollidesWith = collidesWith;
}
private void CalculateDirectionalForce()
@@ -362,7 +358,7 @@ namespace Barotrauma
private void PhysicsBody_OnSeparation(Fixture fixtureA, Fixture fixtureB, Contact contact)
{
Entity entity = GetEntity(fixtureB);
if (entity == null) return;
if (entity == null) { return; }
if (entity is Character character &&
(!character.Enabled || character.Removed) &&
@@ -376,22 +372,25 @@ namespace Barotrauma
//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)
foreach (Fixture fixture in PhysicsBody.FarseerBody.FixtureList)
{
if (contactEdge.Contact != null &&
contactEdge.Contact.Enabled &&
contactEdge.Contact.IsTouching)
ContactEdge contactEdge = fixture.Body.ContactList;
while (contactEdge != null)
{
if (contactEdge.Contact.FixtureA != fixtureA && contactEdge.Contact.FixtureB != fixtureA)
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 != fixture && contactEdge.Contact.FixtureB != fixture)
{
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
contactEdge.Contact.FixtureB :
contactEdge.Contact.FixtureA);
if (otherEntity == entity) { return; }
}
}
contactEdge = contactEdge.Next;
}
contactEdge = contactEdge.Next;
}
if (triggerers.Contains(entity))
@@ -403,10 +402,10 @@ namespace Barotrauma
private Entity GetEntity(Fixture fixture)
{
if (fixture.Body == null || fixture.Body.UserData == null) return null;
if (fixture.Body.UserData is Entity entity) return entity;
if (fixture.Body.UserData is Limb limb) return limb.character;
if (fixture.Body.UserData is SubmarineBody subBody) return subBody.Submarine;
if (fixture.Body == null || fixture.Body.UserData == null) { return null; }
if (fixture.Body.UserData is Entity entity) { return entity; }
if (fixture.Body.UserData is Limb limb) { return limb.character; }
if (fixture.Body.UserData is SubmarineBody subBody) { return subBody.Submarine; }
return null;
}
@@ -416,15 +415,15 @@ namespace Barotrauma
/// </summary>
public void OtherTriggered(LevelObject levelObject, LevelTrigger otherTrigger)
{
if (!triggeredBy.HasFlag(TriggererType.OtherTrigger) || stayTriggeredDelay <= 0.0f) return;
if (!triggeredBy.HasFlag(TriggererType.OtherTrigger) || stayTriggeredDelay <= 0.0f) { return; }
//check if the other trigger has appropriate tags
if (allowedOtherTriggerTags.Count > 0)
{
if (!allowedOtherTriggerTags.Any(t => otherTrigger.tags.Contains(t))) return;
if (!allowedOtherTriggerTags.Any(t => otherTrigger.tags.Contains(t))) { return; }
}
if (Vector2.DistanceSquared(WorldPosition, otherTrigger.WorldPosition) <= otherTrigger.triggerOthersDistance * otherTrigger.triggerOthersDistance)
if (Vector2.DistanceSquared(WorldPosition, otherTrigger.WorldPosition) <= otherTrigger.TriggerOthersDistance * otherTrigger.TriggerOthersDistance)
{
bool wasAlreadyTriggered = IsTriggered;
triggeredTimer = stayTriggeredDelay;
@@ -441,10 +440,10 @@ namespace Barotrauma
triggerers.RemoveWhere(t => t.Removed);
if (physicsBody != null)
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);
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(PhysicsBody.GetMaxExtent() * 5), 5000.0f);
triggerers.RemoveWhere(t =>
{
return Vector2.Distance(t.WorldPosition, WorldPosition) > maxExtent;
@@ -500,17 +499,43 @@ namespace Barotrauma
}
}
if (triggerOnce)
{
if (triggeredOnce) { return; }
if (triggerers.Count > 0) { triggeredOnce = true; }
}
foreach (Entity triggerer in triggerers)
{
foreach (StatusEffect effect in statusEffects)
{
if (triggerer is Character)
Vector2? position = null;
if (effect.HasTargetType(StatusEffect.TargetType.This)) { position = WorldPosition; }
if (triggerer is Character character)
{
effect.Apply(effect.type, deltaTime, triggerer, (Character)triggerer);
effect.Apply(effect.type, deltaTime, triggerer, character, position);
if (effect.HasTargetType(StatusEffect.TargetType.Contained) && character.Inventory != null)
{
foreach (Item item in character.Inventory.AllItemsMod)
{
if (item.ContainedItems == null) { continue; }
foreach (Item containedItem in item.ContainedItems)
{
effect.Apply(effect.type, deltaTime, triggerer, containedItem.AllPropertyObjects, position);
}
}
}
}
else if (triggerer is Item)
else if (triggerer is Item item)
{
effect.Apply(effect.type, deltaTime, triggerer, ((Item)triggerer).AllPropertyObjects);
effect.Apply(effect.type, deltaTime, triggerer, item.AllPropertyObjects, position);
}
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
{
var targets = new List<ISerializableEntity>();
effect.GetNearbyTargets(worldPosition, targets);
effect.Apply(effect.type, deltaTime, triggerer, targets);
}
}
@@ -528,7 +553,7 @@ namespace Barotrauma
float structureDamage = attack.GetStructureDamage(deltaTime);
if (structureDamage > 0.0f)
{
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, damageLevelWalls: false);
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, levelWallDamage: 0.0f);
}
}
@@ -618,7 +643,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)
@@ -36,7 +36,16 @@ namespace Barotrauma
}
}
public float WallDamageOnTouch;
private float wallDamageOnTouch;
public float WallDamageOnTouch
{
get { return wallDamageOnTouch; }
set
{
Cells.ForEach(c => c.DoesDamage = !MathUtils.NearlyEqual(value, 0.0f));
wallDamageOnTouch = value;
}
}
public float MoveSpeed;
@@ -1,9 +1,14 @@
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
#if DEBUG
using System.Xml;
#else
using Barotrauma.IO;
#endif
namespace Barotrauma.RuinGeneration
{