(d9829ac) v0.9.4.0
This commit is contained in:
@@ -209,7 +209,7 @@ namespace Barotrauma
|
||||
distFactors.Add(limb, distFactor);
|
||||
|
||||
List<Affliction> modifiedAfflictions = new List<Affliction>();
|
||||
foreach (Affliction affliction in attack.Afflictions)
|
||||
foreach (Affliction affliction in attack.Afflictions.Keys)
|
||||
{
|
||||
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
|
||||
}
|
||||
@@ -248,26 +248,20 @@ namespace Barotrauma
|
||||
Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
|
||||
limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//sever joints
|
||||
if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
|
||||
{
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (!distFactors.ContainsKey(limb)) continue;
|
||||
|
||||
foreach (LimbJoint joint in c.AnimController.LimbJoints)
|
||||
if (!distFactors.ContainsKey(limb)) { continue; }
|
||||
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
|
||||
{
|
||||
if (joint.IsSevered || (joint.LimbA != limb && joint.LimbB != limb)) continue;
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
|
||||
{
|
||||
c.AnimController.SeverLimbJoint(joint);
|
||||
}
|
||||
c.TrySeverLimbJoints(limb, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace Barotrauma
|
||||
{
|
||||
configPath = filePath;
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
if (doc == null) { return; }
|
||||
|
||||
name = doc.Root.GetAttributeString("name", "");
|
||||
identifier = doc.Root.GetAttributeString("identifier", null) ?? name.ToLowerInvariant().Replace(" ", "");
|
||||
configElement = doc.Root;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma
|
||||
public readonly string Description;
|
||||
|
||||
public readonly List<int> AllowedZones = new List<int>();
|
||||
|
||||
|
||||
public Biome(string name, string description)
|
||||
{
|
||||
Name = name;
|
||||
@@ -55,7 +55,7 @@ namespace Barotrauma
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
private int minWidth, maxWidth, height;
|
||||
|
||||
private Point voronoiSiteInterval;
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma
|
||||
private float cellIrregularity;
|
||||
|
||||
private int mountainCountMin, mountainCountMax;
|
||||
|
||||
|
||||
private int mountainHeightMin, mountainHeightMax;
|
||||
|
||||
private int ruinCount;
|
||||
@@ -134,8 +134,8 @@ namespace Barotrauma
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1000, true), Editable(MinValueInt = 0, MaxValueInt = 100000, ToolTip = "The total number of level objects (vegetation, vents, etc) in the level.")]
|
||||
|
||||
[Serialize(1000, true, description: "The total number of level objects (vegetation, vents, etc) in the level."), Editable(MinValueInt = 0, MaxValueInt = 100000)]
|
||||
public int LevelObjectAmount
|
||||
{
|
||||
get;
|
||||
@@ -162,9 +162,8 @@ namespace Barotrauma
|
||||
get { return height; }
|
||||
set { height = Math.Max(value, 2000); }
|
||||
}
|
||||
|
||||
[Serialize("3000, 3000", true), Editable(
|
||||
ToolTip = "How far from each other voronoi sites are placed. " +
|
||||
|
||||
[Editable, Serialize("3000, 3000", true, description: "How far from each other voronoi sites are placed. " +
|
||||
"Sites determine shape of the voronoi graph which the level walls are generated from. " +
|
||||
"(Decreasing this value causes the number of sites, and the complexity of the level, to increase exponentially - be careful when adjusting)")]
|
||||
public Point VoronoiSiteInterval
|
||||
@@ -177,7 +176,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("700,700", true), Editable(ToolTip = "How much random variation to apply to the positions of the voronoi sites on each axis. "+
|
||||
[Editable, Serialize("700,700", true, description: "How much random variation to apply to the positions of the voronoi sites on each axis. " +
|
||||
"Small values produce roughly rectangular level walls. The larger the values are, the less uniform the shapes get.")]
|
||||
public Point VoronoiSiteVariance
|
||||
{
|
||||
@@ -189,8 +188,8 @@ namespace Barotrauma
|
||||
MathHelper.Clamp(value.Y, 0, voronoiSiteInterval.Y));
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(1000, true), Editable(MinValueInt = 100, MaxValueInt = 10000, ToolTip = "The edges of the individual wall cells are subdivided into edges of this size. "
|
||||
|
||||
[Editable(MinValueInt = 100, MaxValueInt = 10000), Serialize(1000, true, description: "The edges of the individual wall cells are subdivided into edges of this size. "
|
||||
+ "Can be used in conjunction with the rounding values to make the cells rounder. Smaller values will make the cells look smoother, " +
|
||||
"but make the level more performance-intensive as the number of polygons used in rendering and physics calculations increases.")]
|
||||
public int CellSubdivisionLength
|
||||
@@ -203,8 +202,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
|
||||
[Serialize(0.5f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, ToolTip = "How much the individual wall cells are rounded. "
|
||||
+"Note that the final shape of the cells is also affected by the CellSubdivisionLength parameter.")]
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f), Serialize(0.5f, true, description: "How much the individual wall cells are rounded. "
|
||||
+ "Note that the final shape of the cells is also affected by the CellSubdivisionLength parameter.")]
|
||||
public float CellRoundingAmount
|
||||
{
|
||||
get { return cellRoundingAmount; }
|
||||
@@ -214,7 +213,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.1f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, ToolTip = "How much random variance is applied to the edges of the cells. "
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f), Serialize(0.1f, true, description: "How much random variance is applied to the edges of the cells. "
|
||||
+ "Note that the final shape of the cells is also affected by the CellSubdivisionLength parameter.")]
|
||||
public float CellIrregularity
|
||||
{
|
||||
@@ -226,7 +225,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
|
||||
[Serialize("5000, 10000", true), Editable(ToolTip = "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
|
||||
[Editable, Serialize("5000, 10000", true, description: "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
|
||||
public Point MainPathNodeIntervalRange
|
||||
{
|
||||
get { return mainPathNodeIntervalRange; }
|
||||
@@ -237,14 +236,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(5, true), Editable(ToolTip = "The number of small tunnels placed along the main path.")]
|
||||
[Editable, Serialize(5, true, description: "The number of small tunnels placed along the main path.")]
|
||||
public int SmallTunnelCount
|
||||
{
|
||||
get { return smallTunnelCount; }
|
||||
set { smallTunnelCount = MathHelper.Clamp(value, 0, 100); }
|
||||
}
|
||||
|
||||
[Serialize("5000, 10000", true), Editable(ToolTip = "The minimum and maximum length of small tunnels placed along the main path.")]
|
||||
|
||||
[Editable, Serialize("5000, 10000", true, description: "The minimum and maximum length of small tunnels placed along the main path.")]
|
||||
public Point SmallTunnelLengthRange
|
||||
{
|
||||
get { return smallTunnelLengthRange; }
|
||||
@@ -269,21 +268,21 @@ namespace Barotrauma
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(300000, true), Editable(MinValueFloat = Level.MaxEntityDepth, MaxValueFloat = 0.0f, ToolTip = "How far below the level the sea floor is placed.")]
|
||||
[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; }
|
||||
set { seaFloorBaseDepth = MathHelper.Clamp(value, Level.MaxEntityDepth, 0); }
|
||||
}
|
||||
|
||||
[Serialize(1000, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100000.0f, ToolTip = "Variance of the depth of the sea floor. Smaller values produce a smoother sea floor.")]
|
||||
[Serialize(1000, true, description: "Variance of the depth of the sea floor. Smaller values produce a smoother sea floor."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100000.0f)]
|
||||
public int SeaFloorVariance
|
||||
{
|
||||
get { return seaFloorVariance; }
|
||||
set { seaFloorVariance = value; }
|
||||
}
|
||||
|
||||
[Serialize(0, true), Editable(MinValueInt = 0, MaxValueInt = 20, ToolTip = "The minimum number of mountains on the sea floor.")]
|
||||
[Serialize(0, true, description: "The minimum number of mountains on the sea floor."), Editable(MinValueInt = 0, MaxValueInt = 20)]
|
||||
public int MountainCountMin
|
||||
{
|
||||
get { return mountainCountMin; }
|
||||
@@ -293,7 +292,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0, true), Editable(MinValueInt = 0, MaxValueInt = 20, ToolTip = "The maximum number of mountains on the sea floor.")]
|
||||
[Serialize(0, true, description: "The maximum number of mountains on the sea floor."), Editable(MinValueInt = 0, MaxValueInt = 20)]
|
||||
public int MountainCountMax
|
||||
{
|
||||
get { return mountainCountMax; }
|
||||
@@ -302,8 +301,8 @@ namespace Barotrauma
|
||||
mountainCountMax = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(1000, true), Editable(MinValueInt = 0, MaxValueInt = 1000000, ToolTip = "The minimum height of the mountains on the sea floor.")]
|
||||
|
||||
[Serialize(1000, true, description: "The minimum height of the mountains on the sea floor."), Editable(MinValueInt = 0, MaxValueInt = 1000000)]
|
||||
public int MountainHeightMin
|
||||
{
|
||||
get { return mountainHeightMin; }
|
||||
@@ -312,8 +311,8 @@ namespace Barotrauma
|
||||
mountainHeightMin = Math.Max(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(5000, true), Editable(MinValueInt = 0, MaxValueInt = 1000000, ToolTip = "The maximum height of the mountains on the sea floor.")]
|
||||
|
||||
[Serialize(5000, true, description: "The maximum height of the mountains on the sea floor."), Editable(MinValueInt = 0, MaxValueInt = 1000000)]
|
||||
public int MountainHeightMax
|
||||
{
|
||||
get { return mountainHeightMax; }
|
||||
@@ -323,21 +322,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(1, true), Editable(MinValueInt = 0, MaxValueInt = 50, ToolTip = "The number of alien ruins in the level.")]
|
||||
[Serialize(1, true, description: "The number of alien ruins in the level."), Editable(MinValueInt = 0, MaxValueInt = 50)]
|
||||
public int RuinCount
|
||||
{
|
||||
get { return ruinCount; }
|
||||
set { ruinCount = MathHelper.Clamp(value, 0, 10); }
|
||||
}
|
||||
|
||||
[Serialize(0.4f, true), Editable(ToolTip = "The probability for wall cells to be removed from the bottom of the map. A value of 0 will produce a completely enclosed tunnel and 1 will make the entire bottom of the level completely open.")]
|
||||
[Serialize(0.4f, true, description: "The probability for wall cells to be removed from the bottom of the map. A value of 0 will produce a completely enclosed tunnel and 1 will make the entire bottom of the level completely open."), Editable()]
|
||||
public float BottomHoleProbability
|
||||
{
|
||||
get { return bottomHoleProbability; }
|
||||
set { bottomHoleProbability = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
[Serialize(1.0f, true), Editable(ToolTip = "Scale of the water particle texture.")]
|
||||
[Serialize(1.0f, true, description: "Scale of the water particle texture."), Editable]
|
||||
public float WaterParticleScale
|
||||
{
|
||||
get { return waterParticleScale; }
|
||||
@@ -351,7 +350,7 @@ namespace Barotrauma
|
||||
public Sprite WallEdgeSprite { get; private set; }
|
||||
public Sprite WallEdgeSpriteSpecular { get; private set; }
|
||||
public Sprite WaterParticles { get; private set; }
|
||||
|
||||
|
||||
public static List<Biome> GetBiomes()
|
||||
{
|
||||
return biomes;
|
||||
@@ -386,7 +385,7 @@ namespace Barotrauma
|
||||
{
|
||||
Name = element == null ? "default" : element.Name.ToString();
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
|
||||
string biomeStr = element.GetAttributeString("biomes", "");
|
||||
if (string.IsNullOrWhiteSpace(biomeStr))
|
||||
{
|
||||
@@ -450,16 +449,29 @@ namespace Barotrauma
|
||||
{
|
||||
files = new List<string>() { "Content/Map/LevelGenerationParameters.xml" };
|
||||
}
|
||||
|
||||
|
||||
List<XElement> biomeElements = new List<XElement>();
|
||||
List<XElement> levelParamElements = new List<XElement>();
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
biomeElements.Clear();
|
||||
levelParamElements.Clear();
|
||||
DebugConsole.NewMessage($"Overriding the level generation parameters with '{file}'", Color.Yellow);
|
||||
}
|
||||
else if (biomeElements.Any() || levelParamElements.Any())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{file}': Another level generation parameter file already loaded! Use <override></override> tags to override it.");
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
foreach (XElement element in mainElement.Elements())
|
||||
{
|
||||
if (element.Name.ToString().ToLowerInvariant() == "biomes")
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Which sides of a wall the object can appear on.
|
||||
/// </summary>
|
||||
[Serialize((Alignment.Top | Alignment.Bottom | Alignment.Left | Alignment.Right), true), Editable(ToolTip = "Which sides of a wall the object can spawn on.")]
|
||||
[Serialize((Alignment.Top | Alignment.Bottom | Alignment.Left | Alignment.Right), true, description: "Which sides of a wall the object can spawn on."), Editable]
|
||||
public Alignment Alignment
|
||||
{
|
||||
get;
|
||||
@@ -117,15 +117,15 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("0.0,1.0", true), Editable()]
|
||||
[Serialize("0.0,1.0", true), Editable]
|
||||
public Vector2 DepthRange
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f,
|
||||
ToolTip = "The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are used to determine the probability for an object to spawn at a specific position.")]
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f),
|
||||
Serialize(0.0f, true, description: "The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are used to determine the probability for an object to spawn at a specific position.")]
|
||||
/// <summary>
|
||||
/// The tendency for the prefab to form clusters. Used as an exponent for perlin noise values
|
||||
/// that are used to determine the probability for an object to spawn at a specific position.
|
||||
@@ -136,8 +136,8 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f,
|
||||
ToolTip = "A value between 0-1 that determines the z-coordinate to sample perlin noise from when determining the probability " +
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f),
|
||||
Serialize(0.0f, true, description: "A value between 0-1 that determines the z-coordinate to sample perlin noise from when determining the probability " +
|
||||
" for an object to spawn at a specific position. Using the same (or close) value for different objects means the objects tend " +
|
||||
"to form clusters in the same areas.")]
|
||||
/// <summary>
|
||||
@@ -152,15 +152,14 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, true), Editable(ToolTip = "Should the object be rotated to align it with the wall surface it spawns on.")]
|
||||
[Editable, Serialize(false, true, description: "Should the object be rotated to align it with the wall surface it spawns on.")]
|
||||
public bool AlignWithSurface
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f,
|
||||
ToolTip = "Minimum length of a graph edge the object can spawn on.")]
|
||||
[Serialize(0.0f, true, description: "Minimum length of a graph edge the object can spawn on."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
/// <summary>
|
||||
/// Minimum length of a graph edge the object can spawn on.
|
||||
/// </summary>
|
||||
@@ -171,7 +170,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private Vector2 randomRotation;
|
||||
[Serialize("0.0,0.0", true), Editable(ToolTip = "How much the rotation of the object can vary (min and max values in degrees).")]
|
||||
[Editable, Serialize("0.0,0.0", true, description: "How much the rotation of the object can vary (min and max values in degrees).")]
|
||||
public Vector2 RandomRotation
|
||||
{
|
||||
get { return new Vector2(MathHelper.ToDegrees(randomRotation.X), MathHelper.ToDegrees(randomRotation.Y)); }
|
||||
@@ -184,7 +183,7 @@ namespace Barotrauma
|
||||
public Vector2 RandomRotationRad => randomRotation;
|
||||
|
||||
private float swingAmount;
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 360.0f, ToolTip = "How much the object swings (in degrees).")]
|
||||
[Serialize(0.0f, true, description: "How much the object swings (in degrees)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 360.0f)]
|
||||
public float SwingAmount
|
||||
{
|
||||
get { return MathHelper.ToDegrees(swingAmount); }
|
||||
@@ -196,30 +195,30 @@ namespace Barotrauma
|
||||
|
||||
public float SwingAmountRad => swingAmount;
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How fast the object swings.")]
|
||||
[Serialize(0.0f, true, description: "How fast the object swings."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float SwingFrequency
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("0.0,0.0", true), Editable(ToolTip = "How much the scale of the object oscillates on each axis. A value of 0.5,0.5 would make the object's scale oscillate from 100% to 150%.")]
|
||||
[Editable, Serialize("0.0,0.0", true, description: "How much the scale of the object oscillates on each axis. A value of 0.5,0.5 would make the object's scale oscillate from 100% to 150%.")]
|
||||
public Vector2 ScaleOscillation
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How fast the object's scale oscillates.")]
|
||||
[Serialize(0.0f, true, description: "How fast the object's scale oscillates."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float ScaleOscillationFrequency
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, true), Editable(ToolTip = "How likely it is for the object to spawn in a level. "+
|
||||
"This is relative to the commonness of the other objects - for example, having an object with "+
|
||||
"a commonness of 1 and another with a commonness of 10 would mean the latter appears in levels 10 times as frequently as the former. "+
|
||||
[Editable, Serialize(1.0f, true, description: "How likely it is for the object to spawn in a level. " +
|
||||
"This is relative to the commonness of the other objects - for example, having an object with " +
|
||||
"a commonness of 1 and another with a commonness of 10 would mean the latter appears in levels 10 times as frequently as the former. " +
|
||||
"The commonness value can be overridden on specific level types.")]
|
||||
public float Commonness
|
||||
{
|
||||
@@ -227,7 +226,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, ToolTip = "How much the object disrupts submarine's sonar.")]
|
||||
[Serialize(0.0f, true, description: "How much the object disrupts submarine's sonar."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float SonarDisruption
|
||||
{
|
||||
get;
|
||||
@@ -287,9 +286,19 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(configPath);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
if (doc == null) { return; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
DebugConsole.NewMessage($"Overriding all level object prefabs with '{configPath}'", Color.Yellow);
|
||||
list.Clear();
|
||||
}
|
||||
else if (list.Any())
|
||||
{
|
||||
DebugConsole.NewMessage($"Loading additional level object prefabs from file '{configPath}'");
|
||||
}
|
||||
foreach (XElement element in mainElement.Elements())
|
||||
{
|
||||
list.Add(new LevelObjectPrefab(element));
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace Barotrauma
|
||||
attack.Afflictions.Clear();
|
||||
foreach (Affliction affliction in multipliedAfflictions)
|
||||
{
|
||||
attack.Afflictions.Add(affliction);
|
||||
attack.Afflictions.Add(affliction, null);
|
||||
}
|
||||
attacks.Add(attack);
|
||||
break;
|
||||
@@ -313,7 +313,7 @@ namespace Barotrauma
|
||||
if (entity is Character character)
|
||||
{
|
||||
if (character.CurrentHull != null) return false;
|
||||
if (character.ConfigPath == Character.HumanConfigFile)
|
||||
if (character.IsHuman)
|
||||
{
|
||||
if (!triggeredBy.HasFlag(TriggererType.Human)) return false;
|
||||
}
|
||||
|
||||
@@ -35,61 +35,61 @@ namespace Barotrauma.RuinGeneration
|
||||
|
||||
public string Name => "RuinGenerationParams";
|
||||
|
||||
[Serialize("5000,5000", false), Editable()]
|
||||
[Serialize("5000,5000", false), Editable]
|
||||
public Point SizeMin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Serialize("8000,8000", false), Editable()]
|
||||
[Serialize("8000,8000", false), Editable]
|
||||
public Point SizeMax
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(3, false), Editable(MinValueInt = 1, MaxValueInt = 10, ToolTip = "The ruin generation algorithm \"splits\" the ruin area into two, splits these areas again, repeats this for some number of times and creates a room at each of the final split areas. This is value determines the minimum number of times the split is done.")]
|
||||
[Serialize(3, false, description: "The ruin generation algorithm \"splits\" the ruin area into two, splits these areas again, repeats this for some number of times and creates a room at each of the final split areas. This is value determines the minimum number of times the split is done."), Editable(MinValueInt = 1, MaxValueInt = 10)]
|
||||
public int RoomDivisionIterationsMin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(4, false), Editable(MinValueInt = 1, MaxValueInt = 10, ToolTip = "The ruin generation algorithm \"splits\" the ruin area into two, splits these areas again, repeats this for some number of times and creates a room at each of the final split areas. This is value determines the maximum number of times the split is done.")]
|
||||
[Serialize(4, false, description: "The ruin generation algorithm \"splits\" the ruin area into two, splits these areas again, repeats this for some number of times and creates a room at each of the final split areas. This is value determines the maximum number of times the split is done."), Editable(MinValueInt = 1, MaxValueInt = 10)]
|
||||
public int RoomDivisionIterationsMax
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.5f, false), Editable(MinValueFloat = 0.1f, MaxValueFloat = 0.9f, ToolTip = "The probability for the split algorithm to split the area vertically. High values tend to create tall, vertical rooms, and low values wide, horizontal rooms.")]
|
||||
[Serialize(0.5f, false, description: "The probability for the split algorithm to split the area vertically. High values tend to create tall, vertical rooms, and low values wide, horizontal rooms."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 0.9f)]
|
||||
public float VerticalSplitProbability
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(400, false), Editable(ToolTip = "The splitting algorithm attempts to keep the dimensions the split areas larger than this. For example, if the width of the split areas would be smaller than this after a vertical split, the algorithm will do a horizontal split.")]
|
||||
[Serialize(400, false, description: "The splitting algorithm attempts to keep the dimensions the split areas larger than this. For example, if the width of the split areas would be smaller than this after a vertical split, the algorithm will do a horizontal split."), Editable]
|
||||
public int MinSplitWidth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("0.5,0.9", false), Editable(ToolTip = "The minimum and maximum width of a room relative to the areas created by the split algorithm.")]
|
||||
[Serialize("0.5,0.9", false, description: "The minimum and maximum width of a room relative to the areas created by the split algorithm."), Editable]
|
||||
public Vector2 RoomWidthRange
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Serialize("0.5,0.9", false), Editable(ToolTip = "The minimum and maximum height of a room relative to the areas created by the split algorithm.")]
|
||||
[Serialize("0.5,0.9", false, description: "The minimum and maximum height of a room relative to the areas created by the split algorithm."), Editable]
|
||||
public Vector2 RoomHeightRange
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("200,256", false), Editable(ToolTip = "The minimum and maximum width of the corridors between rooms.")]
|
||||
[Serialize("200,256", false, description: "The minimum and maximum width of the corridors between rooms."), Editable]
|
||||
public Point CorridorWidthRange
|
||||
{
|
||||
get;
|
||||
@@ -140,8 +140,19 @@ namespace Barotrauma.RuinGeneration
|
||||
foreach (string configFile in GameMain.Instance.GetFilesOfType(ContentType.RuinConfig))
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(configFile);
|
||||
if (doc?.Root == null) continue;
|
||||
var newParams = new RuinGenerationParams(doc.Root)
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
paramsList.Clear();
|
||||
DebugConsole.NewMessage($"Overriding all ruin configuration parameters using the file {configFile}.", Color.Yellow);
|
||||
}
|
||||
else if (paramsList.Any())
|
||||
{
|
||||
DebugConsole.NewMessage($"Adding additional ruin configuration parameters from file '{configFile}'");
|
||||
}
|
||||
var newParams = new RuinGenerationParams(mainElement)
|
||||
{
|
||||
filePath = configFile
|
||||
};
|
||||
@@ -164,7 +175,7 @@ namespace Barotrauma.RuinGeneration
|
||||
if (configFile != generationParams.filePath) continue;
|
||||
|
||||
XDocument doc = XMLExtensions.TryLoadXml(configFile);
|
||||
if (doc?.Root == null) continue;
|
||||
if (doc == null) { continue; }
|
||||
|
||||
SerializableProperty.SerializeProperties(generationParams, doc.Root);
|
||||
|
||||
@@ -202,34 +213,34 @@ namespace Barotrauma.RuinGeneration
|
||||
private set;
|
||||
} = new Dictionary<string, SerializableProperty>();
|
||||
|
||||
[Serialize(RoomPlacement.Any, false), Editable()]
|
||||
[Serialize(RoomPlacement.Any, false), Editable]
|
||||
public RoomPlacement Placement
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0, false), Editable()]
|
||||
[Serialize(0, false), Editable]
|
||||
public int PlacementOffset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false), Editable()]
|
||||
[Serialize(false, false), Editable]
|
||||
public bool IsCorridor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, false), Editable()]
|
||||
[Serialize(1.0f, false), Editable]
|
||||
public float MinWaterAmount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
[Serialize(1.0f, false), Editable()]
|
||||
[Serialize(1.0f, false), Editable]
|
||||
public float MaxWaterAmount
|
||||
{
|
||||
get;
|
||||
@@ -380,11 +391,11 @@ namespace Barotrauma.RuinGeneration
|
||||
[Serialize(Alignment.Bottom, false), Editable]
|
||||
public Alignment Alignment { get; private set; }
|
||||
|
||||
[Serialize("0,0", false), Editable(ToolTip = "Minimum offset from the anchor position, relative to the size of the room."+
|
||||
" For example, a value of { -0.5,0 } with a Bottom alignment would mean the entity can be placed anywhere between the bottom-left corner of the room and bottom-center.")]
|
||||
[Serialize("0,0", false, description: "Minimum offset from the anchor position, relative to the size of the room." +
|
||||
" For example, a value of { -0.5,0 } with a Bottom alignment would mean the entity can be placed anywhere between the bottom-left corner of the room and bottom-center."), Editable]
|
||||
public Vector2 MinOffset { get; private set; }
|
||||
[Serialize("0,0", false), Editable(ToolTip = "Maximum offset from the anchor position, relative to the size of the room." +
|
||||
" For example, a value of { 0.5,0 } with a Bottom alignment would mean the entity can be placed anywhere between the bottom-right corner of the room and bottom-center.")]
|
||||
[Serialize("0,0", false, description: "Maximum offset from the anchor position, relative to the size of the room." +
|
||||
" For example, a value of { 0.5,0 } with a Bottom alignment would mean the entity can be placed anywhere between the bottom-right corner of the room and bottom-center."), Editable]
|
||||
public Vector2 MaxOffset { get; private set; }
|
||||
|
||||
[Serialize(RuinEntityType.Prop, false), Editable]
|
||||
|
||||
@@ -33,6 +33,15 @@ namespace Barotrauma
|
||||
private bool loadSub;
|
||||
private Submarine sub;
|
||||
|
||||
private ushort originalMyPortID;
|
||||
|
||||
//the ID of the docking port the sub was docked to in the original sub file
|
||||
//(needed when replacing a lost sub)
|
||||
private ushort originalLinkedToID;
|
||||
private DockingPort originalLinkedPort;
|
||||
|
||||
private bool purchasedLostShuttles;
|
||||
|
||||
public Submarine Sub
|
||||
{
|
||||
get
|
||||
@@ -139,13 +148,13 @@ namespace Barotrauma
|
||||
public static LinkedSubmarine Load(XElement element, Submarine submarine)
|
||||
{
|
||||
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
|
||||
|
||||
LinkedSubmarine linkedSub = null;
|
||||
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
linkedSub = CreateDummy(submarine, element, pos);
|
||||
linkedSub.saveElement = element;
|
||||
linkedSub.purchasedLostShuttles = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -154,35 +163,37 @@ namespace Barotrauma
|
||||
saveElement = element
|
||||
};
|
||||
|
||||
linkedSub.purchasedLostShuttles = GameMain.GameSession.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles;
|
||||
string levelSeed = element.GetAttributeString("location", "");
|
||||
if (!string.IsNullOrWhiteSpace(levelSeed) && GameMain.GameSession.Level != null && GameMain.GameSession.Level.Seed != levelSeed)
|
||||
if (!string.IsNullOrWhiteSpace(levelSeed) &&
|
||||
GameMain.GameSession.Level != null &&
|
||||
GameMain.GameSession.Level.Seed != levelSeed &&
|
||||
!linkedSub.purchasedLostShuttles)
|
||||
{
|
||||
linkedSub.loadSub = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
linkedSub.loadSub = true;
|
||||
|
||||
linkedSub.rect.Location = MathUtils.ToPoint(pos);
|
||||
else
|
||||
{
|
||||
linkedSub.loadSub = true;
|
||||
linkedSub.rect.Location = MathUtils.ToPoint(pos);
|
||||
}
|
||||
}
|
||||
|
||||
linkedSub.filePath = element.GetAttributeString("filepath", "");
|
||||
|
||||
string linkedToString = element.GetAttributeString("linkedto", "");
|
||||
if (linkedToString != "")
|
||||
int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
|
||||
for (int i = 0; i < linkedToIds.Length; i++)
|
||||
{
|
||||
string[] linkedToIds = linkedToString.Split(',');
|
||||
for (int i = 0; i < linkedToIds.Length; i++)
|
||||
{
|
||||
linkedSub.linkedToID.Add((ushort)int.Parse(linkedToIds[i]));
|
||||
}
|
||||
linkedSub.linkedToID.Add((ushort)linkedToIds[i]);
|
||||
}
|
||||
return linkedSub;
|
||||
linkedSub.originalLinkedToID = (ushort)element.GetAttributeInt("originallinkedto", 0);
|
||||
linkedSub.originalMyPortID = (ushort)element.GetAttributeInt("originalmyport", 0);
|
||||
|
||||
return linkedSub.loadSub ? linkedSub : null;
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (!loadSub) return;
|
||||
if (!loadSub) { return; }
|
||||
|
||||
sub = Submarine.Load(saveElement, false);
|
||||
|
||||
@@ -196,7 +207,6 @@ namespace Barotrauma
|
||||
sub.SetPosition(WorldPosition);
|
||||
}
|
||||
|
||||
|
||||
DockingPort linkedPort = null;
|
||||
DockingPort myPort = null;
|
||||
|
||||
@@ -212,36 +222,78 @@ namespace Barotrauma
|
||||
|
||||
if (linkedPort == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float closestDistance = 0.0f;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
{
|
||||
if (port.Item.Submarine != sub || port.IsHorizontal != linkedPort.IsHorizontal) continue;
|
||||
|
||||
float dist = Vector2.Distance(port.Item.WorldPosition, linkedPort.Item.WorldPosition);
|
||||
if (myPort == null || dist < closestDistance)
|
||||
if (purchasedLostShuttles)
|
||||
{
|
||||
myPort = port;
|
||||
closestDistance = dist;
|
||||
linkedPort = (FindEntityByID(originalLinkedToID) as Item)?.GetComponent<DockingPort>();
|
||||
}
|
||||
if (linkedPort == null) { return; }
|
||||
}
|
||||
originalLinkedPort = linkedPort;
|
||||
|
||||
myPort = (FindEntityByID(originalMyPortID) as Item)?.GetComponent<DockingPort>();
|
||||
if (myPort == null)
|
||||
{
|
||||
float closestDistance = 0.0f;
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
{
|
||||
if (port.Item.Submarine != sub || port.IsHorizontal != linkedPort.IsHorizontal) { continue; }
|
||||
float dist = Vector2.Distance(port.Item.WorldPosition, linkedPort.Item.WorldPosition);
|
||||
if (myPort == null || dist < closestDistance)
|
||||
{
|
||||
myPort = port;
|
||||
closestDistance = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myPort != null)
|
||||
{
|
||||
originalMyPortID = myPort.Item.ID;
|
||||
|
||||
myPort.Undock();
|
||||
|
||||
Vector2 portDiff = myPort.Item.WorldPosition - sub.WorldPosition;
|
||||
Vector2 offset = (myPort.IsHorizontal ?
|
||||
Vector2.UnitX * Math.Sign(linkedPort.Item.WorldPosition.X - myPort.Item.WorldPosition.X) :
|
||||
Vector2.UnitY * Math.Sign(linkedPort.Item.WorldPosition.Y - myPort.Item.WorldPosition.Y));
|
||||
offset *= myPort.DockedDistance;
|
||||
//something else is already docked to the port this sub should be docked to
|
||||
//may happen if a shuttle is lost, another vehicle docked to where the shuttle used to be,
|
||||
//and the shuttle is then restored in the campaign mode
|
||||
//or if the user connects multiple subs to the same docking ports in the sub editor
|
||||
if (linkedPort.Docked && linkedPort.DockingTarget != null && linkedPort.DockingTarget != myPort)
|
||||
{
|
||||
//just spawn below the main sub
|
||||
sub.SetPosition(
|
||||
linkedPort.Item.Submarine.WorldPosition -
|
||||
new Vector2(0, linkedPort.Item.Submarine.GetDockedBorders().Height / 2 + sub.GetDockedBorders().Height / 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 portDiff = myPort.Item.WorldPosition - sub.WorldPosition;
|
||||
Vector2 offset = (myPort.IsHorizontal ?
|
||||
Vector2.UnitX * Math.Sign(linkedPort.Item.WorldPosition.X - myPort.Item.WorldPosition.X) :
|
||||
Vector2.UnitY * Math.Sign(linkedPort.Item.WorldPosition.Y - myPort.Item.WorldPosition.Y));
|
||||
offset *= myPort.DockedDistance;
|
||||
|
||||
sub.SetPosition((linkedPort.Item.WorldPosition - portDiff) - offset);
|
||||
sub.SetPosition((linkedPort.Item.WorldPosition - portDiff) - offset);
|
||||
|
||||
myPort.Dock(linkedPort);
|
||||
myPort.Lock(true);
|
||||
myPort.Dock(linkedPort);
|
||||
myPort.Lock(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles)
|
||||
{
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != sub) { continue; }
|
||||
for (int i = 0; i < wall.SectionCount; i++)
|
||||
{
|
||||
wall.AddDamage(i, -wall.Prefab.Health);
|
||||
}
|
||||
}
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine != sub) { continue; }
|
||||
hull.WaterVolume = 0.0f;
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
sub.SetPosition(sub.WorldPosition - Submarine.WorldPosition);
|
||||
@@ -258,9 +310,7 @@ namespace Barotrauma
|
||||
{
|
||||
var doc = Submarine.OpenFile(filePath);
|
||||
saveElement = doc.Root;
|
||||
|
||||
saveElement.Name = "LinkedSubmarine";
|
||||
|
||||
saveElement.Add(new XAttribute("filepath", filePath));
|
||||
}
|
||||
else
|
||||
@@ -274,7 +324,7 @@ namespace Barotrauma
|
||||
var linkedPort = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent<DockingPort>() != null);
|
||||
if (linkedPort != null)
|
||||
{
|
||||
if (saveElement.Attribute("linkedto") != null) saveElement.Attribute("linkedto").Remove();
|
||||
saveElement.Attribute("linkedto")?.Remove();
|
||||
saveElement.Add(new XAttribute("linkedto", linkedPort.ID));
|
||||
}
|
||||
}
|
||||
@@ -284,6 +334,11 @@ namespace Barotrauma
|
||||
sub.SaveToXElement(saveElement);
|
||||
}
|
||||
|
||||
saveElement.Attribute("originallinkedto")?.Remove();
|
||||
saveElement.Add(new XAttribute("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID));
|
||||
saveElement.Attribute("originalmyport")?.Remove();
|
||||
saveElement.Add(new XAttribute("originalmyport", originalMyPortID));
|
||||
|
||||
if (sub != null)
|
||||
{
|
||||
bool leaveBehind = false;
|
||||
@@ -300,24 +355,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (leaveBehind)
|
||||
{
|
||||
saveElement.SetAttributeValue("location", Level.Loaded.Seed);
|
||||
saveElement.SetAttributeValue("worldpos", XMLExtensions.Vector2ToString(sub.SubBody.Position));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (saveElement.Attribute("location") != null) saveElement.Attribute("location").Remove();
|
||||
if (saveElement.Attribute("worldpos") != null) saveElement.Attribute("worldpos").Remove();
|
||||
}
|
||||
|
||||
saveElement.SetAttributeValue("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition));
|
||||
}
|
||||
|
||||
|
||||
|
||||
parentElement.Add(saveElement);
|
||||
|
||||
return saveElement;
|
||||
|
||||
@@ -106,12 +106,10 @@ namespace Barotrauma
|
||||
if (jobIdentifier == "")
|
||||
{
|
||||
DebugConsole.ThrowError("Error in location type \""+ Identifier + "\" - hireable jobs should be configured using identifiers instead of names.");
|
||||
jobIdentifier = subElement.GetAttributeString("name", "");
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Name.ToLowerInvariant() == jobIdentifier.ToLowerInvariant());
|
||||
}
|
||||
else
|
||||
{
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Identifier.ToLowerInvariant() == jobIdentifier.ToLowerInvariant());
|
||||
jobPrefab = JobPrefab.Get(jobIdentifier.ToLowerInvariant());
|
||||
}
|
||||
if (jobPrefab == null)
|
||||
{
|
||||
@@ -124,7 +122,7 @@ namespace Barotrauma
|
||||
hireableJobs.Add(hireableJob);
|
||||
break;
|
||||
case "symbol":
|
||||
symbolSprite = new Sprite(subElement);
|
||||
symbolSprite = new Sprite(subElement, lazyLoad: true);
|
||||
SpriteColor = subElement.GetAttributeColor("color", Color.White);
|
||||
break;
|
||||
case "changeto":
|
||||
@@ -192,14 +190,50 @@ namespace Barotrauma
|
||||
public static void Init()
|
||||
{
|
||||
var locationTypeFiles = GameMain.Instance.GetFilesOfType(ContentType.LocationTypes);
|
||||
|
||||
foreach (string file in locationTypeFiles)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc?.Root == null) continue;
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
DebugConsole.NewMessage($"Overriding all location types with '{file}'", Color.Yellow);
|
||||
List.Clear();
|
||||
}
|
||||
else if (List.Any())
|
||||
{
|
||||
DebugConsole.NewMessage($"Loading additional location types from file '{file}'");
|
||||
}
|
||||
foreach (XElement sourceElement in mainElement.Elements())
|
||||
{
|
||||
var element = sourceElement;
|
||||
bool allowOverriding = false;
|
||||
if (sourceElement.IsOverride())
|
||||
{
|
||||
element = sourceElement.FirstElement();
|
||||
allowOverriding = true;
|
||||
}
|
||||
string identifier = element.GetAttributeString("identifier", null);
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{file}': No identifier defined for {element.Name.ToString()}");
|
||||
continue;
|
||||
}
|
||||
var duplicate = List.FirstOrDefault(l => l.Identifier == identifier);
|
||||
if (duplicate != null)
|
||||
{
|
||||
if (allowOverriding)
|
||||
{
|
||||
List.Remove(duplicate);
|
||||
DebugConsole.NewMessage($"Overriding the location type with the identifier '{identifier}' with '{file}'", Color.Yellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{file}': Duplicate identifier defined with the identifier '{identifier}'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LocationType locationType = new LocationType(element);
|
||||
List.Add(locationType);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@ namespace Barotrauma
|
||||
public readonly bool ShowOverlay = true;
|
||||
#endif
|
||||
|
||||
[Serialize(6, true)]
|
||||
[Serialize(6, true)]
|
||||
public int DifficultyZones { get; set; } //Number of difficulty zones
|
||||
|
||||
|
||||
[Serialize(2000, true)]
|
||||
public int Size { get; set; }
|
||||
|
||||
[Serialize(20.0f, true), Editable(0.0f, 5000.0f, ToolTip = "Connections with a length smaller or equal to this generate the smallest possible levels (using the MinWidth parameter in the level generation paramaters).")]
|
||||
[Serialize(20.0f, true, description: "Connections with a length smaller or equal to this generate the smallest possible levels (using the MinWidth parameter in the level generation paramaters)."), Editable(0.0f, 5000.0f)]
|
||||
public float SmallLevelConnectionLength { get; set; }
|
||||
|
||||
[Serialize(200.0f, true), Editable(0.0f, 5000.0f, ToolTip = "Connections with a length larger or equal to this generate the largest possible levels (using the MaxWidth parameter in the level generation paramaters).")]
|
||||
[Serialize(200.0f, true, description: "Connections with a length larger or equal to this generate the largest possible levels (using the MaxWidth parameter in the level generation paramaters)."), Editable(0.0f, 5000.0f)]
|
||||
public float LargeLevelConnectionLength { get; set; }
|
||||
|
||||
[Serialize(1024, true)]
|
||||
@@ -65,77 +65,71 @@ namespace Barotrauma
|
||||
[Serialize("280,80", true), Editable]
|
||||
public Vector2 TileSpriteSpacing { get; set; }
|
||||
|
||||
[Serialize(1.0f, true), Editable(0.0f, 1.0f, ToolTip = "How dark the center of the map is (1.0f = black).")]
|
||||
public float CenterDarkenStrength { get; set; }
|
||||
[Serialize(1.0f, true, description: "How dark the center of the map is (1.0f = black)."), Editable(0.0f, 1.0f)]
|
||||
public float CenterDarkenStrength { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How close to the center the darkening starts (0.8f = 20% from the edge).")]
|
||||
[Serialize(0.9f, true, description: "How close to the center the darkening starts (0.8f = 20% from the edge)."), Editable(0.0f, 1.0f)]
|
||||
public float CenterDarkenRadius { get; set; }
|
||||
|
||||
[Serialize(5, true), Editable(0, 1000,
|
||||
ToolTip = "The edge of the dark center area is wave-shaped, and the frequency is determined by this value." +
|
||||
" I.e. how many points does the star-shaped dark area in the center have.")]
|
||||
[Serialize(5, true, description: "The edge of the dark center area is wave-shaped, and the frequency is determined by this value." +
|
||||
" I.e. how many points does the star-shaped dark area in the center have."), Editable(0, 1000)]
|
||||
public int CenterDarkenWaveFrequency { get; set; }
|
||||
|
||||
[Serialize(15.0f, true), Editable(0, 1000.0f,
|
||||
ToolTip = "How heavily the noise map affects the phase of the edge wave (higher value = more irregular shape).")]
|
||||
[Serialize(15.0f, true, description: "How heavily the noise map affects the phase of the edge wave (higher value = more irregular shape)."), Editable(0, 1000.0f)]
|
||||
public float CenterDarkenWavePhaseNoise { get; set; }
|
||||
|
||||
[Serialize(0.8f, true), Editable(0.0f, 1.0f, ToolTip = "How dark the edges of the map are (1.0f = black).")]
|
||||
[Serialize(0.8f, true, description: "How dark the edges of the map are (1.0f = black)."), Editable(0.0f, 1.0f)]
|
||||
public float EdgeDarkenStrength { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How far from the center the darkening starts (0.95f = 5% from the edge).")]
|
||||
[Serialize(0.9f, true, description: "How far from the center the darkening starts (0.95f = 5% from the edge)."), Editable(0.0f, 1.0f)]
|
||||
public float EdgeDarkenRadius { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How far from the center locations can be placed.")]
|
||||
|
||||
[Serialize(0.9f, true, description: "How far from the center locations can be placed."), Editable(0.0f, 1.0f)]
|
||||
public float LocationRadius { get; set; }
|
||||
|
||||
[Serialize(20.0f, true), Editable(1.0f, 100.0f,
|
||||
ToolTip = "How far from each other voronoi sites are placed. "+
|
||||
"Sites determine shape of the voronoi graph. Locations are placed at the vertices of the voronoi cells. "+
|
||||
"(Decreasing this value causes the number of sites, and the complexity of the map, to increase exponentially - be careful when adjusting)") ]
|
||||
|
||||
[Serialize(20.0f, true, description: "How far from each other voronoi sites are placed. " +
|
||||
"Sites determine shape of the voronoi graph. Locations are placed at the vertices of the voronoi cells. " +
|
||||
"(Decreasing this value causes the number of sites, and the complexity of the map, to increase exponentially - be careful when adjusting)"), Editable(1.0f, 100.0f)]
|
||||
public float VoronoiSiteInterval { get; set; }
|
||||
|
||||
[Serialize(0.3f, true), Editable(0.01f, 1.0f,
|
||||
ToolTip = "How likely it is for a site to be placed at a given spot (e.g. 20% probability for a site to be placed every 5 units of the map). "+
|
||||
"Multiplied with the noise value in the spot, meaning that sites are less likely to appear in dark spots.")]
|
||||
|
||||
[Serialize(0.3f, true, description: "How likely it is for a site to be placed at a given spot (e.g. 20% probability for a site to be placed every 5 units of the map). " +
|
||||
"Multiplied with the noise value in the spot, meaning that sites are less likely to appear in dark spots."), Editable(0.01f, 1.0f)]
|
||||
public float VoronoiSitePlacementProbability { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.01f, 1.0f,
|
||||
ToolTip = "Probability * noise ^ 2 must be higher than this for a site to be placed. "+
|
||||
"= How bright the noise map must be at a given spot for a location to be placed there")]
|
||||
|
||||
[Serialize(0.1f, true, description: "Probability * noise ^ 2 must be higher than this for a site to be placed. " +
|
||||
"= How bright the noise map must be at a given spot for a location to be placed there"), Editable(0.01f, 1.0f)]
|
||||
public float VoronoiSitePlacementMinVal { get; set; }
|
||||
|
||||
[Serialize(10.0f, true), Editable(0.0f, 500.0f, ToolTip = "Connections smaller than this are removed.")]
|
||||
[Serialize(10.0f, true, description: "Connections smaller than this are removed."), Editable(0.0f, 500.0f)]
|
||||
public float MinConnectionDistance { get; set; }
|
||||
|
||||
[Serialize(5.0f, true), Editable(0.0f, 100.0f, ToolTip = "Locations that are closer than this to another location are removed.")]
|
||||
|
||||
[Serialize(5.0f, true, description: "Locations that are closer than this to another location are removed."), Editable(0.0f, 100.0f)]
|
||||
public float MinLocationDistance { get; set; }
|
||||
|
||||
[Serialize(0.2f, true), Editable(0.0f, 10.0f,
|
||||
ToolTip = "Affects how many iterations are done when generating the jagged shape of the connections (iterations = Sqrt(connectionLength * multiplier)).")]
|
||||
[Serialize(0.2f, true, description: "Affects how many iterations are done when generating the jagged shape of the connections (iterations = Sqrt(connectionLength * multiplier))."), Editable(0.0f, 10.0f)]
|
||||
public float ConnectionIterationMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(0.0f, 10.0f, ToolTip = "How large the \"bends\" in the connections are (displacement = connectionLength * multiplier).")]
|
||||
|
||||
[Serialize(0.5f, true, description: "How large the \"bends\" in the connections are (displacement = connectionLength * multiplier)."), Editable(0.0f, 10.0f)]
|
||||
public float ConnectionDisplacementMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.0f, 10.0f, ToolTip = "ConnectionIterationMultiplier for the UI indicator lines between locations.")]
|
||||
|
||||
[Serialize(0.1f, true, description: "ConnectionIterationMultiplier for the UI indicator lines between locations."), Editable(0.0f, 10.0f)]
|
||||
public float ConnectionIndicatorIterationMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.0f, 10.0f, ToolTip = "ConnectionDisplacementMultiplier for the UI indicator lines between locations.")]
|
||||
|
||||
[Serialize(0.1f, true, description: "ConnectionDisplacementMultiplier for the UI indicator lines between locations."), Editable(0.0f, 10.0f)]
|
||||
public float ConnectionIndicatorDisplacementMultiplier { get; set; }
|
||||
|
||||
public Sprite ConnectionSprite { get; private set; }
|
||||
|
||||
#if CLIENT
|
||||
|
||||
[Serialize(15.0f, true), Editable(1.0f, 1000.0f, ToolTip = "Size of the location icons in pixels when at 100% zoom.")]
|
||||
|
||||
[Serialize(15.0f, true, description: "Size of the location icons in pixels when at 100% zoom."), Editable(1.0f, 1000.0f)]
|
||||
public float LocationIconSize { get; set; }
|
||||
|
||||
[Serialize("150,150,150,255", true), Editable(ToolTip = "The color used to display the low-difficulty connections on the map.")]
|
||||
[Serialize("150,150,150,255", true, description: "The color used to display the low-difficulty connections on the map."), Editable()]
|
||||
public Color LowDifficultyColor { get; set; }
|
||||
[Serialize("210,143,83,255", true), Editable(ToolTip = "The color used to display the medium-difficulty connections on the map.")]
|
||||
[Serialize("210,143,83,255", true, description: "The color used to display the medium-difficulty connections on the map."), Editable()]
|
||||
public Color MediumDifficultyColor { get; set; }
|
||||
[Serialize("216,154,138", true), Editable(ToolTip = "The color used to display the high-difficulty connections on the map.")]
|
||||
[Serialize("216,154,138", true, description: "The color used to display the high-difficulty connections on the map."), Editable()]
|
||||
public Color HighDifficultyColor { get; set; }
|
||||
|
||||
public SpriteSheet DecorativeMapSprite { get; private set; }
|
||||
@@ -172,14 +166,35 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("No map generation parameters found in the selected content packages!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's not actually load the parameters until we have solved which file is the last, because loading the parameters takes some resources that would also need to be released.
|
||||
XElement selectedElement = null;
|
||||
foreach (string file in files)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc?.Root == null) return;
|
||||
|
||||
instance = new MapGenerationParams(doc.Root);
|
||||
break;
|
||||
if (doc == null) { continue; }
|
||||
var mainElement = doc.Root;
|
||||
if (doc.Root.IsOverride())
|
||||
{
|
||||
mainElement = doc.Root.FirstElement();
|
||||
if (selectedElement != null)
|
||||
{
|
||||
DebugConsole.NewMessage($"Overriding the map generation parameters with '{file}'", Color.Yellow);
|
||||
}
|
||||
}
|
||||
else if (selectedElement != null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in {file}: Another map generation parameter file already loaded! Use <override></override> tags to override it.");
|
||||
break;
|
||||
}
|
||||
selectedElement = mainElement;
|
||||
}
|
||||
if (selectedElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find a valid element in the map generation parameter files!");
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = new MapGenerationParams(selectedElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -303,6 +304,43 @@ namespace Barotrauma
|
||||
public static object GetSelected()
|
||||
{
|
||||
return (object)selected;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool HandleExisting(string identifier, bool allowOverriding, string file = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == identifier);
|
||||
if (existingPrefab != null)
|
||||
{
|
||||
if (allowOverriding)
|
||||
{
|
||||
string msg = $"Overriding an existing map entity with the identifier '{identifier}'";
|
||||
if (!string.IsNullOrWhiteSpace(file))
|
||||
{
|
||||
msg += $" using the file '{file}'";
|
||||
}
|
||||
msg += ".";
|
||||
DebugConsole.NewMessage(msg, Color.Yellow);
|
||||
List.Remove(existingPrefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(file))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{file}': Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier! " +
|
||||
"Use the <override> XML element as the parent of the map element's definition to override the existing map element.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier! " +
|
||||
"Use the <override> XML element as the parent of the map element's definition to override the existing map element.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("0,0", true), Editable(ToolTip = "The position of the drop shadow relative to the structure. If set to zero, the shadow is positioned automatically so that it points towards the sub's center of mass.")]
|
||||
[Editable, Serialize("0,0", true, description: "The position of the drop shadow relative to the structure. If set to zero, the shadow is positioned automatically so that it points towards the sub's center of mass.")]
|
||||
public Vector2 DropShadowOffset
|
||||
{
|
||||
get;
|
||||
@@ -238,6 +238,29 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//for upgrading the dimensions of a structure from xml
|
||||
[Serialize(0, false)]
|
||||
public int RectWidth
|
||||
{
|
||||
get { return rect.Width; }
|
||||
set
|
||||
{
|
||||
if (value <= 0) { return; }
|
||||
Rect = new Rectangle(rect.X, rect.Y, value, rect.Height);
|
||||
}
|
||||
}
|
||||
//for upgrading the dimensions of a structure from xml
|
||||
[Serialize(0, false)]
|
||||
public int RectHeight
|
||||
{
|
||||
get { return rect.Height; }
|
||||
set
|
||||
{
|
||||
if (value <= 0) { return; }
|
||||
Rect = new Rectangle(rect.X, rect.Y, rect.Width, value);
|
||||
}
|
||||
}
|
||||
|
||||
public float BodyWidth
|
||||
{
|
||||
@@ -412,19 +435,20 @@ namespace Barotrauma
|
||||
private void CreateStairBodies()
|
||||
{
|
||||
Bodies = new List<Body>();
|
||||
|
||||
float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f));
|
||||
|
||||
float bodyWidth = ConvertUnits.ToSimUnits(rect.Width * Math.Sqrt(2.0));
|
||||
float bodyWidth = ConvertUnits.ToSimUnits(rect.Width / Math.Cos(stairAngle));
|
||||
float bodyHeight = ConvertUnits.ToSimUnits(10);
|
||||
|
||||
float stairHeight = rect.Width * (float)Math.Tan(stairAngle);
|
||||
|
||||
Body newBody = BodyFactory.CreateRectangle(GameMain.World,
|
||||
bodyWidth, bodyHeight, 1.5f);
|
||||
|
||||
newBody.BodyType = BodyType.Static;
|
||||
Vector2 stairPos = new Vector2(Position.X, rect.Y - rect.Height + rect.Width / 2.0f);
|
||||
/*stairPos += new Vector2(
|
||||
(StairDirection == Direction.Right) ? -Submarine.GridSize.X * 1.5f : Submarine.GridSize.X * 1.5f,
|
||||
-Submarine.GridSize.Y * 2.0f);*/
|
||||
newBody.Rotation = (StairDirection == Direction.Right) ? MathHelper.PiOver4 : -MathHelper.PiOver4;
|
||||
Vector2 stairPos = new Vector2(Position.X, rect.Y - rect.Height + stairHeight / 2.0f);
|
||||
newBody.Rotation = (StairDirection == Direction.Right) ? stairAngle : -stairAngle;
|
||||
newBody.CollisionCategories = Physics.CollisionStairs;
|
||||
newBody.Friction = 0.8f;
|
||||
newBody.UserData = this;
|
||||
@@ -1191,6 +1215,11 @@ namespace Barotrauma
|
||||
|
||||
SerializableProperty.DeserializeProperties(s, element);
|
||||
|
||||
if (submarine?.GameVersion != null)
|
||||
{
|
||||
SerializableProperty.UpgradeGameVersion(s, s.Prefab.ConfigElement, submarine.GameVersion);
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString())
|
||||
|
||||
@@ -102,6 +102,13 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(45.0f, false)]
|
||||
public float StairAngle
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool NoAITarget
|
||||
{
|
||||
@@ -152,18 +159,39 @@ namespace Barotrauma
|
||||
foreach (string filePath in filePaths)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
foreach (XElement el in doc.Root.Elements())
|
||||
{
|
||||
StructurePrefab sp = Load(el);
|
||||
|
||||
List.Add(sp);
|
||||
if (doc == null) { return; }
|
||||
var rootElement = doc.Root;
|
||||
if (rootElement.IsOverride())
|
||||
{
|
||||
foreach (var element in rootElement.Elements())
|
||||
{
|
||||
foreach (var childElement in element.Elements())
|
||||
{
|
||||
Load(childElement, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var element in rootElement.Elements())
|
||||
{
|
||||
if (element.IsOverride())
|
||||
{
|
||||
foreach (var childElement in element.Elements())
|
||||
{
|
||||
Load(childElement, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Load(element, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static StructurePrefab Load(XElement element)
|
||||
public static StructurePrefab Load(XElement element, bool allowOverride)
|
||||
{
|
||||
StructurePrefab sp = new StructurePrefab
|
||||
{
|
||||
@@ -275,16 +303,10 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError(
|
||||
"Structure prefab \"" + sp.name + "\" has no identifier. All structure prefabs have a unique identifier string that's used to differentiate between items during saving and loading.");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(sp.identifier))
|
||||
if (sp.HandleExisting(sp.Identifier, allowOverride))
|
||||
{
|
||||
MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == sp.identifier);
|
||||
if (existingPrefab != null)
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
"Map entity prefabs \"" + sp.name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!");
|
||||
}
|
||||
List.Add(sp);
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
@@ -315,7 +337,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (ResizeVertical && Math.Abs(placeSize.Y) < Submarine.GridSize.Y)
|
||||
{
|
||||
placeSize.X = Submarine.GridSize.Y;
|
||||
placeSize.Y = Submarine.GridSize.Y;
|
||||
}
|
||||
|
||||
newRect = Submarine.AbsRect(placePosition, placeSize);
|
||||
|
||||
@@ -246,6 +246,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool? subsLeftBehind;
|
||||
public bool SubsLeftBehind
|
||||
{
|
||||
get
|
||||
{
|
||||
if (subsLeftBehind.HasValue) { return subsLeftBehind.Value; }
|
||||
|
||||
CheckSubsLeftBehind();
|
||||
return subsLeftBehind.Value;
|
||||
}
|
||||
//set { subsLeftBehind = value; }
|
||||
}
|
||||
public bool LeftBehindSubDockingPortOccupied
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public new Vector2 DrawPosition
|
||||
{
|
||||
get;
|
||||
@@ -306,6 +323,20 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
private bool? requiredContentPackagesInstalled;
|
||||
public bool RequiredContentPackagesInstalled
|
||||
{
|
||||
get
|
||||
{
|
||||
if (requiredContentPackagesInstalled.HasValue) { return requiredContentPackagesInstalled.Value; }
|
||||
return RequiredContentPackages.All(cp => GameMain.SelectedPackages.Any(cp2 => cp2.Name == cp));
|
||||
}
|
||||
set
|
||||
{
|
||||
requiredContentPackagesInstalled = value;
|
||||
}
|
||||
}
|
||||
|
||||
//constructors & generation ----------------------------------------------------
|
||||
|
||||
public Submarine(string filePath, string hash = "", bool tryLoad = true) : base(null)
|
||||
@@ -325,6 +356,8 @@ namespace Barotrauma
|
||||
this.hash = new Md5Hash(hash);
|
||||
}
|
||||
|
||||
IsFileCorrupted = false;
|
||||
|
||||
if (tryLoad)
|
||||
{
|
||||
XDocument doc = null;
|
||||
@@ -376,6 +409,8 @@ namespace Barotrauma
|
||||
{
|
||||
RequiredContentPackages.Add(contentPackageName);
|
||||
}
|
||||
|
||||
CheckSubsLeftBehind(doc.Root);
|
||||
#if CLIENT
|
||||
string previewImageData = doc.Root.GetAttributeString("previewimage", "");
|
||||
if (!string.IsNullOrEmpty(previewImageData))
|
||||
@@ -437,6 +472,42 @@ namespace Barotrauma
|
||||
tags &= ~tag;
|
||||
}
|
||||
|
||||
public void CheckSubsLeftBehind(XElement element = null)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
XDocument doc = null;
|
||||
int maxLoadRetries = 4;
|
||||
for (int i = 0; i <= maxLoadRetries; i++)
|
||||
{
|
||||
doc = OpenFile(filePath, out Exception e);
|
||||
if (e != null && !(e is IOException)) { break; }
|
||||
if (doc != null || i == maxLoadRetries || !File.Exists(filePath)) { break; }
|
||||
DebugConsole.NewMessage("Opening submarine file \"" + filePath + "\" failed, retrying in 250 ms...");
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
if (doc?.Root == null) { return; }
|
||||
element = doc.Root;
|
||||
}
|
||||
|
||||
subsLeftBehind = false;
|
||||
LeftBehindSubDockingPortOccupied = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "linkedsubmarine") { continue; }
|
||||
if (subElement.Attribute("location") == null) { continue; }
|
||||
|
||||
subsLeftBehind = true;
|
||||
ushort targetDockingPortID = (ushort)subElement.GetAttributeInt("originallinkedto", 0);
|
||||
XElement targetPortElement = targetDockingPortID == 0 ? null :
|
||||
element.Elements().FirstOrDefault(e => e.GetAttributeInt("ID", 0) == targetDockingPortID);
|
||||
if (targetPortElement != null && targetPortElement.GetAttributeIntArray("linked", new int[0]).Length > 0)
|
||||
{
|
||||
LeftBehindSubDockingPortOccupied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MakeOutpost()
|
||||
{
|
||||
IsOutpost = true;
|
||||
@@ -931,7 +1002,7 @@ namespace Barotrauma
|
||||
parents.Add(this);
|
||||
|
||||
flippedX = !flippedX;
|
||||
|
||||
|
||||
Item.UpdateHulls();
|
||||
|
||||
List<Item> bodyItems = Item.ItemList.FindAll(it => it.Submarine == this && it.body != null);
|
||||
@@ -975,6 +1046,8 @@ namespace Barotrauma
|
||||
}
|
||||
entityGrid = Hull.GenerateEntityGrid(this);
|
||||
|
||||
SubBody.FlipX();
|
||||
|
||||
foreach (MapEntity mapEntity in subEntities)
|
||||
{
|
||||
mapEntity.Move(HiddenSubPosition);
|
||||
@@ -1314,6 +1387,12 @@ namespace Barotrauma
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(file);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
exception = e;
|
||||
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (File not found)");
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
@@ -1377,7 +1456,11 @@ namespace Barotrauma
|
||||
DebugConsole.NewMessage("Loading the submarine \"" + Name + "\" failed, retrying in 250 ms...");
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
if (doc == null || doc.Root == null) { return; }
|
||||
if (doc == null || doc.Root == null)
|
||||
{
|
||||
IsFileCorrupted = true;
|
||||
return;
|
||||
}
|
||||
submarineElement = doc.Root;
|
||||
}
|
||||
|
||||
@@ -1574,6 +1657,8 @@ namespace Barotrauma
|
||||
if (e.Submarine != this || !e.ShouldBeSaved) continue;
|
||||
e.Save(element);
|
||||
}
|
||||
|
||||
CheckSubsLeftBehind(element);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,12 @@ namespace Barotrauma
|
||||
public const float DamageDepth = -30000.0f;
|
||||
private const float ImpactDamageMultiplier = 10.0f;
|
||||
|
||||
//limbs with a mass smaller than this won't cause an impact when they hit the sub
|
||||
private const float MinImpactLimbMass = 10.0f;
|
||||
//impacts smaller than this are ignored
|
||||
private const float MinCollisionImpact = 3.0f;
|
||||
//impacts are clamped below this value
|
||||
private const float MaxCollisionImpact = 5.0f;
|
||||
private const float Friction = 0.2f, Restitution = 0.0f;
|
||||
|
||||
public List<Vector2> HullVertices
|
||||
@@ -94,11 +100,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
List<Vector2> convexHull = GenerateConvexHull();
|
||||
HullVertices = convexHull;
|
||||
for (int i = 0; i < convexHull.Count; i++)
|
||||
{
|
||||
convexHull[i] = ConvertUnits.ToSimUnits(convexHull[i]);
|
||||
}
|
||||
HullVertices = convexHull;
|
||||
|
||||
Vector2 minExtents = Vector2.Zero, maxExtents = Vector2.Zero;
|
||||
|
||||
@@ -217,7 +223,6 @@ namespace Barotrauma
|
||||
Body = new PhysicsBody(farseerBody);
|
||||
}
|
||||
|
||||
|
||||
private List<Vector2> GenerateConvexHull()
|
||||
{
|
||||
List<Structure> subWalls = Structure.WallList.FindAll(wall => wall.Submarine == submarine);
|
||||
@@ -405,6 +410,16 @@ namespace Barotrauma
|
||||
depthDamageTimer = 10.0f;
|
||||
}
|
||||
|
||||
public void FlipX()
|
||||
{
|
||||
List<Vector2> convexHull = GenerateConvexHull();
|
||||
for (int i = 0; i < convexHull.Count; i++)
|
||||
{
|
||||
convexHull[i] = ConvertUnits.ToSimUnits(convexHull[i]);
|
||||
}
|
||||
HullVertices = convexHull;
|
||||
}
|
||||
|
||||
public bool OnCollision(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
if (f2.Body.UserData is Limb limb)
|
||||
@@ -418,19 +433,19 @@ namespace Barotrauma
|
||||
return CheckCharacterCollision(contact, character);
|
||||
}
|
||||
|
||||
contact.GetWorldManifold(out Vector2 normal, out FixedArray2<Vector2> points);
|
||||
contact.GetWorldManifold(out Vector2 normal, out FixedArray2<Vector2> _);
|
||||
if (contact.FixtureA.Body == f1.Body)
|
||||
{
|
||||
normal = -normal;
|
||||
}
|
||||
|
||||
if (f2.UserData is VoronoiCell cell)
|
||||
if (f2.UserData is VoronoiCell)
|
||||
{
|
||||
HandleLevelCollision(contact, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (f2.Body.UserData is Structure structure)
|
||||
if (f2.Body.UserData is Structure)
|
||||
{
|
||||
|
||||
HandleLevelCollision(contact, normal);
|
||||
@@ -481,18 +496,19 @@ namespace Barotrauma
|
||||
|
||||
private void HandleLimbCollision(Contact contact, Limb limb)
|
||||
{
|
||||
if (limb.Mass > 100.0f)
|
||||
if (limb.Mass > MinImpactLimbMass)
|
||||
{
|
||||
Vector2 normal = Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
|
||||
Vector2 normal =
|
||||
Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
|
||||
Vector2.UnitY :
|
||||
Vector2.Normalize(Body.SimPosition - limb.SimPosition);
|
||||
|
||||
float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1);
|
||||
float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) * Math.Min(limb.Mass / 100.0f, 1);
|
||||
|
||||
ApplyImpact(impact, -normal, contact, applyDamage: false);
|
||||
ApplyImpact(impact, normal, contact, applyDamage: false);
|
||||
foreach (Submarine dockedSub in submarine.DockedTo)
|
||||
{
|
||||
dockedSub.SubBody.ApplyImpact(impact, -normal, contact, applyDamage: false);
|
||||
dockedSub.SubBody.ApplyImpact(impact, normal, contact, applyDamage: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,9 +574,7 @@ namespace Barotrauma
|
||||
|
||||
float damageAmount = contactDot * Body.Mass / limb.character.Mass;
|
||||
|
||||
Vector2 n;
|
||||
FixedArray2<Vector2> contactPos;
|
||||
contact.GetWorldManifold(out n, out contactPos);
|
||||
contact.GetWorldManifold(out _, out FixedArray2<Vector2> contactPos);
|
||||
limb.character.LastDamageSource = submarine;
|
||||
limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(contactPos[0]), limb,
|
||||
new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(damageAmount) }, 0.0f, true, 0.0f);
|
||||
@@ -587,9 +601,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
Vector2 n;
|
||||
FixedArray2<Vector2> particlePos;
|
||||
contact.GetWorldManifold(out n, out particlePos);
|
||||
contact.GetWorldManifold(out _, out FixedArray2<Vector2> particlePos);
|
||||
|
||||
int particleAmount = (int)Math.Min(wallImpact * 10.0f, 50);
|
||||
for (int i = 0; i < particleAmount; i++)
|
||||
@@ -605,9 +617,7 @@ namespace Barotrauma
|
||||
{
|
||||
Debug.Assert(otherSub != submarine);
|
||||
|
||||
Vector2 normal;
|
||||
FixedArray2<Vector2> points;
|
||||
contact.GetWorldManifold(out normal, out points);
|
||||
contact.GetWorldManifold(out Vector2 normal, out FixedArray2<Vector2> points);
|
||||
if (contact.FixtureA.Body == otherSub.SubBody.Body.FarseerBody)
|
||||
{
|
||||
normal = -normal;
|
||||
@@ -688,15 +698,13 @@ namespace Barotrauma
|
||||
|
||||
private void ApplyImpact(float impact, Vector2 direction, Contact contact, bool applyDamage = true)
|
||||
{
|
||||
float minImpact = 3.0f;
|
||||
|
||||
if (impact < minImpact) { return; }
|
||||
if (impact < MinCollisionImpact) { return; }
|
||||
|
||||
contact.GetWorldManifold(out Vector2 tempNormal, out FixedArray2<Vector2> worldPoints);
|
||||
Vector2 lastContactPoint = worldPoints[0];
|
||||
|
||||
Vector2 impulse = direction * impact * 0.5f;
|
||||
impulse = impulse.ClampLength(5.0f);
|
||||
impulse = impulse.ClampLength(MaxCollisionImpact);
|
||||
|
||||
if (!MathUtils.IsValid(impulse))
|
||||
{
|
||||
@@ -719,23 +727,38 @@ namespace Barotrauma
|
||||
if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
|
||||
{
|
||||
GameMain.GameScreen.Cam.Shake = impact * 2.0f;
|
||||
float angularVelocity =
|
||||
(lastContactPoint.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y
|
||||
- (lastContactPoint.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X;
|
||||
GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f);
|
||||
if (!submarine.IsOutpost && !submarine.DockedTo.Any(s => s.IsOutpost))
|
||||
{
|
||||
float angularVelocity =
|
||||
(lastContactPoint.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y
|
||||
- (lastContactPoint.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X;
|
||||
GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Submarine != submarine) continue;
|
||||
if (impact > 2.0f) c.SetStun((impact - 2.0f) * 0.1f);
|
||||
if (c.Submarine != submarine) { continue; }
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
limb.body.ApplyLinearImpulse(limb.Mass * impulse, 20.0f);
|
||||
limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f);
|
||||
}
|
||||
c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);
|
||||
|
||||
bool holdingOntoSomething = false;
|
||||
if (c.SelectedConstruction != null)
|
||||
{
|
||||
var controller = c.SelectedConstruction.GetComponent<Items.Components.Controller>();
|
||||
holdingOntoSomething = controller != null && controller.LimbPositions.Any();
|
||||
}
|
||||
|
||||
//stun for up to 1 second if the impact equal or higher to the maximum impact
|
||||
if (impact >= MaxCollisionImpact && !holdingOntoSomething)
|
||||
{
|
||||
c.SetStun(Math.Min(impulse.Length() * 0.2f, 1.0f));
|
||||
}
|
||||
c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 20.0f);
|
||||
}
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
@@ -743,7 +766,7 @@ namespace Barotrauma
|
||||
if (item.Submarine != submarine || item.CurrentHull == null ||
|
||||
item.body == null || !item.body.Enabled) continue;
|
||||
|
||||
item.body.ApplyLinearImpulse(item.body.Mass * impulse, 20.0f);
|
||||
item.body.ApplyLinearImpulse(item.body.Mass * impulse, 10.0f);
|
||||
}
|
||||
|
||||
var damagedStructures = Explosion.RangedStructureDamage(
|
||||
@@ -770,7 +793,7 @@ namespace Barotrauma
|
||||
"StructureBlunt",
|
||||
impact * 10.0f,
|
||||
ConvertUnits.ToDisplayUnits(lastContactPoint),
|
||||
MathHelper.Lerp(2000.0f, 10000.0f, (impact - minImpact) / 2.0f),
|
||||
MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
|
||||
maxDamageStructure.Tags);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -648,8 +648,8 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrWhiteSpace(jobIdentifier))
|
||||
{
|
||||
w.assignedJob =
|
||||
JobPrefab.List.Find(jp => jp.Identifier.ToLowerInvariant() == jobIdentifier) ??
|
||||
JobPrefab.List.Find(jp => jp.Name.ToLowerInvariant() == jobIdentifier);
|
||||
JobPrefab.Get(jobIdentifier) ??
|
||||
JobPrefab.List.Values.FirstOrDefault(jp => jp.Name.ToLowerInvariant() == jobIdentifier);
|
||||
}
|
||||
|
||||
w.ladderId = (ushort)element.GetAttributeInt("ladders", 0);
|
||||
|
||||
Reference in New Issue
Block a user