3 using System.Collections.Generic;
8 using FarseerPhysics.Dynamics;
9 using Microsoft.Xna.Framework;
13 internal class GrowIdleState: IBallastFloraState
15 public readonly BallastFloraBehavior Behavior;
16 private float growthTimer;
18 public GrowIdleState(BallastFloraBehavior behavior)
25 public virtual void Enter()
27 foreach (BallastFloraBranch branch
in Behavior.Branches.Where(b => b.CanGrowMore()))
29 if (TryScanTargets(branch)) {
return; }
33 public void Exit() { }
35 private bool TryScanTargets(BallastFloraBranch branch)
37 if (ScanForTargets(branch) is { } newTarget)
39 Behavior.StateMachine.EnterState(
new GrowToTargetState(Behavior, branch, newTarget));
46 public void Update(
float deltaTime)
50 growthTimer -= Behavior.GetGrowthSpeed(deltaTime);
55 UpdateIgnoredTargets();
56 growthTimer = Behavior.GrowthWarps > 0 ? 0f : 5f;
60 protected virtual void Grow()
62 List<BallastFloraBranch> newBranches = GrowRandomly();
64 Behavior.debugSearchLines.Clear();
66 foreach (var branch
in newBranches)
68 TryScanTargets(branch);
72 public void UpdateIgnoredTargets()
74 Behavior.IgnoredTargets.ForEachMod(pair =>
76 var (item, delay) = pair;
80 Behavior.IgnoredTargets.Remove(item);
84 Behavior.IgnoredTargets[item] = --delay;
89 private List<BallastFloraBranch> GrowRandomly()
91 List<BallastFloraBranch> availableBranches = Behavior.Branches.Where(b => !b.DisconnectedFromRoot && b.FailedGrowthAttempts <= 8 && b.CanGrowMore()).ToList();
92 if (availableBranches.Count == 0) {
return availableBranches; }
95 var branch = ToolBox.SelectWeightedRandom(availableBranches, b => b.BranchDepth, Rand.RandSync.Unsynced);
97 TileSide side = branch.GetRandomFreeSide();
98 if (side == TileSide.None) {
return availableBranches; }
100 Behavior.TryGrowBranch(branch, side, out List<BallastFloraBranch> result);
101 availableBranches.Clear();
102 availableBranches.Add(branch);
104 return availableBranches;
107 private Item? ScanForTargets(VineTile branch)
109 Hull parent = Behavior.Parent;
110 Vector2 worldPos = Behavior.GetWorldPosition() + branch.Position;
111 Vector2 pos = parent.Position + Behavior.Offset + branch.Position;
113 Vector2 diameter = ConvertUnits.ToSimUnits(
new Vector2(branch.Rect.Width / 2f, branch.Rect.Height / 2f));
114 Vector2 topLeft = ConvertUnits.ToSimUnits(pos) - diameter;
115 Vector2 bottomRight = ConvertUnits.ToSimUnits(pos) + diameter;
117 int highestPriority = 0;
118 Item? currentItem =
null;
120 foreach (Item item
in Item.ItemList)
122 if (item.Submarine != parent.Submarine || Vector2.DistanceSquared(worldPos, item.WorldPosition) > Behavior.Sight * Behavior.Sight) {
continue; }
123 if (Behavior.ClaimedTargets.Contains(item)) {
continue; }
124 if (Behavior.IgnoredTargets.ContainsKey(item)) {
continue; }
127 foreach (BallastFloraBehavior.AITarget target in Behavior.Targets)
129 if (target.Priority <= highestPriority || !target.Matches(item)) {
continue; }
130 priority = target.Priority;
134 if (priority == 0) {
continue; }
136 Vector2 itemSimPos = ConvertUnits.ToSimUnits(item.Position);
139 Tuple<Vector2, Vector2> debugLine1 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(topLeft), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos - diameter));
140 Tuple<Vector2, Vector2> debugLine2 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(bottomRight), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos + diameter));
141 Behavior.debugSearchLines.Add(debugLine2);
142 Behavior.debugSearchLines.Add(debugLine1);
145 Body? body1 =
Submarine.CheckVisibility(itemSimPos - diameter, topLeft);
146 if (Blocks(body1, item)) {
continue; }
148 Body? body2 =
Submarine.CheckVisibility(itemSimPos + diameter, bottomRight);
149 if (Blocks(body2, item)) {
continue; }
151 highestPriority = priority;
155 if (currentItem !=
null)
157 foreach (BallastFloraBranch existingBranch
in Behavior.Branches)
159 if (existingBranch.Health <= 0 || existingBranch.IsRootGrowth) {
continue; }
160 if (Behavior.BranchContainsTarget(existingBranch, currentItem))
162 Behavior.ClaimTarget(currentItem, existingBranch);
172 static bool Blocks(Body body, Item target)
174 if (body ==
null) {
return false; }
176 switch (body.UserData)
180 case Item it when it != target:
@ Structure
Structures and hulls, but also items (for backwards support)!