v0.10.5.1

This commit is contained in:
Juan Pablo Arce
2020-09-22 11:31:56 -03:00
parent 44032d0ae0
commit 0002ad2c50
343 changed files with 12276 additions and 5023 deletions
@@ -23,8 +23,9 @@ namespace Barotrauma
private readonly float screenColorRange, screenColorDuration;
private bool sparks, shockwave, flames, smoke, flash, underwaterBubble;
private float flashDuration;
private float? flashRange;
private bool applyFireEffects;
private readonly float flashDuration;
private readonly float? flashRange;
private readonly string decal;
private readonly float decalSize;
@@ -57,6 +58,8 @@ namespace Barotrauma
underwaterBubble = element.GetAttributeBool("underwaterbubble", true);
smoke = element.GetAttributeBool("smoke", true);
applyFireEffects = element.GetAttributeBool("applyfireeffects", flames);
flash = element.GetAttributeBool("flash", true);
flashDuration = element.GetAttributeFloat("flashduration", 0.05f);
if (element.Attribute("flashrange") != null) { flashRange = element.GetAttributeFloat("flashrange", 100.0f); }
@@ -64,7 +67,7 @@ namespace Barotrauma
EmpStrength = element.GetAttributeFloat("empstrength", 0.0f);
decal = element.GetAttributeString("decal", "");
decalSize = element.GetAttributeFloat("decalSize", 1.0f);
decalSize = element.GetAttributeFloat(1.0f, "decalSize", "decalsize");
cameraShake = element.GetAttributeFloat("camerashake", attack.Range * 0.1f);
cameraShakeRange = element.GetAttributeFloat("camerashakerange", attack.Range);
@@ -98,9 +101,13 @@ namespace Barotrauma
}
Hull hull = Hull.FindHull(worldPosition);
ExplodeProjSpecific(worldPosition, hull);
if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
{
hull.AddDecal(decal, worldPosition, decalSize, true);
}
float displayRange = attack.Range;
Vector2 cameraPos = Character.Controlled != null ? Character.Controlled.WorldPosition : GameMain.GameScreen.Cam.Position;
@@ -161,7 +168,7 @@ namespace Barotrauma
{
if (item.Condition <= 0.0f) { continue; }
if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; }
if (flames && !item.FireProof)
if (applyFireEffects && !item.FireProof)
{
//don't apply OnFire effects if the item is inside a fireproof container
//(or if it's inside a container that's inside a fireproof container, etc)
@@ -231,8 +238,11 @@ namespace Barotrauma
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
Dictionary<Limb, float> damages = new Dictionary<Limb, float>();
List<Affliction> modifiedAfflictions = new List<Affliction>();
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.IsSevered || limb.ignoreCollisions) { continue; }
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
//calculate distance from the "outer surface" of the physics body
@@ -244,19 +254,49 @@ namespace Barotrauma
float distFactor = 1.0f - dist / attack.Range;
//solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
//solid obstacles between the explosion and the limb reduce the effect of the explosion
var obstacles = Submarine.PickBodies(limb.SimPosition, explosionPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
foreach (var body in obstacles)
{
distFactor *= 0.1f;
if (body.UserData is Item item)
{
var door = item.GetComponent<Door>();
if (door != null && !door.IsBroken) { distFactor *= 0.01f; }
}
else if (body.UserData is Structure structure)
{
int sectionIndex = structure.FindSectionIndex(worldPosition, world: true, clamp: true);
if (structure.SectionBodyDisabled(sectionIndex))
{
continue;
}
else if (structure.SectionIsLeaking(sectionIndex))
{
distFactor *= 0.1f;
}
else
{
distFactor *= 0.01f;
}
}
else
{
distFactor *= 0.1f;
}
}
if (distFactor <= 0.05f) { continue; }
distFactors.Add(limb, distFactor);
List<Affliction> modifiedAfflictions = new List<Affliction>();
int limbCount = c.AnimController.Limbs.Count(l => !l.IsSevered && !l.ignoreCollisions);
modifiedAfflictions.Clear();
foreach (Affliction affliction in attack.Afflictions.Keys)
{
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / limbCount));
//previously the damage would be divided by the number of limbs (the intention was to prevent characters with more limbs taking more damage from explosions)
//that didn't work well on large characters like molochs and endworms: the explosions tend to only damage one or two of their limbs, and since the characters
//have lots of limbs, they tended to only take a fraction of the damage they should
//now we just divide by 10, which keeps the damage to normal-sized characters roughly the same as before and fixes the large characters
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / 10));
}
c.LastDamageSource = damageSource;
if (attacker == null)
@@ -273,7 +313,8 @@ namespace Barotrauma
//use a position slightly from the limb's position towards the explosion
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
Vector2 dir = worldPosition - limb.WorldPosition;
Vector2 hitPos = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
damages.Add(limb, attackResult.Damage);
@@ -29,9 +29,7 @@ namespace Barotrauma
protected bool removed;
#if CLIENT
private List<Decal> burnDecals = new List<Decal>();
#endif
private readonly List<Decal> burnDecals = new List<Decal>();
public Vector2 Position
{
@@ -136,10 +134,8 @@ namespace Barotrauma
- leftEdge;
fireSources[j].position.X = leftEdge;
#if CLIENT
fireSources[j].burnDecals.AddRange(fireSources[i].burnDecals);
fireSources[j].burnDecals.Sort((d1, d2) => { return Math.Sign(d1.WorldPosition.X - d2.WorldPosition.X); });
#endif
fireSources[i].Remove();
}
}
@@ -179,7 +175,33 @@ namespace Barotrauma
LimitSize();
if (size.X > 256.0f)
{
if (burnDecals.Count == 0)
{
var newDecal = hull.AddDecal("burnt", WorldPosition + size / 2, 1f, true);
if (newDecal != null) { burnDecals.Add(newDecal); }
}
else if (WorldPosition.X < burnDecals[0].WorldPosition.X - 256.0f)
{
var newDecal = hull.AddDecal("burnt", WorldPosition, 1f, true);
if (newDecal != null) { burnDecals.Insert(0, newDecal); }
}
else if (WorldPosition.X + size.X > burnDecals[burnDecals.Count - 1].WorldPosition.X + 256.0f)
{
var newDecal = hull.AddDecal("burnt", WorldPosition + Vector2.UnitX * size.X, 1f, true);
if (newDecal != null) { burnDecals.Add(newDecal); }
}
}
foreach (Decal d in burnDecals)
{
//prevent the decals from fading out as long as the firesource is alive
d.ForceRefreshFadeTimer(Math.Min(d.FadeTimer, d.FadeInTime));
}
UpdateProjSpecific(growModifier);
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
@@ -366,12 +388,11 @@ namespace Barotrauma
#if CLIENT
lightSource?.Remove();
lightSource = null;
#endif
foreach (Decal d in burnDecals)
{
d.StopFadeIn();
}
#endif
hull?.RemoveFire(this);
removed = true;
}
@@ -160,9 +160,15 @@ namespace Barotrauma
public override void Move(Vector2 amount)
{
if (!MathUtils.IsValid(amount))
{
DebugConsole.ThrowError($"Attempted to move a gap by an invalid amount ({amount})\n{Environment.StackTrace}");
return;
}
base.Move(amount);
if (!DisableHullRechecks) FindHulls();
if (!DisableHullRechecks) { FindHulls(); }
}
public static void UpdateHulls()
@@ -4,11 +4,100 @@ using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class BackgroundSection
{
public Rectangle Rect;
public int Index;
public int RowIndex;
private Vector4 colorVector4;
private Color color;
public readonly Vector2 Noise;
public readonly Color DirtColor;
public float ColorStrength
{
get;
protected set;
}
public Color Color
{
get { return color; }
protected set
{
color = value;
colorVector4 = new Vector4(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f);
}
}
public BackgroundSection(Rectangle rect, int index, int rowIndex)
{
Rect = rect;
Index = index;
ColorStrength = 0.0f;
RowIndex = rowIndex;
Noise = new Vector2(
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
}
public BackgroundSection(Rectangle rect, int index, float colorStrength, Color color, int rowIndex)
{
System.Diagnostics.Debug.Assert(rect.Width > 0 && rect.Height > 0);
Rect = rect;
Index = index;
ColorStrength = colorStrength;
Color = color;
RowIndex = rowIndex;
Noise = new Vector2(
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
}
public bool SetColor(Color color)
{
if (Color == color) { return false; }
Color = color;
return true;
}
public float SetColorStrength(float colorStrength)
{
if (ColorStrength == colorStrength) { return -1f; }
float previous = ColorStrength;
ColorStrength = colorStrength;
return previous;
}
public bool LerpColor(Color to, float amount)
{
if (Color == to) { return false; }
colorVector4 = Vector4.Lerp(colorVector4, to.ToVector4(), amount);
color = new Color(colorVector4);
return true;
}
public Color GetStrengthAdjustedColor()
{
return Color * ColorStrength;
}
}
partial class Hull : MapEntity, ISerializableEntity, IServerSerializable
{
public static List<Hull> hullList = new List<Hull>();
@@ -29,7 +118,11 @@ namespace Barotrauma
//how much excess water the room can contain, relative to the volume of the room.
//needed to make it possible for pressure to "push" water up through U-shaped hull configurations
public const float MaxCompress = 1.05f;
public const int BackgroundSectionSize = 16;
public const int BackgroundSectionsPerNetworkEvent = 16;
public readonly Dictionary<string, SerializableProperty> properties;
public Dictionary<string, SerializableProperty> SerializableProperties
{
@@ -54,6 +147,11 @@ namespace Barotrauma
private float[] leftDelta;
private float[] rightDelta;
public const int MaxDecalsPerHull = 10;
private readonly List<Decal> decals = new List<Decal>();
public readonly List<Gap> ConnectedGaps = new List<Gap>();
public override string Name
@@ -140,6 +238,8 @@ namespace Barotrauma
OxygenPercentage = prevOxygenPercentage;
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
Pressure = surface;
CreateBackgroundSections();
}
}
@@ -186,6 +286,8 @@ namespace Barotrauma
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
}
private float dirtiedVolume = 0.0f;
public float WaterVolume
{
get { return waterVolume; }
@@ -193,8 +295,25 @@ namespace Barotrauma
{
if (!MathUtils.IsValid(value)) return;
waterVolume = MathHelper.Clamp(value, 0.0f, Volume * MaxCompress);
if (waterVolume < Volume) Pressure = rect.Y - rect.Height + waterVolume / rect.Width;
if (waterVolume > 0.0f) update = true;
if (waterVolume < Volume) { Pressure = rect.Y - rect.Height + waterVolume / rect.Width; }
if (waterVolume > 0.0f)
{
update = true;
if (BackgroundSections != null)
{
float volumeMultiplier = Math.Clamp(waterVolume / Volume, 0f, 1f);
if (Math.Abs(volumeMultiplier - dirtiedVolume) > 0.075f)
{
RefreshSubmergedSections(new Rectangle(new Point(0, -rect.Height), new Point(rect.Width, (int)(rect.Height * volumeMultiplier))));
dirtiedVolume = volumeMultiplier;
}
}
}
else
{
submergedSections.Clear();
dirtiedVolume = 0.0f;
}
}
}
@@ -238,6 +357,36 @@ namespace Barotrauma
get { return waveVel; }
}
// sections of a decorative background that can be painted
public List<BackgroundSection> BackgroundSections
{
get;
private set;
}
private readonly HashSet<int> pendingSectionUpdates = new HashSet<int>();
private readonly List<BackgroundSection> submergedSections = new List<BackgroundSection>();
public int xBackgroundMax, yBackgroundMax;
public bool SupportsPaintedColors
{
get
{
return BackgroundSections != null;
}
}
private const int sectorWidth = 4;
private const int sectorHeight = 4;
private const float minColorStrength = 0.0f;
private const float maxColorStrength = 0.7f;
private bool networkUpdatePending;
private float networkUpdateTimer;
public List<FireSource> FireSources { get; private set; }
public Hull(MapEntityPrefab prefab, Rectangle rectangle)
@@ -250,6 +399,8 @@ namespace Barotrauma
: base (prefab, submarine)
{
rect = rectangle;
if (BackgroundSections == null) { CreateBackgroundSections(); }
OxygenPercentage = 100.0f;
@@ -284,6 +435,8 @@ namespace Barotrauma
Gap.UpdateHulls();
}
CreateBackgroundSections();
WaterVolume = 0.0f;
InsertToList();
@@ -399,6 +552,12 @@ namespace Barotrauma
public override void Move(Vector2 amount)
{
if (!MathUtils.IsValid(amount))
{
DebugConsole.ThrowError($"Attempted to move a hull by an invalid amount ({amount})\n{Environment.StackTrace}");
return;
}
rect.X += (int)amount.X;
rect.Y += (int)amount.Y;
@@ -471,6 +630,48 @@ namespace Barotrauma
FireSources.Add(fireSource);
}
public Decal AddDecal(UInt32 decalId, Vector2 worldPosition, float scale, bool isNetworkEvent)
{
//clients are only allowed to create decals when the server says so
if (!isNetworkEvent && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return null;
}
var decal = GameMain.DecalManager.Prefabs.Find(p => p.UIntIdentifier == decalId);
if (decal == null)
{
DebugConsole.ThrowError($"Could not find a decal prefab with the UInt identifier {decalId}!");
return null;
}
return AddDecal(decal.Name, worldPosition, scale, isNetworkEvent);
}
public Decal AddDecal(string decalName, Vector2 worldPosition, float scale, bool isNetworkEvent)
{
//clients are only allowed to create decals when the server says so
if (!isNetworkEvent && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return null;
}
if (decals.Count >= MaxDecalsPerHull) { return null; }
var decal = GameMain.DecalManager.CreateDecal(decalName, scale, worldPosition, this);
if (decal != null)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { false });
}
decals.Add(decal);
}
return decal;
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
@@ -480,6 +681,13 @@ namespace Barotrauma
FireSource.UpdateAll(FireSources, deltaTime);
foreach (Decal decal in decals)
{
decal.Update(deltaTime);
}
decals.RemoveAll(d => d.FadeTimer >= d.LifeTime || d.BaseAlpha <= 0.001f);
if (aiTarget != null)
{
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
@@ -596,6 +804,12 @@ namespace Barotrauma
}
}
//0.01 increase every ~1000 frames = reaches full dirtiness in ~27 minutes
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(1000) == 1)
{
DirtySections(submergedSections, 0.01f);
}
if (waterVolume < Volume)
{
LethalPressure -= 10.0f * deltaTime;
@@ -906,9 +1120,180 @@ namespace Barotrauma
return "RoomName.Sub" + roomPos.ToString();
}
#region BackgroundSections
private void CreateBackgroundSections()
{
int sectionWidth, sectionHeight;
sectionWidth = sectionHeight = BackgroundSectionSize;
xBackgroundMax = rect.Width / sectionWidth;
yBackgroundMax = rect.Height / sectionHeight;
BackgroundSections = new List<BackgroundSection>(xBackgroundMax * yBackgroundMax);
int sections = xBackgroundMax * yBackgroundMax;
float xSectors = xBackgroundMax / (float)sectorWidth;
for (int y = 0; y < yBackgroundMax; y++)
{
for (int x = 0; x < xBackgroundMax; x++)
{
int index = BackgroundSections.Count;
int sector = (int)Math.Floor(index / (float)sectorWidth - xSectors * y) + y / sectorHeight * (int)Math.Ceiling(xSectors);
BackgroundSections.Add(new BackgroundSection(new Rectangle(x * sectionWidth, y * -sectionHeight, sectionWidth, sectionHeight), index, y));
}
}
#if CLIENT
minimumPaintAmountToDraw = maxColorStrength / BackgroundSections.Count;
#endif
}
public static Hull GetCleanTarget(Vector2 worldPosition)
{
foreach (Hull hull in hullList)
{
Rectangle worldRect = hull.WorldRect;
if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) { continue; }
if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) { continue; }
return hull;
}
return null;
}
public BackgroundSection GetBackgroundSection(Vector2 worldPosition)
{
if (!SupportsPaintedColors) { return null; }
Vector2 subOffset = Submarine == null ? Vector2.Zero : Submarine.Position;
Vector2 relativePosition = new Vector2(worldPosition.X - subOffset.X - rect.X, worldPosition.Y - subOffset.Y - rect.Y);
int xIndex = (int)Math.Floor(relativePosition.X / BackgroundSectionSize);
if (xIndex < 0 || xIndex >= xBackgroundMax) { return null; }
int yIndex = (int)Math.Floor(-relativePosition.Y / BackgroundSectionSize);
if (yIndex < 0 || yIndex >= yBackgroundMax) { return null; }
return BackgroundSections[xIndex + yIndex * xBackgroundMax];
}
public IEnumerable<BackgroundSection> GetBackgroundSectionsViaContaining(Rectangle rectArea)
{
if (BackgroundSections == null || BackgroundSections.Count == 0)
{
yield break;
}
else
{
int xMin = Math.Max(rectArea.X / BackgroundSectionSize, 0);
if (xMin >= xBackgroundMax) { yield break; }
int xMax = Math.Min(rectArea.Right / BackgroundSectionSize, xBackgroundMax - 1);
if (xMax < 0) { yield break; }
int yMin = Math.Max(-rectArea.Bottom / BackgroundSectionSize, 0);
if (yMin >= yBackgroundMax) { yield break; }
int yMax = Math.Min(-rectArea.Y / BackgroundSectionSize, yBackgroundMax - 1);
if (yMax < 0) { yield break; }
for (int x = xMin; x <= xMax; x++)
{
for (int y = yMin; y <= yMax; y++)
{
yield return BackgroundSections[x + y * xBackgroundMax];
}
}
}
}
public void RefreshSubmergedSections(Rectangle waterArea)
{
if (BackgroundSections == null) { return; }
submergedSections.Clear();
foreach (var section in GetBackgroundSectionsViaContaining(waterArea))
{
submergedSections.Add(section);
}
}
public bool DoesSectionMatch(int index, int row)
{
return index >= 0 && row >= 0 && BackgroundSections.Count > index && BackgroundSections[index] != null && BackgroundSections[index].RowIndex == row;
}
public void SetSectionColorOrStrength(BackgroundSection section, Color? color, float? strength, bool requiresUpdate, bool isCleaning)
{
bool sectionUpdated = isCleaning;
if (color != null)
{
if (section.Color != color.Value && strength.HasValue)
{
//already painted with a different color -> interpolate towards the new one
//an ad-hoc formula that makes the color changes faster when the current strength is low
//(-> a barely dirty wall gets recolored almost immediately, while a more heavily colored one takes a while)
float changeSpeed = strength.Value / Math.Max(section.ColorStrength * section.ColorStrength, 0.001f) * 0.1f;
if (section.LerpColor(color.Value, changeSpeed)) { sectionUpdated = true; }
}
else
{
if (section.SetColor(color.Value)) { sectionUpdated = true; }
}
}
if (strength != null)
{
float previous = section.SetColorStrength(Math.Max(minColorStrength, Math.Min(maxColorStrength, section.ColorStrength + strength.Value)));
if (previous != -1f)
{
#if CLIENT
paintAmount = Math.Max(0, paintAmount + (section.ColorStrength - previous) / BackgroundSections.Count);
#endif
sectionUpdated = true;
}
}
if (sectionUpdated && GameMain.NetworkMember != null && requiresUpdate)
{
networkUpdatePending = true;
pendingSectionUpdates.Add((int)Math.Floor(section.Index / (float)BackgroundSectionsPerNetworkEvent));
#if CLIENT
serverUpdateDelay = 0.5f;
#endif
}
}
public void DirtySections(List<BackgroundSection> sections, float dirtyVal)
{
if (sections == null) { return; }
for (int i = 0; i < sections.Count; i++)
{
float sectionDirtyVal = dirtyVal;
SetSectionColorOrStrength(sections[i], sections[i].DirtColor, sectionDirtyVal, false, false);
}
}
public void CleanSection(BackgroundSection section, float cleanVal, bool updateRequired)
{
bool decalsCleaned = false;
for (int i = 0; i < decals.Count; i++)
{
Decal decal = decals[i];
if (decal.AffectsSection(section))
{
decal.Clean(cleanVal);
decalsCleaned = true;
}
}
if (section.ColorStrength == 0 && !decalsCleaned) { return; }
SetSectionColorOrStrength(section, null, cleanVal, updateRequired, true);
}
#endregion
public static Hull Load(XElement element, Submarine submarine)
{
Rectangle rect = Rectangle.Empty;
Rectangle rect;
if (element.Attribute("rect") != null)
{
rect = element.GetAttributeRect("rect", Rectangle.Empty);
@@ -947,6 +1332,41 @@ namespace Barotrauma
hull.OriginalAmbientLight = XMLExtensions.ParseColor(originalAmbientLight, false);
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "decal":
string id = subElement.GetAttributeString("id", "");
Vector2 pos = subElement.GetAttributeVector2("pos", Vector2.Zero);
float scale = subElement.GetAttributeFloat("scale", 1.0f);
float timer = subElement.GetAttributeFloat("timer", 1.0f);
var decal = hull.AddDecal(id, pos + hull.WorldRect.Location.ToVector2(), scale, true);
if (decal != null)
{
decal.FadeTimer = timer;
}
break;
}
}
string backgroundSectionStr = element.GetAttributeString("backgroundsections", "");
if (!string.IsNullOrEmpty(backgroundSectionStr))
{
string[] backgroundSectionStrSplit = backgroundSectionStr.Split(';');
foreach (string str in backgroundSectionStrSplit)
{
string[] backgroundSectionData = str.Split(':');
if (backgroundSectionData.Length != 3) { continue; }
Color color = XMLExtensions.ParseColor(backgroundSectionData[1]);
if (int.TryParse(backgroundSectionData[0], out int index) &&
float.TryParse(backgroundSectionData[2], NumberStyles.Any, CultureInfo.InvariantCulture, out float strength))
{
hull.SetSectionColorOrStrength(hull.BackgroundSections[index], color, strength, false, false);
}
}
}
SerializableProperty.DeserializeProperties(hull, element);
if (element.Attribute("oxygen") == null) { hull.Oxygen = hull.Volume; }
@@ -976,7 +1396,7 @@ namespace Barotrauma
if (linkedTo != null && linkedTo.Count > 0)
{
var saveableLinked = linkedTo.Where(l => l.ShouldBeSaved && !l.Removed).ToList();
var saveableLinked = linkedTo.Where(l => l.ShouldBeSaved && (l.Removed == Removed)).ToList();
element.Add(new XAttribute("linked", string.Join(",", saveableLinked.Select(l => l.ID.ToString()))));
}
@@ -985,6 +1405,25 @@ namespace Barotrauma
element.Add(new XAttribute("originalambientlight", XMLExtensions.ColorToString(OriginalAmbientLight.Value)));
}
if (BackgroundSections != null && BackgroundSections.Count > 0)
{
element.Add(
new XAttribute(
"backgroundsections",
string.Join(';', BackgroundSections.Where(b => b.ColorStrength > 0.01f).Select(b => b.Index + ":" + XMLExtensions.ColorToString(b.Color) + ":" + b.ColorStrength.ToString("G", CultureInfo.InvariantCulture)))));
}
foreach (Decal decal in decals)
{
element.Add(
new XElement("decal",
new XAttribute("id", decal.Prefab.Identifier),
new XAttribute("pos", XMLExtensions.Vector2ToString(decal.NonClampedPosition)),
new XAttribute("scale", decal.Scale.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("timer", decal.FadeTimer.ToString("G", CultureInfo.InvariantCulture))
));
}
SerializableProperty.SerializeProperties(this, element);
parentElement.Add(element);
return element;
@@ -42,9 +42,15 @@ namespace Barotrauma
XDocument doc = XMLExtensions.TryLoadXml(filePath);
if (doc == null) { return; }
originalName = doc.Root.GetAttributeString("name", "");
identifier = doc.Root.GetAttributeString("identifier", null) ?? originalName.ToLowerInvariant().Replace(" ", "");
configElement = doc.Root;
XElement element = doc.Root;
if (element.IsOverride())
{
element = element.Elements().First();
}
originalName = element.GetAttributeString("name", "");
identifier = element.GetAttributeString("identifier", null) ?? originalName.ToLowerInvariant().Replace(" ", "");
configElement = element;
Category = MapEntityCategory.ItemAssembly;
@@ -54,7 +60,7 @@ namespace Barotrauma
Description = TextManager.Get("EntityDescription." + identifier, returnNull: true) ?? Description;
List<ushort> containedItemIDs = new List<ushort>();
foreach (XElement entityElement in doc.Root.Elements())
foreach (XElement entityElement in element.Elements())
{
var containerElement = entityElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("itemcontainer", StringComparison.OrdinalIgnoreCase));
if (containerElement == null) { continue; }
@@ -66,7 +72,7 @@ namespace Barotrauma
int minX = int.MaxValue, minY = int.MaxValue;
int maxX = int.MinValue, maxY = int.MinValue;
DisplayEntities = new List<Pair<MapEntityPrefab, Rectangle>>();
foreach (XElement entityElement in doc.Root.Elements())
foreach (XElement entityElement in element.Elements())
{
ushort id = (ushort)entityElement.GetAttributeInt("ID", 0);
if (id > 0 && containedItemIDs.Contains(id)) { continue; }
@@ -94,7 +100,7 @@ namespace Barotrauma
new Rectangle(0, 0, 1, 1) :
new Rectangle(minX, minY, maxX - minX, maxY - minY);
Prefabs.Add(this, false);
Prefabs.Add(this, doc.Root.IsOverride());
}
public static void Remove(string filePath)
@@ -110,17 +116,17 @@ namespace Barotrauma
public List<MapEntity> CreateInstance(Vector2 position, Submarine sub, bool selectPrefabs = false)
{
List<MapEntity> entities = MapEntity.LoadAll(sub, configElement, FilePath);
if (entities.Count == 0) return entities;
if (entities.Count == 0) { return entities; }
Vector2 offset = sub == null ? Vector2.Zero : sub.HiddenSubPosition;
foreach (MapEntity me in entities)
{
me.Move(position);
Item item = me as Item;
if (item == null) continue;
me.Submarine = sub;
if (!(me is Item item)) { continue; }
Wire wire = item.GetComponent<Wire>();
if (wire != null) wire.MoveNodes(position - offset);
if (wire != null) { wire.MoveNodes(position - offset); }
}
MapEntity.MapLoaded(entities, true);
@@ -171,7 +177,7 @@ namespace Barotrauma
}
//find assembly files in selected content packages
foreach (ContentPackage cp in GameMain.Config.SelectedContentPackages)
foreach (ContentPackage cp in GameMain.Config.AllEnabledPackages)
{
foreach (string filePath in cp.GetFilesOfType(ContentType.ItemAssembly))
{
@@ -445,8 +445,11 @@ namespace Barotrauma
if (mainPathCellCount > tunnel.Count / 2) continue;
var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders);
PositionsOfInterest.Add(new InterestingPosition(tunnel.Last(), PositionType.Cave));
if (tunnel.Count > 4) PositionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count / 2], PositionType.Cave));
if (newPathCells.Any())
{
PositionsOfInterest.Add(new InterestingPosition(newPathCells.Last().Center.ToPoint(), PositionType.Cave));
if (newPathCells.Count > 4) { PositionsOfInterest.Add(new InterestingPosition(newPathCells[newPathCells.Count / 2].Center.ToPoint(), PositionType.Cave)); }
}
validTunnels.Add(tunnel);
pathCells.AddRange(newPathCells);
}
@@ -574,7 +577,7 @@ namespace Barotrauma
Ruins = new List<Ruin>();
for (int i = 0; i < GenerationParams.RuinCount; i++)
{
GenerateRuin(mainPath, this, mirror);
GenerateRuin(mainPath, mirror);
}
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
@@ -1054,7 +1057,7 @@ namespace Barotrauma
return tunnelNodes;
}
private void GenerateRuin(List<VoronoiCell> mainPath, Level level, bool mirror)
private void GenerateRuin(List<VoronoiCell> mainPath, bool mirror)
{
var ruinGenerationParams = RuinGenerationParams.GetRandom();
@@ -1075,14 +1078,19 @@ namespace Barotrauma
ruinPos.Y = Math.Min(ruinPos.Y, borders.Y + borders.Height - ruinSize.Y / 2);
ruinPos.Y = Math.Max(ruinPos.Y, SeaFloorTopPos + ruinSize.Y / 2);
double minDist = ruinRadius * 2;
double minDistSqr = minDist * minDist;
double minMainPathDist = ruinRadius * 2;
double minMainPathDistSqr = minMainPathDist * minMainPathDist;
double minOutpostDist = Math.Min(Math.Min(10000.0f, Size.X / 3), Size.Y / 3);
double minOutpostDistSqr = minOutpostDist * minOutpostDist;
int iter = 0;
while (mainPath.Any(p => MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, p.Site.Coord.X, p.Site.Coord.Y) < minDistSqr) ||
while (mainPath.Any(p => MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, p.Site.Coord.X, p.Site.Coord.Y) < minMainPathDistSqr) ||
Ruins.Any(r => r.Area.Intersects(new Rectangle(ruinPos - new Point(ruinSize.X / 2, ruinSize.Y / 2), ruinSize)) ||
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, StartPosition.X, StartPosition.Y) < minDistSqr ||
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, EndPosition.X, EndPosition.Y) < minDistSqr))
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, StartPosition.X, StartPosition.Y) < minOutpostDistSqr ||
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, StartPosition.X, Size.Y) < minOutpostDistSqr ||
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, EndPosition.X, EndPosition.Y) < minOutpostDistSqr) ||
MathUtils.DistanceSquared(ruinPos.X, ruinPos.Y, EndPosition.X, Size.Y) < minOutpostDistSqr)
{
double weighedPathPosX = ruinPos.X;
double weighedPathPosY = ruinPos.Y;
@@ -1094,11 +1102,11 @@ namespace Barotrauma
double diffY = i == 0 ? ruinPos.Y - StartPosition.Y : ruinPos.Y - StartPosition.Y;
double distSqr = diffX * diffX + diffY * diffY;
if (distSqr < minDistSqr)
if (distSqr < minMainPathDistSqr)
{
double dist = Math.Sqrt(distSqr);
double moveAmountX = minDist * diffX / dist;
double moveAmountY = minDist * diffY / dist;
double moveAmountX = minMainPathDist * diffX / dist;
double moveAmountY = minMainPathDist * diffY / dist;
weighedPathPosX += moveAmountX;
weighedPathPosY += moveAmountY;
weighedPathPosY = Math.Min(borders.Y + borders.Height - ruinSize.Y / 2, weighedPathPosY);
@@ -1315,7 +1323,7 @@ namespace Barotrauma
{
holdable.AttachToWall();
#if CLIENT
item.SpriteRotation = -MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2;
item.Rotation = MathHelper.ToDegrees(-MathUtils.VectorToAngle(edgeNormal) + MathHelper.PiOver2);
#endif
}
}
@@ -1337,7 +1345,11 @@ namespace Barotrauma
{
Loaded.TryGetInterestingPosition(true, spawnPosType, minDistFromSubs, out Vector2 startPos);
startPos += Rand.Vector(Rand.Range(0.0f, randomSpread, Rand.RandSync.Server), Rand.RandSync.Server);
Vector2 offset = Rand.Vector(Rand.Range(0.0f, randomSpread, Rand.RandSync.Server), Rand.RandSync.Server);
if (!cells.Any(c => c.IsPointInside(startPos + offset)))
{
startPos += offset;
}
Vector2 endPos = startPos - Vector2.UnitY * Size.Y;
@@ -1533,7 +1545,7 @@ namespace Barotrauma
var totalSW = new Stopwatch();
var tempSW = new Stopwatch();
totalSW.Start();
var wreckFiles = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Wreck).ToList();
var wreckFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Wreck).ToList();
if (wreckFiles.None())
{
DebugConsole.ThrowError("No wreck files found in the selected content packages!");
@@ -1827,7 +1839,7 @@ namespace Barotrauma
private void CreateOutposts()
{
var outpostFiles = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Outpost).ToList();
var outpostFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Outpost).ToList();
if (!outpostFiles.Any() && !OutpostGenerationParams.Params.Any() && LevelData.ForceOutpostGenerationParams == null)
{
DebugConsole.ThrowError("No outpost files found in the selected content packages");
@@ -2080,7 +2092,7 @@ namespace Barotrauma
job ??= selectedPrefab.GetJobPrefab();
if (job == null) { continue; }
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job);
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.TeamID = Character.TeamType.None;
@@ -2128,6 +2140,11 @@ namespace Barotrauma
StartLocation = newStartLocation;
}
public void DebugSetEndLocation(Location newEndLocation)
{
EndLocation = newEndLocation;
}
public override void Remove()
{
base.Remove();
@@ -31,6 +31,7 @@ namespace Barotrauma
public readonly Point Size;
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
public LevelData(string seed, float difficulty, float sizeFactor, LevelGenerationParams generationParams, Biome biome)
{
@@ -77,6 +78,10 @@ namespace Barotrauma
string[] prefabNames = element.GetAttributeStringArray("eventhistory", new string[] { });
EventHistory.AddRange(EventSet.PrefabList.Where(p => prefabNames.Any(n => p.Identifier.Equals(n, StringComparison.InvariantCultureIgnoreCase))));
string[] nonRepeatablePrefabNames = element.GetAttributeStringArray("nonrepeatableevents", new string[] { });
NonRepeatableEvents.AddRange(EventSet.PrefabList.Where(p => prefabNames.Any(n => p.Identifier.Equals(n, StringComparison.InvariantCultureIgnoreCase))));
}
@@ -153,11 +158,17 @@ namespace Barotrauma
new XAttribute("size", XMLExtensions.PointToString(Size)),
new XAttribute("generationparams", GenerationParams.Identifier));
if (Type == LevelType.Outpost && EventHistory.Any())
if (Type == LevelType.Outpost)
{
newElement.Add(new XAttribute("eventhistory", string.Join(',', EventHistory.Select(p => p.Identifier))));
if (EventHistory.Any())
{
newElement.Add(new XAttribute("eventhistory", string.Join(',', EventHistory.Select(p => p.Identifier))));
}
if (NonRepeatableEvents.Any())
{
newElement.Add(new XAttribute("nonrepeatableevents", string.Join(',', NonRepeatableEvents.Select(p => p.Identifier))));
}
}
parentElement.Add(newElement);
}
}
@@ -40,6 +40,7 @@ namespace Barotrauma
private string filePath;
private bool loadSub;
public bool LoadSub => loadSub;
private Submarine sub;
private ushort originalMyPortID;
@@ -47,6 +48,7 @@ namespace Barotrauma
//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;
public ushort OriginalLinkedToID => originalLinkedToID;
private DockingPort originalLinkedPort;
private bool purchasedLostShuttles;
@@ -160,6 +162,7 @@ namespace Barotrauma
wallVertices = MathUtils.GiftWrap(points);
}
// LinkedSubmarine.Load() is called from MapEntity.LoadAll()
public static LinkedSubmarine Load(XElement element, Submarine submarine)
{
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
@@ -172,17 +175,16 @@ namespace Barotrauma
}
else
{
string levelSeed = element.GetAttributeString("location", "");
LevelData levelData = GameMain.GameSession.Campaign?.NextLevel ?? GameMain.GameSession.Level?.LevelData;
linkedSub = new LinkedSubmarine(submarine)
{
purchasedLostShuttles = GameMain.GameSession.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles,
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 &&
!linkedSub.purchasedLostShuttles)
if (!string.IsNullOrWhiteSpace(levelSeed) && levelData != null &&
levelData.Seed != levelSeed && !linkedSub.purchasedLostShuttles)
{
linkedSub.loadSub = false;
}
@@ -217,12 +219,17 @@ namespace Barotrauma
}
}
public override void OnMapLoaded()
{
if (!loadSub) { return; }
SubmarineInfo info = new SubmarineInfo(Submarine.Info.FilePath, "", saveElement);
if (!info.SubmarineElement.HasElements)
{
DebugConsole.ThrowError("Failed to load a linked submarine (empty XML element). The save file may be corrupted.");
return;
}
sub = Submarine.Load(info, false);
Vector2 worldPos = saveElement.GetAttributeVector2("worldpos", Vector2.Zero);
@@ -294,9 +301,9 @@ namespace Barotrauma
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));
Vector2 offset = myPort.IsHorizontal ?
Vector2.UnitX * myPort.GetDir(linkedPort) :
Vector2.UnitY * myPort.GetDir(linkedPort);
offset *= myPort.DockedDistance;
sub.SetPosition((linkedPort.Item.WorldPosition - portDiff) - offset);
@@ -115,7 +115,7 @@ namespace Barotrauma
{
get
{
availableMissions.RemoveAll(m => m.Completed);
availableMissions.RemoveAll(m => m.Completed || m.Failed);
return availableMissions;
}
}
@@ -6,6 +6,7 @@ using System.Globalization;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -207,6 +208,16 @@ namespace Barotrauma
{
List.Clear();
var locationTypeFiles = GameMain.Instance.GetFilesOfType(ContentType.LocationTypes);
if (!locationTypeFiles.Any())
{
DebugConsole.ThrowError("No location types configured in any of the selected content packages. Attempting to load from the vanilla content package...");
locationTypeFiles = ContentPackage.GetFilesOfType(GameMain.VanillaContent.ToEnumerable(), ContentType.LocationTypes);
if (!locationTypeFiles.Any())
{
throw new Exception("No location types configured in any of the selected content packages. Please try uninstalling mods or reinstalling the game.");
}
}
foreach (ContentFile file in locationTypeFiles)
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
@@ -8,7 +8,9 @@ using Voronoi2;
namespace Barotrauma
{
partial class Map
{
{
public bool AllowDebugTeleport;
private readonly MapGenerationParams generationParams;
private Location furthestDiscoveredLocation;
@@ -112,7 +114,15 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError("Error while loading the map (start location index out of bounds).");
DebugConsole.AddWarning($"Error while loading the map. Start location index out of bounds (index: {startLocationindex}, location count: {Locations.Count}).");
foreach (Location location in Locations)
{
if (!location.Type.HasOutpost) { continue; }
if (StartLocation == null || location.MapPosition.X < StartLocation.MapPosition.X)
{
StartLocation = location;
}
}
}
int endLocationindex = element.GetAttributeInt("endlocation", -1);
if (endLocationindex > 0 && endLocationindex < Locations.Count)
@@ -121,7 +131,7 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError("Error while loading the map (end location index out of bounds).");
DebugConsole.AddWarning($"Error while loading the map. End location index out of bounds (index: {endLocationindex}, location count: {Locations.Count}).");
foreach (Location location in Locations)
{
if (EndLocation == null || location.MapPosition.X > EndLocation.MapPosition.X)
@@ -166,6 +176,7 @@ namespace Barotrauma
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
}
}
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
CurrentLocation.CreateStore();
CurrentLocation.Discovered = true;
@@ -640,7 +651,24 @@ namespace Barotrauma
}
}
public void ProgressWorld()
public void ProgressWorld(CampaignMode.TransitionType transitionType, float roundDuration)
{
//one step per 10 minutes of play time
int steps = (int)Math.Floor(roundDuration / (60.0f * 10.0f));
if (transitionType == CampaignMode.TransitionType.ProgressToNextLocation ||
transitionType == CampaignMode.TransitionType.ProgressToNextEmptyLocation)
{
//at least one step when progressing to the next location, regardless of how long the round took
steps = Math.Max(1, steps);
}
steps = Math.Min(steps, 5);
for (int i = 0; i < steps; i++)
{
ProgressWorld();
}
}
private void ProgressWorld()
{
foreach (Location location in Locations)
{
@@ -651,6 +679,8 @@ namespace Barotrauma
furthestDiscoveredLocation = location;
}
if (location == CurrentLocation || location == SelectedLocation) { continue; }
//find which types of locations this one can change to
List<LocationTypeChange> allowedTypeChanges = new List<LocationTypeChange>();
List<LocationTypeChange> readyTypeChanges = new List<LocationTypeChange>();
@@ -844,6 +874,15 @@ namespace Barotrauma
{
SelectLocation(Connections[currentLocationConnection].OtherLocation(CurrentLocation));
}
else
{
//this should not be possible, you can't enter non-outpost locations (= natural formations)
if (CurrentLocation != null && !CurrentLocation.Type.HasOutpost && SelectedConnection == null)
{
DebugConsole.AddWarning($"Error while loading campaign map state. Submarine in a location with no outpost ({CurrentLocation.Name}). Loading the first adjacent connection...");
SelectLocation(CurrentLocation.Connections[0].OtherLocation(CurrentLocation));
}
}
}
public void Save(XElement element)
@@ -128,7 +128,7 @@ namespace Barotrauma
public static void Init()
{
var files = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.MapGenerationParameters);
var files = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.MapGenerationParameters);
if (!files.Any())
{
DebugConsole.ThrowError("No map generation parameters found in the selected content packages!");
@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
@@ -3,7 +3,6 @@ using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Barotrauma
@@ -68,7 +67,7 @@ namespace Barotrauma
private static Submarine Generate(OutpostGenerationParams generationParams, LocationType locationType, Location location, bool onlyEntrance = false)
{
var outpostModuleFiles = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.OutpostModule);
var outpostModuleFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.OutpostModule);
//load the infos of the outpost module files
List<SubmarineInfo> outpostModules = new List<SubmarineInfo>();
@@ -110,7 +109,7 @@ namespace Barotrauma
selectedModules.Clear();
//select which module types the outpost should consist of
var pendingModuleFlags = onlyEntrance ? new List<string>() : SelectModules(outpostModules, generationParams);
var pendingModuleFlags = onlyEntrance ? new List<string>() { generationParams.ModuleCounts.First().Key } : SelectModules(outpostModules, generationParams);
foreach (string flag in pendingModuleFlags.Distinct().ToList())
{
if (flag.Equals("none", StringComparison.OrdinalIgnoreCase)) { continue; }
@@ -132,13 +131,11 @@ namespace Barotrauma
}
}
//the first airlock is forced to spawn manually, remove it from the list of pending modules
if (pendingModuleFlags.Contains("airlock"))
{
pendingModuleFlags.Remove("airlock");
}
//the first module is spawned separately, remove it from the list of pending modules
string initialModuleFlag = pendingModuleFlags.FirstOrDefault() ?? "airlock";
pendingModuleFlags.Remove(initialModuleFlag);
var initialModule = GetRandomModule(outpostModules, "airlock", locationType);
var initialModule = GetRandomModule(outpostModules, initialModuleFlag, locationType);
if (initialModule == null)
{
throw new Exception("Failed to generate an outpost (no airlock modules found).");
@@ -155,7 +152,7 @@ namespace Barotrauma
}
selectedModules.Add(new PlacedModule(initialModule, null, OutpostModuleInfo.GapPosition.None));
selectedModules.Last().FulfilledModuleTypes.Add("airlock");
selectedModules.Last().FulfilledModuleTypes.Add(initialModuleFlag);
AppendToModule(selectedModules.Last(), outpostModules.ToList(), pendingModuleFlags, selectedModules, locationType);
if (pendingModuleFlags.Any(flag => !flag.Equals("none", StringComparison.OrdinalIgnoreCase)))
{
@@ -202,7 +199,7 @@ namespace Barotrauma
DebugConsole.NewMessage("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead...");
var outpostFiles = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Outpost);
var outpostFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Outpost);
if (!outpostFiles.Any())
{
throw new Exception("Failed to generate an outpost. Could not generate an outpost from the available outpost modules and there are no pre-built outposts available.");
@@ -354,6 +351,9 @@ namespace Barotrauma
int totalModuleCount = generationParams.TotalModuleCount;
var pendingModuleFlags = new List<string>();
bool availableModulesFound = true;
string initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Key;
pendingModuleFlags.Add(initialModuleFlag);
while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound)
{
availableModulesFound = false;
@@ -378,8 +378,13 @@ namespace Barotrauma
//don't place "none" modules at the end because
// a. "filler rooms" at the end of a hallway are pointless
// b. placing the unnecessary filler rooms first give more options for the placement of the more important modules
pendingModuleFlags.Insert(Rand.Int(pendingModuleFlags.Count - 1, Rand.RandSync.Server),"none");
pendingModuleFlags.Insert(Rand.Int(pendingModuleFlags.Count - 1, Rand.RandSync.Server), "none");
}
//make sure the initial module is inserted first
pendingModuleFlags.Remove(initialModuleFlag);
pendingModuleFlags.Insert(0, initialModuleFlag);
return pendingModuleFlags;
}
@@ -615,7 +620,7 @@ namespace Barotrauma
Vector2 moveDir = GetMoveDir(module.ThisGapPosition);
Vector2 moveStep = moveDir * 50.0f;
Vector2 currentMove = Vector2.Zero;
float maxMoveAmount = 1500.0f;
float maxMoveAmount = 2000.0f;
List<PlacedModule> subsequentModules2 = new List<PlacedModule>();
GetSubsequentModules(module, movableModules, ref subsequentModules2);
@@ -1342,7 +1347,7 @@ namespace Barotrauma
Dictionary<HumanPrefab, CharacterInfo> selectedCharacters = new Dictionary<HumanPrefab, CharacterInfo>();
foreach (HumanPrefab humanPrefab in outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.Server))
{
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server));
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
if (location != null && location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
{
killedCharacters.Add(humanPrefab);
@@ -1357,7 +1362,7 @@ namespace Barotrauma
int tries = 0;
while (tries < 100)
{
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: killedCharacter.GetJobPrefab(Rand.RandSync.Server));
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: killedCharacter.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
if (!location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
{
selectedCharacters.Add(killedCharacter, characterInfo);
@@ -19,7 +19,7 @@ namespace Barotrauma
public Rectangle rect;
public float damage;
public Gap gap;
public WallSection(Rectangle rect)
{
System.Diagnostics.Debug.Assert(rect.Width > 0 && rect.Height > 0);
@@ -58,7 +58,7 @@ namespace Barotrauma
{
get;
private set;
}
}
public override Sprite Sprite
{
@@ -215,6 +215,7 @@ namespace Barotrauma
set { textureOffset = value; }
}
private Rectangle defaultRect;
/// <summary>
/// Unscaled rect
@@ -309,6 +310,12 @@ namespace Barotrauma
public override void Move(Vector2 amount)
{
if (!MathUtils.IsValid(amount))
{
DebugConsole.ThrowError($"Attempted to move a structure by an invalid amount ({amount})\n{Environment.StackTrace}");
return;
}
base.Move(amount);
for (int i = 0; i < Sections.Length; i++)
@@ -397,7 +404,7 @@ namespace Barotrauma
if (StairDirection != Direction.None)
{
CreateStairBodies();
}
}
}
}
@@ -473,7 +480,7 @@ namespace Barotrauma
{
int xsections = 1, ysections = 1;
int width = rect.Width, height = rect.Height;
if (!HasBody)
{
if (FlippedX && IsHorizontal)
@@ -623,11 +630,11 @@ namespace Barotrauma
if (StairDirection == Direction.Left)
{
return MathUtils.LineToPointDistance(new Vector2(WorldRect.X, WorldRect.Y), new Vector2(WorldRect.Right, WorldRect.Y - WorldRect.Height), position) < 40.0f;
return MathUtils.LineToPointDistanceSquared(new Vector2(WorldRect.X, WorldRect.Y), new Vector2(WorldRect.Right, WorldRect.Y - WorldRect.Height), position) < 1600.0f;
}
else
{
return MathUtils.LineToPointDistance(new Vector2(WorldRect.X, WorldRect.Y - rect.Height), new Vector2(WorldRect.Right, WorldRect.Y), position) < 40.0f;
return MathUtils.LineToPointDistanceSquared(new Vector2(WorldRect.X, WorldRect.Y - rect.Height), new Vector2(WorldRect.Right, WorldRect.Y), position) < 1600.0f;
}
}
}
@@ -726,7 +733,7 @@ namespace Barotrauma
return Sections[sectionIndex];
}
public bool SectionBodyDisabled(int sectionIndex)
{
if (sectionIndex < 0 || sectionIndex >= Sections.Length) return false;
@@ -289,7 +289,6 @@ namespace Barotrauma
public WreckAI WreckAI { get; private set; }
public bool CreateWreckAI()
{
MakeWreck();
WreckAI = new WreckAI(this);
return WreckAI != null;
}
@@ -946,9 +945,29 @@ namespace Barotrauma
{
if (item.Submarine != this) { continue; }
var steering = item.GetComponent<Steering>();
if (steering == null) { continue; }
if (steering == null || item.Connections == null) { continue; }
//find all the engines and pumps the nav terminal is connected to
List<Item> connectedItems = new List<Item>();
foreach (Connection c in item.Connections)
{
if (c.IsPower) { continue; }
connectedItems.AddRange(item.GetConnectedComponentsRecursive<Engine>(c).Select(engine => engine.Item));
connectedItems.AddRange(item.GetConnectedComponentsRecursive<Pump>(c).Select(pump => pump.Item));
}
//if more than 50% of the connected engines/pumps are in another sub,
//assume this terminal is used to remotely control something and don't automatically enable autopilot
if (connectedItems.Count(it => it.Submarine != item.Submarine) > connectedItems.Count / 2)
{
continue;
}
steering.MaintainPos = true;
steering.AutoPilot = true;
#if SERVER
steering.UnsentChanges = true;
#endif
}
}
@@ -1369,8 +1388,17 @@ namespace Barotrauma
foreach (MapEntity e in MapEntity.mapEntityList.OrderBy(e => e.ID))
{
if (e.Submarine != this || !e.ShouldBeSaved) continue;
if (e is Item item && item.FindParentInventory(inv => inv is CharacterInventory) != null) continue;
if (!e.ShouldBeSaved) { continue; }
if (e is Item item)
{
if (item.FindParentInventory(inv => inv is CharacterInventory) != null) { continue; }
if (e.Submarine != this && item.GetRootContainer()?.Submarine != this) { continue; }
}
else
{
if (e.Submarine != this) { continue; }
}
e.Save(element);
}
@@ -109,7 +109,7 @@ namespace Barotrauma
this.submarine = sub;
Body farseerBody = null;
if (!Hull.hullList.Any())
if (!Hull.hullList.Any(h => h.Submarine == sub))
{
farseerBody = GameMain.World.CreateRectangle(1.0f, 1.0f, 1.0f);
if (showWarningMessages)
@@ -2,7 +2,11 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
#if DEBUG
using System.IO;
#else
using Barotrauma.IO;
#endif
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -149,7 +153,7 @@ namespace Barotrauma
get
{
if (requiredContentPackagesInstalled.HasValue) { return requiredContentPackagesInstalled.Value; }
return RequiredContentPackages.All(cp => GameMain.SelectedPackages.Any(cp2 => cp2.Name == cp));
return RequiredContentPackages.All(cp => GameMain.Config.AllEnabledPackages.Any(cp2 => cp2.Name == cp));
}
set
{
@@ -512,7 +516,7 @@ namespace Barotrauma
public static void RefreshSavedSubs()
{
var contentPackageSubs = ContentPackage.GetFilesOfType(
GameMain.Config.SelectedContentPackages,
GameMain.Config.AllEnabledPackages,
ContentType.Submarine, ContentType.Outpost, ContentType.OutpostModule, ContentType.Wreck);
for (int i = savedSubmarines.Count - 1; i >= 0; i--)
@@ -707,7 +707,7 @@ namespace Barotrauma
int i = 0;
foreach (MapEntity e in linkedTo)
{
if (!e.ShouldBeSaved || e.Removed) { continue; }
if (!e.ShouldBeSaved || (e.Removed != Removed)) { continue; }
if (e.Submarine?.Info.Type != Submarine?.Info.Type) { continue; }
element.Add(new XAttribute("linkedto" + i, e.ID));
i += 1;