(5a377a8ee) Unstable v0.9.1000.0
This commit is contained in:
@@ -155,16 +155,16 @@ namespace Barotrauma
|
||||
Prefabs.RemoveByFile(filePath);
|
||||
}
|
||||
|
||||
public void GiveItems(Character character)
|
||||
public void GiveItems(Character character, Submarine submarine)
|
||||
{
|
||||
var spawnItems = ToolBox.SelectWeightedRandom(ItemSets.Keys.ToList(), ItemSets.Values.ToList(), Rand.RandSync.Unsynced);
|
||||
foreach (XElement itemElement in spawnItems.GetChildElements("item"))
|
||||
{
|
||||
InitializeItems(character, itemElement);
|
||||
InitializeItems(character, itemElement, submarine);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeItems(Character character, XElement itemElement, Item parentItem = null)
|
||||
private void InitializeItems(Character character, XElement itemElement, Submarine submarine, Item parentItem = null)
|
||||
{
|
||||
ItemPrefab itemPrefab;
|
||||
string itemIdentifier = itemElement.GetAttributeString("identifier", "");
|
||||
@@ -201,9 +201,10 @@ namespace Barotrauma
|
||||
{
|
||||
character.Inventory.TryPutItem(item, null, item.AllowedSlots);
|
||||
}
|
||||
if (item.Prefab.Identifier == "idcard")
|
||||
if (item.Prefab.Identifier == "idcard" || item.Prefab.Identifier == "idcardwreck")
|
||||
{
|
||||
item.AddTag("name:" + character.Name);
|
||||
item.ReplaceTag("wreck_id", Level.Loaded.GetWreckIDTag("wreck_id", submarine));
|
||||
var job = character.Info?.Job;
|
||||
if (job != null)
|
||||
{
|
||||
@@ -220,7 +221,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (XElement childItemElement in itemElement.Elements())
|
||||
{
|
||||
InitializeItems(character, childItemElement, item);
|
||||
InitializeItems(character, childItemElement, submarine, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Barotrauma
|
||||
{
|
||||
id += 1;
|
||||
IDfound = dictionary.ContainsKey(id);
|
||||
} while (IDfound);
|
||||
} while (IDfound || id == NullEntityID || id == EntitySpawnerID);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ namespace Barotrauma
|
||||
|
||||
public float EmpStrength { get; set; }
|
||||
|
||||
public Explosion(float range, float force, float damage, float structureDamage, float empStrength = 0.0f)
|
||||
public Explosion(float range, float force, float damage, float structureDamage, float itemDamage, float empStrength = 0.0f)
|
||||
{
|
||||
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range)
|
||||
attack = new Attack(damage, 0.0f, 0.0f, structureDamage, itemDamage, range)
|
||||
{
|
||||
SeverLimbsProbability = 1.0f
|
||||
};
|
||||
@@ -157,42 +157,47 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
if (flames)
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
if (item.Condition <= 0.0f) { continue; }
|
||||
if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; }
|
||||
if (flames && !item.FireProof)
|
||||
{
|
||||
if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f) { continue; }
|
||||
|
||||
//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)
|
||||
Item container = item.Container;
|
||||
bool fireProof = false;
|
||||
while (container != null)
|
||||
{
|
||||
if (container.FireProof) { fireProof = true; break; }
|
||||
if (container.FireProof)
|
||||
{
|
||||
fireProof = true;
|
||||
break;
|
||||
}
|
||||
container = container.Container;
|
||||
}
|
||||
|
||||
if (fireProof || Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; }
|
||||
|
||||
item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
|
||||
if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
if (!fireProof)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
|
||||
item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
|
||||
if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
|
||||
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
|
||||
{
|
||||
float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
|
||||
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
|
||||
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
|
||||
if (dist > attack.Range)
|
||||
{
|
||||
float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
|
||||
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
|
||||
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
|
||||
|
||||
if (dist > attack.Range) { continue; }
|
||||
|
||||
float distFactor = 1.0f - dist / attack.Range;
|
||||
float damageAmount = attack.GetItemDamage(1.0f);
|
||||
item.Condition -= damageAmount * distFactor;
|
||||
continue;
|
||||
}
|
||||
float distFactor = 1.0f - dist / attack.Range;
|
||||
float damageAmount = attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
|
||||
item.Condition -= damageAmount * distFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,10 +205,9 @@ namespace Barotrauma
|
||||
|
||||
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull);
|
||||
|
||||
|
||||
public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
|
||||
{
|
||||
if (attack.Range <= 0.0f) return;
|
||||
if (attack.Range <= 0.0f) { return; }
|
||||
|
||||
//long range for the broad distance check, because large characters may still be in range even if their collider isn't
|
||||
float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);
|
||||
@@ -220,19 +224,20 @@ namespace Barotrauma
|
||||
Vector2 explosionPos = worldPosition;
|
||||
if (c.Submarine != null) { explosionPos -= c.Submarine.Position; }
|
||||
|
||||
Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
|
||||
Hull hull = Hull.FindHull(explosionPos, null, false);
|
||||
bool underWater = hull == null || explosionPos.Y < hull.Surface;
|
||||
|
||||
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
|
||||
|
||||
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
|
||||
Dictionary<Limb, float> damages = new Dictionary<Limb, float>();
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
|
||||
|
||||
//calculate distance from the "outer surface" of the physics body
|
||||
//doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
|
||||
float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
|
||||
float limbRadius = limb.body.GetMaxExtent();
|
||||
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
|
||||
|
||||
if (dist > attack.Range) { continue; }
|
||||
@@ -240,14 +245,18 @@ 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) distFactor *= 0.1f;
|
||||
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
|
||||
{
|
||||
distFactor *= 0.1f;
|
||||
}
|
||||
|
||||
distFactors.Add(limb, distFactor);
|
||||
|
||||
List<Affliction> modifiedAfflictions = new List<Affliction>();
|
||||
int limbCount = c.AnimController.Limbs.Count(l => !l.IsSevered && !l.ignoreCollisions);
|
||||
foreach (Affliction affliction in attack.Afflictions.Keys)
|
||||
{
|
||||
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
|
||||
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / limbCount));
|
||||
}
|
||||
c.LastDamageSource = damageSource;
|
||||
if (attacker == null)
|
||||
@@ -255,14 +264,18 @@ namespace Barotrauma
|
||||
if (damageSource is Item item)
|
||||
{
|
||||
attacker = item.GetComponent<Projectile>()?.User;
|
||||
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
|
||||
if (attacker == null)
|
||||
{
|
||||
attacker = item.GetComponent<MeleeWeapon>()?.User;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
|
||||
AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
|
||||
damages.Add(limb, attackResult.Damage);
|
||||
|
||||
if (attack.StatusEffects != null && attack.StatusEffects.Any())
|
||||
{
|
||||
@@ -287,14 +300,19 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//sever joints
|
||||
if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
|
||||
if (attack.SeverLimbsProbability > 0.0f)
|
||||
{
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (!distFactors.ContainsKey(limb)) { continue; }
|
||||
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
|
||||
if (limb.character.Removed || limb.Removed) { continue; }
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (!c.IsDead && !limb.CanBeSeveredAlive) { continue; }
|
||||
if (distFactors.TryGetValue(limb, out float distFactor))
|
||||
{
|
||||
c.TrySeverLimbJoints(limb, 1.0f);
|
||||
if (damages.TryGetValue(limb, out float damage))
|
||||
{
|
||||
c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Extensions;
|
||||
#if CLIENT
|
||||
using Barotrauma.Sounds;
|
||||
using Barotrauma.Lights;
|
||||
using Barotrauma.Particles;
|
||||
#endif
|
||||
using FarseerPhysics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -15,6 +17,7 @@ namespace Barotrauma
|
||||
{
|
||||
const float OxygenConsumption = 50.0f;
|
||||
const float GrowSpeed = 20.0f;
|
||||
const float MaxDamageRange = 250.0f;
|
||||
|
||||
protected Hull hull;
|
||||
|
||||
@@ -65,7 +68,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual float DamageRange
|
||||
{
|
||||
get { return (float)Math.Sqrt(size.X) * 20.0f; }
|
||||
get { return Math.Min((float)Math.Sqrt(size.X) * 10.0f, MaxDamageRange); }
|
||||
}
|
||||
|
||||
public Hull Hull
|
||||
@@ -123,8 +126,8 @@ namespace Barotrauma
|
||||
{
|
||||
i = Math.Min(i, fireSources.Count - 1);
|
||||
j = Math.Min(j, i - 1);
|
||||
|
||||
if (!fireSources[i].CheckOverLap(fireSources[j])) continue;
|
||||
|
||||
if (!fireSources[i].CheckOverLap(fireSources[j])) { continue; }
|
||||
|
||||
float leftEdge = Math.Min(fireSources[i].position.X, fireSources[j].position.X);
|
||||
|
||||
@@ -133,12 +136,10 @@ 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();
|
||||
}
|
||||
}
|
||||
@@ -210,21 +211,32 @@ namespace Barotrauma
|
||||
|
||||
private void DamageCharacters(float deltaTime)
|
||||
{
|
||||
if (size.X <= 0.0f) return;
|
||||
if (size.X <= 0.0f) { return; }
|
||||
|
||||
for (int i = 0; i < Character.CharacterList.Count; i++)
|
||||
{
|
||||
Character c = Character.CharacterList[i];
|
||||
if (c.AnimController.CurrentHull == null || c.IsDead) continue;
|
||||
if (c.CurrentHull == null || c.IsDead) { continue; }
|
||||
|
||||
if (!IsInDamageRange(c, DamageRange)) continue;
|
||||
if (!IsInDamageRange(c, DamageRange)) { continue; }
|
||||
|
||||
float dmg = (float)Math.Sqrt(size.X) * deltaTime / c.AnimController.Limbs.Length;
|
||||
//GetApproximateDistance returns float.MaxValue if there's no path through open gaps between the hulls (e.g. if there's a door/wall in between)
|
||||
if (hull.GetApproximateDistance(Position, c.Position, c.CurrentHull, 10000.0f) > size.X + DamageRange)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float dmg = (float)Math.Sqrt(Math.Min(500, size.X)) * deltaTime / c.AnimController.Limbs.Count(l => !l.IsSevered);
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
c.LastDamageSource = null;
|
||||
c.DamageLimb(WorldPosition, limb, new List<Affliction>() { AfflictionPrefab.Burn.Instantiate(dmg) }, 0.0f, false, 0.0f);
|
||||
c.DamageLimb(WorldPosition, limb, AfflictionPrefab.Burn.Instantiate(dmg).ToEnumerable(), 0.0f, false, 0.0f);
|
||||
}
|
||||
#if CLIENT
|
||||
//let clients display the client-side damage immediately, otherwise they may not be able to react to the damage fast enough
|
||||
c.CharacterHealth.DisplayedVitality = c.Vitality;
|
||||
#endif
|
||||
c.ApplyStatusEffects(ActionType.OnFire, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Barotrauma
|
||||
//the force of the water flow which is exerted on physics bodies
|
||||
private Vector2 flowForce;
|
||||
private Hull flowTargetHull;
|
||||
|
||||
private float openedTimer = 1.0f;
|
||||
|
||||
private float higherSurface;
|
||||
private float lowerSurface;
|
||||
@@ -54,7 +56,11 @@ namespace Barotrauma
|
||||
public float Open
|
||||
{
|
||||
get { return open; }
|
||||
set { open = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
set
|
||||
{
|
||||
if (value > open) { openedTimer = 1.0f; }
|
||||
open = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public float Size => IsHorizontal ? Rect.Height : Rect.Width;
|
||||
@@ -124,6 +130,7 @@ namespace Barotrauma
|
||||
InsertToList();
|
||||
|
||||
outsideCollisionBlocker = GameMain.World.CreateEdge(-Vector2.UnitX * 2.0f, Vector2.UnitX * 2.0f);
|
||||
outsideCollisionBlocker.UserData = $"CollisionBlocker (Gap {ID})";
|
||||
outsideCollisionBlocker.BodyType = BodyType.Static;
|
||||
outsideCollisionBlocker.CollisionCategories = Physics.CollisionWall;
|
||||
outsideCollisionBlocker.CollidesWith = Physics.CollisionCharacter;
|
||||
@@ -277,57 +284,18 @@ namespace Barotrauma
|
||||
|
||||
flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
|
||||
flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
|
||||
lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);
|
||||
if (openedTimer > 0.0f && flowForce.Length() > lerpedFlowForce.Length())
|
||||
{
|
||||
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
|
||||
lerpedFlowForce = flowForce;
|
||||
}
|
||||
else
|
||||
{
|
||||
lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);
|
||||
}
|
||||
openedTimer -= deltaTime;
|
||||
|
||||
EmitParticles(deltaTime);
|
||||
|
||||
if (flowTargetHull != null && lerpedFlowForce.LengthSquared() > 0.0001f)
|
||||
{
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.CurrentHull == null) continue;
|
||||
if (character.CurrentHull != linkedTo[0] as Hull &&
|
||||
(linkedTo.Count < 2 || character.CurrentHull != linkedTo[1] as Hull))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (!limb.inWater) continue;
|
||||
|
||||
float dist = Vector2.Distance(limb.WorldPosition, WorldPosition);
|
||||
if (dist > lerpedFlowForce.Length()) continue;
|
||||
|
||||
Vector2 force = lerpedFlowForce / (float)Math.Max(Math.Sqrt(dist), 20.0f) * 0.025f;
|
||||
|
||||
//vertical gaps only apply forces if the character is roughly above/below the gap
|
||||
if (!IsHorizontal)
|
||||
{
|
||||
float xDist = Math.Abs(limb.WorldPosition.X - WorldPosition.X);
|
||||
if (xDist > rect.Width || rect.Width == 0) break;
|
||||
|
||||
force *= 1.0f - xDist / rect.Width;
|
||||
}
|
||||
|
||||
if (!MathUtils.IsValid(force))
|
||||
{
|
||||
string errorMsg = "Attempted to apply invalid flow force to the character \"" + character.Name +
|
||||
"\", gap pos: " + WorldPosition +
|
||||
", limb pos: " + limb.WorldPosition +
|
||||
", flowforce: " + flowForce + ", lerpedFlowForce:" + lerpedFlowForce +
|
||||
", dist: " + dist;
|
||||
|
||||
DebugConsole.Log(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Gap.Update:InvalidFlowForce:" + character.Name,
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMsg);
|
||||
continue;
|
||||
}
|
||||
character.AnimController.Collider.ApplyForce(force * limb.body.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void EmitParticles(float deltaTime);
|
||||
@@ -510,7 +478,7 @@ namespace Barotrauma
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size * open * open;
|
||||
|
||||
float delta = hull1.Volume * Hull.MaxCompress * sizeModifier * deltaTime;
|
||||
float delta = 500.0f * sizeModifier * deltaTime;
|
||||
|
||||
//make sure not to place more water to the target room than it can hold
|
||||
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
|
||||
@@ -595,7 +563,7 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateOutsideColliderPos(Hull hull)
|
||||
{
|
||||
if (Submarine == null || IsRoomToRoom) { return; }
|
||||
if (Submarine == null || IsRoomToRoom || Level.Loaded == null) { return; }
|
||||
|
||||
Vector2 rayDir;
|
||||
if (IsHorizontal)
|
||||
|
||||
@@ -22,9 +22,9 @@ namespace Barotrauma
|
||||
public const float OxygenConsumptionSpeed = 700.0f;
|
||||
|
||||
public const int WaveWidth = 32;
|
||||
public static float WaveStiffness = 0.02f;
|
||||
public static float WaveSpread = 0.05f;
|
||||
public static float WaveDampening = 0.05f;
|
||||
public static float WaveStiffness = 0.01f;
|
||||
public static float WaveSpread = 0.02f;
|
||||
public static float WaveDampening = 0.02f;
|
||||
|
||||
//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
|
||||
@@ -83,6 +83,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Color ambientLight;
|
||||
|
||||
[Editable, Serialize("0,0,0,0", true)]
|
||||
public Color AmbientLight
|
||||
{
|
||||
get { return ambientLight; }
|
||||
set
|
||||
{
|
||||
ambientLight = value;
|
||||
#if CLIENT
|
||||
lastAmbientLightEditTime = Timing.TotalTime;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public override Rectangle Rect
|
||||
{
|
||||
get
|
||||
@@ -501,54 +516,34 @@ namespace Barotrauma
|
||||
rightDelta[i] = WaveSpread * (waveY[i] - waveY[i + 1]);
|
||||
waveVel[i + 1] += rightDelta[i];
|
||||
}
|
||||
|
||||
for (int i = 1; i < waveY.Length - 1; i++)
|
||||
{
|
||||
waveY[i - 1] += leftDelta[i];
|
||||
waveY[i + 1] += rightDelta[i];
|
||||
}
|
||||
}
|
||||
|
||||
//make waves propagate through horizontal gaps
|
||||
foreach (Gap gap in ConnectedGaps)
|
||||
{
|
||||
if (!gap.IsRoomToRoom || !gap.IsHorizontal || gap.Open <= 0.0f) continue;
|
||||
if (surface > gap.Rect.Y || surface < gap.Rect.Y - gap.Rect.Height) continue;
|
||||
|
||||
Hull hull2 = this == gap.linkedTo[0] as Hull ? (Hull)gap.linkedTo[1] : (Hull)gap.linkedTo[0];
|
||||
float otherSurfaceY = hull2.surface;
|
||||
if (otherSurfaceY > gap.Rect.Y || otherSurfaceY < gap.Rect.Y - gap.Rect.Height) continue;
|
||||
|
||||
float surfaceDiff = (surface - otherSurfaceY) * gap.Open;
|
||||
if (this != gap.linkedTo[0] as Hull)
|
||||
{
|
||||
//the first hull linked to the gap handles the wave propagation,
|
||||
//the second just updates the surfaces to the same level
|
||||
if (surfaceDiff < 32.0f)
|
||||
{
|
||||
hull2.waveY[hull2.waveY.Length - 1] = surfaceDiff * 0.5f;
|
||||
waveY[0] = -surfaceDiff * 0.5f;
|
||||
}
|
||||
//let the first linked hull handle the water propagation
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gap.IsRoomToRoom || !gap.IsHorizontal || gap.Open <= 0.0f) { continue; }
|
||||
if (surface > gap.Rect.Y || surface < gap.Rect.Y - gap.Rect.Height) { continue; }
|
||||
|
||||
Hull hull2 = this == gap.linkedTo[0] as Hull ? (Hull)gap.linkedTo[1] : (Hull)gap.linkedTo[0];
|
||||
float otherSurfaceY = hull2.surface;
|
||||
if (otherSurfaceY > gap.Rect.Y || otherSurfaceY < gap.Rect.Y - gap.Rect.Height) { continue; }
|
||||
|
||||
float surfaceDiff = (surface - otherSurfaceY) * gap.Open;
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
int i = waveY.Length - 1;
|
||||
rightDelta[waveY.Length - 1] = WaveSpread * (hull2.waveY[0] - waveY[waveY.Length - 1] - surfaceDiff) * 0.5f;
|
||||
waveVel[waveY.Length - 1] += rightDelta[waveY.Length - 1];
|
||||
waveY[waveY.Length - 1] += rightDelta[waveY.Length - 1];
|
||||
|
||||
leftDelta[i] = WaveSpread * (waveY[i] - waveY[i - 1]);
|
||||
waveVel[i - 1] += leftDelta[i];
|
||||
|
||||
rightDelta[i] = WaveSpread * (waveY[i] - hull2.waveY[0] + surfaceDiff);
|
||||
hull2.waveVel[0] += rightDelta[i];
|
||||
|
||||
i = 0;
|
||||
|
||||
hull2.leftDelta[i] = WaveSpread * (hull2.waveY[i] - waveY[waveY.Length - 1] - surfaceDiff);
|
||||
waveVel[waveVel.Length - 1] += hull2.leftDelta[i];
|
||||
|
||||
hull2.rightDelta[i] = WaveSpread * (hull2.waveY[i] - hull2.waveY[i + 1]);
|
||||
hull2.waveVel[i + 1] += hull2.rightDelta[i];
|
||||
hull2.leftDelta[0] = WaveSpread * (waveY[waveY.Length - 1] - hull2.waveY[0] + surfaceDiff) * 0.5f;
|
||||
hull2.waveVel[0] += hull2.leftDelta[0];
|
||||
hull2.waveY[0] += hull2.leftDelta[0];
|
||||
}
|
||||
|
||||
if (surfaceDiff < 32.0f)
|
||||
@@ -557,13 +552,19 @@ namespace Barotrauma
|
||||
hull2.waveY[0] = surfaceDiff * 0.5f;
|
||||
waveY[waveY.Length - 1] = -surfaceDiff * 0.5f;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
|
||||
//apply spread (two iterations)
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
for (int i = 1; i < waveY.Length - 1; i++)
|
||||
{
|
||||
hull2.waveY[0] += rightDelta[waveY.Length - 1];
|
||||
waveY[waveY.Length - 1] += hull2.leftDelta[0];
|
||||
waveY[i - 1] += leftDelta[i];
|
||||
waveY[i + 1] += rightDelta[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (waterVolume < Volume)
|
||||
{
|
||||
LethalPressure -= 10.0f * deltaTime;
|
||||
@@ -609,37 +610,33 @@ namespace Barotrauma
|
||||
FireSources.Remove(fire);
|
||||
}
|
||||
|
||||
private HashSet<Hull> adjacentHulls = new HashSet<Hull>();
|
||||
public IEnumerable<Hull> GetConnectedHulls(bool includingThis, int? searchDepth = null)
|
||||
private readonly HashSet<Hull> adjacentHulls = new HashSet<Hull>();
|
||||
public IEnumerable<Hull> GetConnectedHulls(bool includingThis, int? searchDepth = null, bool ignoreClosedGaps = false)
|
||||
{
|
||||
adjacentHulls.Clear();
|
||||
int startStep = 0;
|
||||
searchDepth = searchDepth ?? 100;
|
||||
return GetAdjacentHulls(includingThis, adjacentHulls, ref startStep, searchDepth.Value);
|
||||
searchDepth ??= 100;
|
||||
GetAdjacentHulls(adjacentHulls, ref startStep, searchDepth.Value, ignoreClosedGaps);
|
||||
if (!includingThis) { adjacentHulls.Remove(this); }
|
||||
return adjacentHulls;
|
||||
}
|
||||
|
||||
private HashSet<Hull> GetAdjacentHulls(bool includingThis, HashSet<Hull> connectedHulls, ref int step, int searchDepth)
|
||||
private void GetAdjacentHulls(HashSet<Hull> connectedHulls, ref int step, int searchDepth, bool ignoreClosedGaps = false)
|
||||
{
|
||||
if (includingThis)
|
||||
{
|
||||
connectedHulls.Add(this);
|
||||
}
|
||||
if (step > searchDepth)
|
||||
{
|
||||
return connectedHulls;
|
||||
}
|
||||
connectedHulls.Add(this);
|
||||
if (step > searchDepth) { return; }
|
||||
foreach (Gap g in ConnectedGaps)
|
||||
{
|
||||
if (ignoreClosedGaps && g.Open <= 0.0f) { continue; }
|
||||
for (int i = 0; i < 2 && i < g.linkedTo.Count; i++)
|
||||
{
|
||||
if (g.linkedTo[i] is Hull hull && !connectedHulls.Contains(hull))
|
||||
{
|
||||
step++;
|
||||
hull.GetAdjacentHulls(true, connectedHulls, ref step, searchDepth);
|
||||
hull.GetAdjacentHulls(connectedHulls, ref step, searchDepth, ignoreClosedGaps);
|
||||
}
|
||||
}
|
||||
}
|
||||
return connectedHulls;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -666,7 +663,7 @@ namespace Barotrauma
|
||||
if (g.ConnectedDoor != null && !g.ConnectedDoor.IsBroken)
|
||||
{
|
||||
//gap blocked if the door is not open or the predicted state is not open
|
||||
if (!g.ConnectedDoor.IsOpen || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
|
||||
if ((!g.ConnectedDoor.IsOpen && !g.ConnectedDoor.IsBroken) || (g.ConnectedDoor.PredictedState.HasValue && !g.ConnectedDoor.PredictedState.Value))
|
||||
{
|
||||
if (g.ConnectedDoor.OpenState < 0.1f) continue;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Barotrauma
|
||||
DisplayEntities = new List<Pair<MapEntityPrefab, Rectangle>>();
|
||||
foreach (XElement entityElement in doc.Root.Elements())
|
||||
{
|
||||
string identifier = entityElement.GetAttributeString("identifier", "");
|
||||
string identifier = entityElement.GetAttributeString("identifier", entityElement.Name.ToString().ToLowerInvariant());
|
||||
MapEntityPrefab mapEntity = List.FirstOrDefault(p => p.Identifier == identifier);
|
||||
if (mapEntity == null)
|
||||
{
|
||||
@@ -64,9 +64,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Rectangle rect = entityElement.GetAttributeRect("rect", Rectangle.Empty);
|
||||
if (mapEntity != null && !entityElement.GetAttributeBool("hideinassemblypreview", false))
|
||||
if (mapEntity != null && !entityElement.Elements().Any(e => e.Name.LocalName.Equals("wire", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
DisplayEntities.Add(new Pair<MapEntityPrefab, Rectangle>(mapEntity, rect));
|
||||
if (!entityElement.GetAttributeBool("hideinassemblypreview", false)) { DisplayEntities.Add(new Pair<MapEntityPrefab, Rectangle>(mapEntity, rect)); }
|
||||
minX = Math.Min(minX, rect.X);
|
||||
minY = Math.Min(minY, rect.Y - rect.Height);
|
||||
maxX = Math.Max(maxX, rect.Right);
|
||||
@@ -74,7 +74,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
Bounds = new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
Bounds = minX == int.MaxValue ?
|
||||
new Rectangle(0, 0, 1, 1) :
|
||||
new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
|
||||
Prefabs.Add(this, false);
|
||||
}
|
||||
@@ -89,7 +91,7 @@ namespace Barotrauma
|
||||
CreateInstance(rect.Location.ToVector2(), Submarine.MainSub);
|
||||
}
|
||||
|
||||
public List<MapEntity> CreateInstance(Vector2 position, Submarine sub)
|
||||
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;
|
||||
@@ -107,10 +109,10 @@ namespace Barotrauma
|
||||
|
||||
MapEntity.MapLoaded(entities, true);
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
if (Screen.Selected == GameMain.SubEditorScreen && selectPrefabs)
|
||||
{
|
||||
MapEntity.SelectedList.Clear();
|
||||
entities.ForEach(e => MapEntity.AddSelection(e));
|
||||
entities.ForEach(MapEntity.AddSelection);
|
||||
}
|
||||
#endif
|
||||
return entities;
|
||||
|
||||
@@ -27,19 +27,19 @@ namespace Barotrauma
|
||||
|
||||
foreach (GraphEdge ge in graphEdges)
|
||||
{
|
||||
if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f) continue;
|
||||
if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f) { continue; }
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Site site = (i == 0) ? ge.Site1 : ge.Site2;
|
||||
|
||||
int x = (int)(Math.Floor((site.Coord.X-borders.X) / gridCellSize));
|
||||
int y = (int)(Math.Floor((site.Coord.Y-borders.Y) / gridCellSize));
|
||||
int x = (int)(Math.Floor((site.Coord.X - borders.X) / gridCellSize));
|
||||
int y = (int)(Math.Floor((site.Coord.Y - borders.Y) / gridCellSize));
|
||||
|
||||
x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0)-1);
|
||||
y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1)-1);
|
||||
|
||||
VoronoiCell cell = cellGrid[x,y].Find(c => c.Site == site);
|
||||
x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0) - 1);
|
||||
y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1) - 1);
|
||||
|
||||
VoronoiCell cell = cellGrid[x, y].Find(c => c.Site == site);
|
||||
|
||||
if (cell == null)
|
||||
{
|
||||
@@ -60,14 +60,62 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//add edges to the borders of the graph
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
Vector2? point1 = null, point2 = null;
|
||||
foreach (GraphEdge ge in cell.Edges)
|
||||
{
|
||||
if (MathUtils.NearlyEqual(ge.Point1.X, borders.X) || MathUtils.NearlyEqual(ge.Point1.X, borders.Right) ||
|
||||
MathUtils.NearlyEqual(ge.Point1.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point1.Y, borders.Bottom))
|
||||
{
|
||||
if (point1 == null)
|
||||
{
|
||||
point1 = ge.Point1;
|
||||
}
|
||||
else if (point2 == null)
|
||||
{
|
||||
if (MathUtils.NearlyEqual(point1.Value, ge.Point1)) { continue; }
|
||||
point2 = ge.Point1;
|
||||
}
|
||||
}
|
||||
if (MathUtils.NearlyEqual(ge.Point2.X, borders.X) || MathUtils.NearlyEqual(ge.Point2.X, borders.Right) ||
|
||||
MathUtils.NearlyEqual(ge.Point2.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point2.Y, borders.Bottom))
|
||||
{
|
||||
if (point1 == null)
|
||||
{
|
||||
point1 = ge.Point2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MathUtils.NearlyEqual(point1.Value, ge.Point2)) { continue; }
|
||||
point2 = ge.Point2;
|
||||
}
|
||||
}
|
||||
if (point1.HasValue && point2.HasValue)
|
||||
{
|
||||
Debug.Assert(point1 != point2);
|
||||
var newEdge = new GraphEdge(point1.Value, point2.Value)
|
||||
{
|
||||
Cell1 = cell,
|
||||
IsSolid = true,
|
||||
Site1 = cell.Site,
|
||||
OutsideLevel = true
|
||||
};
|
||||
cell.Edges.Add(newEdge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
|
||||
private static Vector2 GetEdgeNormal(GraphEdge edge, VoronoiCell cell = null)
|
||||
{
|
||||
if (cell == null) cell = edge.AdjacentCell(null);
|
||||
if (cell == null) return Vector2.UnitX;
|
||||
if (cell == null) { cell = edge.AdjacentCell(null); }
|
||||
if (cell == null) { return Vector2.UnitX; }
|
||||
|
||||
CompareCCW compare = new CompareCCW(cell.Center);
|
||||
if (compare.Compare(edge.Point1, edge.Point2) == -1)
|
||||
@@ -77,9 +125,7 @@ namespace Barotrauma
|
||||
edge.Point2 = temp;
|
||||
}
|
||||
|
||||
Vector2 normal = Vector2.Zero;
|
||||
|
||||
normal = Vector2.Normalize(edge.Point2 - edge.Point1);
|
||||
Vector2 normal = Vector2.Normalize(edge.Point2 - edge.Point1);
|
||||
Vector2 diffToCell = Vector2.Normalize(cell.Center - edge.Point2);
|
||||
|
||||
normal = new Vector2(-normal.Y, normal.X);
|
||||
@@ -148,7 +194,7 @@ namespace Barotrauma
|
||||
foreach (GraphEdge edge in currentCell.Edges)
|
||||
{
|
||||
var adjacentCell = edge.AdjacentCell(currentCell);
|
||||
if (limits.Contains(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y))
|
||||
if (adjacentCell != null && limits.Contains(adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y))
|
||||
{
|
||||
allowedEdges.Add(edge);
|
||||
}
|
||||
@@ -161,6 +207,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < currentCell.Edges.Count; i++)
|
||||
{
|
||||
var adjacentCell = currentCell.Edges[i].AdjacentCell(currentCell);
|
||||
if (adjacentCell == null) { continue; }
|
||||
double dist = MathUtils.Distance(
|
||||
adjacentCell.Site.Coord.X, adjacentCell.Site.Coord.Y,
|
||||
targetCells[currentTargetIndex].Site.Coord.X, targetCells[currentTargetIndex].Site.Coord.Y);
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace Barotrauma
|
||||
//all entities are disabled after they reach this depth
|
||||
public const int MaxEntityDepth = -300000;
|
||||
public const float ShaftHeight = 1000.0f;
|
||||
/// <summary>
|
||||
/// The level generator won't try to adjust the width of the main path above this limit.
|
||||
/// </summary>
|
||||
public const int MaxSubmarineWidth = 16000;
|
||||
|
||||
public static Level Loaded
|
||||
{
|
||||
@@ -141,8 +145,6 @@ namespace Barotrauma
|
||||
get { return positionsOfInterest; }
|
||||
}
|
||||
|
||||
public readonly List<InterestingPosition> UsedPositions = new List<InterestingPosition>();
|
||||
|
||||
public Submarine StartOutpost { get; private set; }
|
||||
public Submarine EndOutpost { get; private set; }
|
||||
|
||||
@@ -166,6 +168,11 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Entity> EntitiesBeforeGenerate { get; private set; } = new List<Entity>();
|
||||
public int EntityCountBeforeGenerate { get; private set; }
|
||||
public int EntityCountAfterGenerate { get; private set; }
|
||||
|
||||
|
||||
public float Difficulty
|
||||
{
|
||||
get;
|
||||
@@ -281,8 +288,11 @@ namespace Barotrauma
|
||||
|
||||
public void Generate(bool mirror)
|
||||
{
|
||||
if (loaded != null) loaded.Remove();
|
||||
if (loaded != null) { loaded.Remove(); }
|
||||
loaded = this;
|
||||
|
||||
EntitiesBeforeGenerate = GetEntityList();
|
||||
EntityCountBeforeGenerate = EntitiesBeforeGenerate.Count();
|
||||
|
||||
levelObjectManager = new LevelObjectManager();
|
||||
|
||||
@@ -316,26 +326,25 @@ namespace Barotrauma
|
||||
SeaFloorTopPos = generationParams.SeaFloorDepth + generationParams.MountainHeightMax + generationParams.SeaFloorVariance;
|
||||
|
||||
int minWidth = 6500;
|
||||
int maxWidth = 50000;
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders();
|
||||
dockedSubBorders.Inflate(dockedSubBorders.Size.ToVector2() * 0.05f);
|
||||
dockedSubBorders.Inflate(dockedSubBorders.Size.ToVector2() * 0.15f);
|
||||
minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height));
|
||||
minWidth = Math.Min(minWidth, maxWidth);
|
||||
minWidth = Math.Min(minWidth, MaxSubmarineWidth);
|
||||
}
|
||||
|
||||
Rectangle pathBorders = borders;
|
||||
pathBorders.Inflate(-minWidth * 2, -minWidth);
|
||||
pathBorders.Inflate(-Math.Min(minWidth * 2, MaxSubmarineWidth), -minWidth);
|
||||
|
||||
Debug.Assert(pathBorders.Width > 0 && pathBorders.Height > 0, "The size of the level's path area was negative.");
|
||||
|
||||
startPosition = new Point(
|
||||
Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
|
||||
minWidth,
|
||||
Rand.Range(borders.Height / 2, borders.Height - minWidth * 2, Rand.RandSync.Server));
|
||||
|
||||
endPosition = new Point(
|
||||
borders.Width - Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server),
|
||||
borders.Width - minWidth,
|
||||
Rand.Range(borders.Height / 2, borders.Height - minWidth * 2, Rand.RandSync.Server));
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -348,19 +357,25 @@ namespace Barotrauma
|
||||
Point nodeInterval = generationParams.MainPathNodeIntervalRange;
|
||||
|
||||
for (int x = startPosition.X + nodeInterval.X;
|
||||
x < endPosition.X - nodeInterval.X;
|
||||
x < endPosition.X - nodeInterval.X;
|
||||
x += Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server))
|
||||
{
|
||||
pathNodes.Add(new Point(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, Rand.RandSync.Server)));
|
||||
}
|
||||
|
||||
pathNodes.Add(new Point(endPosition.X, borders.Height));
|
||||
|
||||
if (pathNodes.Count <= 2)
|
||||
if (pathNodes.Count == 1)
|
||||
{
|
||||
pathNodes.Insert(1, borders.Center);
|
||||
pathNodes.Add(new Point(pathBorders.Center.X, pathBorders.Y));
|
||||
}
|
||||
//if all nodes ended up high up in the level, move one down to make sure we utilize the full height of the level
|
||||
else if (pathNodes.GetRange(1, pathNodes.Count - 1).All(p => p.Y > pathBorders.Y + pathBorders.Height * 0.25f))
|
||||
{
|
||||
int nodeIndex = Rand.Range(1, pathNodes.Count, Rand.RandSync.Server);
|
||||
pathNodes[nodeIndex] = new Point(pathNodes[nodeIndex].X, pathBorders.Y);
|
||||
}
|
||||
|
||||
pathNodes.Add(new Point(endPosition.X, borders.Height));
|
||||
|
||||
GenerateTunnels(pathNodes, minWidth);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -539,21 +554,21 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
if (mirroredEdges.Contains(edge)) continue;
|
||||
if (mirroredEdges.Contains(edge)) { continue; }
|
||||
edge.Point1.X = borders.Width - edge.Point1.X;
|
||||
edge.Point2.X = borders.Width - edge.Point2.X;
|
||||
if (!mirroredSites.Contains(edge.Site1))
|
||||
if (edge.Site1 != null && !mirroredSites.Contains(edge.Site1))
|
||||
{
|
||||
//make sure that sites right at the edge of a grid cell end up in the same cell as in the non-mirrored level
|
||||
if (edge.Site1.Coord.X % GridCellSize < 1.0f &&
|
||||
edge.Site1.Coord.X % GridCellSize >= 0.0f) edge.Site1.Coord.X += 1.0f;
|
||||
edge.Site1.Coord.X % GridCellSize >= 0.0f) { edge.Site1.Coord.X += 1.0f; }
|
||||
edge.Site1.Coord.X = borders.Width - edge.Site1.Coord.X;
|
||||
mirroredSites.Add(edge.Site1);
|
||||
}
|
||||
if (!mirroredSites.Contains(edge.Site2))
|
||||
if (edge.Site2 != null && !mirroredSites.Contains(edge.Site2))
|
||||
{
|
||||
if (edge.Site2.Coord.X % GridCellSize < 1.0f &&
|
||||
edge.Site2.Coord.X % GridCellSize >= 0.0f) edge.Site2.Coord.X += 1.0f;
|
||||
edge.Site2.Coord.X % GridCellSize >= 0.0f) { edge.Site2.Coord.X += 1.0f; }
|
||||
edge.Site2.Coord.X = borders.Width - edge.Site2.Coord.X;
|
||||
mirroredSites.Add(edge.Site2);
|
||||
}
|
||||
@@ -759,6 +774,8 @@ namespace Barotrauma
|
||||
DebugConsole.NewMessage("Generated level with the seed " + seed + " (type: " + generationParams.Name + ")", Color.White);
|
||||
}
|
||||
|
||||
EntityCountAfterGenerate = Entity.GetEntityList().Count();
|
||||
|
||||
//assign an ID to make entity events work
|
||||
ID = FindFreeID();
|
||||
}
|
||||
@@ -1531,6 +1548,13 @@ namespace Barotrauma
|
||||
return tempCells;
|
||||
}
|
||||
|
||||
public string GetWreckIDTag(string originalTag, Submarine wreck)
|
||||
{
|
||||
string shortSeed = ToolBox.StringToInt(seed + wreck.Info.Name).ToString();
|
||||
if (shortSeed.Length > 6) { shortSeed = shortSeed.Substring(0, 6); }
|
||||
return originalTag + "_" + shortSeed;
|
||||
}
|
||||
|
||||
// For debugging
|
||||
private readonly Dictionary<Submarine, List<Vector2>> wreckPositions = new Dictionary<Submarine, List<Vector2>>();
|
||||
private readonly Dictionary<Submarine, List<Rectangle>> blockedRects = new Dictionary<Submarine, List<Rectangle>>();
|
||||
@@ -1546,6 +1570,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
wreckFiles.Shuffle(Rand.RandSync.Server);
|
||||
|
||||
int wreckCount = Math.Min(Loaded.GenerationParams.WreckCount, wreckFiles.Count);
|
||||
// Min distance between a wreck and the start/end/other wreck.
|
||||
float minDistance = Sonar.DefaultSonarRange;
|
||||
@@ -1610,7 +1635,6 @@ namespace Barotrauma
|
||||
Type = SubmarineInfo.SubmarineType.Wreck
|
||||
};
|
||||
Submarine wreck = new Submarine(info);
|
||||
//wreck.Load(unloadPrevious: false);
|
||||
wreck.MakeWreck();
|
||||
tempSW.Stop();
|
||||
Debug.WriteLine($"Wreck {wreck.Info.Name} loaded in { tempSW.ElapsedMilliseconds.ToString()} (ms)");
|
||||
@@ -1626,17 +1650,18 @@ namespace Barotrauma
|
||||
hull.WaterVolume = hull.Volume * Rand.Range(Loaded.GenerationParams.WreckFloodingHullMinWaterPercentage, Loaded.GenerationParams.WreckFloodingHullMaxWaterPercentage, Rand.RandSync.Server);
|
||||
}
|
||||
}
|
||||
if (Rand.Value(Rand.RandSync.Server) <= Loaded.GenerationParams.ThalamusProbability)
|
||||
// Only spawn thalamus when the wreck has some thalamus items defined.
|
||||
if (Rand.Value(Rand.RandSync.Server) <= Loaded.GenerationParams.ThalamusProbability && wreck.GetItems(false).Any(i => i.Prefab.Category == MapEntityCategory.Thalamus))
|
||||
{
|
||||
if (!wreck.CreateThalamus())
|
||||
if (!wreck.CreateWreckAI())
|
||||
{
|
||||
DebugConsole.NewMessage($"Failed to create thalamus inside {wreckName}.", Color.Red);
|
||||
wreck.DisableThalamus();
|
||||
DebugConsole.NewMessage($"Failed to create wreck AI inside {wreckName}.", Color.Red);
|
||||
wreck.DisableWreckAI();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wreck.DisableThalamus();
|
||||
wreck.DisableWreckAI();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1959,13 +1984,13 @@ namespace Barotrauma
|
||||
int corpseCount = Rand.Range(Loaded.GenerationParams.MinCorpseCount, Loaded.GenerationParams.MaxCorpseCount);
|
||||
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);
|
||||
var corpsePoints = allSpawnPoints.FindAll(wp => wp.SpawnType == SpawnType.Corpse);
|
||||
var spawnPoints = corpsePoints.Union(pathPoints).ToList();
|
||||
spawnPoints.Shuffle(Rand.RandSync.Unsynced);
|
||||
corpsePoints.Shuffle(Rand.RandSync.Unsynced);
|
||||
int spawnCounter = 0;
|
||||
for (int j = 0; j < corpseCount; j++)
|
||||
{
|
||||
WayPoint sp = spawnPoints.FirstOrDefault();
|
||||
WayPoint sp = corpsePoints.FirstOrDefault() ?? pathPoints.FirstOrDefault();
|
||||
JobPrefab job = sp?.AssignedJob;
|
||||
CorpsePrefab selectedPrefab;
|
||||
if (job == null)
|
||||
@@ -1979,18 +2004,19 @@ namespace Barotrauma
|
||||
selectedPrefab = GetCorpsePrefab(p => p.SpawnPosition == PositionType.Wreck && (p.Job == "any" || p.Job == job.Identifier));
|
||||
if (selectedPrefab == null)
|
||||
{
|
||||
spawnPoints.Remove(sp);
|
||||
sp = spawnPoints.FirstOrDefault(sp => sp.AssignedJob == null);
|
||||
corpsePoints.Remove(sp);
|
||||
pathPoints.Remove(sp);
|
||||
sp = corpsePoints.FirstOrDefault(sp => sp.AssignedJob == null) ?? pathPoints.FirstOrDefault(sp => sp.AssignedJob == null);
|
||||
// Deduce the job from the selected prefab
|
||||
selectedPrefab = GetCorpsePrefab(p => p.SpawnPosition == PositionType.Wreck);
|
||||
job = GetJobPrefab();
|
||||
}
|
||||
}
|
||||
if (selectedPrefab == null) { continue; }
|
||||
Vector2 pos;
|
||||
Vector2 worldPos;
|
||||
if (sp == null)
|
||||
{
|
||||
if (!TryGetExtraSpawnPoint(out pos))
|
||||
if (!TryGetExtraSpawnPoint(out worldPos))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -1998,15 +2024,17 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = sp.WorldPosition;
|
||||
spawnPoints.Remove(sp);
|
||||
worldPos = sp.WorldPosition;
|
||||
corpsePoints.Remove(sp);
|
||||
pathPoints.Remove(sp);
|
||||
}
|
||||
if (job == null) { continue; }
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job);
|
||||
var corpse = Character.Create(CharacterPrefab.HumanConfigFile, pos, ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
|
||||
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;
|
||||
corpse.EnableDespawn = false;
|
||||
selectedPrefab.GiveItems(corpse);
|
||||
selectedPrefab.GiveItems(corpse, wreck);
|
||||
corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false);
|
||||
spawnCounter++;
|
||||
|
||||
@@ -2018,9 +2046,9 @@ namespace Barotrauma
|
||||
|
||||
JobPrefab GetJobPrefab() => selectedPrefab.Job != null && selectedPrefab.Job != "any" ? JobPrefab.Get(selectedPrefab.Job) : JobPrefab.Random();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{spawnCounter}/{corpseCount} corpses spawned in {wreck.Info.Name}.", spawnCounter == corpseCount ? Color.Green : Color.Yellow);
|
||||
|
||||
#endif
|
||||
bool TryGetExtraSpawnPoint(out Vector2 point)
|
||||
{
|
||||
point = Vector2.Zero;
|
||||
|
||||
@@ -238,7 +238,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
|
||||
[Editable, Serialize("5000, 10000", true, description: "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
|
||||
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
|
||||
Serialize("5000, 10000", true, description: "The distance between the nodes that are used to generate the main path through the level (min, max). Larger values produce a straighter path.")]
|
||||
public Point MainPathNodeIntervalRange
|
||||
{
|
||||
get { return mainPathNodeIntervalRange; }
|
||||
@@ -256,7 +257,8 @@ namespace Barotrauma
|
||||
set { smallTunnelCount = MathHelper.Clamp(value, 0, 100); }
|
||||
}
|
||||
|
||||
[Editable, Serialize("5000, 10000", true, description: "The minimum and maximum length of small tunnels placed along the main path.")]
|
||||
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
|
||||
Serialize("5000, 10000", true, description: "The minimum and maximum length of small tunnels placed along the main path.")]
|
||||
public Point SmallTunnelLengthRange
|
||||
{
|
||||
get { return smallTunnelLengthRange; }
|
||||
@@ -349,8 +351,7 @@ namespace Barotrauma
|
||||
[Serialize(5, true, description: "The maximum number of corpses per wreck."), Editable(MinValueInt = 0, MaxValueInt = 20)]
|
||||
public int MaxCorpseCount { get; set; }
|
||||
|
||||
// TODO: default to 0
|
||||
[Serialize(1f, true, description: "How likely is it that a Thalamus inhabits a wreck. Percentage from 0 to 1 per wreck."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
[Serialize(0.0f, true, description: "How likely is it that a Thalamus inhabits a wreck. Percentage from 0 to 1 per wreck."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
public float ThalamusProbability { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, description: "How likely the water level of a hull inside a wreck is randomly set."), Editable(MinValueFloat = 0, MaxValueFloat = 1)]
|
||||
|
||||
@@ -358,19 +358,23 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
//check if there are any other contacts with the entity
|
||||
//check if there are contacts with any other fixture of the trigger
|
||||
//(the OnSeparation callback happens when two fixtures separate,
|
||||
//e.g. if a body stops touching the circular fixture at the end of a capsule-shaped body)
|
||||
ContactEdge contactEdge = fixtureA.Body.ContactList;
|
||||
while (contactEdge != null)
|
||||
{
|
||||
if (contactEdge.Contact != null &&
|
||||
contactEdge.Contact.Enabled &&
|
||||
contactEdge.Contact.IsTouching)
|
||||
{
|
||||
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
|
||||
contactEdge.Contact.FixtureB :
|
||||
contactEdge.Contact.FixtureA);
|
||||
if (otherEntity == entity) return;
|
||||
if (contactEdge.Contact.FixtureA != fixtureA && contactEdge.Contact.FixtureB != fixtureA)
|
||||
{
|
||||
var otherEntity = GetEntity(contactEdge.Contact.FixtureB == fixtureB ?
|
||||
contactEdge.Contact.FixtureB :
|
||||
contactEdge.Contact.FixtureA);
|
||||
if (otherEntity == entity) { return; }
|
||||
}
|
||||
}
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
@@ -418,10 +422,20 @@ namespace Barotrauma
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ParentTrigger != null && !ParentTrigger.IsTriggered) return;
|
||||
if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; }
|
||||
|
||||
triggerers.RemoveWhere(t => t.Removed);
|
||||
|
||||
if (physicsBody != null)
|
||||
{
|
||||
//failsafe to ensure triggerers get removed when they're far from the trigger
|
||||
float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(physicsBody.GetMaxExtent() * 5), 5000.0f);
|
||||
triggerers.RemoveWhere(t =>
|
||||
{
|
||||
return Vector2.Distance(t.WorldPosition, WorldPosition) > maxExtent;
|
||||
});
|
||||
}
|
||||
|
||||
bool isNotClient = true;
|
||||
#if CLIENT
|
||||
isNotClient = GameMain.Client == null;
|
||||
@@ -511,6 +525,7 @@ namespace Barotrauma
|
||||
ApplyForce(character.AnimController.Collider, deltaTime);
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
ApplyForce(limb.body, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.RuinGeneration
|
||||
@@ -174,7 +174,7 @@ namespace Barotrauma.RuinGeneration
|
||||
|
||||
public static void SaveAll()
|
||||
{
|
||||
XmlWriterSettings settings = new XmlWriterSettings
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
NewLineOnAttributes = true
|
||||
|
||||
@@ -654,13 +654,14 @@ namespace Barotrauma.RuinGeneration
|
||||
{
|
||||
connectionPanel.Locked = true;
|
||||
connectionPanel.CanBeSelected = false;
|
||||
connectionPanel.Item.ShouldBeSaved = false;
|
||||
}
|
||||
|
||||
// Hide wires for now
|
||||
// Hide wires
|
||||
if (ic is Wire wire)
|
||||
{
|
||||
wire.Hidden = true;
|
||||
wire.CanBeSelected = false;
|
||||
wire.Item.ShouldBeSaved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,17 @@ namespace Barotrauma
|
||||
//Prefabs.Remove(this);
|
||||
}
|
||||
|
||||
public readonly Submarine mainSub;
|
||||
public readonly SubmarineInfo subInfo;
|
||||
|
||||
public LinkedSubmarinePrefab(Submarine submarine)
|
||||
public LinkedSubmarinePrefab(SubmarineInfo subInfo)
|
||||
{
|
||||
this.mainSub = submarine;
|
||||
this.subInfo = subInfo;
|
||||
}
|
||||
|
||||
protected override void CreateInstance(Rectangle rect)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(Submarine.MainSub != null);
|
||||
|
||||
LinkedSubmarine.CreateDummy(Submarine.MainSub, mainSub.Info.FilePath, rect.Location.ToVector2());
|
||||
LinkedSubmarine.CreateDummy(Submarine.MainSub, subInfo.FilePath, rect.Location.ToVector2());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +112,9 @@ namespace Barotrauma
|
||||
(int)sl.wallVertices.Max(v => v.X + position.X),
|
||||
(int)sl.wallVertices.Min(v => v.Y + position.Y));
|
||||
|
||||
sl.Rect = new Rectangle(sl.rect.X, sl.rect.Y, sl.rect.Width - sl.rect.X, sl.rect.Y - sl.rect.Height);
|
||||
int width = sl.rect.Width - sl.rect.X;
|
||||
int height = sl.rect.Y - sl.rect.Height;
|
||||
sl.Rect = new Rectangle((int)(position.X - width / 2), (int)(position.Y + height / 2), width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -162,8 +163,7 @@ namespace Barotrauma
|
||||
public static LinkedSubmarine Load(XElement element, Submarine submarine)
|
||||
{
|
||||
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
|
||||
LinkedSubmarine linkedSub = null;
|
||||
|
||||
LinkedSubmarine linkedSub;
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
linkedSub = CreateDummy(submarine, element, pos);
|
||||
@@ -198,21 +198,26 @@ namespace Barotrauma
|
||||
for (int i = 0; i < linkedToIds.Length; i++)
|
||||
{
|
||||
linkedSub.linkedToID.Add((ushort)linkedToIds[i]);
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
if (FindEntityByID((ushort)linkedToIds[i]) is MapEntity linked)
|
||||
{
|
||||
linkedSub.linkedTo.Add(linked);
|
||||
}
|
||||
}
|
||||
}
|
||||
linkedSub.originalLinkedToID = (ushort)element.GetAttributeInt("originallinkedto", 0);
|
||||
linkedSub.originalMyPortID = (ushort)element.GetAttributeInt("originalmyport", 0);
|
||||
|
||||
|
||||
return linkedSub.loadSub ? linkedSub : null;
|
||||
}
|
||||
|
||||
public void LinkDummyToMainSubmarine()
|
||||
{
|
||||
if (Screen.Selected != GameMain.SubEditorScreen) { return; }
|
||||
for (int i = 0; i < linkedToID.Count; i++)
|
||||
{
|
||||
if (FindEntityByID(linkedToID[i]) is MapEntity linked)
|
||||
{
|
||||
linkedTo.Add(linked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (!loadSub) { return; }
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -56,6 +56,15 @@ namespace Barotrauma
|
||||
|
||||
Level.Loaded.TopBarrier.Enabled = false;
|
||||
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
character.AnimController.Frozen = true;
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
limb.body.PhysEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
cam.TargetPos = Vector2.Zero;
|
||||
float timer = 0.0f;
|
||||
float initialZoom = cam.Zoom;
|
||||
|
||||
@@ -283,6 +283,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool NoAITarget
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
@@ -354,6 +361,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
StairDirection = Prefab.StairDirection;
|
||||
NoAITarget = Prefab.NoAITarget;
|
||||
SerializableProperties = SerializableProperty.GetProperties(this);
|
||||
|
||||
InitProjSpecific();
|
||||
@@ -381,7 +389,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Only add ai targets automatically to submarine/outpost walls
|
||||
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !submarine.Info.IsWreck && !Prefab.NoAITarget)
|
||||
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !submarine.Info.IsWreck && !NoAITarget)
|
||||
{
|
||||
aiTarget = new AITarget(this)
|
||||
{
|
||||
@@ -818,7 +826,10 @@ namespace Barotrauma
|
||||
Vector2 sectionPos = new Vector2(
|
||||
Sections[sectionIndex].rect.X + Sections[sectionIndex].rect.Width / 2.0f,
|
||||
Sections[sectionIndex].rect.Y - Sections[sectionIndex].rect.Height / 2.0f);
|
||||
if (world && Submarine != null) sectionPos += Submarine.Position;
|
||||
if (world && Submarine != null)
|
||||
{
|
||||
sectionPos += Submarine.Position;
|
||||
}
|
||||
return sectionPos;
|
||||
}
|
||||
else
|
||||
@@ -839,7 +850,10 @@ namespace Barotrauma
|
||||
(float)Math.Cos(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation),
|
||||
(float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation)) * diffFromCenter;
|
||||
|
||||
if (world && Submarine != null) sectionPos += Submarine.Position;
|
||||
if (world && Submarine != null)
|
||||
{
|
||||
sectionPos += Submarine.Position;
|
||||
}
|
||||
return sectionPos;
|
||||
}
|
||||
|
||||
@@ -913,7 +927,7 @@ namespace Barotrauma
|
||||
//the structure doesn't have any other gap, log the structure being fixed
|
||||
if (noGaps && attacker != null)
|
||||
{
|
||||
GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall repaired by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
|
||||
GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall repaired by " + GameServer.CharacterLogName(attacker), ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#endif
|
||||
DebugConsole.Log("Removing gap (ID " + Sections[sectionIndex].gap.ID + ", section: " + sectionIndex + ") from wall " + ID);
|
||||
@@ -987,11 +1001,11 @@ namespace Barotrauma
|
||||
//the structure didn't have any other gaps yet, log the breach
|
||||
if (noGaps && attacker != null)
|
||||
{
|
||||
GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall breached by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
|
||||
GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall breached by " + GameServer.CharacterLogName(attacker), ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
float gapOpen = (damage / Prefab.Health - LeakThreshold) * (1.0f / (1.0f - LeakThreshold));
|
||||
Sections[sectionIndex].gap.Open = gapOpen;
|
||||
}
|
||||
@@ -1255,6 +1269,11 @@ namespace Barotrauma
|
||||
s.UseDropShadow = prefab.Body;
|
||||
}
|
||||
|
||||
if (element.Attribute("noaitarget") == null)
|
||||
{
|
||||
s.NoAITarget = prefab.NoAITarget;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1327,6 +1346,7 @@ namespace Barotrauma
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
|
||||
Sprite.ReloadXML();
|
||||
SpriteDepth = Sprite.Depth;
|
||||
NoAITarget = Prefab.NoAITarget;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -4,7 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -128,7 +128,7 @@ namespace Barotrauma
|
||||
|
||||
public PhysicsBody PhysicsBody
|
||||
{
|
||||
get { return subBody.Body; }
|
||||
get { return subBody?.Body; }
|
||||
}
|
||||
|
||||
public Rectangle Borders
|
||||
@@ -178,23 +178,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool? subsLeftBehind;
|
||||
public bool SubsLeftBehind
|
||||
{
|
||||
get
|
||||
{
|
||||
if (subsLeftBehind.HasValue) { return subsLeftBehind.Value; }
|
||||
|
||||
CheckSubsLeftBehind(Info.SubmarineElement);
|
||||
|
||||
return subsLeftBehind.Value;
|
||||
}
|
||||
//set { subsLeftBehind = value; }
|
||||
}
|
||||
public bool LeftBehindSubDockingPortOccupied
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public new Vector2 DrawPosition
|
||||
{
|
||||
@@ -222,7 +205,7 @@ namespace Barotrauma
|
||||
|
||||
public List<Vector2> HullVertices
|
||||
{
|
||||
get { return subBody.HullVertices; }
|
||||
get { return subBody?.HullVertices; }
|
||||
}
|
||||
|
||||
public bool AtDamageDepth
|
||||
@@ -232,7 +215,7 @@ namespace Barotrauma
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Barotrauma.Submarine (" + Info?.Name ?? "[NULL INFO]" + ")";
|
||||
return "Barotrauma.Submarine (" + (Info?.Name ?? "[NULL INFO]") + ", " + IdOffset + ")";
|
||||
}
|
||||
|
||||
public override bool Removed
|
||||
@@ -249,87 +232,69 @@ namespace Barotrauma
|
||||
ShowSonarMarker = false;
|
||||
PhysicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
TeamID = Character.TeamType.None;
|
||||
|
||||
string defaultTag = Level.Loaded.GetWreckIDTag("wreck_id", this);
|
||||
ReplaceIDCardTagRequirements("wreck_id", defaultTag);
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != this) { continue; }
|
||||
if (item.prefab.Identifier == "idcardwreck" || item.prefab.Identifier == "idcard")
|
||||
{
|
||||
foreach (string tag in item.GetTags().ToList())
|
||||
{
|
||||
if (tag == "smallitem") { continue; }
|
||||
string newTag = Level.Loaded.GetWreckIDTag(tag, this);
|
||||
item.ReplaceTag(tag, newTag);
|
||||
ReplaceIDCardTagRequirements(tag, newTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceIDCardTagRequirements(string oldTag, string newTag)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != this) { continue; }
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
ReplaceIDCardTagRequirement(ic, RelatedItem.RelationType.Picked, oldTag, newTag);
|
||||
ReplaceIDCardTagRequirement(ic, RelatedItem.RelationType.Equipped, oldTag, newTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ReplaceIDCardTagRequirement(ItemComponent ic, RelatedItem.RelationType relationType, string oldTag, string newTag)
|
||||
{
|
||||
if (!ic.requiredItems.ContainsKey(relationType)) { return; }
|
||||
foreach (RelatedItem requiredItem in ic.requiredItems[relationType])
|
||||
{
|
||||
int index = Array.IndexOf(requiredItem.Identifiers, oldTag);
|
||||
if (index == -1) { continue; }
|
||||
requiredItem.Identifiers[index] = newTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WreckAI ThalamusAI { get; private set; }
|
||||
public bool CreateThalamus()
|
||||
public WreckAI WreckAI { get; private set; }
|
||||
public bool CreateWreckAI()
|
||||
{
|
||||
MakeWreck();
|
||||
var thalamusPrefabs = ItemPrefab.Prefabs.Where(p => p.Category == MapEntityCategory.Thalamus || p.Tags.Contains("thalamus"));
|
||||
var brainPrefab = thalamusPrefabs.GetRandom(i => i.Tags.Contains("thalamusbrain"), Rand.RandSync.Server);
|
||||
if (brainPrefab == null) { return false; }
|
||||
var allItems = GetItems(false);
|
||||
var thalamusItems = allItems.FindAll(i => i.Prefab.Category == MapEntityCategory.Thalamus || i.HasTag("thalamus"));
|
||||
var hulls = GetHulls(false);
|
||||
Item brain = new Item(brainPrefab, Vector2.Zero, this);
|
||||
Vector2 negativeMargin = new Vector2(40, 20);
|
||||
Vector2 minSize = brain.Rect.Size.ToVector2() - negativeMargin;
|
||||
Vector2 maxSize = new Vector2(brain.Rect.Width * 3, brain.Rect.Height * 3);
|
||||
// First try to get a room that is not too big and not in the edges of the sub.
|
||||
// Also try not to create the brain in a room that already have carrier items inside.
|
||||
// Ignore hulls that have any linked hulls to keep the calculations simple.
|
||||
// Shrink the horizontal axis so that the brain is not placed in the left or right side, where we often have curved walls.
|
||||
// Also ignore hulls that have open gaps, because we'll want the room to be full of water. The room will be filled with water when the brain is inserted in the room.
|
||||
Rectangle shrinkedBounds = ToolBox.GetWorldBounds(WorldPosition.ToPoint(), new Point(Borders.Width - 500, Borders.Height));
|
||||
bool BaseCondition(Hull h) => h.RectWidth > minSize.X && h.RectHeight > minSize.Y && h.GetLinkedEntities<Hull>().None() && h.ConnectedGaps.None(g => g.Open > 0);
|
||||
bool IsNotTooBig(Hull h) => h.RectWidth < maxSize.X && h.RectHeight < maxSize.Y;
|
||||
bool IsNotInFringes(Hull h) => shrinkedBounds.ContainsWorld(h.WorldRect);
|
||||
bool DoesNotContainOtherItems(Hull h) => thalamusItems.None(i => i.CurrentHull == h);
|
||||
Hull brainHull = hulls.GetRandom(h => BaseCondition(h) && IsNotTooBig(h) && IsNotInFringes(h) && DoesNotContainOtherItems(h), Rand.RandSync.Server);
|
||||
if (brainHull == null)
|
||||
{
|
||||
brainHull = hulls.GetRandom(h => BaseCondition(h) && IsNotInFringes(h) && DoesNotContainOtherItems(h), Rand.RandSync.Server);
|
||||
}
|
||||
if (brainHull == null)
|
||||
{
|
||||
brainHull = hulls.GetRandom(h => BaseCondition(h) && (IsNotInFringes(h) || DoesNotContainOtherItems(h)), Rand.RandSync.Server);
|
||||
}
|
||||
if (brainHull == null)
|
||||
{
|
||||
brainHull = hulls.GetRandom(BaseCondition, Rand.RandSync.Server);
|
||||
}
|
||||
var thalamusStructs = StructurePrefab.Prefabs.Where(p => p.Category == MapEntityCategory.Thalamus);
|
||||
if (brainHull == null) { return false; }
|
||||
brainHull.WaterVolume = brainHull.Volume;
|
||||
brain.SetTransform(brainHull.SimPosition, rotation: 0, findNewHull: false);
|
||||
brain.CurrentHull = brainHull;
|
||||
var backgroundPrefab = thalamusStructs.GetRandom(i => i.Tags.Contains("brainroombackground"), Rand.RandSync.Server);
|
||||
if (backgroundPrefab != null)
|
||||
{
|
||||
new Structure(brainHull.Rect, backgroundPrefab, this);
|
||||
}
|
||||
var horizontalWallPrefab = thalamusStructs.GetRandom(p => p.Tags.Contains("thalamuswall_horizontal_decorative"), Rand.RandSync.Server);
|
||||
if (horizontalWallPrefab != null)
|
||||
{
|
||||
int height = (int)horizontalWallPrefab.Size.Y;
|
||||
int halfHeight = height / 2;
|
||||
int quarterHeight = halfHeight / 2;
|
||||
new Structure(new Rectangle(brainHull.Rect.Left, brainHull.Rect.Top + quarterHeight, brainHull.Rect.Width, height), horizontalWallPrefab, this);
|
||||
new Structure(new Rectangle(brainHull.Rect.Left, brainHull.Rect.Top - brainHull.Rect.Height + halfHeight + quarterHeight, brainHull.Rect.Width, height), horizontalWallPrefab, this);
|
||||
}
|
||||
var verticalWallPrefab = thalamusStructs.GetRandom(p => p.Tags.Contains("thalamuswall_vertical_decorative"), Rand.RandSync.Server);
|
||||
if (verticalWallPrefab != null)
|
||||
{
|
||||
int width = (int)verticalWallPrefab.Size.X;
|
||||
int halfWidth = width / 2;
|
||||
int quarterWidth = halfWidth / 2;
|
||||
new Structure(new Rectangle(brainHull.Rect.Left - quarterWidth, brainHull.Rect.Top, width, brainHull.Rect.Height), verticalWallPrefab, this);
|
||||
new Structure(new Rectangle(brainHull.Rect.Right - halfWidth - quarterWidth, brainHull.Rect.Top, width, brainHull.Rect.Height), verticalWallPrefab, this);
|
||||
}
|
||||
ThalamusAI = new WreckAI(this, brain, allItems);
|
||||
return true;
|
||||
WreckAI = new WreckAI(this);
|
||||
return WreckAI != null;
|
||||
}
|
||||
|
||||
public void DisableThalamus()
|
||||
public void DisableWreckAI()
|
||||
{
|
||||
var thalamusEntities = GetEntities(false, MapEntity.mapEntityList).FindAll(e => e.prefab.Category == MapEntityCategory.Thalamus || e.prefab.Tags.Contains("thalamus")).ToList();
|
||||
|
||||
foreach (var entity in thalamusEntities)
|
||||
if (WreckAI == null)
|
||||
{
|
||||
entity.Remove();
|
||||
WreckAI.RemoveThalamusItems(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
WreckAI?.Remove();
|
||||
WreckAI = null;
|
||||
}
|
||||
ThalamusAI?.Kill();
|
||||
ThalamusAI = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -775,7 +740,7 @@ namespace Barotrauma
|
||||
|
||||
public void FlipX(List<Submarine> parents = null)
|
||||
{
|
||||
if (parents == null) parents = new List<Submarine>();
|
||||
if (parents == null) { parents = new List<Submarine>(); }
|
||||
parents.Add(this);
|
||||
|
||||
flippedX = !flippedX;
|
||||
@@ -783,20 +748,25 @@ namespace Barotrauma
|
||||
Item.UpdateHulls();
|
||||
|
||||
List<Item> bodyItems = Item.ItemList.FindAll(it => it.Submarine == this && it.body != null);
|
||||
|
||||
List<MapEntity> subEntities = MapEntity.mapEntityList.FindAll(me => me.Submarine == this);
|
||||
|
||||
foreach (MapEntity e in subEntities)
|
||||
{
|
||||
if (e is Item) continue;
|
||||
if (e is LinkedSubmarine)
|
||||
if (e is LinkedSubmarine linkedSub)
|
||||
{
|
||||
Submarine sub = ((LinkedSubmarine)e).Sub;
|
||||
if (!parents.Contains(sub))
|
||||
Submarine sub = linkedSub.Sub;
|
||||
if (sub == null)
|
||||
{
|
||||
Vector2 relative1 = linkedSub.Position - SubBody.Position;
|
||||
relative1.X = -relative1.X;
|
||||
linkedSub.Rect = new Rectangle((relative1 + SubBody.Position).ToPoint(), linkedSub.Rect.Size);
|
||||
}
|
||||
else if (!parents.Contains(sub))
|
||||
{
|
||||
Vector2 relative1 = sub.SubBody.Position - SubBody.Position;
|
||||
relative1.X = -relative1.X;
|
||||
sub.SetPosition(relative1 + SubBody.Position);
|
||||
sub.SetPosition(relative1 + SubBody.Position, new List<Submarine>(parents));
|
||||
sub.FlipX(parents);
|
||||
}
|
||||
}
|
||||
@@ -814,7 +784,7 @@ namespace Barotrauma
|
||||
Vector2 pos = new Vector2(subBody.Position.X, subBody.Position.Y);
|
||||
subBody.Body.Remove();
|
||||
subBody = new SubmarineBody(this);
|
||||
SetPosition(pos);
|
||||
SetPosition(pos, new List<Submarine>(parents.Where(p => p != this)));
|
||||
|
||||
if (entityGrid != null)
|
||||
{
|
||||
@@ -853,14 +823,15 @@ namespace Barotrauma
|
||||
{
|
||||
//if (PlayerInput.KeyHit(InputType.Crouch) && (this == MainSub)) FlipX();
|
||||
|
||||
if (Level.Loaded == null || subBody == null) { return; }
|
||||
|
||||
if (Info.Type == SubmarineInfo.SubmarineType.Wreck)
|
||||
if (Info.IsWreck)
|
||||
{
|
||||
ThalamusAI?.Update(deltaTime);
|
||||
WreckAI?.Update(deltaTime);
|
||||
}
|
||||
|
||||
if (WorldPosition.Y < Level.MaxEntityDepth &&
|
||||
if (subBody?.Body == null) { return; }
|
||||
|
||||
if (Level.Loaded != null &&
|
||||
WorldPosition.Y < Level.MaxEntityDepth &&
|
||||
subBody.Body.Enabled &&
|
||||
(GameMain.NetworkMember?.RespawnManager == null || this != GameMain.NetworkMember.RespawnManager.RespawnShuttle))
|
||||
{
|
||||
@@ -887,17 +858,17 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
subBody.Body.LinearVelocity = new Vector2(
|
||||
LockX ? 0.0f : subBody.Body.LinearVelocity.X,
|
||||
LockY ? 0.0f : subBody.Body.LinearVelocity.Y);
|
||||
|
||||
|
||||
subBody.Update(deltaTime);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (MainSubs[i] == null) continue;
|
||||
if (this != MainSubs[i] && MainSubs[i].DockedTo.Contains(this)) return;
|
||||
if (MainSubs[i] == null) { continue; }
|
||||
if (this != MainSubs[i] && MainSubs[i].DockedTo.Contains(this)) { return; }
|
||||
}
|
||||
|
||||
//send updates more frequently if moving fast
|
||||
@@ -919,9 +890,9 @@ namespace Barotrauma
|
||||
prevPosition = position;
|
||||
}
|
||||
|
||||
public void SetPosition(Vector2 position, List<Submarine> checkd=null)
|
||||
public void SetPosition(Vector2 position, List<Submarine> checkd = null)
|
||||
{
|
||||
if (!MathUtils.IsValid(position)) return;
|
||||
if (!MathUtils.IsValid(position)) { return; }
|
||||
|
||||
if (checkd == null) { checkd = new List<Submarine>(); }
|
||||
if (checkd.Contains(this)) { return; }
|
||||
@@ -933,9 +904,16 @@ namespace Barotrauma
|
||||
|
||||
foreach (Submarine dockedSub in DockedTo)
|
||||
{
|
||||
if (dockedSub.PhysicsBody.BodyType == BodyType.Static)
|
||||
{
|
||||
if (ConnectedDockingPorts.TryGetValue(dockedSub, out DockingPort port))
|
||||
{
|
||||
port.Undock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Vector2? expectedLocation = CalculateDockOffset(this, dockedSub);
|
||||
if (expectedLocation == null) { continue; }
|
||||
|
||||
dockedSub.SetPosition(position + expectedLocation.Value, checkd);
|
||||
dockedSub.UpdateTransform(interpolate: false);
|
||||
}
|
||||
@@ -992,6 +970,11 @@ namespace Barotrauma
|
||||
return list.FindAll(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetEntities<T>(bool includingConnectedSubs, IEnumerable<T> list) where T : MapEntity
|
||||
{
|
||||
return list.Where(e => IsEntityFoundOnThisSub(e, includingConnectedSubs));
|
||||
}
|
||||
|
||||
public bool IsEntityFoundOnThisSub(MapEntity entity, bool includingConnectedSubs)
|
||||
{
|
||||
if (entity == null) { return false; }
|
||||
@@ -999,7 +982,7 @@ namespace Barotrauma
|
||||
if (entity.Submarine == null) { return false; }
|
||||
if (includingConnectedSubs)
|
||||
{
|
||||
return GetConnectedSubs().Any(s => s == entity.Submarine && entity.Submarine.TeamID == TeamID);
|
||||
return GetConnectedSubs().Any(s => s == entity.Submarine && entity.Submarine.TeamID == TeamID && entity.Submarine.Info.Type == Info.Type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1182,6 +1165,13 @@ namespace Barotrauma
|
||||
Loading = false;
|
||||
|
||||
MapEntity.MapLoaded(newEntities, true);
|
||||
foreach (MapEntity me in MapEntity.mapEntityList)
|
||||
{
|
||||
if (me is LinkedSubmarine linkedSub && linkedSub.Submarine == this)
|
||||
{
|
||||
linkedSub.LinkDummyToMainSubmarine();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Hull hull in matchingHulls)
|
||||
{
|
||||
@@ -1221,28 +1211,6 @@ namespace Barotrauma
|
||||
return sub;
|
||||
}
|
||||
|
||||
public void CheckSubsLeftBehind(XElement element = null)
|
||||
{
|
||||
if (element == null) { element = Info.SubmarineElement; }
|
||||
|
||||
subsLeftBehind = false;
|
||||
LeftBehindSubDockingPortOccupied = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("linkedsubmarine")) { continue; }
|
||||
if (subElement.Attribute("location") == null) { continue; }
|
||||
|
||||
subsLeftBehind = true;
|
||||
ushort targetDockingPortID = (ushort)subElement.GetAttributeInt("originallinkedto", 0);
|
||||
XElement targetPortElement = targetDockingPortID == 0 ? null :
|
||||
element.Elements().FirstOrDefault(e => e.GetAttributeInt("ID", 0) == targetDockingPortID);
|
||||
if (targetPortElement != null && targetPortElement.GetAttributeIntArray("linked", new int[0]).Length > 0)
|
||||
{
|
||||
LeftBehindSubDockingPortOccupied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveToXElement(XElement element)
|
||||
{
|
||||
element.Add(new XAttribute("name", Info.Name));
|
||||
@@ -1264,14 +1232,17 @@ namespace Barotrauma
|
||||
e.Save(element);
|
||||
}
|
||||
|
||||
CheckSubsLeftBehind(element);
|
||||
Info.CheckSubsLeftBehind(element);
|
||||
}
|
||||
|
||||
public bool SaveAs(string filePath, MemoryStream previewImage = null)
|
||||
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage = null)
|
||||
{
|
||||
var newInfo = new SubmarineInfo(this);
|
||||
newInfo.FilePath = filePath;
|
||||
newInfo.Name = Path.GetFileNameWithoutExtension(filePath);
|
||||
var newInfo = new SubmarineInfo(this)
|
||||
{
|
||||
GameVersion = GameMain.Version,
|
||||
FilePath = filePath,
|
||||
Name = Path.GetFileNameWithoutExtension(filePath)
|
||||
};
|
||||
Info.Dispose(); Info = newInfo;
|
||||
|
||||
return newInfo.SaveAs(filePath, previewImage);
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma
|
||||
farseerBody.UserData = this;
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != submarine) continue;
|
||||
if (wall.Submarine != submarine || wall.IsPlatform) { continue; }
|
||||
|
||||
Rectangle rect = wall.Rect;
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine != submarine) continue;
|
||||
if (hull.Submarine != submarine) { continue; }
|
||||
|
||||
Rectangle rect = hull.Rect;
|
||||
farseerBody.CreateRectangle(
|
||||
@@ -167,7 +167,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.StaticBodyConfig == null || item.Submarine != submarine) continue;
|
||||
if (item.StaticBodyConfig == null || item.Submarine != submarine) { continue; }
|
||||
|
||||
float radius = item.StaticBodyConfig.GetAttributeFloat("radius", 0.0f) * item.Scale;
|
||||
float width = item.StaticBodyConfig.GetAttributeFloat("width", 0.0f) * item.Scale;
|
||||
@@ -180,7 +180,7 @@ namespace Barotrauma
|
||||
|
||||
if (width > 0.0f && height > 0.0f)
|
||||
{
|
||||
farseerBody.CreateRectangle(simWidth, simHeight, 5.0f, simPos).UserData = item;
|
||||
item.StaticFixtures.Add(farseerBody.CreateRectangle(simWidth, simHeight, 5.0f, simPos));
|
||||
|
||||
minExtents.X = Math.Min(item.Position.X - width / 2, minExtents.X);
|
||||
minExtents.Y = Math.Min(item.Position.Y - height / 2, minExtents.Y);
|
||||
@@ -189,9 +189,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (radius > 0.0f && width > 0.0f)
|
||||
{
|
||||
farseerBody.CreateRectangle(simWidth, simRadius * 2, 5.0f, simPos).UserData = item;
|
||||
farseerBody.CreateCircle(simRadius, 5.0f, simPos - Vector2.UnitX * simWidth / 2).UserData = item;
|
||||
farseerBody.CreateCircle(simRadius, 5.0f, simPos + Vector2.UnitX * simWidth / 2).UserData = item;
|
||||
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));
|
||||
minExtents.X = Math.Min(item.Position.X - width / 2 - radius, minExtents.X);
|
||||
minExtents.Y = Math.Min(item.Position.Y - radius, minExtents.Y);
|
||||
maxExtents.X = Math.Max(item.Position.X + width / 2 + radius, maxExtents.X);
|
||||
@@ -199,9 +199,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (radius > 0.0f && height > 0.0f)
|
||||
{
|
||||
farseerBody.CreateRectangle(simRadius * 2, height, 5.0f, simPos).UserData = item;
|
||||
farseerBody.CreateCircle(simRadius, 5.0f, simPos - Vector2.UnitY * simHeight / 2).UserData = item;
|
||||
farseerBody.CreateCircle(simRadius, 5.0f, simPos + Vector2.UnitX * simHeight / 2).UserData = item;
|
||||
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));
|
||||
minExtents.X = Math.Min(item.Position.X - radius, minExtents.X);
|
||||
minExtents.Y = Math.Min(item.Position.Y - height / 2 - radius, minExtents.Y);
|
||||
maxExtents.X = Math.Max(item.Position.X + radius, maxExtents.X);
|
||||
@@ -209,12 +209,13 @@ namespace Barotrauma
|
||||
}
|
||||
else if (radius > 0.0f)
|
||||
{
|
||||
farseerBody.CreateCircle(simRadius, 5.0f, simPos).UserData = item;
|
||||
item.StaticFixtures.Add(farseerBody.CreateCircle(simRadius, 5.0f, simPos));
|
||||
minExtents.X = Math.Min(item.Position.X - radius, minExtents.X);
|
||||
minExtents.Y = Math.Min(item.Position.Y - radius, minExtents.Y);
|
||||
maxExtents.X = Math.Max(item.Position.X + radius, maxExtents.X);
|
||||
maxExtents.Y = Math.Max(item.Position.Y + radius, maxExtents.Y);
|
||||
}
|
||||
item.StaticFixtures.ForEach(f => f.UserData = item);
|
||||
}
|
||||
|
||||
Borders = new Rectangle((int)minExtents.X, (int)maxExtents.Y, (int)(maxExtents.X - minExtents.X), (int)(maxExtents.Y - minExtents.Y));
|
||||
@@ -301,7 +302,7 @@ namespace Barotrauma
|
||||
//-------------------------
|
||||
|
||||
//if outside left or right edge of the level
|
||||
if (Position.X < 0 || Position.X > Level.Loaded.Size.X)
|
||||
if (Level.Loaded != null && (Position.X < 0 || Position.X > Level.Loaded.Size.X))
|
||||
{
|
||||
Rectangle worldBorders = Borders;
|
||||
worldBorders.Location += MathUtils.ToPoint(Position);
|
||||
@@ -385,12 +386,13 @@ namespace Barotrauma
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null && c.AnimController.CanEnterSubmarine) continue;
|
||||
if (c.AnimController.CurrentHull != null && c.AnimController.CanEnterSubmarine) { continue; }
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
//if the character isn't inside the bounding box, continue
|
||||
if (!Submarine.RectContains(worldBorders, limb.WorldPosition)) continue;
|
||||
if (!Submarine.RectContains(worldBorders, limb.WorldPosition)) { continue; }
|
||||
|
||||
//cast a line from the position of the character to the same direction as the translation of the sub
|
||||
//and see where it intersects with the bounding box
|
||||
@@ -454,11 +456,11 @@ namespace Barotrauma
|
||||
|
||||
depthDamageTimer -= deltaTime;
|
||||
|
||||
if (depthDamageTimer > 0.0f) return;
|
||||
if (depthDamageTimer > 0.0f) { return; }
|
||||
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != submarine) continue;
|
||||
if (wall.Submarine != submarine) { continue; }
|
||||
|
||||
if (wall.Health < depth * 0.01f)
|
||||
{
|
||||
@@ -498,10 +500,14 @@ namespace Barotrauma
|
||||
}
|
||||
return collision;
|
||||
}
|
||||
if (f2.Body.UserData is Character character)
|
||||
else if (f2.Body.UserData is Character character)
|
||||
{
|
||||
return CheckCharacterCollision(contact, character);
|
||||
}
|
||||
else if (f2.UserData is Items.Components.DockingPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (impactQueue)
|
||||
{
|
||||
@@ -692,8 +698,8 @@ namespace Barotrauma
|
||||
|
||||
//find all contacts between this sub and level walls
|
||||
List<Contact> levelContacts = new List<Contact>();
|
||||
ContactEdge contactEdge = Body.FarseerBody.ContactList;
|
||||
while (contactEdge.Next != null)
|
||||
ContactEdge contactEdge = Body?.FarseerBody?.ContactList;
|
||||
while (contactEdge?.Next != null)
|
||||
{
|
||||
if (contactEdge.Contact.Enabled &&
|
||||
contactEdge.Other.UserData is VoronoiCell &&
|
||||
@@ -705,7 +711,7 @@ namespace Barotrauma
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
|
||||
if (levelContacts.Count == 0) return;
|
||||
if (levelContacts.Count == 0) { return; }
|
||||
|
||||
//if this sub is in contact with the level, apply artifical impacts
|
||||
//to both subs to prevent the other sub from bouncing on top of this one
|
||||
@@ -794,6 +800,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f);
|
||||
}
|
||||
c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);
|
||||
|
||||
@@ -2,7 +2,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -68,6 +68,8 @@ namespace Barotrauma
|
||||
public bool IsOutpost => Type == SubmarineType.Outpost;
|
||||
public bool IsWreck => Type == SubmarineType.Wreck;
|
||||
|
||||
public bool IsPlayer => Type == SubmarineType.Player;
|
||||
|
||||
public enum SubmarineType { Player, Outpost, Wreck }
|
||||
public SubmarineType Type { get; set; }
|
||||
|
||||
@@ -99,7 +101,11 @@ namespace Barotrauma
|
||||
set;
|
||||
}
|
||||
|
||||
public readonly XElement SubmarineElement;
|
||||
public XElement SubmarineElement
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -126,6 +132,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool? subsLeftBehind;
|
||||
public bool SubsLeftBehind
|
||||
{
|
||||
get
|
||||
{
|
||||
if (subsLeftBehind.HasValue) { return subsLeftBehind.Value; }
|
||||
CheckSubsLeftBehind(SubmarineElement);
|
||||
return subsLeftBehind.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool LeftBehindSubDockingPortOccupied
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
//constructors & generation ----------------------------------------------------
|
||||
public SubmarineInfo()
|
||||
{
|
||||
@@ -135,7 +157,7 @@ namespace Barotrauma
|
||||
RequiredContentPackages = new HashSet<string>();
|
||||
}
|
||||
|
||||
public SubmarineInfo(string filePath, string hash = "", XElement element = null)
|
||||
public SubmarineInfo(string filePath, string hash = "", XElement element = null, bool tryLoad = true)
|
||||
{
|
||||
FilePath = filePath;
|
||||
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
|
||||
@@ -160,41 +182,23 @@ namespace Barotrauma
|
||||
|
||||
RequiredContentPackages = new HashSet<string>();
|
||||
|
||||
if (element == null)
|
||||
if (element == null && tryLoad)
|
||||
{
|
||||
XDocument doc = null;
|
||||
int maxLoadRetries = 4;
|
||||
for (int i = 0; i <= maxLoadRetries; i++)
|
||||
{
|
||||
doc = OpenFile(filePath, out Exception e);
|
||||
if (e != null && !(e is IOException)) { break; }
|
||||
if (doc != null || i == maxLoadRetries || !File.Exists(filePath)) { break; }
|
||||
DebugConsole.NewMessage("Opening submarine file \"" + filePath + "\" failed, retrying in 250 ms...");
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
if (doc == null || doc.Root == null)
|
||||
{
|
||||
IsFileCorrupted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(hash))
|
||||
{
|
||||
StartHashDocTask(doc);
|
||||
}
|
||||
|
||||
SubmarineElement = doc.Root;
|
||||
Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
SubmarineElement = element;
|
||||
}
|
||||
|
||||
Name = SubmarineElement.GetAttributeString("name", null) ?? Name;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public SubmarineInfo(Submarine sub) : this(sub.Info)
|
||||
{
|
||||
GameVersion = GameMain.Version;
|
||||
SubmarineElement = new XElement("Submarine");
|
||||
sub.SaveToXElement(SubmarineElement);
|
||||
Init();
|
||||
@@ -222,6 +226,30 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
XDocument doc = null;
|
||||
int maxLoadRetries = 4;
|
||||
for (int i = 0; i <= maxLoadRetries; i++)
|
||||
{
|
||||
doc = OpenFile(FilePath, out Exception e);
|
||||
if (e != null && !(e is System.IO.IOException)) { break; }
|
||||
if (doc != null || i == maxLoadRetries || !File.Exists(FilePath)) { break; }
|
||||
DebugConsole.NewMessage("Opening submarine file \"" + FilePath + "\" failed, retrying in 250 ms...");
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
if (doc == null || doc.Root == null)
|
||||
{
|
||||
IsFileCorrupted = true;
|
||||
return;
|
||||
}
|
||||
if (hash == null)
|
||||
{
|
||||
StartHashDocTask(doc);
|
||||
}
|
||||
SubmarineElement = doc.Root;
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
DisplayName = TextManager.Get("Submarine.Name." + Name, true);
|
||||
@@ -317,8 +345,31 @@ namespace Barotrauma
|
||||
Tags &= ~tag;
|
||||
}
|
||||
|
||||
public void CheckSubsLeftBehind(XElement element = null)
|
||||
{
|
||||
if (element == null) { element = SubmarineElement; }
|
||||
|
||||
subsLeftBehind = false;
|
||||
LeftBehindSubDockingPortOccupied = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("linkedsubmarine", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (subElement.Attribute("location") == null) { continue; }
|
||||
|
||||
subsLeftBehind = true;
|
||||
ushort targetDockingPortID = (ushort)subElement.GetAttributeInt("originallinkedto", 0);
|
||||
XElement targetPortElement = targetDockingPortID == 0 ? null :
|
||||
element.Elements().FirstOrDefault(e => e.GetAttributeInt("ID", 0) == targetDockingPortID);
|
||||
if (targetPortElement != null && targetPortElement.GetAttributeIntArray("linked", new int[0]).Length > 0)
|
||||
{
|
||||
LeftBehindSubDockingPortOccupied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//saving/loading ----------------------------------------------------
|
||||
public bool SaveAs(string filePath, MemoryStream previewImage=null)
|
||||
public bool SaveAs(string filePath, System.IO.MemoryStream previewImage=null)
|
||||
{
|
||||
var newElement = new XElement(SubmarineElement.Name,
|
||||
SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase)),
|
||||
@@ -405,7 +456,11 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
filePaths = Directory.GetFiles(SavePath).ToList();
|
||||
subDirectories = Directory.GetDirectories(SavePath);
|
||||
subDirectories = Directory.GetDirectories(SavePath).Where(s =>
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(s);
|
||||
return (dir.Attributes & System.IO.FileAttributes.Hidden) == 0;
|
||||
}).ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -504,12 +559,12 @@ namespace Barotrauma
|
||||
|
||||
if (extension == ".sub")
|
||||
{
|
||||
Stream stream = null;
|
||||
System.IO.Stream stream = null;
|
||||
try
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(file);
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
catch (System.IO.FileNotFoundException e)
|
||||
{
|
||||
exception = e;
|
||||
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (File not found) " + Environment.StackTrace, e);
|
||||
|
||||
@@ -124,13 +124,15 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (iconSprites == null)
|
||||
{
|
||||
iconSprites = new Dictionary<SpawnType, Sprite>()
|
||||
iconSprites = new Dictionary<string, Sprite>()
|
||||
{
|
||||
{ SpawnType.Path, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,0,128,128)) },
|
||||
{ SpawnType.Human, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,0,128,128)) },
|
||||
{ SpawnType.Enemy, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256,0,128,128)) },
|
||||
{ SpawnType.Cargo, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384,0,128,128)) },
|
||||
{ SpawnType.Corpse, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512,0,128,128)) }
|
||||
{ "Path", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,0,128,128)) },
|
||||
{ "Human", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,0,128,128)) },
|
||||
{ "Enemy", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256,0,128,128)) },
|
||||
{ "Cargo", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384,0,128,128)) },
|
||||
{ "Corpse", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512,0,128,128)) },
|
||||
{ "Ladder", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(0,128,128,128)) },
|
||||
{ "Door", new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(128,128,128,128)) }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user