Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -18,45 +18,72 @@ namespace Barotrauma.MapCreatures.Behavior
public readonly BallastFloraBehavior? ParentBallastFlora;
public int ID = -1;
public Item ClaimedItem;
public Item? ClaimedItem;
public int ClaimedItemId = -1;
public float MaxHealth = 100f;
public float Health = 100f;
private float health = 100;
public float Health
{
get { return health; }
set { health = MathHelper.Clamp(value, 0.0f, MaxHealth); }
}
public float RemoveTimer = 60.0f;
public bool SpawningItem;
public Item? AttackItem;
public bool IsRoot;
/// <summary>
/// Decorative branches that grow around the root
/// </summary>
public bool IsRootGrowth;
public bool Removed;
public bool DisconnectedFromRoot;
public Hull? CurrentHull;
public float Pulse = 1.0f;
private bool inflate;
private float pulseDelay = Rand.Range(0f, 3f);
public readonly BallastFloraBranch? ParentBranch;
/// <summary>
/// How far from the root this branch is
/// </summary>
public readonly int BranchDepth;
public float AccumulatedDamage;
public float DamageVisualizationTimer;
public Vector2 ShakeAmount;
// Adjacent tiles, used to free up sides when this branch gets removed
public readonly Dictionary<TileSide, BallastFloraBranch> Connections = new Dictionary<TileSide, BallastFloraBranch>();
public BallastFloraBranch(BallastFloraBehavior? parent, Vector2 position, VineTileType type, FoliageConfig? flowerConfig = null, FoliageConfig? leafConfig = null, Rectangle? rect = null)
public BallastFloraBranch(BallastFloraBehavior? parent, BallastFloraBranch? parentBranch, Vector2 position, VineTileType type, FoliageConfig? flowerConfig = null, FoliageConfig? leafConfig = null, Rectangle? rect = null)
: base(null, position, type, flowerConfig, leafConfig, rect)
{
ParentBranch = parentBranch;
ParentBallastFlora = parent;
if (parentBranch != null)
{
BranchDepth = parentBranch.BranchDepth + 1;
}
}
public void UpdateHealth()
{
if (MaxHealth <= Health) { return; }
Color healthColor = Color.White * (1.0f - Health / MaxHealth);
HealthColor = healthColor;
HealthColor = Color.Lerp(HealthColor, healthColor, 0.05f);
}
public void UpdatePulse(float deltaTime, float inflateSpeed, float deflateSpeed, float delay)
{
if (ParentBallastFlora == null) { return; }
if (ParentBallastFlora == null || DisconnectedFromRoot) { return; }
if (pulseDelay > 0)
{
@@ -91,7 +118,7 @@ namespace Barotrauma.MapCreatures.Behavior
public List<Tuple<Vector2, Vector2>> debugSearchLines = new List<Tuple<Vector2, Vector2>>();
#endif
private static List<BallastFloraBehavior> _entityList = new List<BallastFloraBehavior>();
private readonly static List<BallastFloraBehavior> _entityList = new List<BallastFloraBehavior>();
public static IEnumerable<BallastFloraBehavior> EntityList => _entityList;
public enum NetworkHeader
@@ -101,119 +128,126 @@ namespace Barotrauma.MapCreatures.Behavior
BranchCreate,
BranchRemove,
BranchDamage,
Infect
Infect,
Remove
}
public enum AttackType
{
Fire,
Explosives,
Other
Other,
CutFromRoot
}
public struct AITarget
{
public string[] Tags;
public Identifier[] Tags;
public int Priority;
public AITarget(XElement element)
public AITarget(ContentXElement element)
{
Tags = element.GetAttributeStringArray("tags", new string[0]);
Tags = element.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>())!;
Priority = element.GetAttributeInt("priority", 0);
}
public bool Matches(Item item)
{
foreach (string tag in item.GetTags())
{
foreach (Identifier targetTag in Tags)
{
foreach (string targetTag in Tags)
{
if (tag == targetTag) { return true; }
}
if (item.HasTag(targetTag)) { return true; }
}
return false;
}
}
[Serialize(0.25f, true, "Scale of the branches")]
[Serialize(0.25f, IsPropertySaveable.Yes, "Scale of the branches.")]
public float BaseBranchScale { get; set; }
[Serialize(0.25f, true, "Scale of the flowers")]
[Serialize(0.25f, IsPropertySaveable.Yes, "Scale of the flowers.")]
public float BaseFlowerScale { get; set; }
[Serialize(0.5f, true, "Scale of the leaves")]
[Serialize(0.5f, IsPropertySaveable.Yes, "Scale of the leaves.")]
public float BaseLeafScale { get; set; }
[Serialize(0.33f, true, "Chance for a flower to appear on the branch")]
[Serialize(0.33f, IsPropertySaveable.Yes, "Chance for a flower to appear on a branch.")]
public float FlowerProbability { get; set; }
[Serialize(0.7f, true, "Change for leaves to appear for the branch")]
[Serialize(0.7f, IsPropertySaveable.Yes, "Chance for leaves to appear on a branch.")]
public float LeafProbability { get; set; }
[Serialize(3f, true, "Delay between pulses")]
[Serialize(3f, IsPropertySaveable.Yes, "Delay between pulses.")]
public float PulseDelay { get; set; }
[Serialize(3f, true, "How fast the flower inflates during a pulse")]
[Serialize(3f, IsPropertySaveable.Yes, "How fast the flower inflates during a pulse.")]
public float PulseInflateSpeed { get; set; }
[Serialize(1f, true, "How fast the flower deflates")]
[Serialize(1f, IsPropertySaveable.Yes, "How fast the flower deflates.")]
public float PulseDeflateSpeed { get; set; }
[Serialize(32, true, "How many vines must grow before the plant breaks thru the wall")]
[Serialize(32, IsPropertySaveable.Yes, "How many vines must grow before the plant breaks through the wall.")]
public int BreakthroughPoint { get; set; }
[Serialize(false, true, "Has the plant grown large enough to expose itself")]
[Serialize(false, IsPropertySaveable.Yes, "Has the plant grown large enough to expose itself.")]
public bool HasBrokenThrough { get; set; }
[Serialize(300, true, "How far the ballast flora can detect items")]
[Serialize(300, IsPropertySaveable.Yes, "How far the ballast flora can detect items from.")]
public int Sight { get; set; }
[Serialize(100, true, "How much health the branches have")]
[Serialize(100, IsPropertySaveable.Yes, "How much health the branches have.")]
public int BranchHealth { get; set; }
[Serialize(400, true, "How much health the stem has")]
public int StemHealth { get; set; }
[Serialize(400, IsPropertySaveable.Yes, "How much health the root has.")]
public int RootHealth { get; set; }
[Serialize(300f, true, "How much power the ballast flora takes from junction boxes")]
[Serialize(0.0005f, IsPropertySaveable.Yes, "How fast the root's health regenerates per each grown branch.")]
public float HealthRegenPerBranch { get; set; }
[Serialize(30, IsPropertySaveable.Yes, "How far away from the root branches can regenerate health (in number of branches). The amount of regen decreases lineary further from the root.")]
public int MaxBranchHealthRegenDistance { get; set; }
[Serialize("255,255,255,255", IsPropertySaveable.Yes)]
public Color RootColor { get; set; }
[Serialize(300f, IsPropertySaveable.Yes, "How much power the ballast flora takes from junction boxes.")]
public float PowerConsumptionMin { get; set; }
[Serialize(3000f, true, "How much the power drain spikes")]
[Serialize(3000f, IsPropertySaveable.Yes, "How much the power drain spikes.")]
public float PowerConsumptionMax { get; set; }
[Serialize(10f, true, "How long it takes for power drain to wind down")]
[Serialize(10f, IsPropertySaveable.Yes, "How long it takes for power drain to wind down.")]
public float PowerConsumptionDuration { get; set; }
[Serialize(250f, true, "How much power does it take to accelerate growth")]
[Serialize(250f, IsPropertySaveable.Yes, "How much power does it take to accelerate growth.")]
public float PowerRequirement { get; set; }
[Serialize(5f, true, "Maximum anger, anger increases when the plant gets damaged and increases growth speed")]
[Serialize(5f, IsPropertySaveable.Yes, "Maximum anger, anger increases when the plant gets damaged and increases growth speed.")]
public float MaxAnger { get; set; }
[Serialize(10000f, true, "Maximum power buffer")]
[Serialize(10000f, IsPropertySaveable.Yes, "Maximum power buffer.")]
public float MaxPowerCapacity { get; set; }
[Serialize("", true, "Item prefab that is spawned when threatened")]
public string AttackItemPrefab { get; set; } = "";
[Serialize("", IsPropertySaveable.Yes, "Item prefab that is spawned when threatened.")]
public Identifier AttackItemPrefab { get; set; } = Identifier.Empty;
[Serialize(0.8f, true, "How resistant the ballast flora is to exlposives before it blooms")]
[Serialize(0.8f, IsPropertySaveable.Yes, "How resistant the ballast flora is to explosives before it blooms.")]
public float ExplosionResistance { get; set; }
[Serialize(5f, true, "How much damage is taken from open fires")]
[Serialize(5f, IsPropertySaveable.Yes, "How much damage is taken from open fires.")]
public float FireVulnerability { get; set; }
[Serialize(0.5f, true, "How much resistance against fire is gained while submerged.")]
[Serialize(0.5f, IsPropertySaveable.Yes, "How much resistance against fire is gained while submerged.")]
public float SubmergedWaterResistance { get; set; }
[Serialize(0.8f, true, "What depth the branches will be drawn on")]
[Serialize(0.8f, IsPropertySaveable.Yes, "What depth the branches will be drawn on.")]
public float BranchDepth { get; set; }
[Serialize("", true, "What sound to play when the ballast flora bursts thru walls")]
[Serialize("", IsPropertySaveable.Yes, "What sound to play when the ballast flora bursts through walls.")]
public string BurstSound { get; set; } = "";
private float availablePower;
[Serialize(0f, true, "How much power the ballast flora has stored.")]
[Serialize(0f, IsPropertySaveable.Yes, "How much power the ballast flora has stored.")]
public float AvailablePower
{
get => availablePower;
@@ -222,7 +256,7 @@ namespace Barotrauma.MapCreatures.Behavior
private float anger;
[Serialize(1f, true, "How enraged the flora is, affects how fast it grows.")]
[Serialize(1f, IsPropertySaveable.Yes, "How enraged the flora is, affects how fast it grows.")]
public float Anger
{
get => anger;
@@ -235,13 +269,13 @@ namespace Barotrauma.MapCreatures.Behavior
public BallastFloraPrefab Prefab { get; private set; }
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
public Vector2 Offset;
public readonly List<Item> ClaimedTargets = new List<Item>();
public readonly List<PowerTransfer> ClaimedJunctionBoxes = new List<PowerTransfer>();
public readonly List<PowerContainer> ClaimedBatteries = new List<PowerContainer>();
public readonly HashSet<Item> ClaimedTargets = new HashSet<Item>();
public readonly HashSet<PowerTransfer> ClaimedJunctionBoxes = new HashSet<PowerTransfer>();
public readonly HashSet<PowerContainer> ClaimedBatteries = new HashSet<PowerContainer>();
public readonly Dictionary<Item, int> IgnoredTargets = new Dictionary<Item, int>();
private readonly List<Tuple<UInt16, int>> tempClaimedTargets = new List<Tuple<ushort, int>>();
@@ -252,11 +286,12 @@ namespace Barotrauma.MapCreatures.Behavior
public float PowerConsumptionTimer;
private float defenseCooldown, toxinsCooldown, fireCheckCooldown;
private float damageIndicatorTimer, selfDamageTimer, toxinsTimer;
private float selfDamageTimer, toxinsTimer;
private readonly List<BallastFloraBranch> branchesVulnerableToFire = new List<BallastFloraBranch>();
public readonly List<BallastFloraBranch> Branches = new List<BallastFloraBranch>();
private BallastFloraBranch? root;
private readonly List<Body> bodies = new List<Body>();
public readonly BallastFloraStateMachine StateMachine;
@@ -319,15 +354,15 @@ namespace Barotrauma.MapCreatures.Behavior
SerializableProperties = SerializableProperty.DeserializeProperties(this, prefab.Element);
LoadPrefab(prefab.Element);
StateMachine = new BallastFloraStateMachine(this);
if (firstGrowth) { GenerateStem(); }
if (firstGrowth) { GenerateRoot(); }
_entityList.Add(this);
}
partial void LoadPrefab(XElement element);
partial void LoadPrefab(ContentXElement element);
public void LoadTargets(XElement element)
public void LoadTargets(ContentXElement element)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
Targets.Add(new AITarget(subElement));
}
@@ -358,6 +393,10 @@ namespace Barotrauma.MapCreatures.Behavior
{
be.Add(new XAttribute("claimed", (int)(branch.ClaimedItem?.ID ?? -1)));
}
if (branch.ParentBranch != null)
{
be.Add(new XAttribute("parentbranch", (int)(branch.ParentBranch?.ID ?? -1)));
}
saveElement.Add(be);
}
@@ -382,7 +421,7 @@ namespace Barotrauma.MapCreatures.Behavior
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
Offset = element.GetAttributeVector2("offset", Vector2.Zero);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -412,8 +451,15 @@ namespace Barotrauma.MapCreatures.Behavior
int sides = getInt("sides");
int blockedSides = getInt("blockedsides");
int claimedId = branchElement.GetAttributeInt("claimed", -1);
int parentBranchId = branchElement.GetAttributeInt("parentbranch", -1);
BallastFloraBranch newBranch = new BallastFloraBranch(this, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig))
BallastFloraBranch? parentBranch = null;
if (parentBranchId > -1)
{
parentBranch = Branches[parentBranchId];
}
BallastFloraBranch newBranch = new BallastFloraBranch(this, parentBranch, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig))
{
ID = id,
Health = health,
@@ -422,6 +468,7 @@ namespace Barotrauma.MapCreatures.Behavior
BlockedSides = (TileSide) blockedSides,
IsRoot = isRoot
};
if (newBranch.IsRoot) { root = newBranch; }
if (claimedId > -1)
{
@@ -437,6 +484,15 @@ namespace Barotrauma.MapCreatures.Behavior
public void Update(float deltaTime)
{
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
if (Branches.Count == 0)
{
Remove();
return;
}
}
foreach (BallastFloraBranch branch in Branches)
{
branch.UpdateScale(deltaTime);
@@ -446,38 +502,25 @@ namespace Barotrauma.MapCreatures.Behavior
#endif
}
if (damageIndicatorTimer <= 0)
{
foreach (BallastFloraBranch branch in Branches)
{
if (branch.AccumulatedDamage > 0)
{
#if CLIENT
CreateDamageParticle(branch, branch.AccumulatedDamage);
if (GameMain.DebugDraw)
{
var pos = (Parent?.Position ?? Vector2.Zero) + Offset + branch.Position;
GUI.AddMessage($"{(int)branch.AccumulatedDamage}", GUI.Style.Red, pos, Vector2.UnitY * 10.0f, 3f, playSound: false, subId: Parent?.Submarine?.ID ?? -1);
}
#elif SERVER
SendNetworkMessage(this, NetworkHeader.BranchDamage, branch, branch.AccumulatedDamage);
#endif
}
branch.AccumulatedDamage = 0f;
}
damageIndicatorTimer = 1f;
}
damageIndicatorTimer -= deltaTime;
UpdateDamage(deltaTime);
UpdatePowerDrain(deltaTime);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (root != null && HealthRegenPerBranch > 0.0f)
{
float healAmount = Branches.Count(b => !b.IsRoot && !b.IsRootGrowth && !b.DisconnectedFromRoot) * HealthRegenPerBranch;
foreach (BallastFloraBranch branch in Branches)
{
if (branch.Health > branch.MaxHealth * 0.9f || branch.DisconnectedFromRoot) { continue; }
float branchHealAmount = (float)(MaxBranchHealthRegenDistance - branch.BranchDepth) / MaxBranchHealthRegenDistance * healAmount;
if (branchHealAmount <= 0.0f) { continue; }
branch.Health += branchHealAmount;
branch.AccumulatedDamage -= branchHealAmount;
}
}
StateMachine.Update(deltaTime);
if (HasBrokenThrough)
@@ -512,12 +555,12 @@ namespace Barotrauma.MapCreatures.Behavior
// This entire scope is probably very heavy for GC, need to experiment
if (toxinsTimer > 0.1f)
{
if (!string.IsNullOrWhiteSpace(AttackItemPrefab))
if (!AttackItemPrefab.IsEmpty)
{
Dictionary<Hull, List<BallastFloraBranch>> branches = new Dictionary<Hull, List<BallastFloraBranch>>();
foreach (BallastFloraBranch branch in Branches)
{
if (branch.CurrentHull == null || branch.FlowerConfig.Variant < 0) { continue; }
if (branch.CurrentHull == null || branch.FlowerConfig.Variant < 0 || branch.DisconnectedFromRoot) { continue; }
if (branches.TryGetValue(branch.CurrentHull, out List<BallastFloraBranch>? list))
{
@@ -534,11 +577,12 @@ namespace Barotrauma.MapCreatures.Behavior
List<BallastFloraBranch> list = branches[hull];
if (!list.Any(HasAcidEmitter))
{
BallastFloraBranch randomBranch = branches[hull].GetRandom();
BallastFloraBranch randomBranch = branches[hull].GetRandomUnsynced();
randomBranch.SpawningItem = true;
ItemPrefab prefab = ItemPrefab.Find(null, AttackItemPrefab);
Entity.Spawner?.AddToSpawnQueue(prefab, Parent.Position + Offset + randomBranch.Position, Parent.Submarine, onSpawned: item =>
#warning TODO: Parent needs a nullability sanity check
Entity.Spawner?.AddItemToSpawnQueue(prefab, Parent!.Position + Offset + randomBranch.Position, Parent.Submarine, onSpawned: item =>
{
randomBranch.AttackItem = item;
randomBranch.SpawningItem = false;
@@ -563,38 +607,49 @@ namespace Barotrauma.MapCreatures.Behavior
}
}
partial void UpdateDamage(float deltaTime);
private readonly List<BallastFloraBranch> toBeRemoved = new List<BallastFloraBranch>();
private void UpdateSelfDamage(float deltaTime)
{
if (selfDamageTimer <= 0)
{
bool hasRoot = false;
foreach (BallastFloraBranch branch in Branches)
{
if (branch.IsRoot)
{
hasRoot = true;
break;
}
}
if (!hasRoot)
{
Kill();
return;
}
if (!HasBrokenThrough && !CanGrowMore())
{
Branches.ForEachMod(branch =>
{
float maxHealth = branch.IsRoot ? StemHealth : BranchHealth;
float maxHealth = branch.IsRoot ? RootHealth : BranchHealth;
DamageBranch(branch, Rand.Range(1f, maxHealth), AttackType.Other);
});
}
selfDamageTimer = 1f;
}
toBeRemoved.Clear();
foreach (BallastFloraBranch branch in Branches)
{
if (branch.ParentBranch != null && (branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f))
{
DamageBranch(branch, deltaTime * MathHelper.Lerp(10.0f, 0.01f, branch.ParentBranch.Health / branch.ParentBranch.MaxHealth), AttackType.CutFromRoot);
}
if (branch.Health <= 0.0f)
{
if (branch.ClaimedItem != null)
{
RemoveClaim(branch.ClaimedItem);
branch.ClaimedItem = null;
}
branch.RemoveTimer -= deltaTime;
if (branch.RemoveTimer <= 0.0f)
{
toBeRemoved.Add(branch);
}
}
}
foreach (BallastFloraBranch branch in toBeRemoved)
{
RemoveBranch(branch);
}
selfDamageTimer -= deltaTime;
}
@@ -675,20 +730,25 @@ namespace Barotrauma.MapCreatures.Behavior
branch.CurrentHull = Hull.FindHull(GetWorldPosition() + branch.Position, Parent, true);
}
private void GenerateStem()
private void GenerateRoot()
{
BallastFloraBranch stem = new BallastFloraBranch(this, Vector2.Zero, VineTileType.Stem, FoliageConfig.EmptyConfig, FoliageConfig.EmptyConfig)
if (root != null)
{
DebugConsole.ThrowError("Error in ballast flora: tried to grow a root even though root has already been created.\n" + Environment.StackTrace);
}
root = new BallastFloraBranch(this, null, Vector2.Zero, VineTileType.Stem, FoliageConfig.EmptyConfig, FoliageConfig.EmptyConfig)
{
BlockedSides = TileSide.Bottom | TileSide.Left | TileSide.Right,
GrowthStep = 1f,
Health = StemHealth,
MaxHealth = StemHealth,
MaxHealth = RootHealth,
Health = RootHealth,
IsRoot = true,
CurrentHull = Parent
};
Branches.Add(stem);
CreateBody(stem);
Branches.Add(root);
CreateBody(root);
}
public float GetGrowthSpeed(float deltaTime)
@@ -704,19 +764,19 @@ namespace Barotrauma.MapCreatures.Behavior
return deltaTime;
}
public bool TryGrowBranch(BallastFloraBranch parent, TileSide side, out List<BallastFloraBranch> result)
public bool TryGrowBranch(BallastFloraBranch parent, TileSide side, out List<BallastFloraBranch> result, bool isRootGrowth = false, Vector2? forcePosition = null)
{
result = new List<BallastFloraBranch>();
if (parent.IsSideBlocked(side)) { return false; }
if (!isRootGrowth && parent.IsSideBlocked(side)) { return false; }
Vector2 pos = parent.AdjacentPositions[side];
Vector2 pos = forcePosition ?? parent.AdjacentPositions[side];
Rectangle rect = VineTile.CreatePlantRect(pos);
if (CollidesWithWorld(rect))
if (CollidesWithWorld(rect, checkOtherBranches: !isRootGrowth))
{
parent.BlockedSides |= side;
parent.FailedGrowthAttempts++;
return false;
return false;
}
FoliageConfig flowerConfig = FoliageConfig.EmptyConfig;
@@ -732,21 +792,22 @@ namespace Barotrauma.MapCreatures.Behavior
leafConfig = FoliageConfig.CreateRandomConfig(leafVariants, 0.5f, 1.0f);
}
BallastFloraBranch newBranch = new BallastFloraBranch(this, pos, VineTileType.CrossJunction, flowerConfig, leafConfig, rect)
BallastFloraBranch newBranch = new BallastFloraBranch(this, parent, pos, VineTileType.CrossJunction, flowerConfig, leafConfig, rect)
{
ID = CreateID(),
MaxHealth = BranchHealth,
Health = BranchHealth,
MaxHealth = BranchHealth
IsRootGrowth = isRootGrowth
};
SetHull(newBranch);
if (newBranch.CurrentHull == null || newBranch.CurrentHull.Submarine != Parent.Submarine)
{
parent.BlockedSides |= side;
if (!isRootGrowth) { parent.BlockedSides |= side; }
parent.FailedGrowthAttempts++;
return false;
}
}
UpdateConnections(newBranch, parent);
@@ -760,12 +821,28 @@ namespace Barotrauma.MapCreatures.Behavior
GrowthWarps--;
}
int rootGrowthCount = Branches.Count(b => b.IsRootGrowth);
if (rootGrowthCount < GetDesiredRootGrowthAmount())
{
if (root != null)
{
Vector2 rootGrowthPos = Rand.Vector(rootGrowthCount * Rand.Range(3.0f, 5.0f));
TryGrowBranch(root, TileSide.None, out List<BallastFloraBranch> newRootGrowth, isRootGrowth: true, forcePosition: rootGrowthPos);
}
}
#if SERVER
SendNetworkMessage(this, NetworkHeader.BranchCreate, newBranch, parent.ID);
#endif
return true;
}
private int GetDesiredRootGrowthAmount()
{
if (root == null) { return 0; }
return MathHelper.Clamp(Branches.Count(b => !b.IsRootGrowth && b.Health > 0) / 20, 3, 30);
}
public bool BranchContainsTarget(BallastFloraBranch branch, Item target)
{
Rectangle worldRect = branch.Rect;
@@ -894,6 +971,14 @@ namespace Barotrauma.MapCreatures.Behavior
public void DamageBranch(BallastFloraBranch branch, float amount, AttackType type, Character? attacker = null)
{
float damage = amount;
if (type != AttackType.Other && type != AttackType.CutFromRoot)
{
branch.DamageVisualizationTimer = 1.0f;
}
if (branch.IsRootGrowth && root != null && root.Health > 0.0f) { return; }
// damage is handled server side currently
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
@@ -922,12 +1007,10 @@ namespace Barotrauma.MapCreatures.Behavior
}
}
branch.AccumulatedDamage += damage;
branch.Health -= damage;
if (type != AttackType.Other)
if (type != AttackType.Other && type != AttackType.CutFromRoot)
{
branch.AccumulatedDamage += damage;
Anger += damage * 0.001f;
}
@@ -935,30 +1018,13 @@ namespace Barotrauma.MapCreatures.Behavior
GameMain.Server?.KarmaManager?.OnBallastFloraDamaged(attacker, damage);
#endif
if (branch.Health < 0)
if (branch.Health <= 0 && type != AttackType.CutFromRoot)
{
RemoveBranch(branch);
if (branch.IsRoot) { Kill(); }
}
}
public void Remove()
{
foreach (Body body in bodies)
{
GameMain.World.Remove(body);
}
Parent.BallastFlora = null;
Branches.Clear();
foreach (Item target in ClaimedTargets)
{
target.Infector = null;
}
_entityList.Remove(this);
}
public void RemoveBranch(BallastFloraBranch branch)
{
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
@@ -969,6 +1035,21 @@ namespace Barotrauma.MapCreatures.Behavior
Branches.Remove(branch);
branch.Removed = true;
bool foundDisconnected = false;
do
{
foundDisconnected = false;
foreach (BallastFloraBranch otherBranch in Branches)
{
if (otherBranch.ParentBranch == null || otherBranch.DisconnectedFromRoot) { continue; }
if (otherBranch.ParentBranch.Removed || otherBranch.ParentBranch.DisconnectedFromRoot)
{
otherBranch.DisconnectedFromRoot = true;
foundDisconnected = true;
}
}
} while (foundDisconnected);
bodies.ForEachMod(body =>
{
if (body.UserData == branch)
@@ -995,11 +1076,21 @@ namespace Barotrauma.MapCreatures.Behavior
});
#if CLIENT
CreateDeathParticle(branch);
CreateDeathParticle(branch, 1.0f);
#endif
if (isClient) { return; }
int rootGrowthCount = Branches.Count(b => b.IsRootGrowth);
if (rootGrowthCount > GetDesiredRootGrowthAmount())
{
var rootGrowth = Branches.LastOrDefault(b => b.IsRootGrowth);
if (rootGrowth != null)
{
RemoveBranch(rootGrowth);
}
}
if (branch.ClaimedItem != null)
{
RemoveClaim(branch.ClaimedItem);
@@ -1050,8 +1141,10 @@ namespace Barotrauma.MapCreatures.Behavior
public void Kill()
{
Branches.ForEachMod(RemoveBranch);
Parent.BallastFlora = null;
foreach (var branch in Branches)
{
branch.DisconnectedFromRoot = true;
}
foreach (Item target in ClaimedTargets)
{
@@ -1059,6 +1152,19 @@ namespace Barotrauma.MapCreatures.Behavior
}
StateMachine?.State?.Exit();
#if SERVER
SendNetworkMessage(this, NetworkHeader.Kill);
#endif
}
public void Remove()
{
Kill();
Branches.ForEachMod(RemoveBranch);
Branches.Clear();
toBeRemoved.Clear();
Parent.BallastFlora = null;
// clean up leftover (can probably be removed)
foreach (Body body in bodies)
@@ -1067,8 +1173,9 @@ namespace Barotrauma.MapCreatures.Behavior
GameMain.World.Remove(body);
}
_entityList.Remove(this);
#if SERVER
SendNetworkMessage(this, NetworkHeader.Kill);
SendNetworkMessage(this, NetworkHeader.Remove);
#endif
}
@@ -1093,9 +1200,9 @@ namespace Barotrauma.MapCreatures.Behavior
private bool CanGrowMore() => Branches.Any(b => b.CanGrowMore());
private bool CollidesWithWorld(Rectangle rect)
private bool CollidesWithWorld(Rectangle rect, bool checkOtherBranches = true)
{
if (Branches.Any(g => g.Rect.Contains(rect))) { return true; }
if (checkOtherBranches && Branches.Any(g => g.Rect.Contains(rect))) { return true; }
Rectangle worldRect = rect;
worldRect.Location = (Parent.Position + Offset).ToPoint() + worldRect.Location;