Client LuaCsForBarotrauma
GrowIdleState.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
7 using FarseerPhysics;
8 using FarseerPhysics.Dynamics;
9 using Microsoft.Xna.Framework;
10 
12 {
13  internal class GrowIdleState: IBallastFloraState
14  {
15  public readonly BallastFloraBehavior Behavior;
16  private float growthTimer;
17 
18  public GrowIdleState(BallastFloraBehavior behavior)
19  {
20  Behavior = behavior;
21  }
22 
23  public virtual ExitState GetState() => ExitState.Running;
24 
25  public virtual void Enter()
26  {
27  foreach (BallastFloraBranch branch in Behavior.Branches.Where(b => b.CanGrowMore()))
28  {
29  if (TryScanTargets(branch)) { return; }
30  }
31  }
32 
33  public void Exit() { }
34 
35  private bool TryScanTargets(BallastFloraBranch branch)
36  {
37  if (ScanForTargets(branch) is { } newTarget)
38  {
39  Behavior.StateMachine.EnterState(new GrowToTargetState(Behavior, branch, newTarget));
40  return true;
41  }
42 
43  return false;
44  }
45 
46  public void Update(float deltaTime)
47  {
48  if (growthTimer > 0)
49  {
50  growthTimer -= Behavior.GetGrowthSpeed(deltaTime);
51  }
52  else
53  {
54  Grow();
55  UpdateIgnoredTargets();
56  growthTimer = Behavior.GrowthWarps > 0 ? 0f : 5f;
57  }
58  }
59 
60  protected virtual void Grow()
61  {
62  List<BallastFloraBranch> newBranches = GrowRandomly();
63 #if DEBUG
64  Behavior.debugSearchLines.Clear();
65 #endif
66  foreach (var branch in newBranches)
67  {
68  TryScanTargets(branch);
69  }
70  }
71 
72  public void UpdateIgnoredTargets()
73  {
74  Behavior.IgnoredTargets.ForEachMod(pair =>
75  {
76  var (item, delay) = pair;
77 
78  if (delay <= 0)
79  {
80  Behavior.IgnoredTargets.Remove(item);
81  }
82  else
83  {
84  Behavior.IgnoredTargets[item] = --delay;
85  }
86  });
87  }
88 
89  private List<BallastFloraBranch> GrowRandomly()
90  {
91  List<BallastFloraBranch> availableBranches = Behavior.Branches.Where(b => !b.DisconnectedFromRoot && b.FailedGrowthAttempts <= 8 && b.CanGrowMore()).ToList();
92  if (availableBranches.Count == 0) { return availableBranches; }
93 
94  //prefer growing from the branches furthest from the root (ones with the largest branch depth)
95  var branch = ToolBox.SelectWeightedRandom(availableBranches, b => b.BranchDepth, Rand.RandSync.Unsynced);
96 
97  TileSide side = branch.GetRandomFreeSide();
98  if (side == TileSide.None) { return availableBranches; }
99 
100  Behavior.TryGrowBranch(branch, side, out List<BallastFloraBranch> result);
101  availableBranches.Clear();
102  availableBranches.Add(branch);
103 
104  return availableBranches;
105  }
106 
107  private Item? ScanForTargets(VineTile branch)
108  {
109  Hull parent = Behavior.Parent;
110  Vector2 worldPos = Behavior.GetWorldPosition() + branch.Position;
111  Vector2 pos = parent.Position + Behavior.Offset + branch.Position;
112 
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;
116 
117  int highestPriority = 0;
118  Item? currentItem = null;
119 
120  foreach (Item item in Item.ItemList)
121  {
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; }
125 
126  int priority = 0;
127  foreach (BallastFloraBehavior.AITarget target in Behavior.Targets)
128  {
129  if (target.Priority <= highestPriority || !target.Matches(item)) { continue; }
130  priority = target.Priority;
131  break;
132  }
133 
134  if (priority == 0) { continue; }
135 
136  Vector2 itemSimPos = ConvertUnits.ToSimUnits(item.Position);
137 
138 #if DEBUG
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);
143 #endif
144 
145  Body? body1 = Submarine.CheckVisibility(itemSimPos - diameter, topLeft);
146  if (Blocks(body1, item)) { continue; }
147 
148  Body? body2 = Submarine.CheckVisibility(itemSimPos + diameter, bottomRight);
149  if (Blocks(body2, item)) { continue; }
150 
151  highestPriority = priority;
152  currentItem = item;
153  }
154 
155  if (currentItem != null)
156  {
157  foreach (BallastFloraBranch existingBranch in Behavior.Branches)
158  {
159  if (existingBranch.Health <= 0 || existingBranch.IsRootGrowth) { continue; }
160  if (Behavior.BranchContainsTarget(existingBranch, currentItem))
161  {
162  Behavior.ClaimTarget(currentItem, existingBranch);
163  return null;
164  }
165  }
166 
167  return currentItem;
168  }
169 
170  return null;
171 
172  static bool Blocks(Body body, Item target)
173  {
174  if (body == null) { return false; }
175 
176  switch (body.UserData)
177  {
178  case Submarine _:
179  case Structure _:
180  case Item it when it != target:
181  return true;
182  default:
183  return false;
184  }
185  }
186  }
187  }
188 }
@ Structure
Structures and hulls, but also items (for backwards support)!