This commit is contained in:
Evil Factory
2022-02-24 14:30:39 -03:00
364 changed files with 10838 additions and 3966 deletions
@@ -18,8 +18,8 @@ namespace Barotrauma.MapCreatures.Behavior
public readonly BallastFloraBehavior? ParentBallastFlora;
public int ID = -1;
public ushort ClaimedItem;
public bool HasClaimedItem;
public Item ClaimedItem;
public int ClaimedItemId = -1;
public float MaxHealth = 100f;
public float Health = 100f;
@@ -271,10 +271,29 @@ namespace Barotrauma.MapCreatures.Behavior
{
ClaimTarget(item, Branches.FirstOrDefault(b => b.ID == branchid), true);
}
else
{
string errorMsg = $"Error in BallastFloraBehavior.OnMapLoaded: could not find the item claimed by the ballast flora.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.OnMapLoaded:ClaimedItemNotFound", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
}
}
foreach (BallastFloraBranch branch in Branches)
{
if (branch.ClaimedItemId > -1)
{
if (Entity.FindEntityByID((ushort)branch.ClaimedItemId) is Item item)
{
branch.ClaimedItem = item;
}
else
{
string errorMsg = $"Error in BallastFloraBehavior.OnMapLoaded: could not find the item claimed by a branch.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.OnMapLoaded:BranchClaimedItemNotFound", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
}
}
UpdateConnections(branch);
CreateBody(branch);
}
@@ -335,9 +354,9 @@ namespace Barotrauma.MapCreatures.Behavior
new XAttribute("sides", (int)branch.Sides),
new XAttribute("blockedsides", (int)branch.BlockedSides));
if (branch.HasClaimedItem)
if (branch.ClaimedItem != null)
{
be.Add(new XAttribute("claimed", (int)branch.ClaimedItem));
be.Add(new XAttribute("claimed", (int)(branch.ClaimedItem?.ID ?? -1)));
}
saveElement.Add(be);
@@ -345,6 +364,13 @@ namespace Barotrauma.MapCreatures.Behavior
foreach (Item target in ClaimedTargets)
{
if (target.Infector == null)
{
string errorMsg = $"Error in BallastFloraBehavior.Save: claimed target \"{target.Prefab.Identifier}\" had no infector set.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("BallastFloraBehavior.Save:InfectorNull", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
continue;
}
XElement te = new XElement("ClaimedTarget", new XAttribute("id", target.ID), new XAttribute("branchId", target.Infector.ID));
saveElement.Add(te);
}
@@ -352,7 +378,7 @@ namespace Barotrauma.MapCreatures.Behavior
element.Add(saveElement);
}
public void LoadSave(XElement element)
public void LoadSave(XElement element, IdRemap idRemap)
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
Offset = element.GetAttributeVector2("offset", Vector2.Zero);
@@ -361,21 +387,20 @@ namespace Barotrauma.MapCreatures.Behavior
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "branch":
LoadBranch(subElement);
LoadBranch(subElement, idRemap);
break;
case "claimedtarget":
int id = subElement.GetAttributeInt("id", -1);
int branchId = subElement.GetAttributeInt("branchId", -1);
if (id > 0)
{
tempClaimedTargets.Add(Tuple.Create((UInt16)id, branchId));
tempClaimedTargets.Add(Tuple.Create(idRemap.GetOffsetId(id), branchId));
}
break;
}
}
void LoadBranch(XElement branchElement)
void LoadBranch(XElement branchElement, IdRemap idRemap)
{
Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero);
bool isRoot = branchElement.GetAttributeBool("isroot", false);
@@ -400,8 +425,7 @@ namespace Barotrauma.MapCreatures.Behavior
if (claimedId > -1)
{
newBranch.HasClaimedItem = true;
newBranch.ClaimedItem = (ushort) claimedId;
newBranch.ClaimedItemId = idRemap.GetOffsetId((ushort)claimedId);
}
Branches.Add(newBranch);
@@ -767,8 +791,7 @@ namespace Barotrauma.MapCreatures.Behavior
if (branch != null)
{
branch.ClaimedItem = target.ID;
branch.HasClaimedItem = true;
branch.ClaimedItem = target;
}
#if SERVER
@@ -794,7 +817,7 @@ namespace Barotrauma.MapCreatures.Behavior
if (parent != null)
{
if (otherBranch.BlockedSides.IsBitSet(connectingSide))
if (otherBranch.BlockedSides.HasFlag(connectingSide))
{
branch.BlockedSides |= oppositeSide;
continue;
@@ -977,7 +1000,7 @@ namespace Barotrauma.MapCreatures.Behavior
if (isClient) { return; }
if (branch.HasClaimedItem)
if (branch.ClaimedItem != null)
{
RemoveClaim(branch.ClaimedItem);
}
@@ -995,41 +1018,34 @@ namespace Barotrauma.MapCreatures.Behavior
#endif
}
public void RemoveClaim(ushort id)
public void RemoveClaim(Item item)
{
ClaimedTargets.ForEachMod(item =>
if (!IgnoredTargets.ContainsKey(item))
{
if (item.ID == id)
IgnoredTargets.Add(item, 10);
}
ClaimedTargets.Remove(item);
item.Infector = null;
ClaimedJunctionBoxes.ForEachMod(jb =>
{
if (jb.Item == item)
{
if (!IgnoredTargets.ContainsKey(item))
{
IgnoredTargets.Add(item, 10);
}
ClaimedTargets.Remove(item);
item.Infector = null;
ClaimedJunctionBoxes.ForEachMod(jb =>
{
if (jb.Item == item)
{
ClaimedJunctionBoxes.Remove(jb);
}
});
ClaimedBatteries.ForEachMod(bat =>
{
if (bat.Item == item)
{
ClaimedBatteries.Remove(bat);
}
});
#if SERVER
SendNetworkMessage(this, NetworkHeader.Infect, item.ID, false);
#endif
ClaimedJunctionBoxes.Remove(jb);
}
});
ClaimedBatteries.ForEachMod(bat =>
{
if (bat.Item == item)
{
ClaimedBatteries.Remove(bat);
}
});
#if SERVER
SendNetworkMessage(this, NetworkHeader.Infect, item.ID, false);
#endif
}
public void Kill()
@@ -68,10 +68,9 @@ namespace Barotrauma
force = element.GetAttributeFloat("force", 0.0f);
abilityExplosion = element.GetAttributeBool("abilityexplosion", false);
applyToSelf = element.GetAttributeBool("applytoself", true);
bool showEffects = !abilityExplosion;
bool showEffects = !element.GetAttributeBool("abilityexplosion", false) && element.GetAttributeBool("showeffects", true);
sparks = element.GetAttributeBool("sparks", showEffects);
shockwave = element.GetAttributeBool("shockwave", showEffects);
flames = element.GetAttributeBool("flames", showEffects);
@@ -151,7 +150,7 @@ namespace Barotrauma
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
{
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker, IgnoredSubmarines);
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker, IgnoredSubmarines, Attack.EmitStructureDamageParticles);
}
if (BallastFloraDamage > 0.0f)
@@ -388,7 +387,7 @@ namespace Barotrauma
{
if (damages.TryGetValue(limb, out float damage))
{
c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true, attacker: attacker);
}
}
}
@@ -396,13 +395,15 @@ namespace Barotrauma
}
}
private static readonly List<Structure> damagedStructureList = new List<Structure>();
private static readonly Dictionary<Structure, float> damagedStructures = new Dictionary<Structure, float>();
/// <summary>
/// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
/// </summary>
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null)
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null, bool emitWallDamageParticles = true)
{
List<Structure> structureList = new List<Structure>();
float dist = 600.0f;
damagedStructureList.Clear();
foreach (MapEntity entity in MapEntity.mapEntityList)
{
if (!(entity is Structure structure)) { continue; }
@@ -412,19 +413,19 @@ namespace Barotrauma
!structure.IsPlatform &&
Vector2.Distance(structure.WorldPosition, worldPosition) < dist * 3.0f)
{
structureList.Add(structure);
damagedStructureList.Add(structure);
}
}
Dictionary<Structure, float> damagedStructures = new Dictionary<Structure, float>();
foreach (Structure structure in structureList)
damagedStructures.Clear();
foreach (Structure structure in damagedStructureList)
{
for (int i = 0; i < structure.SectionCount; i++)
{
float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
if (distFactor <= 0.0f) { continue; }
structure.AddDamage(i, damage * distFactor, attacker);
structure.AddDamage(i, damage * distFactor, attacker, emitParticles: emitWallDamageParticles);
if (damagedStructures.ContainsKey(structure))
{
@@ -302,7 +302,7 @@ namespace Barotrauma
Hull hull2 = linkedTo.Count < 2 ? null : (Hull)linkedTo[1];
if (hull1 == hull2) { return; }
UpdateOxygen(hull1, hull2);
UpdateOxygen(hull1, hull2, deltaTime);
if (linkedTo.Count == 1)
{
@@ -317,7 +317,7 @@ namespace Barotrauma
flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
if (openedTimer > 0.0f && flowForce.Length() > lerpedFlowForce.Length())
if (openedTimer > 0.0f && flowForce.LengthSquared() > lerpedFlowForce.LengthSquared())
{
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
lerpedFlowForce = flowForce;
@@ -326,6 +326,15 @@ namespace Barotrauma
{
lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);
}
if (FlowTargetHull != null && IsRoomToRoom)
{
var otherRoom = linkedTo[1] == FlowTargetHull ? linkedTo[0] : linkedTo[1];
if ((otherRoom as Hull).Volume < FlowTargetHull.Volume)
{
lerpedFlowForce = Vector2.Zero;
}
}
openedTimer -= deltaTime;
EmitParticles(deltaTime);
@@ -345,7 +354,7 @@ namespace Barotrauma
subOffset = hull2.Submarine.Position - Submarine.Position;
}
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) return;
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) { return; }
float size = IsHorizontal ? rect.Height : rect.Width;
@@ -367,7 +376,7 @@ namespace Barotrauma
//water flowing from the righthand room to the lefthand room
if (dir == -1)
{
if (!(hull2.WaterVolume > 0.0f)) return;
if (!(hull2.WaterVolume > 0.0f)) { return; }
lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1];
//delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume));
//delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress);
@@ -375,10 +384,10 @@ namespace Barotrauma
flowTargetHull = hull1;
//make sure not to move more than what the room contains
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 5.0f * sizeModifier, Math.Min(hull2.WaterVolume, hull2.Volume));
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 300.0f * sizeModifier * deltaTime, Math.Min(hull2.WaterVolume, hull2.Volume));
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - (hull1.WaterVolume));
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
hull1.WaterVolume += delta;
hull2.WaterVolume -= delta;
if (hull1.WaterVolume > hull1.Volume)
@@ -386,20 +395,20 @@ namespace Barotrauma
hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure+subOffset.Y) / 2);
}
flowForce = new Vector2(-delta, 0.0f);
flowForce = new Vector2(-delta * (float)(Timing.Step / deltaTime), 0.0f);
}
else if (dir == 1)
{
if (!(hull1.WaterVolume > 0.0f)) return;
if (!(hull1.WaterVolume > 0.0f)) { return; }
lowerSurface = hull2.Surface - hull2.WaveY[hull2.WaveY.Length - 1];
flowTargetHull = hull2;
//make sure not to move more than what the room contains
delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 5.0f * sizeModifier, Math.Min(hull1.WaterVolume, hull1.Volume));
delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 300.0f * sizeModifier * deltaTime, Math.Min(hull1.WaterVolume, hull1.Volume));
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - (hull2.WaterVolume));
delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - hull2.WaterVolume);
hull1.WaterVolume -= delta;
hull2.WaterVolume += delta;
if (hull2.WaterVolume > hull2.Volume)
@@ -407,10 +416,10 @@ namespace Barotrauma
hull2.Pressure = Math.Max(hull2.Pressure, ((hull1.Pressure-subOffset.Y) + hull2.Pressure) / 2);
}
flowForce = new Vector2(delta, 0.0f);
flowForce = new Vector2(delta * (float)(Timing.Step / deltaTime), 0.0f);
}
if (delta > 100.0f && subOffset == Vector2.Zero)
if (delta > 1.5f && subOffset == Vector2.Zero)
{
float avg = (hull1.Surface + hull2.Surface) / 2.0f;
@@ -450,7 +459,7 @@ namespace Barotrauma
flowForce = new Vector2(
0.0f,
Math.Min(Math.Min((hull2.Pressure + subOffset.Y) - hull1.Pressure, 200.0f), delta));
Math.Min(Math.Min((hull2.Pressure + subOffset.Y) - hull1.Pressure, 200.0f), delta * (float)(Timing.Step / deltaTime)));
flowTargetHull = hull1;
@@ -478,7 +487,7 @@ namespace Barotrauma
flowForce = new Vector2(
hull1.WaveY[hull1.GetWaveIndex(rect.X)] - hull1.WaveY[hull1.GetWaveIndex(rect.Right)],
MathHelper.Clamp(-delta, -200.0f, 0.0f));
MathHelper.Clamp(-delta * (float)(Timing.Step / deltaTime), -200.0f, 0.0f));
if (hull2.WaterVolume > hull2.Volume)
{
@@ -517,7 +526,7 @@ namespace Barotrauma
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
hull1.WaterVolume += delta;
if (hull1.WaterVolume > hull1.Volume) hull1.Pressure += 0.5f;
if (hull1.WaterVolume > hull1.Volume) { hull1.Pressure += 30.0f * deltaTime; }
flowTargetHull = hull1;
@@ -526,12 +535,12 @@ namespace Barotrauma
//water flowing from right to left
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
{
flowForce = new Vector2(-delta, 0.0f);
flowForce = new Vector2(-delta * (float)(Timing.Step / deltaTime), 0.0f);
}
else
{
flowForce = new Vector2(delta, 0.0f);
flowForce = new Vector2(delta * (float)(Timing.Step / deltaTime), 0.0f);
}
higherSurface = hull1.Surface;
@@ -542,39 +551,39 @@ namespace Barotrauma
{
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
{
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f;
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 6.0f;
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
hull1.WaveVel[hull1.WaveY.Length - 1] += vel;
hull1.WaveVel[hull1.WaveY.Length - 2] += vel;
hull1.WaveVel[hull1.WaveY.Length - 1] += vel * deltaTime;
hull1.WaveVel[hull1.WaveY.Length - 2] += vel * deltaTime;
}
else
{
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 0.1f;
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 6.0f;
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
hull1.WaveVel[0] += vel;
hull1.WaveVel[1] += vel;
hull1.WaveVel[0] += vel * deltaTime;
hull1.WaveVel[1] += vel * deltaTime;
}
}
else
{
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : 10.0f) * deltaTime;
}
}
else
{
if (rect.Y > hull1.Rect.Y - hull1.Rect.Height / 2.0f)
{
flowForce = new Vector2(0.0f, -delta);
flowForce = new Vector2(0.0f, -delta * (float)(Timing.Step / deltaTime));
}
else
{
flowForce = new Vector2(0.0f, delta);
flowForce = new Vector2(0.0f, delta * (float)(Timing.Step / deltaTime));
}
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
{
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : 10.0f) * deltaTime;
}
}
}
@@ -640,7 +649,7 @@ namespace Barotrauma
}
}
private void UpdateOxygen(Hull hull1, Hull hull2)
private void UpdateOxygen(Hull hull1, Hull hull2, float deltaTime)
{
if (hull1 == null || hull2 == null) { return; }
@@ -656,10 +665,10 @@ namespace Barotrauma
return;
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
float totalVolume = (hull1.Volume + hull2.Volume);
float totalVolume = hull1.Volume + hull2.Volume;
float deltaOxygen = (totalOxygen * hull1.Volume / totalVolume) - hull1.Oxygen;
deltaOxygen = MathHelper.Clamp(deltaOxygen, -Hull.OxygenDistributionSpeed, Hull.OxygenDistributionSpeed);
deltaOxygen = MathHelper.Clamp(deltaOxygen, -Hull.OxygenDistributionSpeed * deltaTime, Hull.OxygenDistributionSpeed * deltaTime);
hull1.Oxygen += deltaOxygen;
hull2.Oxygen -= deltaOxygen;
@@ -107,7 +107,7 @@ namespace Barotrauma
public static bool ShowHulls = true;
public static bool EditWater, EditFire;
public const float OxygenDistributionSpeed = 500.0f;
public const float OxygenDistributionSpeed = 30000.0f;
public const float OxygenDeteriorationSpeed = 0.3f;
public const float OxygenConsumptionSpeed = 700.0f;
@@ -132,7 +132,7 @@ namespace Barotrauma
private float lethalPressure;
private float surface, drawSurface;
private float surface;
private float waterVolume;
private float pressure;
@@ -241,7 +241,10 @@ namespace Barotrauma
}
OxygenPercentage = prevOxygenPercentage;
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
surface = rect.Y - rect.Height + WaterVolume / rect.Width;
#if CLIENT
drawSurface = surface;
#endif
Pressure = surface;
CreateBackgroundSections();
@@ -275,17 +278,6 @@ namespace Barotrauma
get { return surface; }
}
public float DrawSurface
{
get { return drawSurface; }
set
{
if (Math.Abs(drawSurface - value) < 0.00001f) return;
drawSurface = MathHelper.Clamp(value, rect.Y - rect.Height, rect.Y);
update = true;
}
}
public float WorldSurface
{
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
@@ -628,7 +620,10 @@ namespace Barotrauma
Gap.UpdateHulls();
}
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
surface = rect.Y - rect.Height + WaterVolume / rect.Width;
#if CLIENT
drawSurface = surface;
#endif
Pressure = surface;
}
@@ -753,8 +748,6 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
BallastFlora?.Update(deltaTime);
UpdateProjSpecific(deltaTime, cam);
@@ -808,11 +801,6 @@ namespace Barotrauma
surface,
rect.Y - rect.Height + waterDepth,
deltaTime * 10.0f), rect.Y - rect.Height);
//interpolate the position of the rendered surface towards the "target surface"
drawSurface = Math.Max(MathHelper.Lerp(
drawSurface,
rect.Y - rect.Height + waterDepth,
deltaTime * 10.0f), rect.Y - rect.Height);
for (int i = 0; i < waveY.Length; i++)
{
@@ -900,10 +888,10 @@ 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)
//0.016 increase every ~2000 frames = reaches full dirtiness in ~35 minutes
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(2000) == 1)
{
DirtySections(submergedSections, 0.01f);
DirtySections(submergedSections, deltaTime);
}
if (waterVolume < Volume)
@@ -911,11 +899,13 @@ namespace Barotrauma
LethalPressure -= 10.0f * deltaTime;
if (WaterVolume <= 0.0f)
{
#if CLIENT
//wait for the surface to be lerped back to bottom and the waves to settle until disabling update
if (drawSurface > rect.Y - rect.Height + 1) return;
if (drawSurface > rect.Y - rect.Height + 1) { return; }
#endif
for (int i = 1; i < waveY.Length - 1; i++)
{
if (waveY[i] > 0.1f) return;
if (waveY[i] > 0.1f) { return; }
}
update = false;
@@ -1233,7 +1223,7 @@ namespace Barotrauma
}
}
Rectangle subRect = Submarine.CalculateDimensions();
Rectangle subRect = Submarine.Borders;
Alignment roomPos;
if (rect.Y - rect.Height / 2 > subRect.Y + subRect.Height * 0.66f)
@@ -1540,7 +1530,7 @@ namespace Barotrauma
if (prefab != null)
{
hull.BallastFlora = new BallastFloraBehavior(hull, prefab, Vector2.Zero);
hull.BallastFlora.LoadSave(subElement);
hull.BallastFlora.LoadSave(subElement, idRemap);
}
break;
}
@@ -205,6 +205,8 @@ namespace Barotrauma
foreach (var cell in Cells)
{
cell.CellType = CellType.Removed;
cell.OnDestroyed?.Invoke();
cell.OnDestroyed = null;
}
GameMain.World.Remove(Body);
Dispose();
@@ -25,7 +25,17 @@ namespace Barotrauma
/// </summary>
public const int MaxSubmarineWidth = 16000;
public static Level Loaded { get; private set; }
private static Level loaded;
public static Level Loaded
{
get { return loaded; }
private set
{
if (loaded == value) { return; }
loaded = value;
GameAnalyticsManager.SetCurrentLevel(loaded?.LevelData);
}
}
[Flags]
public enum PositionType
@@ -574,12 +584,12 @@ namespace Barotrauma
siteCoordsX = new List<double>((borders.Height / siteInterval.Y) * (borders.Width / siteInterval.Y));
siteCoordsY = new List<double>((borders.Height / siteInterval.Y) * (borders.Width / siteInterval.Y));
int caveSiteInterval = 500;
for (int x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X)
for (int x = siteInterval.X / 2; x < borders.Width - siteInterval.X / 2; x += siteInterval.X)
{
for (int y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y)
for (int y = siteInterval.Y / 2; y < borders.Height - siteInterval.Y / 2; y += siteInterval.Y)
{
int siteX = x + Rand.Range(-siteVariance.X, siteVariance.X, Rand.RandSync.Server);
int siteY = y + Rand.Range(-siteVariance.Y, siteVariance.Y, Rand.RandSync.Server);
int siteX = x + Rand.Range(-siteVariance.X, siteVariance.X + 1, Rand.RandSync.Server);
int siteY = y + Rand.Range(-siteVariance.Y, siteVariance.Y + 1, Rand.RandSync.Server);
bool closeToTunnel = false;
bool closeToCave = false;
@@ -613,8 +623,11 @@ namespace Barotrauma
if (Rand.Range(0, 10, Rand.RandSync.Server) != 0) { continue; }
}
siteCoordsX.Add(siteX);
siteCoordsY.Add(siteY);
if (!TooClose(siteX, siteY))
{
siteCoordsX.Add(siteX);
siteCoordsY.Add(siteY);
}
if (closeToCave)
{
@@ -625,24 +638,45 @@ namespace Barotrauma
int caveSiteX = x2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.Server);
int caveSiteY = y2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.Server);
bool tooClose = false;
for (int i = 0; i < siteCoordsX.Count; i++)
if (!TooClose(caveSiteX, caveSiteY))
{
if (MathUtils.DistanceSquared(caveSiteX, caveSiteY, siteCoordsX[i], siteCoordsY[i]) < 10.0f * 10.0f)
{
tooClose = true;
break;
}
siteCoordsX.Add(caveSiteX);
siteCoordsY.Add(caveSiteY);
}
if (tooClose) { continue; }
siteCoordsX.Add(caveSiteX);
siteCoordsY.Add(caveSiteY);
}
}
}
}
}
bool TooClose(double siteX, double siteY)
{
for (int i = 0; i < siteCoordsX.Count; i++)
{
if (MathUtils.DistanceSquared(siteCoordsX[i], siteCoordsY[i], siteX, siteY) < 10.0f * 10.0f)
{
return true;
}
}
return false;
}
for (int i = 0; i < siteCoordsX.Count; i++)
{
Debug.Assert(
siteCoordsX[i] > 0 || siteCoordsY[i] > 0,
$"Potential error in level generation: a voronoi site was outside the bounds of the level ({siteCoordsX[i]}, {siteCoordsY[i]})");
Debug.Assert(
siteCoordsX[i] < borders.Width || siteCoordsY[i] < borders.Height,
$"Potential error in level generation: a voronoi site was outside the bounds of the level ({siteCoordsX[i]}, {siteCoordsY[i]})");
for (int j = i + 1; j < siteCoordsX.Count; j++)
{
Debug.Assert(
MathUtils.DistanceSquared(siteCoordsX[i], siteCoordsY[i], siteCoordsX[j], siteCoordsY[j]) > 1.0f,
"Potential error in level generation: two voronoi sites are extremely close to each other.");
}
}
EqualityCheckValues.Add(Rand.Int(int.MaxValue, Rand.RandSync.Server));
//----------------------------------------------------------------------------------
@@ -1011,7 +1045,7 @@ namespace Barotrauma
foreach (InterestingPosition pos in PositionsOfInterest)
{
if (pos.PositionType != PositionType.MainPath && pos.PositionType != PositionType.SidePath) { continue; }
if (pos.Position.X < 5000 || pos.Position.X > Size.X - 5000) { continue; }
if (pos.Position.X < pathBorders.X + minMainPathWidth || pos.Position.X > pathBorders.Right - minMainPathWidth) { continue; }
if (Math.Abs(pos.Position.X - startPosition.X) < minMainPathWidth * 2 || Math.Abs(pos.Position.X - endPosition.X) < minMainPathWidth * 2) { continue; }
if (GetTooCloseCells(pos.Position.ToVector2(), minMainPathWidth * 0.7f).Count > 0) { continue; }
iceChunkPositions.Add(pos.Position);
@@ -1689,7 +1723,7 @@ namespace Barotrauma
{
vertices[j] += position;
}
var newChunk = new LevelWall(vertices, GenerationParams.WallColor, this);
var newChunk = new LevelWall(vertices, GenerationParams.WallColor, this, createBody: false);
AbyssIslands.Add(new AbyssIsland(islandArea, newChunk.Cells));
continue;
}
@@ -1752,12 +1786,12 @@ namespace Barotrauma
new Point(0, BottomPos)
};
int mountainCount = Rand.Range(GenerationParams.MountainCountMin, GenerationParams.MountainCountMax, Rand.RandSync.Server);
int mountainCount = Rand.Range(GenerationParams.MountainCountMin, GenerationParams.MountainCountMax + 1, Rand.RandSync.Server);
for (int i = 0; i < mountainCount; i++)
{
bottomPositions.Add(
new Point(Size.X / (mountainCount + 1) * (i + 1),
BottomPos + Rand.Range(GenerationParams.MountainHeightMin, GenerationParams.MountainHeightMax, Rand.RandSync.Server)));
BottomPos + Rand.Range(GenerationParams.MountainHeightMin, GenerationParams.MountainHeightMax + 1, Rand.RandSync.Server)));
}
bottomPositions.Add(new Point(Size.X, BottomPos));
@@ -1770,7 +1804,7 @@ namespace Barotrauma
bottomPositions.Insert(i + 1,
new Point(
(bottomPositions[i].X + bottomPositions[i + 1].X) / 2,
(bottomPositions[i].Y + bottomPositions[i + 1].Y) / 2 + Rand.Range(0, GenerationParams.SeaFloorVariance, Rand.RandSync.Server)));
(bottomPositions[i].Y + bottomPositions[i + 1].Y) / 2 + Rand.Range(0, GenerationParams.SeaFloorVariance + 1, Rand.RandSync.Server)));
i++;
}
@@ -1808,7 +1842,7 @@ namespace Barotrauma
Rectangle allowedArea = new Rectangle(padding, padding, Size.X - padding * 2, Size.Y - padding * 2);
int radius = Math.Max(caveSize.X, caveSize.Y) / 2;
var cavePos = FindPosAwayFromMainPath((parentTunnel.MinWidth + radius) * 1.5f, asCloseAsPossible: true, allowedArea);
var cavePos = FindPosAwayFromMainPath((parentTunnel.MinWidth + radius) * 1.25f, asCloseAsPossible: true, allowedArea);
GenerateCave(caveParams, parentTunnel, cavePos, caveSize);
@@ -1857,7 +1891,7 @@ namespace Barotrauma
Tunnels.Add(tunnel);
caveBranches.Add(tunnel);
int branches = Rand.Range(caveParams.MinBranchCount, caveParams.MaxBranchCount, Rand.RandSync.Server);
int branches = Rand.Range(caveParams.MinBranchCount, caveParams.MaxBranchCount + 1, Rand.RandSync.Server);
for (int j = 0; j < branches; j++)
{
Tunnel parentBranch = caveBranches.GetRandom(Rand.RandSync.Server);
@@ -2073,12 +2107,42 @@ namespace Barotrauma
private Point FindPosAwayFromMainPath(double minDistance, bool asCloseAsPossible, Rectangle? limits = null)
{
var validPoints = distanceField.FindAll(d => d.distance >= minDistance && (limits == null || limits.Value.Contains(d.point)));
validPoints.RemoveAll(d => d.point.Y < GetBottomPosition(d.point.X).Y + minDistance);
if (asCloseAsPossible || !validPoints.Any())
var pointsAboveBottom = distanceField.FindAll(d => d.point.Y > GetBottomPosition(d.point.X).Y + minDistance);
if (pointsAboveBottom.Count == 0)
{
DebugConsole.ThrowError("Error in FindPosAwayFromMainPath: no valid positions above the bottom of the sea floor. Has the position of the sea floor been set too high up?");
return distanceField[Rand.Int(distanceField.Count, Rand.RandSync.Server)].point;
}
var validPoints = pointsAboveBottom.FindAll(d => d.distance >= minDistance && (limits == null || limits.Value.Contains(d.point)));
if (!validPoints.Any())
{
DebugConsole.AddWarning("Failed to find a valid position far enough from the main path. Choosing the furthest possible position.\n" + Environment.StackTrace);
if (limits != null)
{
//try choosing something within the specified limits
validPoints = pointsAboveBottom.FindAll(d => limits.Value.Contains(d.point));
}
if (!validPoints.Any())
{
//couldn't find anything, let's just go with the furthest one
validPoints = pointsAboveBottom;
}
(Point position, double distance) furthestPoint = validPoints.First();
foreach (var point in validPoints)
{
if (point.distance > furthestPoint.distance)
{
furthestPoint = point;
}
}
return furthestPoint.position;
}
if (asCloseAsPossible)
{
if (!validPoints.Any()) { validPoints = distanceField; }
(Point position, double distance) closestPoint = validPoints.First();
(Point position, double distance) closestPoint = validPoints.First();
foreach (var point in validPoints)
{
if (point.distance < closestPoint.distance)
@@ -2138,7 +2202,7 @@ namespace Barotrauma
{
double xDiff = Math.Abs(point.X - ruinPos.X);
double yDiff = Math.Abs(point.Y - ruinPos.Y);
if (xDiff < ruinSize || yDiff < ruinSize)
if (xDiff < ruinSize && yDiff < ruinSize)
{
shortestDistSqr = 0.0f;
}
@@ -2417,7 +2481,7 @@ namespace Barotrauma
}, randSync: Rand.RandSync.Server);
if (location.Cell == null || location.Edge == null) { break; }
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y, Rand.RandSync.Server);
int clusterSize = Rand.Range(GenerationParams.ResourceClusterSizeRange.X, GenerationParams.ResourceClusterSizeRange.Y + 1, Rand.RandSync.Server);
PlaceResources(itemPrefab, clusterSize, location, out var abyssResources);
var abyssClusterLocation = new ClusterLocation(location.Cell, location.Edge, initializeResourceList: true);
abyssClusterLocation.Resources.AddRange(abyssResources);
@@ -2897,6 +2961,7 @@ namespace Barotrauma
float? edgeLength = null, float maxResourceOverlap = 0.4f)
{
edgeLength ??= Vector2.Distance(location.Edge.Point1, location.Edge.Point2);
Vector2 edgeDir = (location.Edge.Point2 - location.Edge.Point1) / edgeLength.Value;
var minResourceOverlap = -((edgeLength.Value - (resourceCount * resourcePrefab.Size.X)) / (resourceCount * resourcePrefab.Size.X));
minResourceOverlap = Math.Max(minResourceOverlap, 0.0f);
var lerpAmounts = new float[resourceCount];
@@ -2912,7 +2977,7 @@ namespace Barotrauma
placedResources = new List<Item>();
for (int i = 0; i < resourceCount; i++)
{
Vector2 selectedPos = Vector2.Lerp(location.Edge.Point1, location.Edge.Point2, startOffset + lerpAmounts[i]);
Vector2 selectedPos = Vector2.Lerp(location.Edge.Point1 + edgeDir * resourcePrefab.Size.X / 2, location.Edge.Point2 - edgeDir * resourcePrefab.Size.X / 2, startOffset + lerpAmounts[i]);
var item = new Item(resourcePrefab, selectedPos, submarine: null);
Vector2 edgeNormal = location.Edge.GetNormal(location.Cell);
float moveAmount = (item.body == null ? item.Rect.Height / 2 : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent() * 0.7f));
@@ -3285,13 +3350,6 @@ namespace Barotrauma
return pathCells;
}
public string GetWreckIDTag(string originalTag, Submarine wreck)
{
string shortSeed = ToolBox.StringToInt(LevelData.Seed + wreck?.Info.Name).ToString();
if (shortSeed.Length > 6) { shortSeed = shortSeed.Substring(0, 6); }
return originalTag + "_" + shortSeed;
}
public bool IsCloseToStart(Vector2 position, float minDist) => IsCloseToStart(position.ToPoint(), minDist);
public bool IsCloseToEnd(Vector2 position, float minDist) => IsCloseToEnd(position.ToPoint(), minDist);
@@ -3627,10 +3685,23 @@ namespace Barotrauma
Wrecks = new List<Submarine>(wreckCount);
for (int i = 0; i < wreckCount; i++)
{
ContentFile contentFile = wreckFiles[i];
if (contentFile == null) { continue; }
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
SpawnSubOnPath(wreckName, contentFile, SubmarineType.Wreck);
//how many times we'll try placing another sub before giving up
const int MaxSubsToTry = 2;
int attempts = 0;
while (wreckFiles.Any() && attempts < MaxSubsToTry)
{
ContentFile contentFile = wreckFiles.First();
wreckFiles.RemoveAt(0);
if (contentFile == null) { continue; }
string wreckName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path);
if (SpawnSubOnPath(wreckName, contentFile, SubmarineType.Wreck) != null)
{
//placed successfully
break;
}
attempts++;
}
}
totalSW.Stop();
Debug.WriteLine($"{Wrecks.Count} wrecks created in { totalSW.ElapsedMilliseconds} (ms)");
@@ -4020,7 +4091,7 @@ namespace Barotrauma
foreach (Submarine wreck in Wrecks)
{
int corpseCount = Rand.Range(Loaded.GenerationParams.MinCorpseCount, Loaded.GenerationParams.MaxCorpseCount);
int corpseCount = Rand.Range(Loaded.GenerationParams.MinCorpseCount, Loaded.GenerationParams.MaxCorpseCount + 1);
var allSpawnPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == wreck && wp.CurrentHull != null);
var pathPoints = allSpawnPoints.FindAll(wp => wp.SpawnType == SpawnType.Path);
pathPoints.Shuffle(Rand.RandSync.Unsynced);
@@ -4077,6 +4148,7 @@ namespace Barotrauma
corpse.EnableDespawn = false;
selectedPrefab.GiveItems(corpse, wreck);
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
corpse.GiveIdCardTags(sp);
spawnCounter++;
static CorpsePrefab GetCorpsePrefab(Func<CorpsePrefab, bool> predicate)
@@ -304,7 +304,7 @@ namespace Barotrauma
foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
{
int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
int childCount = Rand.Range(child.MinCount, child.MaxCount + 1, Rand.RandSync.Server);
for (int j = 0; j < childCount; j++)
{
var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
@@ -433,6 +433,32 @@ namespace Barotrauma
return false;
}
/// <summary>
/// Are there any active contacts between the physics body and the target entity
/// </summary>
public static bool CheckContactsForEntity(PhysicsBody triggerBody, Entity targetEntity)
{
foreach (Fixture fixture in triggerBody.FarseerBody.FixtureList)
{
ContactEdge contactEdge = fixture.Body.ContactList;
while (contactEdge != null)
{
if (contactEdge.Contact != null &&
contactEdge.Contact.Enabled &&
contactEdge.Contact.IsTouching)
{
if ((contactEdge.Contact.FixtureA.Body == triggerBody.FarseerBody && GetEntity(contactEdge.Contact.FixtureB) == targetEntity) ||
(contactEdge.Contact.FixtureB.Body == triggerBody.FarseerBody && GetEntity(contactEdge.Contact.FixtureA) == targetEntity))
{
return true;
}
}
contactEdge = contactEdge.Next;
}
}
return false;
}
public static Entity GetEntity(Fixture fixture)
{
if (fixture.Body == null || fixture.Body.UserData == null) { return null; }
@@ -472,9 +498,6 @@ namespace Barotrauma
{
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
triggerers.RemoveWhere(t => t.Removed);
RemoveDistantTriggerers(PhysicsBody, triggerers, WorldPosition);
bool isNotClient = true;
#if CLIENT
@@ -518,7 +541,9 @@ namespace Barotrauma
}
}
}
RemoveInActiveTriggerers(PhysicsBody, triggerers);
if (stayTriggeredDelay > 0.0f)
{
if (triggerers.Count == 0)
@@ -538,6 +563,8 @@ namespace Barotrauma
foreach (Entity triggerer in triggerers)
{
if (triggerer.Removed) { continue; }
ApplyStatusEffects(statusEffects, worldPosition, triggerer, deltaTime, targets);
if (triggerer is IDamageable damageable)
@@ -583,15 +610,27 @@ namespace Barotrauma
}
}
public static void RemoveDistantTriggerers(PhysicsBody physicsBody, HashSet<Entity> triggerers, Vector2 calculateDistanceTo)
private static readonly List<Entity> triggerersToRemove = new List<Entity>();
public static void RemoveInActiveTriggerers(PhysicsBody physicsBody, HashSet<Entity> triggerers)
{
//failsafe to ensure triggerers get removed when they're far from the trigger
if (physicsBody == null) { return; }
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(physicsBody.GetMaxExtent() * 5), 5000.0f);
triggerers.RemoveWhere(t =>
triggerersToRemove.Clear();
foreach (var triggerer in triggerers)
{
return Vector2.Distance(t.WorldPosition, calculateDistanceTo) > maxExtent;
});
if (triggerer.Removed)
{
triggerersToRemove.Add(triggerer);
}
else if (!CheckContactsForEntity(physicsBody, triggerer))
{
triggerersToRemove.Add(triggerer);
}
}
foreach (var triggerer in triggerersToRemove)
{
triggerers.Remove(triggerer);
}
}
public static void ApplyStatusEffects(List<StatusEffect> statusEffects, Vector2 worldPosition, Entity triggerer, float deltaTime, List<ISerializableEntity> targets)
@@ -650,13 +689,15 @@ namespace Barotrauma
float structureDamage = attack.GetStructureDamage(deltaTime);
if (structureDamage > 0.0f)
{
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, levelWallDamage: 0.0f);
Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, levelWallDamage: 0.0f, emitWallDamageParticles: attack.EmitStructureDamageParticles);
}
}
}
private void ApplyForce(PhysicsBody body)
{
if (body == null) { return; }
float distFactor = 1.0f;
if (ForceFalloff)
{
@@ -57,7 +57,7 @@ namespace Barotrauma
set { moveState = MathHelper.Clamp(value, 0.0f, MathHelper.TwoPi); }
}
public LevelWall(List<Vector2> vertices, Color color, Level level, bool giftWrap = false)
public LevelWall(List<Vector2> vertices, Color color, Level level, bool giftWrap = false, bool createBody = true)
{
this.level = level;
this.color = color;
@@ -74,14 +74,17 @@ namespace Barotrauma
wallCell.Edges[i].IsSolid = true;
}
Cells = new List<VoronoiCell>() { wallCell };
Body = CaveGenerator.GeneratePolygons(Cells, level, out triangles);
if (triangles.Count == 0)
if (createBody)
{
throw new ArgumentException("Failed to generate a wall (not enough triangles). Original vertices: " + string.Join(", ", originalVertices.Select(v => v.ToString())));
}
Body = CaveGenerator.GeneratePolygons(Cells, level, out triangles);
if (triangles.Count == 0)
{
throw new ArgumentException("Failed to generate a wall (not enough triangles). Original vertices: " + string.Join(", ", originalVertices.Select(v => v.ToString())));
}
#if CLIENT
GenerateVertices();
GenerateVertices();
#endif
}
}
public LevelWall(List<Vector2> edgePositions, Vector2 extendAmount, Color color, Level level)
@@ -147,8 +147,6 @@ namespace Barotrauma
{
List<Vector2> points = new List<Vector2>();
var wallPrefabs = StructurePrefab.Prefabs.Where(mp => mp.Body);
foreach (XElement element in rootElement.Elements())
{
if (element.Name != "Structure") { continue; }
@@ -159,8 +157,12 @@ namespace Barotrauma
StructurePrefab prefab = Structure.FindPrefab(name, identifier);
if (prefab == null) { continue; }
float scale = element.GetAttributeFloat("scale", prefab.Scale);
var rect = element.GetAttributeVector4("rect", Vector4.Zero);
rect.Z *= scale / prefab.Scale;
rect.W *= scale / prefab.Scale;
points.Add(new Vector2(rect.X, rect.Y));
points.Add(new Vector2(rect.X + rect.Z, rect.Y));
points.Add(new Vector2(rect.X, rect.Y - rect.W));
@@ -352,6 +354,7 @@ namespace Barotrauma
if (hull.Submarine != sub) { continue; }
hull.WaterVolume = 0.0f;
hull.OxygenPercentage = 100.0f;
hull.BallastFlora?.Kill();
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Abilities;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -132,6 +133,7 @@ namespace Barotrauma
#endregion
private const float MechanicalMaxDiscountPercentage = 50.0f;
private const float HealMaxDiscountPercentage = 10.0f;
private readonly List<TakenItem> takenItems = new List<TakenItem>();
public IEnumerable<TakenItem> TakenItems
@@ -780,7 +782,7 @@ namespace Barotrauma
{
if (priceInfo.MaxAvailableAmount > priceInfo.MinAvailableAmount)
{
quantity = Rand.Range(priceInfo.MinAvailableAmount, priceInfo.MaxAvailableAmount);
quantity = Rand.Range(priceInfo.MinAvailableAmount, priceInfo.MaxAvailableAmount + 1);
}
else
{
@@ -908,6 +910,12 @@ namespace Barotrauma
return (int) Math.Ceiling((1.0f - discount) * cost * MechanicalPriceMultiplier);
}
public int GetAdjustedHealCost(int cost)
{
float discount = Reputation.Value / Reputation.MaxReputation * (HealMaxDiscountPercentage / 100.0f);
return (int) Math.Ceiling((1.0f - discount) * cost * PriceMultiplier);
}
/// <param name="force">If true, the store will be recreated if it already exists.</param>
public void CreateStore(bool force = false)
{
@@ -1002,7 +1010,7 @@ namespace Barotrauma
private void GenerateRandomPriceModifier()
{
StorePriceModifier = Rand.Range(-StorePriceModifierRange, StorePriceModifierRange);
StorePriceModifier = Rand.Range(-StorePriceModifierRange, StorePriceModifierRange + 1);
}
private void CreateStoreSpecials()
@@ -1110,7 +1118,7 @@ namespace Barotrauma
Discovered = true;
if (checkTalents)
{
GameSession.GetSessionCrewCharacters().ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new Abilities.AbilityLocation(this)));
GameSession.GetSessionCrewCharacters().ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new AbilityLocation(this)));
}
}
@@ -1263,5 +1271,15 @@ namespace Barotrauma
{
HireManager?.Remove();
}
class AbilityLocation : AbilityObject, IAbilityLocation
{
public AbilityLocation(Location location)
{
Location = location;
}
public Location Location { get; set; }
}
}
}
@@ -112,7 +112,7 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(!Locations.Contains(null));
for (int i = 0; i < Locations.Count; i++)
{
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, Locations[i], $"location.{i}", -100, 100, Rand.Range(-10, 11, Rand.RandSync.Server));
}
List<XElement> connectionElements = new List<XElement>();
@@ -214,7 +214,7 @@ namespace Barotrauma
for (int i = 0; i < Locations.Count; i++)
{
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server));
Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, Locations[i], $"location.{i}", -100, 100, Rand.Range(-10, 11, Rand.RandSync.Server));
}
foreach (Location location in Locations)
@@ -486,7 +486,8 @@ namespace Barotrauma
{
location.LevelData = new LevelData(location)
{
Difficulty = MathHelper.Clamp(GetLevelDifficulty(location.MapPosition.X / Width), 0.0f, 100.0f)
Difficulty = MathHelper.Clamp(location.MapPosition.X / Width * 100, 0.0f, 100.0f)
//Difficulty = MathHelper.Clamp(GetLevelDifficulty(location.MapPosition.X / Width), 0.0f, 100.0f)
};
location.UnlockInitialMissions();
}
@@ -3,12 +3,10 @@ using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -20,14 +18,17 @@ namespace Barotrauma
protected List<ushort> linkedToID;
public List<ushort> unresolvedLinkedToID;
private const int GapUpdateInterval = 4;
private static int gapUpdateTimer;
/// <summary>
/// List of upgrades this item has
/// </summary>
protected readonly List<Upgrade> Upgrades = new List<Upgrade>();
public HashSet<string> disallowedUpgrades = new HashSet<string>();
[Editable, Serialize("", true)]
public string DisallowedUpgrades
{
@@ -101,7 +102,7 @@ namespace Barotrauma
return !DrawBelowWater;
}
}
public virtual bool Linkable
{
get { return false; }
@@ -231,6 +232,9 @@ namespace Barotrauma
protected set;
} = true;
[Serialize("", true, "Submarine editor layer")]
public string Layer { get; set; }
/// <summary>
/// The index of the outpost module this entity originally spawned in (-1 if not an outpost item)
/// </summary>
@@ -242,7 +246,7 @@ namespace Barotrauma
{
get { return ""; }
}
public MapEntity(MapEntityPrefab prefab, Submarine submarine, ushort id) : base(submarine, id)
{
this.prefab = prefab;
@@ -303,7 +307,7 @@ namespace Barotrauma
{
return GetUpgrade(identifier) != null;
}
public Upgrade GetUpgrade(string identifier)
{
return Upgrades.Find(upgrade => upgrade.Identifier == identifier);
@@ -329,7 +333,7 @@ namespace Barotrauma
}
DebugConsole.Log($"Set (ID: {ID} {prefab.Name})'s \"{upgrade.Prefab.Name}\" upgrade to level {upgrade.Level}");
}
/// <summary>
/// Adds a new upgrade to the item
/// </summary>
@@ -407,6 +411,7 @@ namespace Barotrauma
}
//connect clone wires to the clone items and refresh links between doors and gaps
List<Wire> orphanedWires = new List<Wire>();
for (int i = 0; i < clones.Count; i++)
{
if (!(clones[i] is Item cloneItem)) { continue; }
@@ -435,11 +440,11 @@ namespace Barotrauma
disconnectedFromClone.DisconnectedWires.Add(cloneWire);
if (cloneWire.Item.body != null) { cloneWire.Item.body.Enabled = false; }
cloneWire.IsActive = false;
continue;
continue;
}
var connectedItem = originalWire.Connections[n].Item;
if (connectedItem == null) { continue; }
if (connectedItem == null || !entitiesToClone.Contains(connectedItem)) { continue; }
//index of the item the wire is connected to
int itemIndex = entitiesToClone.IndexOf(connectedItem);
@@ -466,6 +471,20 @@ namespace Barotrauma
(clones[itemIndex] as Item).Connections[connectionIndex].TryAddLink(cloneWire);
cloneWire.Connect((clones[itemIndex] as Item).Connections[connectionIndex], false);
}
if ((cloneWire.Connections[0] == null || cloneWire.Connections[1] == null) && cloneItem.GetComponent<DockingPort>() == null)
{
if (!clones.Any(c => (c as Item)?.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(cloneWire) ?? false))
{
orphanedWires.Add(cloneWire);
}
}
}
foreach (var orphanedWire in orphanedWires)
{
orphanedWire.Item.Remove();
clones.Remove(orphanedWire.Item);
}
return clones;
@@ -535,59 +554,48 @@ namespace Barotrauma
linkedTo.Clear();
}
}
static int tick = 0;
/// <summary>
/// Call Update() on every object in Entity.list
/// </summary>
public static void UpdateAll(float deltaTime, Camera cam)
{
tick++;
if (tick % GameMain.Lua.game.mapEntityUpdateRate == 0)
foreach (Hull hull in Hull.hullList)
{
hull.Update(deltaTime, cam);
}
#if CLIENT
Hull.UpdateCheats(deltaTime, cam);
#endif
foreach (Hull hull in Hull.hullList)
{
hull.Update(deltaTime * GameMain.Lua.game.mapEntityUpdateRate, cam);
}
foreach (Structure structure in Structure.WallList)
{
structure.Update(deltaTime * GameMain.Lua.game.mapEntityUpdateRate, cam);
}
//update gaps in random order, because otherwise in rooms with multiple gaps
//the water/air will always tend to flow through the first gap in the list,
//which may lead to weird behavior like water draining down only through
//one gap in a room even if there are several
foreach (Gap gap in Gap.GapList.OrderBy(g => Rand.Int(int.MaxValue)))
{
gap.Update(deltaTime * GameMain.Lua.game.mapEntityUpdateRate, cam);
}
Powered.UpdatePower(deltaTime * GameMain.Lua.game.mapEntityUpdateRate);
foreach (Item item in Item.ItemList)
{
if (GameMain.Lua.game.updatePriorityItems.Contains(item)) continue;
item.Update(deltaTime * GameMain.Lua.game.mapEntityUpdateRate, cam);
}
foreach (Structure structure in Structure.WallList)
{
structure.Update(deltaTime, cam);
}
foreach (var item in GameMain.Lua.game.updatePriorityItems)
//update gaps in random order, because otherwise in rooms with multiple gaps
//the water/air will always tend to flow through the first gap in the list,
//which may lead to weird behavior like water draining down only through
//one gap in a room even if there are several
gapUpdateTimer++;
if (gapUpdateTimer >= GapUpdateInterval)
{
if (item.Removed) continue;
foreach (Gap gap in Gap.GapList.OrderBy(g => Rand.Int(int.MaxValue)))
{
gap.Update(deltaTime * GapUpdateInterval, cam);
}
gapUpdateTimer = 0;
}
Powered.UpdatePower(deltaTime);
foreach (Item item in Item.ItemList)
{
item.Update(deltaTime, cam);
}
if (tick % GameMain.Lua.game.mapEntityUpdateRate == 0)
{
UpdateAllProjSpecific(deltaTime * GameMain.Lua.game.mapEntityUpdateRate);
UpdateAllProjSpecific(deltaTime);
Spawner?.Update();
}
Spawner?.Update();
}
static partial void UpdateAllProjSpecific(float deltaTime);
@@ -743,11 +751,11 @@ namespace Barotrauma
foreach (ushort i in e.linkedToID)
{
if (FindEntityByID(i) is MapEntity linked)
if (FindEntityByID(i) is MapEntity linked)
{
e.linkedTo.Add(linked);
}
else
e.linkedTo.Add(linked);
}
else
{
#if DEBUG
DebugConsole.ThrowError($"Linking the entity \"{e.Name}\" to another entity failed. Could not find an entity with the ID \"{i}\".");
@@ -788,7 +796,7 @@ namespace Barotrauma
/// <summary>
/// Gets all linked entities of specific type.
/// </summary>
private static void GetLinkedEntitiesRecursive<T>(MapEntity mapEntity, HashSet<T> linkedTargets, ref int depth, int? maxDepth = null, Func<T, bool> filter = null)
private static void GetLinkedEntitiesRecursive<T>(MapEntity mapEntity, HashSet<T> linkedTargets, ref int depth, int? maxDepth = null, Func<T, bool> filter = null)
where T : MapEntity
{
if (depth > maxDepth) { return; }
@@ -806,4 +814,4 @@ namespace Barotrauma
}
}
}
}
}
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -317,6 +318,11 @@ namespace Barotrauma
return null;
}
public static MapEntityPrefab GetRandom(Predicate<MapEntityPrefab> predicate, Rand.RandSync sync)
{
return List.GetRandom(p => predicate(p), sync);
}
/// <summary>
/// Find a matching map entity prefab
/// </summary>
@@ -338,6 +344,8 @@ namespace Barotrauma
if (target == null) { return false; }
if (target is StructurePrefab && AllowedLinks.Contains("structure")) { return true; }
if (target is ItemPrefab && AllowedLinks.Contains("item")) { return true; }
if (target is LinkedSubmarinePrefab && Tags.Contains("dock")) { return true; }
if (this is LinkedSubmarinePrefab && target.Tags.Contains("dock")) { return true; }
return AllowedLinks.Contains(target.Identifier) || target.AllowedLinks.Contains(identifier)
|| target.Tags.Any(t => AllowedLinks.Contains(t)) || Tags.Any(t => target.AllowedLinks.Contains(t));
}
@@ -115,7 +115,7 @@ namespace Barotrauma
private float? maxHealth;
[Serialize(100.0f, true)]
[Serialize(100.0f, true), Editable(MinValueFloat = 0)]
public float MaxHealth
{
get => maxHealth ?? Prefab.Health;
@@ -704,7 +704,7 @@ namespace Barotrauma
if (BodyWidth > 0.0f) { rectSize.X = BodyWidth; }
if (BodyHeight > 0.0f) { rectSize.Y = BodyHeight; }
Vector2 bodyPos = WorldPosition + BodyOffset;
Vector2 bodyPos = WorldPosition + BodyOffset * Scale;
Vector2 transformedMousePos = MathUtils.RotatePointAroundTarget(position, bodyPos, BodyRotation);
@@ -876,7 +876,7 @@ namespace Barotrauma
return true;
}
public void AddDamage(int sectionIndex, float damage, Character attacker = null)
public void AddDamage(int sectionIndex, float damage, Character attacker = null, bool emitParticles = true)
{
if (!Prefab.Body || Prefab.Platform || Indestructible) { return; }
@@ -885,7 +885,7 @@ namespace Barotrauma
var section = Sections[sectionIndex];
#if CLIENT
if (damage > 0)
if (damage > 0 && emitParticles)
{
float dmg = Math.Min(MaxHealth - section.damage, damage);
float particleAmount = MathHelper.Lerp(0, 25, MathUtils.InverseLerp(0, 100, dmg * Rand.Range(0.75f, 1.25f)));
@@ -898,8 +898,8 @@ namespace Barotrauma
{
var worldRect = section.WorldRect;
Vector2 particlePos = new Vector2(
Rand.Range(worldRect.X, worldRect.Right),
Rand.Range(worldRect.Y - worldRect.Height, worldRect.Y));
Rand.Range(worldRect.X, worldRect.Right + 1),
Rand.Range(worldRect.Y - worldRect.Height, worldRect.Y + 1));
var particle = GameMain.ParticleManager.CreateParticle("shrapnel", particlePos, Rand.Vector(Rand.Range(1.0f, 50.0f)), collisionIgnoreTimer: 1f);
if (particle == null) break;
@@ -1016,7 +1016,10 @@ namespace Barotrauma
damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker);
#if CLIENT
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
if (attack.EmitStructureDamageParticles)
{
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
}
#endif
}
}
@@ -1034,7 +1037,7 @@ namespace Barotrauma
if (Submarine != null && damageAmount > 0 && attacker != null)
{
var abilityAttackerSubmarine = new AbilityCharacterSubmarine(attacker, Submarine);
var abilityAttackerSubmarine = new AbilityAttackerSubmarine(attacker, Submarine);
foreach (Character character in Character.CharacterList)
{
character.CheckTalents(AbilityEffectType.AfterSubmarineAttacked, abilityAttackerSubmarine);
@@ -1529,6 +1532,7 @@ namespace Barotrauma
public virtual void Reset()
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
MaxHealth = Prefab.Health;
Sprite.ReloadXML();
SpriteDepth = Sprite.Depth;
NoAITarget = Prefab.NoAITarget;
@@ -1542,4 +1546,15 @@ namespace Barotrauma
}
}
}
class AbilityAttackerSubmarine : AbilityObject, IAbilityCharacter, IAbilitySubmarine
{
public AbilityAttackerSubmarine(Character character, Submarine submarine)
{
Character = character;
Submarine = submarine;
}
public Character Character { get; set; }
public Submarine Submarine { get; set; }
}
}
@@ -1535,7 +1535,7 @@ namespace Barotrauma
element.Add(new XAttribute("tags", Info.Tags.ToString()));
element.Add(new XAttribute("gameversion", GameMain.Version.ToString()));
Rectangle dimensions = CalculateDimensions();
Rectangle dimensions = VisibleBorders;
element.Add(new XAttribute("dimensions", XMLExtensions.Vector2ToString(dimensions.Size.ToVector2())));
var cargoContainers = GetCargoContainers();
element.Add(new XAttribute("cargocapacity", cargoContainers.Sum(c => c.container.Capacity)));
@@ -1615,7 +1615,7 @@ namespace Barotrauma
Info.CheckSubsLeftBehind(element);
}
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
public bool TrySaveAs(string filePath, System.IO.MemoryStream previewImage = null)
{
var newInfo = new SubmarineInfo(this)
{
@@ -1628,8 +1628,19 @@ namespace Barotrauma
//remove reference to the preview image from the old info, so we don't dispose it (the new info still uses the texture)
Info.PreviewImage = null;
#endif
Info.Dispose(); Info = newInfo;
return newInfo.SaveAs(filePath, previewImage);
Info.Dispose();
Info = newInfo;
try
{
newInfo.SaveAs(filePath, previewImage);
}
catch (Exception e)
{
DebugConsole.ThrowError($"Saving submarine \"{filePath}\" failed!", e);
return false;
}
return true;
}
public static bool Unloading
@@ -1643,9 +1654,8 @@ namespace Barotrauma
Unloading = true;
#if CLIENT
RemoveAllRoundSounds(); //Sound.OnGameEnd();
if (GameMain.LightManager != null) GameMain.LightManager.ClearLights();
RemoveAllRoundSounds();
GameMain.LightManager?.ClearLights();
#endif
var _loaded = new List<Submarine>(loaded);
@@ -1766,7 +1776,7 @@ namespace Barotrauma
if (connectedWp.isObstructed) { continue; }
Vector2 start = ConvertUnits.ToSimUnits(wp.WorldPosition);
Vector2 end = ConvertUnits.ToSimUnits(connectedWp.WorldPosition);
var body = Submarine.PickBody(start, end, null, Physics.CollisionLevel, allowInsideFixture: false);
var body = PickBody(start, end, null, Physics.CollisionLevel, allowInsideFixture: false);
if (body != null)
{
connectedWp.isObstructed = true;
@@ -1793,7 +1803,7 @@ namespace Barotrauma
foreach (var connection in node.connections)
{
var connectedWp = connection.Waypoint;
if (connectedWp.isObstructed) { continue; }
if (connectedWp.isObstructed || connectedWp.Ladders != null) { continue; }
Vector2 start = ConvertUnits.ToSimUnits(wp.WorldPosition) - otherSub.SimPosition;
Vector2 end = ConvertUnits.ToSimUnits(connectedWp.WorldPosition) - otherSub.SimPosition;
var body = PickBody(start, end, null, Physics.CollisionWall, allowInsideFixture: true);
@@ -115,6 +115,8 @@ namespace Barotrauma
{
this.submarine = sub;
Vector2 minExtents = Vector2.Zero, maxExtents = Vector2.Zero;
Vector2 visibleMinExtents = Vector2.Zero, visibleMaxExtents = Vector2.Zero;
Body farseerBody = null;
if (!Hull.hullList.Any(h => h.Submarine == sub))
{
@@ -133,8 +135,6 @@ namespace Barotrauma
}
HullVertices = convexHull;
Vector2 minExtents = Vector2.Zero, maxExtents = Vector2.Zero;
Vector2 visibleMinExtents = Vector2.Zero, visibleMaxExtents = Vector2.Zero;
farseerBody = GameMain.World.CreateBody();
farseerBody.UserData = this;
@@ -142,25 +142,18 @@ namespace Barotrauma
{
if (mapEntity.Submarine != submarine || !(mapEntity is Structure wall)) { continue; }
bool hasCollider = wall.HasBody && !wall.IsPlatform && wall.StairDirection == Direction.None;
Rectangle rect = wall.Rect;
visibleMinExtents.X = Math.Min(rect.X, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(rect.Y - rect.Height, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(rect.Right, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(rect.Y, visibleMaxExtents.Y);
if (!wall.HasBody || wall.IsPlatform || wall.StairDirection != Direction.None) { continue; }
farseerBody.CreateRectangle(
ConvertUnits.ToSimUnits(wall.BodyWidth),
ConvertUnits.ToSimUnits(wall.BodyHeight),
50.0f,
-wall.BodyRotation,
ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2) + wall.BodyOffset)).UserData = wall;
minExtents.X = Math.Min(visibleMinExtents.X, minExtents.X);
minExtents.Y = Math.Min(visibleMinExtents.Y, minExtents.Y);
maxExtents.X = Math.Max(visibleMaxExtents.X, maxExtents.X);
maxExtents.Y = Math.Max(visibleMaxExtents.Y, maxExtents.Y);
SetExtents(new Vector2(rect.X, rect.Y - rect.Height), new Vector2(rect.Right, rect.Y), hasCollider);
if (hasCollider)
{
farseerBody.CreateRectangle(
ConvertUnits.ToSimUnits(wall.BodyWidth),
ConvertUnits.ToSimUnits(wall.BodyHeight),
50.0f,
-wall.BodyRotation,
ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2) + wall.BodyOffset)).UserData = wall;
}
}
foreach (Hull hull in Hull.hullList)
@@ -168,21 +161,13 @@ namespace Barotrauma
if (hull.Submarine != submarine || hull.IdFreed) { continue; }
Rectangle rect = hull.Rect;
SetExtents(new Vector2(rect.X, rect.Y - rect.Height), new Vector2(rect.Right, rect.Y), hasCollider: true);
farseerBody.CreateRectangle(
ConvertUnits.ToSimUnits(rect.Width),
ConvertUnits.ToSimUnits(rect.Height),
100.0f,
ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2))).UserData = hull;
visibleMinExtents.X = Math.Min(rect.X, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(rect.Y - rect.Height, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(rect.Right, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(rect.Y, visibleMaxExtents.Y);
minExtents.X = Math.Min(visibleMinExtents.X, minExtents.X);
minExtents.Y = Math.Min(visibleMinExtents.Y, minExtents.Y);
maxExtents.X = Math.Max(visibleMaxExtents.X, maxExtents.X);
maxExtents.Y = Math.Max(visibleMaxExtents.Y, maxExtents.Y);
}
foreach (Item item in Item.ItemList)
@@ -207,31 +192,21 @@ namespace Barotrauma
if (width > 0.0f && height > 0.0f)
{
item.StaticFixtures.Add(farseerBody.CreateRectangle(simWidth, simHeight, 5.0f, simPos));
visibleMinExtents.X = Math.Min(item.Position.X - width / 2, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(item.Position.Y - height / 2, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(item.Position.X + width / 2, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(item.Position.Y + height / 2, visibleMaxExtents.Y);
SetExtents(item.Position - new Vector2(width, height) / 2, item.Position + new Vector2(width, height) / 2, hasCollider: true);
}
else if (radius > 0.0f && width > 0.0f)
{
item.StaticFixtures.Add(farseerBody.CreateRectangle(simWidth, simRadius * 2, 5.0f, simPos));
item.StaticFixtures.Add(farseerBody.CreateCircle(simRadius, 5.0f, simPos - Vector2.UnitX * simWidth / 2));
item.StaticFixtures.Add(farseerBody.CreateCircle(simRadius, 5.0f, simPos + Vector2.UnitX * simWidth / 2));
visibleMinExtents.X = Math.Min(item.Position.X - width / 2 - radius, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(item.Position.Y - radius, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(item.Position.X + width / 2 + radius, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(item.Position.Y + radius, visibleMaxExtents.Y);
SetExtents(item.Position - new Vector2(width / 2 + radius, height / 2), item.Position + new Vector2(width / 2 + radius, height / 2), hasCollider: true);
}
else if (radius > 0.0f && height > 0.0f)
{
item.StaticFixtures.Add(farseerBody.CreateRectangle(simRadius * 2, height, 5.0f, simPos));
item.StaticFixtures.Add(farseerBody.CreateCircle(simRadius, 5.0f, simPos - Vector2.UnitY * simHeight / 2));
item.StaticFixtures.Add(farseerBody.CreateCircle(simRadius, 5.0f, simPos + Vector2.UnitX * simHeight / 2));
visibleMinExtents.X = Math.Min(item.Position.X - radius, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(item.Position.Y - height / 2 - radius, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(item.Position.X + radius, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(item.Position.Y + height / 2 + radius, visibleMaxExtents.Y);
SetExtents(item.Position - new Vector2(width / 2, height / 2 + radius), item.Position + new Vector2(width / 2, height / 2 + radius), hasCollider: true);
}
else if (radius > 0.0f)
{
@@ -240,12 +215,8 @@ namespace Barotrauma
visibleMinExtents.Y = Math.Min(item.Position.Y - radius, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(item.Position.X + radius, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(item.Position.Y + radius, visibleMaxExtents.Y);
SetExtents(item.Position - new Vector2(radius, radius), item.Position + new Vector2(radius, radius), hasCollider: true);
}
item.StaticFixtures.ForEach(f => f.UserData = item);
minExtents.X = Math.Min(visibleMinExtents.X, minExtents.X);
minExtents.Y = Math.Min(visibleMinExtents.Y, minExtents.Y);
maxExtents.X = Math.Max(visibleMaxExtents.X, maxExtents.X);
maxExtents.Y = Math.Max(visibleMaxExtents.Y, maxExtents.Y);
}
Borders = new Rectangle((int)minExtents.X, (int)maxExtents.Y, (int)(maxExtents.X - minExtents.X), (int)(maxExtents.Y - minExtents.Y));
@@ -271,6 +242,21 @@ namespace Barotrauma
farseerBody.UserData = submarine;
Body = new PhysicsBody(farseerBody);
void SetExtents(Vector2 min, Vector2 max, bool hasCollider)
{
visibleMinExtents.X = Math.Min(min.X, visibleMinExtents.X);
visibleMinExtents.Y = Math.Min(min.Y, visibleMinExtents.Y);
visibleMaxExtents.X = Math.Max(max.X, visibleMaxExtents.X);
visibleMaxExtents.Y = Math.Max(max.Y, visibleMaxExtents.Y);
if (hasCollider)
{
minExtents.X = Math.Min(min.X, minExtents.X);
minExtents.Y = Math.Min(min.Y, minExtents.Y);
maxExtents.X = Math.Max(max.X, maxExtents.X);
maxExtents.Y = Math.Max(max.Y, maxExtents.Y);
}
}
}
private List<Vector2> GenerateConvexHull()
@@ -853,6 +839,8 @@ namespace Barotrauma
Vector2 impulse = direction * impact * 0.5f;
impulse = impulse.ClampLength(MaxCollisionImpact);
float impulseMagnitude = impulse.Length();
if (!MathUtils.IsValid(impulse))
{
string errorMsg =
@@ -919,8 +907,9 @@ namespace Barotrauma
foreach (Item item in Item.ItemList)
{
if (item.Submarine != submarine || item.CurrentHull == null || item.body == null || !item.body.Enabled) { continue; }
if (item.body.Mass > impulseMagnitude) { continue; }
item.body.ApplyLinearImpulse(item.body.Mass * impulse, 10.0f);
item.body.ApplyLinearImpulse(impulse, 10.0f);
item.PositionUpdateInterval = 0.0f;
}
@@ -401,6 +401,7 @@ namespace Barotrauma
public bool IsVanillaSubmarine()
{
if (FilePath == null) { return false; }
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{
@@ -521,7 +522,7 @@ namespace Barotrauma
}
//saving/loading ----------------------------------------------------
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
public void SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
{
var newElement = new XElement(
SubmarineElement.Name,
@@ -543,18 +544,9 @@ namespace Barotrauma
{
doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray())));
}
try
{
SaveUtil.CompressStringToFile(filePath, doc.ToString());
Md5Hash.RemoveFromCache(filePath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving submarine \"" + filePath + "\" failed!", e);
return false;
}
return true;
SaveUtil.CompressStringToFile(filePath, doc.ToString());
Md5Hash.RemoveFromCache(filePath);
}
public static void AddToSavedSubs(SubmarineInfo subInfo)