Unstable 0.15.15.0 (and the one before it I forgor)

This commit is contained in:
Markus Isberg
2021-11-18 21:34:30 +09:00
parent 10e5fd5f3e
commit 80f39cd2a3
257 changed files with 4916 additions and 2582 deletions
@@ -7,7 +7,7 @@ using System.Text;
namespace Barotrauma
{
class Entity : ISpatialEntity
abstract class Entity : ISpatialEntity
{
public const ushort NullEntityID = 0;
public const ushort EntitySpawnerID = ushort.MaxValue;
@@ -16,8 +16,10 @@ namespace Barotrauma
public const ushort ReservedIDStart = ushort.MaxValue - 3;
public const ushort MaxEntityCount = ushort.MaxValue - 2; //ushort.MaxValue - 2 because 0 and ushort.MaxValue are reserved values
private static Dictionary<ushort, Entity> dictionary = new Dictionary<ushort, Entity>();
public static IEnumerable<Entity> GetEntities()
public static IReadOnlyCollection<Entity> GetEntities()
{
return dictionary.Values;
}
@@ -28,11 +30,9 @@ namespace Barotrauma
protected AITarget aiTarget;
private bool idFreed;
public bool Removed { get; private set; }
public virtual bool Removed { get; private set; }
public bool IdFreed => idFreed;
public bool IdFreed { get; private set; }
public readonly ushort ID;
@@ -75,43 +75,65 @@ namespace Barotrauma
this.Submarine = submarine;
spawnTime = Timing.TotalTime;
if (id != NullEntityID && dictionary.ContainsKey(id))
{
throw new Exception($"ID {id} is taken by {dictionary[id]}");
}
//give a unique ID
ID = DetermineID(id, submarine);
if (dictionary.ContainsKey(ID))
{
throw new Exception($"ID {ID} is taken by {dictionary[ID]}");
}
dictionary.Add(ID, this);
}
protected virtual ushort DetermineID(ushort id, Submarine submarine)
{
return id != NullEntityID ?
id :
FindFreeID(submarine == null ? (ushort)1 : submarine.IdOffset);
return id != NullEntityID
? id
: FindFreeId(submarine == null ? (ushort)1 : submarine.IdOffset);
}
public static ushort FindFreeID(ushort idOffset = 0)
private static ushort FindFreeId(ushort idOffset)
{
//ushort.MaxValue - 2 because 0 and ushort.MaxValue are reserved values
if (dictionary.Count >= ushort.MaxValue - 2)
if (dictionary.Count >= MaxEntityCount)
{
throw new Exception("Maximum amount of entities (" + (ushort.MaxValue - 1) + ") reached!");
throw new Exception($"Maximum amount of entities ({MaxEntityCount}) reached!");
}
idOffset = Math.Max(idOffset, (ushort)1);
bool IDfound;
ushort id = idOffset;
do
while (id < ReservedIDStart)
{
id += 1;
IDfound = dictionary.ContainsKey(id);
} while (IDfound || id == NullEntityID || id > ReservedIDStart);
if (!dictionary.ContainsKey(id)) { break; }
id++;
};
return id;
}
/// <summary>
/// Finds a contiguous block of free IDs of at least the given size
/// </summary>
/// <returns>The first ID in the found block, or zero if none are found</returns>
public static int FindFreeIdBlock(int minBlockSize)
{
int currentBlockSize = 0;
for (int i = 1; i < ReservedIDStart; i++)
{
if (dictionary.ContainsKey((ushort)i))
{
currentBlockSize = 0;
}
else
{
currentBlockSize++;
if (currentBlockSize >= minBlockSize)
{
return i - (currentBlockSize-1);
}
}
}
return 0;
}
/// <summary>
/// Find an entity based on the ID
/// </summary>
@@ -134,11 +156,11 @@ namespace Barotrauma
}
catch (Exception exception)
{
DebugConsole.ThrowError("Error while removing entity \"" + e.ToString() + "\"", exception);
DebugConsole.ThrowError($"Error while removing entity \"{e}\"", exception);
GameAnalyticsManager.AddErrorEventOnce(
"Entity.RemoveAll:Exception" + e.ToString(),
$"Entity.RemoveAll:Exception{e}",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Error while removing entity \"" + e.ToString() + " (" + exception.Message + ")\n" + exception.StackTrace.CleanupStackTrace());
$"Error while removing entity \"{e} ({exception.Message})\n{exception.StackTrace.CleanupStackTrace()}");
}
}
StringBuilder errorMsg = new StringBuilder();
@@ -167,7 +189,7 @@ namespace Barotrauma
}
catch (Exception exception)
{
DebugConsole.ThrowError("Error while removing item \"" + item.ToString() + "\"", exception);
DebugConsole.ThrowError($"Error while removing item \"{item}\"", exception);
}
}
Item.ItemList.Clear();
@@ -189,7 +211,7 @@ namespace Barotrauma
}
catch (Exception exception)
{
DebugConsole.ThrowError("Error while removing character \"" + character.ToString() + "\"", exception);
DebugConsole.ThrowError($"Error while removing character \"{character}\"", exception);
}
}
Character.CharacterList.Clear();
@@ -214,35 +236,33 @@ namespace Barotrauma
/// </summary>
public void FreeID()
{
DebugConsole.Log("Removing entity " + ToString() + " (" + ID + ") from entity dictionary.");
if (IdFreed) { return; }
DebugConsole.Log($"Removing entity {ToString()} ({ID}) from entity dictionary.");
if (!dictionary.TryGetValue(ID, out Entity existingEntity))
{
DebugConsole.Log("Entity " + ToString() + " (" + ID + ") not present in entity dictionary.");
DebugConsole.ThrowError($"Entity {ToString()} ({ID}) not present in entity dictionary.");
GameAnalyticsManager.AddErrorEventOnce(
"Entity.FreeID:EntityNotFound" + ID,
$"Entity.FreeID:EntityNotFound{ID}",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Entity " + ToString() + " (" + ID + ") not present in entity dictionary.\n" + Environment.StackTrace.CleanupStackTrace());
$"Entity {ToString()} ({ID}) not present in entity dictionary.\n{Environment.StackTrace.CleanupStackTrace()}");
}
else if (existingEntity != this)
{
DebugConsole.Log("Entity ID mismatch in entity dictionary. Entity " + existingEntity + " had the ID " + ID + " (expecting " + ToString() + ")");
DebugConsole.ThrowError($"Entity ID mismatch in entity dictionary. Entity {existingEntity} had the ID {ID} (expecting {ToString()})");
GameAnalyticsManager.AddErrorEventOnce("Entity.FreeID:EntityMismatch" + ID,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Entity ID mismatch in entity dictionary. Entity " + existingEntity + " had the ID " + ID + " (expecting " + ToString() + ")");
foreach (var keyValuePair in dictionary.Where(kvp => kvp.Value == this).ToList())
{
dictionary.Remove(keyValuePair.Key);
}
$"Entity ID mismatch in entity dictionary. Entity {existingEntity} had the ID {ID} (expecting {ToString()})");
}
dictionary.Remove(ID);
idFreed = true;
else
{
dictionary.Remove(ID);
}
IdFreed = true;
}
public virtual void Remove()
{
if (!idFreed) FreeID();
FreeID();
Removed = true;
}
@@ -255,8 +275,8 @@ namespace Barotrauma
List<string> lines = new List<string>();
for (int i = 0; i < count; i++)
{
lines.Add(entities[i].ID + ": " + entities[i].ToString());
DebugConsole.ThrowError(entities[i].ID + ": " + entities[i].ToString());
lines.Add($"{entities[i].ID}: {entities[i]}");
DebugConsole.ThrowError($"{entities[i].ID}: {entities[i]}");
}
if (!string.IsNullOrWhiteSpace(filename))
@@ -39,6 +39,8 @@ namespace Barotrauma
private readonly float itemRepairStrength;
public readonly HashSet<Submarine> IgnoredSubmarines = new HashSet<Submarine>();
public float EmpStrength { get; set; }
public float BallastFloraDamage { get; set; }
@@ -149,7 +151,7 @@ namespace Barotrauma
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
{
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker);
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker, IgnoredSubmarines);
}
if (BallastFloraDamage > 0.0f)
@@ -397,13 +399,14 @@ namespace Barotrauma
/// <summary>
/// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
/// </summary>
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null)
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null)
{
List<Structure> structureList = new List<Structure>();
float dist = 600.0f;
foreach (MapEntity entity in MapEntity.mapEntityList)
{
if (!(entity is Structure structure)) { continue; }
if (ignoredSubmarines != null && entity.Submarine != null && ignoredSubmarines.Contains(entity.Submarine)) { continue; }
if (structure.HasBody &&
!structure.IsPlatform &&
@@ -26,25 +26,25 @@ namespace Barotrauma
}
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
private float open;
private float open;
//the force of the water flow which is exerted on physics bodies
private Vector2 flowForce;
private Hull flowTargetHull;
private float openedTimer = 1.0f;
private float higherSurface;
private float lowerSurface;
private Vector2 lerpedFlowForce;
//if set to true, hull connections of this gap won't be updated when changes are being done to hulls
public bool DisableHullRechecks;
//can ambient light get through the gap even if it's not open
public bool PassAmbientLight;
//a collider outside the gap (for example an ice wall next to the sub)
//used by ragdolls to prevent them from ending up inside colliders when teleporting out of the sub
@@ -54,11 +54,11 @@ namespace Barotrauma
public float Open
{
get { return open; }
set
set
{
if (float.IsNaN(value)) { return; }
if (value > open) { openedTimer = 1.0f; }
open = MathHelper.Clamp(value, 0.0f, 1.0f);
open = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
@@ -319,7 +319,7 @@ namespace Barotrauma
if (openedTimer > 0.0f && flowForce.Length() > lerpedFlowForce.Length())
{
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
lerpedFlowForce = flowForce;
lerpedFlowForce = flowForce;
}
else
{
@@ -375,7 +375,7 @@ namespace Barotrauma
//make sure not to move more than what the room contains
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 5.0f * sizeModifier, Math.Min(hull2.WaterVolume, hull2.Volume));
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - (hull1.WaterVolume));
hull1.WaterVolume += delta;
@@ -405,7 +405,7 @@ namespace Barotrauma
{
hull2.Pressure = Math.Max(hull2.Pressure, ((hull1.Pressure-subOffset.Y) + hull2.Pressure) / 2);
}
flowForce = new Vector2(delta, 0.0f);
}
@@ -448,7 +448,7 @@ namespace Barotrauma
hull2.WaterVolume -= delta;
flowForce = new Vector2(
0.0f,
0.0f,
Math.Min(Math.Min((hull2.Pressure + subOffset.Y) - hull1.Pressure, 200.0f), delta));
flowTargetHull = hull1;
@@ -456,7 +456,7 @@ namespace Barotrauma
if (hull1.WaterVolume > hull1.Volume)
{
hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + (hull2.Pressure + subOffset.Y)) / 2);
}
}
}
//there's water in the upper room, drop to lower
@@ -494,7 +494,7 @@ namespace Barotrauma
hull1.LethalPressure = avgLethality;
hull2.LethalPressure = avgLethality;
}
else
else
{
hull1.LethalPressure = 0.0f;
hull2.LethalPressure = 0.0f;
@@ -511,7 +511,7 @@ namespace Barotrauma
float sizeModifier = size * open * open;
float delta = 500.0f * sizeModifier * deltaTime;
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
hull1.WaterVolume += delta;
@@ -526,7 +526,7 @@ namespace Barotrauma
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
{
flowForce = new Vector2(-delta, 0.0f);
}
else
{
@@ -554,7 +554,7 @@ namespace Barotrauma
hull1.WaveVel[0] += vel;
hull1.WaveVel[1] += vel;
}
}
}
else
{
@@ -651,12 +651,12 @@ namespace Barotrauma
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
float totalVolume = (hull1.Volume + hull2.Volume);
float deltaOxygen = (totalOxygen * hull1.Volume / totalVolume) - hull1.Oxygen;
deltaOxygen = MathHelper.Clamp(deltaOxygen, -Hull.OxygenDistributionSpeed, Hull.OxygenDistributionSpeed);
hull1.Oxygen += deltaOxygen;
hull2.Oxygen -= deltaOxygen;
hull2.Oxygen -= deltaOxygen;
}
public static Gap FindAdjacent(IEnumerable<Gap> gaps, Vector2 worldPos, float allowedOrthogonalDist)
@@ -724,7 +724,7 @@ namespace Barotrauma
{
if (!DisableHullRechecks) FindHulls();
}
public static Gap Load(XElement element, Submarine submarine, IdRemap idRemap)
{
Rectangle rect = Rectangle.Empty;
@@ -755,6 +755,8 @@ namespace Barotrauma
{
linkedToID = new List<ushort>(),
};
g.HiddenInGame = element.GetAttributeBool(nameof(HiddenInGame).ToLower(), g.HiddenInGame);
return g;
}
@@ -764,7 +766,8 @@ namespace Barotrauma
element.Add(
new XAttribute("ID", ID),
new XAttribute("horizontal", IsHorizontal ? "true" : "false"));
new XAttribute("horizontal", IsHorizontal ? "true" : "false"),
new XAttribute(nameof(HiddenInGame).ToLower(), HiddenInGame));
element.Add(new XAttribute("rect",
(int)(rect.X - Submarine.HiddenSubPosition.X) + "," +
@@ -1058,11 +1058,17 @@ namespace Barotrauma
/// <param name="inclusive">Does being exactly at the edge of the hull count as being inside?</param>
public static Hull FindHull(Vector2 position, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = true)
{
if (EntityGrids == null) return null;
if (EntityGrids == null)
{
return null;
}
if (guess != null)
{
if (Submarine.RectContains(useWorldCoordinates ? guess.WorldRect : guess.rect, position, inclusive)) return guess;
if (Submarine.RectContains(useWorldCoordinates ? guess.WorldRect : guess.rect, position, inclusive))
{
return guess;
}
}
foreach (EntityGrid entityGrid in EntityGrids)
@@ -1088,15 +1094,19 @@ namespace Barotrauma
continue;
}
}
Vector2 transformedPosition = position;
if (useWorldCoordinates && entityGrid.Submarine != null) transformedPosition -= entityGrid.Submarine.Position;
if (useWorldCoordinates && entityGrid.Submarine != null)
{
transformedPosition -= entityGrid.Submarine.Position;
}
var entities = entityGrid.GetEntities(transformedPosition);
if (entities == null) continue;
if (entities == null) { continue; }
foreach (Hull hull in entities)
{
if (Submarine.RectContains(hull.rect, transformedPosition, inclusive)) return hull;
if (Submarine.RectContains(hull.rect, transformedPosition, inclusive))
{
return hull;
}
}
}
@@ -145,8 +145,7 @@ namespace Barotrauma
public static List<MapEntity> PasteEntities(Vector2 position, Submarine sub, XElement configElement, string filePath = null, bool selectInstance = false)
{
int idOffset = Entity.FindFreeID(1);
if (MapEntity.mapEntityList.Any()) { idOffset = MapEntity.mapEntityList.Max(e => e.ID); }
int idOffset = Entity.FindFreeIdBlock(configElement.Elements().Count());
List<MapEntity> entities = MapEntity.LoadAll(sub, configElement, filePath, idOffset);
if (entities.Count == 0) { return entities; }
@@ -202,10 +202,10 @@ namespace Barotrauma
get { return startPosition.ToVector2(); }
}
private Vector2 startExitPosition;
private Point startExitPosition;
public Vector2 StartExitPosition
{
get { return startExitPosition; }
get { return startExitPosition.ToVector2(); }
}
public Point Size
@@ -218,10 +218,10 @@ namespace Barotrauma
get { return endPosition.ToVector2(); }
}
private Vector2 endExitPosition;
private Point endExitPosition;
public Vector2 EndExitPosition
{
get { return endExitPosition; }
get { return endExitPosition.ToVector2(); }
}
public int BottomPos
@@ -366,9 +366,6 @@ namespace Barotrauma
{
this.LevelData = levelData;
borders = new Rectangle(Point.Zero, levelData.Size);
//remove from entity dictionary
//base.Remove();
}
public static Level Generate(LevelData levelData, bool mirror, SubmarineInfo startOutpost = null, SubmarineInfo endOutpost = null)
@@ -462,12 +459,12 @@ namespace Barotrauma
startPosition = new Point(
(int)MathHelper.Lerp(minMainPathWidth, borders.Width - minMainPathWidth, GenerationParams.StartPosition.X),
(int)MathHelper.Lerp(borders.Bottom - Math.Max(minMainPathWidth, ExitDistance * 1.5f), borders.Y + minMainPathWidth, GenerationParams.StartPosition.Y));
startExitPosition = new Vector2(startPosition.X, borders.Bottom);
startExitPosition = new Point(startPosition.X, borders.Bottom);
endPosition = new Point(
(int)MathHelper.Lerp(minMainPathWidth, borders.Width - minMainPathWidth, GenerationParams.EndPosition.X),
(int)MathHelper.Lerp(borders.Bottom - Math.Max(minMainPathWidth, ExitDistance * 1.5f), borders.Y + minMainPathWidth, GenerationParams.EndPosition.Y));
endExitPosition = new Vector2(endPosition.X, borders.Bottom);
endExitPosition = new Point(endPosition.X, borders.Bottom);
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
@@ -486,25 +483,25 @@ namespace Barotrauma
{
startPath = new Tunnel(
TunnelType.SidePath,
new List<Point>() { startExitPosition.ToPoint(), startPosition },
new List<Point>() { startExitPosition, startPosition },
minWidth / 2, parentTunnel: mainPath);
Tunnels.Add(startPath);
}
else
{
startExitPosition = StartPosition;
startExitPosition = startPosition;
}
if (GenerationParams.EndPosition.Y < 0.5f && (Mirrored ? !HasStartOutpost() : !HasEndOutpost()))
{
endPath = new Tunnel(
TunnelType.SidePath,
new List<Point>() { endPosition, endExitPosition.ToPoint() },
new List<Point>() { endPosition, endExitPosition },
minWidth / 2, parentTunnel: mainPath);
Tunnels.Add(endPath);
}
else
{
endExitPosition = EndPosition;
endExitPosition = endPosition;
}
if (GenerationParams.CreateHoleNextToEnd)
@@ -513,14 +510,14 @@ namespace Barotrauma
{
endHole = new Tunnel(
TunnelType.SidePath,
new List<Point>() { startPosition, startExitPosition.ToPoint(), new Point(0, Size.Y) },
new List<Point>() { startPosition, startExitPosition, new Point(0, Size.Y) },
minWidth / 2, parentTunnel: mainPath);
}
else
{
endHole = new Tunnel(
TunnelType.SidePath,
new List<Point>() { endPosition, endExitPosition.ToPoint(), Size },
new List<Point>() { endPosition, endExitPosition, Size },
minWidth / 2, parentTunnel: mainPath);
}
Tunnels.Add(endHole);
@@ -799,8 +796,12 @@ namespace Barotrauma
for (int i = 0; i < GenerationParams.RuinCount; i++)
{
Point ruinSize = new Point(5000);
ruinPositions.Add(FindPosAwayFromMainPath((Math.Max(ruinSize.X, ruinSize.Y) + mainPath.MinWidth) * 1.2f, asCloseAsPossible: true,
limits: new Rectangle(new Point(ruinSize.X / 2, ruinSize.Y / 2), Size - ruinSize)));
int limitLeft = Math.Max(startPosition.X, ruinSize.X / 2);
int limitRight = Math.Min(endPosition.X, Size.X - ruinSize.X / 2);
Rectangle limits = new Rectangle(limitLeft, ruinSize.Y, limitRight - limitLeft, Size.Y - ruinSize.Y);
Debug.Assert(limits.Width > 0);
Debug.Assert(limits.Height > 0);
ruinPositions.Add(FindPosAwayFromMainPath((Math.Max(ruinSize.X, ruinSize.Y) + mainPath.MinWidth) * 1.2f, asCloseAsPossible: true, limits: limits));
CalculateTunnelDistanceField(ruinPositions);
}
@@ -1004,7 +1005,7 @@ namespace Barotrauma
{
if (pos.PositionType != PositionType.MainPath && pos.PositionType != PositionType.SidePath) { continue; }
if (pos.Position.X < 5000 || pos.Position.X > Size.X - 5000) { continue; }
if (Math.Abs(pos.Position.X - StartPosition.X) < minMainPathWidth * 2 || Math.Abs(pos.Position.X - EndPosition.X) < minMainPathWidth * 2) { continue; }
if (Math.Abs(pos.Position.X - startPosition.X) < minMainPathWidth * 2 || Math.Abs(pos.Position.X - endPosition.X) < minMainPathWidth * 2) { continue; }
if (GetTooCloseCells(pos.Position.ToVector2(), minMainPathWidth * 0.7f).Count > 0) { continue; }
iceChunkPositions.Add(pos.Position);
}
@@ -1187,19 +1188,19 @@ namespace Barotrauma
startPosition = endPosition;
endPosition = tempP;
Vector2 tempV = startExitPosition;
tempP = startExitPosition;
startExitPosition = endExitPosition;
endExitPosition = tempV;
endExitPosition = tempP;
}
if (StartOutpost != null)
{
startExitPosition = StartOutpost.WorldPosition;
startPosition = startExitPosition.ToPoint();
startExitPosition = StartOutpost.WorldPosition.ToPoint();
startPosition = startExitPosition;
}
if (EndOutpost != null)
{
endExitPosition = EndOutpost.WorldPosition;
endPosition = endExitPosition.ToPoint();
endExitPosition = EndOutpost.WorldPosition.ToPoint();
endPosition = endExitPosition;
}
CreateWrecks();
@@ -2321,7 +2322,7 @@ namespace Barotrauma
{
if (l.Cell == null || l.Edge == null) { return false; }
if (resourceInfo.IsIslandSpecifc && !l.Cell.Island) { return false; }
if (!resourceInfo.AllowAtStart && l.EdgeCenter.Y > StartPosition.Y && l.EdgeCenter.X < Size.X * 0.25f) { return false; }
if (!resourceInfo.AllowAtStart && l.EdgeCenter.Y > startPosition.Y && l.EdgeCenter.X < Size.X * 0.25f) { return false; }
if (l.EdgeCenter.Y < AbyssArea.Bottom) { return false; }
return resourceInfo.ClusterSize <= GetMaxResourcesOnEdge(itemPrefab, l, out _);
@@ -2738,10 +2739,26 @@ namespace Barotrauma
allValidLocations.Sort((x, y) => Vector2.DistanceSquared(poiPos, x.EdgeCenter)
.CompareTo(Vector2.DistanceSquared(poiPos, y.EdgeCenter)));
var maxResourceOverlap = 0.4f;
// TODO: Find multiple locations if there's too many resources to fit on a sigle edge
var selectedLocation = allValidLocations.FirstOrDefault(l =>
Vector2.Distance(l.Edge.Point1, l.Edge.Point2) is float edgeLength &&
requiredAmount <= (int)Math.Floor(edgeLength / ((1.0f - maxResourceOverlap) * prefab.Size.X)));
if (selectedLocation.Edge == null)
{
//couldn't find a long enough edge, find the largest one
float longestEdge = 0.0f;
foreach (var validLocation in allValidLocations)
{
if (Vector2.Distance(validLocation.Edge.Point1, validLocation.Edge.Point2) is float edgeLength && edgeLength > longestEdge)
{
selectedLocation = validLocation;
longestEdge = edgeLength;
}
}
}
if (selectedLocation.Edge == null)
{
throw new Exception("Failed to find a suitable level wall edge to place level resources on.");
}
PlaceResources(prefab, requiredAmount, selectedLocation, out placedResources);
var edgeNormal = selectedLocation.Edge.GetNormal(selectedLocation.Cell);
rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
@@ -3197,12 +3214,12 @@ namespace Barotrauma
public bool IsCloseToStart(Point position, float minDist)
{
return MathUtils.LineSegmentToPointDistanceSquared(StartPosition.ToPoint(), StartExitPosition.ToPoint(), position) < minDist * minDist;
return MathUtils.LineSegmentToPointDistanceSquared(startPosition, startExitPosition, position) < minDist * minDist;
}
public bool IsCloseToEnd(Point position, float minDist)
{
return MathUtils.LineSegmentToPointDistanceSquared(EndPosition.ToPoint(), EndExitPosition.ToPoint(), position) < minDist * minDist;
return MathUtils.LineSegmentToPointDistanceSquared(endPosition, endExitPosition, position) < minDist * minDist;
}
private Submarine SpawnSubOnPath(string subName, ContentFile contentFile, SubmarineType type)
@@ -3214,6 +3231,7 @@ namespace Barotrauma
var waypoints = WayPoint.WayPointList.Where(wp =>
wp.Submarine == null &&
wp.SpawnType == SpawnType.Path &&
wp.WorldPosition.X < EndExitPosition.X &&
!IsCloseToStart(wp.WorldPosition, minDistance) &&
!IsCloseToEnd(wp.WorldPosition, minDistance)).ToList();
@@ -3971,7 +3989,7 @@ namespace Barotrauma
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job, randSync: Rand.RandSync.Server);
var corpse = Character.Create(CharacterPrefab.HumanConfigFile, worldPos, ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
corpse.AnimController.FindHull(worldPos, true);
corpse.AnimController.FindHull(worldPos, setSubmarine: true);
corpse.TeamID = CharacterTeamType.None;
corpse.EnableDespawn = false;
selectedPrefab.GiveItems(corpse, wreck);
@@ -143,14 +143,20 @@ namespace Barotrauma
var rand = new MTRandom(ToolBox.StringToInt(Seed));
InitialDepth = (int)MathHelper.Lerp(GenerationParams.InitialDepthMin, GenerationParams.InitialDepthMax, (float)rand.NextDouble());
//minimum difficulty of the level before hunting grounds can appear
float huntingGroundsDifficultyThreshold = 25;
//probability of hunting grounds appearing in 100% difficulty levels
float maxHuntingGroundsProbability = 0.3f;
HasHuntingGrounds = OriginallyHadHuntingGrounds = rand.NextDouble() < MathUtils.InverseLerp(huntingGroundsDifficultyThreshold, 100.0f, Difficulty) * maxHuntingGroundsProbability;
HasBeaconStation = !HasHuntingGrounds && rand.NextDouble() < locationConnection.Locations.Select(l => l.Type.BeaconStationChance).Max();
if (Biome.IsEndBiome)
{
HasHuntingGrounds = false;
HasBeaconStation = false;
}
else
{
//minimum difficulty of the level before hunting grounds can appear
float huntingGroundsDifficultyThreshold = 25;
//probability of hunting grounds appearing in 100% difficulty levels
float maxHuntingGroundsProbability = 0.3f;
HasHuntingGrounds = OriginallyHadHuntingGrounds = rand.NextDouble() < MathUtils.InverseLerp(huntingGroundsDifficultyThreshold, 100.0f, Difficulty) * maxHuntingGroundsProbability;
HasBeaconStation = !HasHuntingGrounds && rand.NextDouble() < locationConnection.Locations.Select(l => l.Type.BeaconStationChance).Max();
}
IsBeaconActive = false;
}
@@ -35,9 +35,6 @@ namespace Barotrauma.RuinGeneration
private static List<RuinGenerationParams> paramsList;
private readonly string filePath;
public override string Name => "RuinGenerationParams";
private RuinGenerationParams(XElement element, string filePath) : base(element, filePath)
{
@@ -1,7 +1,7 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using Voronoi2;
namespace Barotrauma.RuinGeneration
@@ -41,6 +41,9 @@ namespace Barotrauma.RuinGeneration
Submarine.Info.Name = $"Ruin ({level.Seed})";
Submarine.Info.Type = SubmarineType.Ruin;
Submarine.TeamID = CharacterTeamType.None;
//prevent the ruin from extending above the level "ceiling"
position.Y = Math.Min(level.Size.Y - (Submarine.Borders.Height / 2) - 100, position.Y);
Submarine.SetPosition(position.ToVector2());
if (mirror)
@@ -52,9 +55,9 @@ namespace Barotrauma.RuinGeneration
worldBorders.Location += Submarine.WorldPosition.ToPoint();
Area = new Rectangle(worldBorders.X, worldBorders.Y - worldBorders.Height, worldBorders.Width, worldBorders.Height);
List<WayPoint> subWaypoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == Submarine);
var waypoints = WayPoint.WayPointList.FindAll(wp => wp.Ruin == this || wp.Submarine == Submarine);
int interestingPosCount = 0;
foreach (WayPoint wp in subWaypoints)
foreach (WayPoint wp in waypoints)
{
if (wp.SpawnType != SpawnType.Enemy) { continue; }
level.PositionsOfInterest.Add(new Level.InterestingPosition(wp.WorldPosition.ToPoint(), Level.PositionType.Ruin, this));
@@ -63,8 +66,8 @@ namespace Barotrauma.RuinGeneration
if (interestingPosCount == 0)
{
//make sure there's at least on PositionsOfInterest in the ruins
level.PositionsOfInterest.Add(new Level.InterestingPosition(subWaypoints.GetRandom(Rand.RandSync.Server).WorldPosition.ToPoint(), Level.PositionType.Ruin, this));
//make sure there's at least one PositionsOfInterest in the ruins
level.PositionsOfInterest.Add(new Level.InterestingPosition(waypoints.GetRandom(Rand.RandSync.Server).WorldPosition.ToPoint(), Level.PositionType.Ruin, this));
}
}
}
@@ -175,9 +175,10 @@ namespace Barotrauma
{
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
LinkedSubmarine linkedSub;
idRemap.AssignMaxId(out ushort id);
if (Screen.Selected == GameMain.SubEditorScreen)
{
linkedSub = CreateDummy(submarine, element, pos, idRemap.AssignMaxId());
linkedSub = CreateDummy(submarine, element, pos, id);
linkedSub.saveElement = element;
linkedSub.purchasedLostShuttles = false;
}
@@ -185,7 +186,7 @@ namespace Barotrauma
{
string levelSeed = element.GetAttributeString("location", "");
LevelData levelData = GameMain.GameSession?.Campaign?.NextLevel ?? GameMain.GameSession?.LevelData;
linkedSub = new LinkedSubmarine(submarine, idRemap.AssignMaxId())
linkedSub = new LinkedSubmarine(submarine, id)
{
purchasedLostShuttles = GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles,
saveElement = element
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using StoreBalanceStatus = Barotrauma.LocationType.StoreBalanceStatus;
namespace Barotrauma
{
@@ -87,16 +88,12 @@ namespace Barotrauma
public int TurnsInRadiation { get; set; }
#region Store
private const float StoreMaxReputationModifier = 0.1f;
private const float StoreSellPriceModifier = 0.8f;
private const float DailySpecialPriceModifier = 0.5f;
private const float RequestGoodPriceModifier = 1.5f;
public const int StoreInitialBalance = 5000;
/// <summary>
/// In percentages
/// </summary>
private const int StorePriceModifierRange = 5;
private float StoreMaxReputationModifier => Type.StoreMaxReputationModifier;
private float StoreSellPriceModifier => Type.StoreSellPriceModifier;
private float DailySpecialPriceModifier => Type.DailySpecialPriceModifier;
private float RequestGoodPriceModifier => Type.RequestGoodPriceModifier;
public int StoreInitialBalance => Type.StoreInitialBalance;
private int StorePriceModifierRange => Type.StorePriceModifierRange;
/// <summary>
/// In percentages. Larger values make buying more expensive and selling less profitable, and vice versa.
/// </summary>
@@ -104,26 +101,7 @@ namespace Barotrauma
public Color BalanceColor => ActiveStoreBalanceStatus.Color;
public StoreBalanceStatus ActiveStoreBalanceStatus { get; private set; }
private static StoreBalanceStatus DefaultBalanceStatus { get; } = new StoreBalanceStatus(1.0f, 1.0f, Color.White);
private static List<StoreBalanceStatus> StoreBalanceStatuses { get; } = new List<StoreBalanceStatus>
{
new StoreBalanceStatus(0.5f, 0.75f, Color.Orange),
new StoreBalanceStatus(0.25f, 0.2f, Color.Red),
};
public struct StoreBalanceStatus
{
public float PercentageOfInitialBalance { get; }
public float SellPriceModifier { get; }
public Color Color { get; }
public StoreBalanceStatus(float percentage, float sellPriceModifier, Color color)
{
PercentageOfInitialBalance = percentage;
SellPriceModifier = sellPriceModifier;
Color = color;
}
}
private List<StoreBalanceStatus> StoreBalanceStatuses => Type.StoreBalanceStatuses;
private int storeCurrentBalance;
public int StoreCurrentBalance
@@ -1111,15 +1089,16 @@ namespace Barotrauma
}
}
public static StoreBalanceStatus GetStoreBalanceStatus(int balance)
public StoreBalanceStatus GetStoreBalanceStatus(int balance)
{
StoreBalanceStatus nextStatus = DefaultBalanceStatus;
foreach (var balanceStatus in StoreBalanceStatuses)
StoreBalanceStatus nextStatus = StoreBalanceStatuses[0];
for (int i = 1; i < StoreBalanceStatuses.Count; i++)
{
if (balanceStatus.PercentageOfInitialBalance < nextStatus.PercentageOfInitialBalance &&
((float)balance / StoreInitialBalance) < balanceStatus.PercentageOfInitialBalance)
var status = StoreBalanceStatuses[i];
if (status.PercentageOfInitialBalance < nextStatus.PercentageOfInitialBalance &&
((float)balance / StoreInitialBalance) < status.PercentageOfInitialBalance)
{
nextStatus = balanceStatus;
nextStatus = status;
}
}
return nextStatus;
@@ -68,6 +68,37 @@ namespace Barotrauma
private set;
}
public float StoreMaxReputationModifier { get; } = 0.1f;
public float StoreSellPriceModifier { get; } = 0.8f;
public float DailySpecialPriceModifier { get; } = 0.5f;
public float RequestGoodPriceModifier { get; } = 1.5f;
public int StoreInitialBalance { get; } = 5000;
/// <summary>
/// In percentages
/// </summary>
public int StorePriceModifierRange { get; } = 5;
public List<StoreBalanceStatus> StoreBalanceStatuses { get; } = new List<StoreBalanceStatus>()
{
new StoreBalanceStatus(1.0f, 1.0f, Color.White),
new StoreBalanceStatus(0.5f, 0.75f, Color.Orange),
new StoreBalanceStatus(0.25f, 0.2f, Color.Red)
};
public struct StoreBalanceStatus
{
public float PercentageOfInitialBalance { get; }
public float SellPriceModifier { get; }
public Color Color { get; }
public StoreBalanceStatus(float percentage, float sellPriceModifier, Color color)
{
PercentageOfInitialBalance = percentage;
SellPriceModifier = sellPriceModifier;
Color = color;
}
}
public override string ToString()
{
return $"LocationType (" + Identifier + ")";
@@ -163,6 +194,26 @@ namespace Barotrauma
portraits.Add(portrait);
}
break;
case "store":
StoreMaxReputationModifier = subElement.GetAttributeFloat("maxreputationmodifier", StoreMaxReputationModifier);
StoreSellPriceModifier = subElement.GetAttributeFloat("sellpricemodifier", StoreSellPriceModifier);
DailySpecialPriceModifier = subElement.GetAttributeFloat("dailyspecialpricemodifier", DailySpecialPriceModifier);
RequestGoodPriceModifier = subElement.GetAttributeFloat("requestgoodpricemodifier", RequestGoodPriceModifier);
StoreInitialBalance = subElement.GetAttributeInt("initialbalance", StoreInitialBalance);
StorePriceModifierRange = subElement.GetAttributeInt("pricemodifierrange", StorePriceModifierRange);
var balanceStatusElements = subElement.GetChildElements("balancestatus");
if (balanceStatusElements.Any())
{
StoreBalanceStatuses.Clear();
foreach (var balanceStatusElement in balanceStatusElements)
{
float percentage = balanceStatusElement.GetAttributeFloat("percentage", 1.0f);
float modifier = balanceStatusElement.GetAttributeFloat("sellpricemodifier", 1.0f);
Color color = balanceStatusElement.GetAttributeColor("color", Color.White);
StoreBalanceStatuses.Add(new StoreBalanceStatus(percentage, modifier, color));
}
}
break;
}
}
}
@@ -472,8 +472,11 @@ namespace Barotrauma
foreach (LocationConnection connection in Connections)
{
float difficulty = GetLevelDifficulty(connection.CenterPos.X / Width);
connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 1.2f, 100.0f);
//float difficulty = GetLevelDifficulty(connection.CenterPos.X / Width);
//connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 1.2f, 100.0f);
float difficulty = connection.CenterPos.X / Width * 100;
float random = difficulty > 10 ? 5 : 0;
connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-random, random, Rand.RandSync.Server), 1.0f, 100.0f);
}
AssignBiomes();
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma
@@ -22,6 +23,8 @@ namespace Barotrauma
public readonly Map Map;
public readonly RadiationParams Params;
private Affliction radiationAffliction;
private float radiationTimer;
private float increasedAmount;
@@ -93,6 +96,8 @@ namespace Barotrauma
increasedAmount = lastIncrease = amount;
}
public void UpdateRadiation(float deltaTime)
{
if (!(GameMain.GameSession?.IsCurrentLocationRadiated() ?? false)) { return; }
@@ -105,6 +110,8 @@ namespace Barotrauma
return;
}
radiationAffliction ??= new Affliction(AfflictionPrefab.RadiationSickness, Params.RadiationDamageAmount);
radiationTimer = Params.RadiationDamageDelay;
foreach (Character character in Character.CharacterList)
@@ -113,7 +120,11 @@ namespace Barotrauma
if (IsEntityRadiated(character))
{
health.ApplyAffliction(null, new Affliction(AfflictionPrefab.RadiationSickness, Params.RadiationDamageAmount));
foreach (Limb limb in character.AnimController.Limbs)
{
AttackResult attackResult = limb.AddDamage(limb.SimPosition, radiationAffliction.ToEnumerable(), playSound: false);
character.CharacterHealth.ApplyDamage(limb, attackResult);
}
}
}
}
@@ -262,7 +262,7 @@ namespace Barotrauma
item.GetComponent<ConnectionPanel>()?.InitializeLinks();
item.GetComponent<ItemContainer>()?.OnMapLoaded();
}
idOffset = moduleEntities.Max(e => e.ID);
idOffset = moduleEntities.Max(e => e.ID) + 1;
var wallEntities = moduleEntities.Where(e => e is Structure).Cast<Structure>();
var hullEntities = moduleEntities.Where(e => e is Hull).Cast<Hull>();
@@ -1483,7 +1483,7 @@ namespace Barotrauma
}
characterInfo.TeamID = CharacterTeamType.FriendlyNPC;
var npc = Character.Create(CharacterPrefab.HumanConfigFile, SpawnAction.OffsetSpawnPos(gotoTarget.WorldPosition, 100.0f), ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
npc.AnimController.FindHull(gotoTarget.WorldPosition, true);
npc.AnimController.FindHull(gotoTarget.WorldPosition, setSubmarine: true);
npc.TeamID = CharacterTeamType.FriendlyNPC;
npc.Prefab = humanPrefab;
if (!outpost.Info.OutpostNPCs.ContainsKey(humanPrefab.Identifier))
@@ -1503,7 +1503,7 @@ namespace Barotrauma
foreach (Item item in npc.Inventory.FindAllItems(it => it != null, recursive: true))
{
item.AllowStealing = outpost.Info.OutpostGenerationParams.AllowStealing;
item.SpawnedInOutpost = true;
item.SpawnedInCurrentOutpost = true;
}
npc.GiveIdCardTags(gotoTarget as WayPoint);
humanPrefab.InitializeCharacter(npc, gotoTarget);
@@ -44,7 +44,7 @@ namespace Barotrauma
#endif
}
private IEnumerable<object> Update(List<Submarine> subs, Camera cam)
private IEnumerable<CoroutineStatus> Update(List<Submarine> subs, Camera cam)
{
if (!subs.Any()) yield return CoroutineStatus.Success;
@@ -274,14 +274,6 @@ namespace Barotrauma
return "Barotrauma.Submarine (" + (Info?.Name ?? "[NULL INFO]") + ", " + IdOffset + ")";
}
public override bool Removed
{
get
{
return !loaded.Contains(this);
}
}
public int CalculateBasePrice()
{
int minPrice = 1000;
@@ -1126,22 +1118,6 @@ namespace Barotrauma
}
}
/// <summary>
/// Run the power logic so the sub is already powered up at the start of the round (as long as the reactor was on)
/// </summary>
public void WarmStartPower()
{
for (int i = 0; i < 600; i++)
{
Powered.UpdatePower((float)Timing.Step);
foreach (Entity e in Item.ItemList)
{
if (!(e is Item item) || item.GetComponent<Powered>() == null || e.Submarine != this) { continue; }
item.Update((float)Timing.Step, GameMain.GameScreen.Cam);
}
}
}
public void SetPrevTransform(Vector2 position)
{
prevPosition = position;
@@ -1398,7 +1374,7 @@ namespace Barotrauma
if (me.Submarine != this) { continue; }
if (me is Item item)
{
item.SpawnedInOutpost = info.OutpostGenerationParams != null;
item.SpawnedInCurrentOutpost = info.OutpostGenerationParams != null;
item.AllowStealing = info.OutpostGenerationParams?.AllowStealing ?? true;
if (item.GetComponent<Repairable>() != null && indestructible)
{
@@ -575,7 +575,7 @@ namespace Barotrauma
if (newHull != null)
{
CoroutineManager.Invoke(() =>
character.AnimController.FindHull(newHull.WorldPosition, true));
character.AnimController.FindHull(newHull.WorldPosition, setSubmarine: true));
}
return false;
@@ -194,7 +194,7 @@ namespace Barotrauma
float minDist = 100.0f;
float heightFromFloor = 110.0f;
float hullMinHeight = 100;
var removals = new List<WayPoint>();
var removals = new HashSet<WayPoint>();
foreach (Hull hull in Hull.hullList)
{
if (isFlooded)
@@ -492,17 +492,18 @@ namespace Barotrauma
{
outsideWaypoints.RemoveAll(w => w.Item1 == wp);
}
removals.ForEach(wp => wp.Remove());
for (int i = 0; i < outsideWaypoints.Count; i++)
{
WayPoint current = outsideWaypoints[i].Item1;
if (current.linkedTo.Count > 1) { continue; }
if (current.linkedTo.Count(l => !removals.Contains(l)) > 1) { continue; }
WayPoint next = null;
int maxConnections = 2;
float tooFar = outSideWaypointInterval * 5;
for (int j = 0; j < maxConnections; j++)
{
if (current.linkedTo.Count >= maxConnections) { break; }
tooFar /= current.linkedTo.Count;
tooFar /= current.linkedTo.Count(l => !removals.Contains(l));
next = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp.Item1 != next && wp.Item1.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.Item1.linkedTo.Count < 2 && wp.Item2 < i);
if (next != null)
{
@@ -511,18 +512,19 @@ namespace Barotrauma
}
}
}
foreach (Structure wall in Structure.WallList)
foreach (MapEntity mapEntity in mapEntityList.ToList())
{
if (wall.StairDirection == Direction.None) { continue; }
if (!(mapEntity is Structure structure)) { continue; }
if (structure.StairDirection == Direction.None) { continue; }
WayPoint[] stairPoints = new WayPoint[3];
stairPoints[0] = new WayPoint(
new Vector2(wall.Rect.X - 32.0f,
wall.Rect.Y - (wall.StairDirection == Direction.Left ? 80 : wall.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);
new Vector2(structure.Rect.X - 32.0f,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? 80 : structure.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);
stairPoints[1] = new WayPoint(
new Vector2(wall.Rect.Right + 32.0f,
wall.Rect.Y - (wall.StairDirection == Direction.Left ? wall.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);
new Vector2(structure.Rect.Right + 32.0f,
structure.Rect.Y - (structure.StairDirection == Direction.Left ? structure.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);
for (int i = 0; i < 2; i++)
{
@@ -869,10 +871,10 @@ namespace Barotrauma
}
}
public static WayPoint GetRandom(SpawnType spawnType = SpawnType.Human, JobPrefab assignedJob = null, Submarine sub = null, bool useSyncedRand = false, string spawnPointTag = null)
public static WayPoint GetRandom(SpawnType spawnType = SpawnType.Human, JobPrefab assignedJob = null, Submarine sub = null, bool useSyncedRand = false, string spawnPointTag = null, bool ignoreSubmarine = false)
{
return WayPointList.GetRandom(wp =>
wp.Submarine == sub &&
(ignoreSubmarine || wp.Submarine == sub) &&
wp.spawnType == spawnType &&
(string.IsNullOrEmpty(spawnPointTag) || wp.Tags.Any(t => t.Equals(spawnPointTag, StringComparison.OrdinalIgnoreCase))) &&
(assignedJob == null || (assignedJob != null && wp.AssignedJob == assignedJob)),