v0.11.0.9
This commit is contained in:
+49
@@ -0,0 +1,49 @@
|
||||
#nullable enable
|
||||
namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
class BallastFloraStateMachine
|
||||
{
|
||||
private readonly BallastFloraBehavior parent;
|
||||
|
||||
public BallastFloraStateMachine(BallastFloraBehavior parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
private IBallastFloraState? lastState;
|
||||
public IBallastFloraState? State;
|
||||
|
||||
public void EnterState(IBallastFloraState newState)
|
||||
{
|
||||
lastState = State;
|
||||
State?.Exit();
|
||||
newState.Enter();
|
||||
State = newState;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (State == null)
|
||||
{
|
||||
EnterState(new GrowIdleState(parent));
|
||||
return;
|
||||
}
|
||||
|
||||
State.Update(deltaTime);
|
||||
|
||||
switch (State.GetState())
|
||||
{
|
||||
case ExitState.Running:
|
||||
break;
|
||||
|
||||
case ExitState.ReturnLast when lastState != null && lastState.GetState() == ExitState.Running:
|
||||
EnterState(lastState);
|
||||
break;
|
||||
|
||||
default:
|
||||
EnterState(new GrowIdleState(parent));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
class DefendWithPumpState : IBallastFloraState
|
||||
{
|
||||
private readonly BallastFloraBranch targetBranch;
|
||||
private readonly List<Pump> allAvailablePumps = new List<Pump>();
|
||||
private readonly List<Door> allAvailableDoors = new List<Door>();
|
||||
private readonly List<Pump> targetPumps = new List<Pump>();
|
||||
private readonly List<Door> jammedDoors = new List<Door>();
|
||||
|
||||
private bool isFinished;
|
||||
private float timer = 10f;
|
||||
private bool filled;
|
||||
private bool tryDrown;
|
||||
private readonly Character attacker;
|
||||
|
||||
public DefendWithPumpState(BallastFloraBranch branch, List<Item> items, Character attacker)
|
||||
{
|
||||
targetBranch = branch;
|
||||
this.attacker = attacker;
|
||||
|
||||
foreach (Item item in items)
|
||||
{
|
||||
if (item.GetComponent<Pump>() is { } pump)
|
||||
{
|
||||
allAvailablePumps.Add(pump);
|
||||
}
|
||||
|
||||
if (item.GetComponent<Door>() is { } door)
|
||||
{
|
||||
allAvailableDoors.Add(door);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ExitState GetState()
|
||||
{
|
||||
if (isFinished) { return ExitState.ReturnLast; }
|
||||
if (targetBranch.CurrentHull == null) { return ExitState.ReturnLast; }
|
||||
return timer < 0 ? ExitState.ReturnLast : ExitState.Running;
|
||||
}
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
foreach (Pump pump in allAvailablePumps)
|
||||
{
|
||||
if (pump.Item.CurrentHull == targetBranch.CurrentHull)
|
||||
{
|
||||
targetPumps.Add(pump);
|
||||
SetPump(pump);
|
||||
pump.Hijacked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetPumps.Any() || targetPumps.All(p => !p.HasPower))
|
||||
{
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
// lock the doors if the attacker is in the same hull as the ballast flora to try to drown them
|
||||
if (targetBranch.CurrentHull != null && attacker != null && attacker.CurrentHull == targetBranch.CurrentHull)
|
||||
{
|
||||
foreach (Door door in allAvailableDoors)
|
||||
{
|
||||
if (door.LinkedGap != null && door.LinkedGap.linkedTo.Contains(targetBranch.CurrentHull))
|
||||
{
|
||||
door.TrySetState(false, false, true);
|
||||
door.IsJammed = true;
|
||||
jammedDoors.Add(door);
|
||||
}
|
||||
}
|
||||
|
||||
tryDrown = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
foreach (Pump pump in targetPumps)
|
||||
{
|
||||
pump.Hijacked = false;
|
||||
}
|
||||
|
||||
foreach (Door door in jammedDoors)
|
||||
{
|
||||
door.IsJammed = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPump(Pump pump)
|
||||
{
|
||||
if (pump.TargetLevel != null)
|
||||
{
|
||||
pump.TargetLevel = 100f;
|
||||
}
|
||||
else
|
||||
{
|
||||
pump.FlowPercentage = 100f;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
foreach (Pump pump in targetPumps)
|
||||
{
|
||||
SetPump(pump);
|
||||
}
|
||||
|
||||
if (tryDrown && !filled)
|
||||
{
|
||||
// keep the ballast filled for extra 10 seconds
|
||||
if (targetBranch.CurrentHull == null || targetBranch.CurrentHull.WaterPercentage >= 95f)
|
||||
{
|
||||
filled = true;
|
||||
timer += 10f;
|
||||
}
|
||||
}
|
||||
|
||||
timer -= deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
internal class GrowIdleState: IBallastFloraState
|
||||
{
|
||||
public readonly BallastFloraBehavior Behavior;
|
||||
private float growthTimer;
|
||||
|
||||
public GrowIdleState(BallastFloraBehavior behavior)
|
||||
{
|
||||
Behavior = behavior;
|
||||
}
|
||||
|
||||
public virtual ExitState GetState() => ExitState.Running;
|
||||
|
||||
public virtual void Enter()
|
||||
{
|
||||
foreach (BallastFloraBranch branch in Behavior.Branches.Where(b => b.CanGrowMore()))
|
||||
{
|
||||
if (TryScanTargets(branch)) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit() { }
|
||||
|
||||
private bool TryScanTargets(BallastFloraBranch branch)
|
||||
{
|
||||
if (ScanForTargets(branch) is { } newTarget)
|
||||
{
|
||||
Behavior.StateMachine.EnterState(new GrowToTargetState(Behavior, branch, newTarget));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (growthTimer > 0)
|
||||
{
|
||||
growthTimer -= Behavior.GetGrowthSpeed(deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grow();
|
||||
UpdateIgnoredTargets();
|
||||
growthTimer = Behavior.GrowthWarps > 0 ? 0f : 5f;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Grow()
|
||||
{
|
||||
List<BallastFloraBranch> newTiles = GrowRandomly();
|
||||
#if DEBUG || UNSTABLE
|
||||
Behavior.debugSearchLines.Clear();
|
||||
#endif
|
||||
if (newTiles.Any(TryScanTargets)) { return; }
|
||||
}
|
||||
|
||||
public void UpdateIgnoredTargets()
|
||||
{
|
||||
Behavior.IgnoredTargets.ForEachMod(pair =>
|
||||
{
|
||||
var (item, delay) = pair;
|
||||
|
||||
if (delay <= 0)
|
||||
{
|
||||
Behavior.IgnoredTargets.Remove(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
Behavior.IgnoredTargets[item] = --delay;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<BallastFloraBranch> GrowRandomly()
|
||||
{
|
||||
List<BallastFloraBranch> newBranches = new List<BallastFloraBranch>();
|
||||
List<BallastFloraBranch> newList = new List<BallastFloraBranch>(Behavior.Branches);
|
||||
foreach (BallastFloraBranch branch in newList)
|
||||
{
|
||||
if (branch.FailedGrowthAttempts > 8 || !branch.CanGrowMore()) { continue; }
|
||||
|
||||
if (Rand.Range(0, Behavior.Branches.Count(tile => tile.CanGrowMore())) != 0) { continue; }
|
||||
|
||||
TileSide side = branch.GetRandomFreeSide();
|
||||
|
||||
if (side == TileSide.None) { continue; }
|
||||
|
||||
Behavior.TryGrowBranch(branch, side, out List<BallastFloraBranch> result);
|
||||
newBranches.AddRange(result);
|
||||
}
|
||||
|
||||
return newBranches;
|
||||
}
|
||||
|
||||
private Item? ScanForTargets(VineTile branch)
|
||||
{
|
||||
Hull parent = Behavior.Parent;
|
||||
Vector2 worldPos = Behavior.GetWorldPosition() + branch.Position;
|
||||
Vector2 pos = parent.Position + Behavior.Offset + branch.Position;
|
||||
|
||||
Vector2 diameter = ConvertUnits.ToSimUnits(new Vector2(branch.Rect.Width / 2f, branch.Rect.Height / 2f));
|
||||
Vector2 topLeft = ConvertUnits.ToSimUnits(pos) - diameter;
|
||||
Vector2 bottomRight = ConvertUnits.ToSimUnits(pos) + diameter;
|
||||
|
||||
int highestPriority = 0;
|
||||
Item? currentItem = null;
|
||||
|
||||
foreach (Item item in Item.ItemList.Where(it => !Behavior.ClaimedTargets.Contains(it)))
|
||||
{
|
||||
if (Behavior.IgnoredTargets.ContainsKey(item)) { continue; }
|
||||
|
||||
int priority = 0;
|
||||
foreach (BallastFloraBehavior.AITarget target in Behavior.Targets)
|
||||
{
|
||||
if (!target.Matches(item) || target.Priority <= highestPriority) { continue; }
|
||||
priority = target.Priority;
|
||||
break;
|
||||
}
|
||||
|
||||
if (priority == 0) { continue; }
|
||||
|
||||
if (item.Submarine != parent.Submarine || Vector2.Distance(worldPos, item.WorldPosition) > Behavior.Sight) { continue; }
|
||||
|
||||
Vector2 itemSimPos = ConvertUnits.ToSimUnits(item.Position);
|
||||
|
||||
#if DEBUG || UNSTABLE
|
||||
Tuple<Vector2, Vector2> debugLine1 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(topLeft), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos - diameter));
|
||||
Tuple<Vector2, Vector2> debugLine2 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(bottomRight), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos + diameter));
|
||||
Behavior.debugSearchLines.Add(debugLine2);
|
||||
Behavior.debugSearchLines.Add(debugLine1);
|
||||
#endif
|
||||
|
||||
Body? body1 = Submarine.CheckVisibility(itemSimPos - diameter, topLeft);
|
||||
if (Blocks(body1, item)) { continue; }
|
||||
|
||||
Body? body2 = Submarine.CheckVisibility(itemSimPos + diameter, bottomRight);
|
||||
if (Blocks(body2, item)) { continue; }
|
||||
|
||||
highestPriority = priority;
|
||||
currentItem = item;
|
||||
}
|
||||
|
||||
if (currentItem != null)
|
||||
{
|
||||
foreach (BallastFloraBranch existingBranch in Behavior.Branches)
|
||||
{
|
||||
if (Behavior.BranchContainsTarget(existingBranch, currentItem))
|
||||
{
|
||||
Behavior.ClaimTarget(currentItem, existingBranch);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return currentItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
static bool Blocks(Body body, Item target)
|
||||
{
|
||||
if (body == null) { return false; }
|
||||
|
||||
switch (body.UserData)
|
||||
{
|
||||
case Submarine _:
|
||||
case Structure _:
|
||||
case Item it when it != target:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
class GrowToTargetState: GrowIdleState
|
||||
{
|
||||
public readonly List<BallastFloraBranch> TargetBranches = new List<BallastFloraBranch>();
|
||||
public readonly Item Target;
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public GrowToTargetState(BallastFloraBehavior behavior, BallastFloraBranch starter, Item target) : base(behavior)
|
||||
{
|
||||
Target = target;
|
||||
TargetBranches.Add(starter);
|
||||
}
|
||||
|
||||
// do nothing
|
||||
public override void Enter() { }
|
||||
|
||||
public override ExitState GetState() => isFinished ? ExitState.Terminate : ExitState.Running;
|
||||
|
||||
protected override void Grow()
|
||||
{
|
||||
if (Target == null || Target.Removed)
|
||||
{
|
||||
isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
GrowTowardsTarget();
|
||||
}
|
||||
|
||||
private void GrowTowardsTarget()
|
||||
{
|
||||
bool succeeded = false;
|
||||
|
||||
List<BallastFloraBranch> newList = new List<BallastFloraBranch>(TargetBranches);
|
||||
foreach (BallastFloraBranch branch in newList)
|
||||
{
|
||||
if (branch.FailedGrowthAttempts > 8 || !branch.CanGrowMore()) { continue; }
|
||||
|
||||
// Get what side gets us closest to the target
|
||||
TileSide side = GetClosestSide(branch, Target.WorldPosition);
|
||||
|
||||
if (branch.IsSideBlocked(side)) { continue; }
|
||||
|
||||
succeeded |= Behavior.TryGrowBranch(branch, side, out List<BallastFloraBranch> newBranches);
|
||||
TargetBranches.AddRange(newBranches);
|
||||
|
||||
foreach (BallastFloraBranch newBranch in newBranches)
|
||||
{
|
||||
Rectangle worldRect = newBranch.Rect;
|
||||
worldRect.Location = Behavior.GetWorldPosition().ToPoint() + worldRect.Location;
|
||||
if (Behavior.BranchContainsTarget(newBranch, Target))
|
||||
{
|
||||
Behavior.ClaimTarget(Target, newBranch);
|
||||
isFinished = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
if (!Behavior.IgnoredTargets.ContainsKey(Target))
|
||||
{
|
||||
Behavior.IgnoredTargets.Add(Target, 1);
|
||||
}
|
||||
|
||||
isFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
private TileSide GetClosestSide(VineTile tile, Vector2 targetPos)
|
||||
{
|
||||
var (distX, distY) = tile.Position + Behavior.GetWorldPosition() - targetPos;
|
||||
int absDistX = (int) Math.Abs(distX), absDistY = (int) Math.Abs(distY);
|
||||
|
||||
return absDistX > absDistY ? distX > 0 ? TileSide.Left : TileSide.Right : distY > 0 ? TileSide.Bottom : TileSide.Top;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma.MapCreatures.Behavior
|
||||
{
|
||||
enum ExitState
|
||||
{
|
||||
Running, // State is running
|
||||
Terminate, // State has exited
|
||||
ReturnLast // Return to the last running state if any
|
||||
}
|
||||
|
||||
interface IBallastFloraState
|
||||
{
|
||||
public void Enter();
|
||||
public void Exit();
|
||||
public void Update(float deltaTime);
|
||||
public ExitState GetState();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user