Client LuaCsForBarotrauma
SwarmBehavior.cs
1 using FarseerPhysics;
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using System.Xml.Linq;
8 
9 namespace Barotrauma
10 {
12  {
13  private readonly float minDistFromClosest;
14  private readonly float maxDistFromCenter;
15  private readonly float cohesion;
16  public bool ForceActive { get; private set; }
17 
18  public List<AICharacter> Members { get; private set; } = new List<AICharacter>();
19  public HashSet<AICharacter> ActiveMembers { get; private set; } = new HashSet<AICharacter>();
20 
21  private EnemyAIController ai;
22 
23  public bool IsActive { get; set; }
24  public bool IsEnoughMembers => ActiveMembers.Count > 1;
25 
26 
27  public SwarmBehavior(XElement element, EnemyAIController ai)
28  {
29  this.ai = ai;
30  minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat(nameof(minDistFromClosest), 10.0f));
31  maxDistFromCenter = ConvertUnits.ToSimUnits(element.GetAttributeFloat(nameof(maxDistFromCenter), 1000.0f));
32  cohesion = element.GetAttributeFloat(nameof(cohesion), 1) / 10;
33  ForceActive = element.GetAttributeBool(nameof(ForceActive), false);
34  }
35 
36  public static void CreateSwarm(IEnumerable<AICharacter> swarm)
37  {
38  var aiControllers = new List<EnemyAIController>();
39  foreach (AICharacter character in swarm)
40  {
41  if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
42  {
43  aiControllers.Add(enemyAI);
44  }
45  }
46  var filteredMembers = aiControllers.Select(m => m.Character as AICharacter).Where(m => m != null);
47  foreach (EnemyAIController ai in aiControllers)
48  {
49  ai.SwarmBehavior.Members = filteredMembers.ToList();
50  }
51  }
52 
53  public void Refresh()
54  {
55  Members.RemoveAll(m => m.IsDead || m.Removed || m.AIController is EnemyAIController ai && ai.State == AIState.Flee);
56  foreach (var member in Members)
57  {
58  if (!member.AIController.Enabled && member.IsRemotePlayer || Character.Controlled == member || !((EnemyAIController)member.AIController).SwarmBehavior.IsActive)
59  {
60  ActiveMembers.Remove(member);
61  }
62  else
63  {
64  ActiveMembers.Add(member);
65  }
66  }
67  }
68 
69  public void UpdateSteering(float deltaTime)
70  {
71  if (!IsActive) { return; }
72  if (!IsEnoughMembers) { return; }
73  //calculate the "center of mass" of the swarm and the distance to the closest character in the swarm
74  float closestDistSqr = float.MaxValue;
75  Vector2 center = Vector2.Zero;
76  AICharacter closest = null;
77  foreach (AICharacter member in Members)
78  {
79  center += member.SimPosition;
80  if (member == ai.Character) { continue; }
81  float distSqr = Vector2.DistanceSquared(member.SimPosition, ai.Character.SimPosition);
82  if (distSqr < closestDistSqr)
83  {
84  closestDistSqr = distSqr;
85  closest = member;
86  }
87  }
88  center /= Members.Count;
89 
90  if (closest == null) { return; }
91 
92  //steer away from the closest if too close
93  float closestDist = (float)Math.Sqrt(closestDistSqr);
94  if (closestDist < minDistFromClosest)
95  {
96  Vector2 diff = closest.SimPosition - ai.SimPosition;
97  if (diff.LengthSquared() < 0.0001f)
98  {
99  diff = Vector2.UnitX;
100  }
101  ai.SteeringManager.SteeringManual(deltaTime, -diff);
102  }
103  //steer closer to the center of mass if too far
104  else if (Vector2.DistanceSquared(center, ai.SimPosition) > maxDistFromCenter * maxDistFromCenter)
105  {
106  float distFromCenter = Vector2.Distance(center, ai.SimPosition);
107  ai.SteeringManager.SteeringSeek(center, (distFromCenter - maxDistFromCenter) / 10.0f);
108  }
109 
110  //keep the characters moving in roughly the same direction
111  if (cohesion > 0.0f)
112  {
113  Vector2 avgVel = Vector2.Zero;
114  foreach (AICharacter member in Members)
115  {
116  avgVel += member.AnimController.TargetMovement;
117  }
118  avgVel /= Members.Count;
119  ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion);
120  }
121  }
122  }
123 }
void SteeringManual(float deltaTime, Vector2 velocity)
void SteeringSeek(Vector2 targetSimPos, float weight=1)
void UpdateSteering(float deltaTime)
HashSet< AICharacter > ActiveMembers
static void CreateSwarm(IEnumerable< AICharacter > swarm)
SwarmBehavior(XElement element, EnemyAIController ai)
List< AICharacter > Members