(24c49c72a) Added swarm behavior to crawlers. Any group of monsters spawned by a single MonsterEvent will now act as a swarm if they have SwarmBehavior defined in their xml
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\PathFinder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringPath.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SwarmBehavior.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\AnimController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\FishAnimController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\HumanoidAnimController.cs" />
|
||||
|
||||
@@ -122,6 +122,7 @@ namespace Barotrauma
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
|
||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||
public SwarmBehavior SwarmBehavior { get; private set; }
|
||||
|
||||
public bool AttackHumans
|
||||
{
|
||||
@@ -215,6 +216,10 @@ namespace Barotrauma
|
||||
case "latchonto":
|
||||
LatchOntoAI = new LatchOntoAI(subElement, this);
|
||||
break;
|
||||
case "swarm":
|
||||
case "swarmbehavior":
|
||||
SwarmBehavior = new SwarmBehavior(subElement, this);
|
||||
break;
|
||||
case "targetpriority":
|
||||
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
|
||||
break;
|
||||
@@ -364,12 +369,8 @@ namespace Barotrauma
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// Just some debug code that makes the characters to follow the mouse cursor
|
||||
//run = true;
|
||||
//Vector2 mousePos = ConvertUnits.ToSimUnits(Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
//steeringManager.SteeringSeek(mousePos, Character.AnimController.GetCurrentSpeed(run));
|
||||
|
||||
|
||||
SwarmBehavior?.Update(deltaTime);
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||
}
|
||||
|
||||
@@ -790,6 +791,7 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class SwarmBehavior
|
||||
{
|
||||
private float minDistFromClosest;
|
||||
private float maxDistFromCenter;
|
||||
private float cohesion;
|
||||
|
||||
private List<AICharacter> members = new List<AICharacter>();
|
||||
|
||||
private AIController ai;
|
||||
|
||||
public SwarmBehavior(XElement element, AIController ai)
|
||||
{
|
||||
this.ai = ai;
|
||||
minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f));
|
||||
maxDistFromCenter = ConvertUnits.ToSimUnits(element.GetAttributeFloat("maxdistfromcenter", 1000.0f));
|
||||
cohesion = element.GetAttributeFloat("cohesion", 0.1f);
|
||||
}
|
||||
|
||||
public static void CreateSwarm(IEnumerable<AICharacter> swarm)
|
||||
{
|
||||
foreach (AICharacter character in swarm)
|
||||
{
|
||||
if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
|
||||
{
|
||||
enemyAI.SwarmBehavior.members = swarm.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
members.RemoveAll(m => m.IsDead || m.Removed);
|
||||
if (members.Count < 2) { return; }
|
||||
|
||||
//calculate the "center of mass" of the swarm and the distance to the closest character in the swarm
|
||||
float closestDistSqr = float.MaxValue;
|
||||
Vector2 center = Vector2.Zero;
|
||||
AICharacter closest = null;
|
||||
foreach (AICharacter member in members)
|
||||
{
|
||||
center += member.SimPosition;
|
||||
if (member == ai.Character) { continue; }
|
||||
float distSqr = Vector2.DistanceSquared(member.SimPosition, ai.Character.SimPosition);
|
||||
if (distSqr < closestDistSqr)
|
||||
{
|
||||
closestDistSqr = distSqr;
|
||||
closest = member;
|
||||
}
|
||||
}
|
||||
center /= members.Count;
|
||||
|
||||
if (closest == null) { return; }
|
||||
|
||||
//steer away from the closest if too close
|
||||
float closestDist = (float)Math.Sqrt(closestDistSqr);
|
||||
if (closestDist < minDistFromClosest)
|
||||
{
|
||||
Vector2 diff = closest.SimPosition - ai.SimPosition;
|
||||
if (diff.LengthSquared() < 0.0001f)
|
||||
{
|
||||
diff = Vector2.UnitX;
|
||||
}
|
||||
ai.SteeringManager.SteeringManual(deltaTime, -diff);
|
||||
}
|
||||
//steer closer to the center of mass if too far
|
||||
else if (Vector2.DistanceSquared(center, ai.SimPosition) > maxDistFromCenter * maxDistFromCenter)
|
||||
{
|
||||
float distFromCenter = Vector2.Distance(center, ai.SimPosition);
|
||||
ai.SteeringManager.SteeringSeek(center, distFromCenter - maxDistFromCenter);
|
||||
}
|
||||
|
||||
//keep the characters moving in roughly the same direction
|
||||
if (cohesion > 0.0f)
|
||||
{
|
||||
Vector2 avgVel = Vector2.Zero;
|
||||
foreach (AICharacter member in members)
|
||||
{
|
||||
avgVel += member.AnimController.TargetMovement;
|
||||
}
|
||||
avgVel /= members.Count;
|
||||
ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,17 +227,17 @@ namespace Barotrauma
|
||||
monsters = new List<Character>();
|
||||
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
bool isClient = false;
|
||||
#if CLIENT
|
||||
isClient = GameMain.Client != null;
|
||||
#endif
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
|
||||
if (monsters.Count == amount)
|
||||
{
|
||||
spawnReady = true;
|
||||
//this will do nothing if the monsters have no swarm behavior defined,
|
||||
//otherwise it'll make the spawned characters act as a swarm
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
}
|
||||
}, Rand.Range(0f, amount / 2, Rand.RandSync.Server));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user