(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\PathFinder.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringManager.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringManager.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringPath.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\AnimController.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\FishAnimController.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\FishAnimController.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\HumanoidAnimController.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\HumanoidAnimController.cs" />
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ namespace Barotrauma
|
|||||||
private readonly float memoryFadeTime = 0.5f;
|
private readonly float memoryFadeTime = 0.5f;
|
||||||
|
|
||||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||||
|
public SwarmBehavior SwarmBehavior { get; private set; }
|
||||||
|
|
||||||
public bool AttackHumans
|
public bool AttackHumans
|
||||||
{
|
{
|
||||||
@@ -215,6 +216,10 @@ namespace Barotrauma
|
|||||||
case "latchonto":
|
case "latchonto":
|
||||||
LatchOntoAI = new LatchOntoAI(subElement, this);
|
LatchOntoAI = new LatchOntoAI(subElement, this);
|
||||||
break;
|
break;
|
||||||
|
case "swarm":
|
||||||
|
case "swarmbehavior":
|
||||||
|
SwarmBehavior = new SwarmBehavior(subElement, this);
|
||||||
|
break;
|
||||||
case "targetpriority":
|
case "targetpriority":
|
||||||
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
|
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
|
||||||
break;
|
break;
|
||||||
@@ -364,12 +369,8 @@ namespace Barotrauma
|
|||||||
default:
|
default:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just some debug code that makes the characters to follow the mouse cursor
|
SwarmBehavior?.Update(deltaTime);
|
||||||
//run = true;
|
|
||||||
//Vector2 mousePos = ConvertUnits.ToSimUnits(Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition));
|
|
||||||
//steeringManager.SteeringSeek(mousePos, Character.AnimController.GetCurrentSpeed(run));
|
|
||||||
|
|
||||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,6 +791,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
|
UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
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>();
|
monsters = new List<Character>();
|
||||||
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
|
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
|
||||||
for (int i = 0; i < amount; i++)
|
for (int i = 0; i < amount; i++)
|
||||||
{
|
{
|
||||||
CoroutineManager.InvokeAfter(() =>
|
CoroutineManager.InvokeAfter(() =>
|
||||||
{
|
{
|
||||||
bool isClient = false;
|
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||||
#if CLIENT
|
|
||||||
isClient = GameMain.Client != null;
|
|
||||||
#endif
|
|
||||||
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
|
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
|
||||||
if (monsters.Count == amount)
|
if (monsters.Count == amount)
|
||||||
{
|
{
|
||||||
spawnReady = true;
|
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));
|
}, Rand.Range(0f, amount / 2, Rand.RandSync.Server));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user