188 lines
6.4 KiB
C#
188 lines
6.4 KiB
C#
#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> newBranches = GrowRandomly();
|
|
#if DEBUG
|
|
Behavior.debugSearchLines.Clear();
|
|
#endif
|
|
foreach (var branch in newBranches)
|
|
{
|
|
TryScanTargets(branch);
|
|
}
|
|
}
|
|
|
|
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> availableBranches = Behavior.Branches.Where(b => !b.DisconnectedFromRoot && b.FailedGrowthAttempts <= 8 && b.CanGrowMore()).ToList();
|
|
if (availableBranches.Count == 0) { return availableBranches; }
|
|
|
|
//prefer growing from the branches furthest from the root (ones with the largest branch depth)
|
|
var branch = ToolBox.SelectWeightedRandom(availableBranches, b => b.BranchDepth, Rand.RandSync.Unsynced);
|
|
|
|
TileSide side = branch.GetRandomFreeSide();
|
|
if (side == TileSide.None) { return availableBranches; }
|
|
|
|
Behavior.TryGrowBranch(branch, side, out List<BallastFloraBranch> result);
|
|
availableBranches.Clear();
|
|
availableBranches.Add(branch);
|
|
|
|
return availableBranches;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (item.Submarine != parent.Submarine || Vector2.DistanceSquared(worldPos, item.WorldPosition) > Behavior.Sight * Behavior.Sight) { continue; }
|
|
if (Behavior.ClaimedTargets.Contains(item)) { continue; }
|
|
if (Behavior.IgnoredTargets.ContainsKey(item)) { continue; }
|
|
|
|
int priority = 0;
|
|
foreach (BallastFloraBehavior.AITarget target in Behavior.Targets)
|
|
{
|
|
if (target.Priority <= highestPriority || !target.Matches(item)) { continue; }
|
|
priority = target.Priority;
|
|
break;
|
|
}
|
|
|
|
if (priority == 0) { continue; }
|
|
|
|
Vector2 itemSimPos = ConvertUnits.ToSimUnits(item.Position);
|
|
|
|
#if DEBUG
|
|
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 (existingBranch.Health <= 0 || existingBranch.IsRootGrowth) { continue; }
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |