using System;
using System.Collections.Generic;
using Content.Server.AI.EntitySystems;
using Content.Server.AI.Utility.Actions;
using Content.Server.AI.Utility.AiLogic;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
namespace Content.Server.AI.Utility
{
internal interface INpcBehaviorManager
{
void Initialize();
void AddBehaviorSet(UtilityAi npc, string behaviorSet, bool rebuild = true);
void RemoveBehaviorSet(UtilityAi npc, string behaviorSet, bool rebuild = true);
void RebuildActions(UtilityAi npc);
}
///
/// Handles BehaviorSets and adding / removing behaviors to NPCs
///
internal sealed class NpcBehaviorManager : INpcBehaviorManager
{
[Dependency] private readonly IDynamicTypeFactory _typeFactory = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly NpcActionComparer _comparer = new();
private Dictionary> _behaviorSets = new();
public void Initialize()
{
IoCManager.InjectDependencies(this);
var protoManager = IoCManager.Resolve();
var reflectionManager = IoCManager.Resolve();
foreach (var bSet in protoManager.EnumeratePrototypes())
{
var actions = new List();
foreach (var act in bSet.Actions)
{
if (!reflectionManager.TryLooseGetType(act, out var parsedType) ||
!typeof(IAiUtility).IsAssignableFrom(parsedType))
{
Logger.Error($"Unable to parse AI action for {act}");
}
else
{
actions.Add(parsedType);
}
}
_behaviorSets[bSet.ID] = actions;
}
}
///
/// Adds the BehaviorSet to the NPC.
///
///
///
/// Set to false if you want to manually rebuild it after bulk updates.
public void AddBehaviorSet(UtilityAi npc, string behaviorSet, bool rebuild = true)
{
if (!_behaviorSets.ContainsKey(behaviorSet))
{
Logger.Error($"Tried to add BehaviorSet {behaviorSet} to {npc} but no such BehaviorSet found!");
return;
}
if (!npc.BehaviorSets.Add(behaviorSet))
{
Logger.Error($"Tried to add BehaviorSet {behaviorSet} to {npc} which already has the BehaviorSet!");
return;
}
if (rebuild)
RebuildActions(npc);
if (npc.BehaviorSets.Count == 1 && !npc.Awake)
{
EntitySystem.Get().WakeNPC(npc);
}
}
///
/// Removes the BehaviorSet from the NPC.
///
///
///
/// Set to false if yo uwant to manually rebuild it after bulk updates.
public void RemoveBehaviorSet(UtilityAi npc, string behaviorSet, bool rebuild = true)
{
if (!_behaviorSets.TryGetValue(behaviorSet, out var actions))
{
Logger.Error($"Tried to remove BehaviorSet {behaviorSet} from {npc} but no such BehaviorSet found!");
return;
}
if (!npc.BehaviorSets.Remove(behaviorSet))
{
Logger.Error($"Tried to remove BehaviorSet {behaviorSet} from {npc} but it doesn't have that BehaviorSet!");
return;
}
if (rebuild)
RebuildActions(npc);
if (npc.BehaviorSets.Count == 0 && npc.Awake)
{
EntitySystem.Get().SleepNPC(npc);
}
}
///
/// Clear our actions and re-instantiate them from our BehaviorSets.
/// Will ensure each action is unique.
///
///
public void RebuildActions(UtilityAi npc)
{
npc.AvailableActions.Clear();
foreach (var bSet in npc.BehaviorSets)
{
foreach (var action in GetActions(bSet))
{
if (npc.AvailableActions.Contains(action)) continue;
// Setup
action.Owner = npc.Owner;
// Ad to actions.
npc.AvailableActions.Add(action);
}
}
SortActions(npc);
}
private IEnumerable GetActions(string behaviorSet)
{
foreach (var action in _behaviorSets[behaviorSet])
{
yield return (IAiUtility) _typeFactory.CreateInstance(action);
}
}
///
/// Whenever the behavior sets are changed we'll re-sort the actions by bonus
///
private void SortActions(UtilityAi npc)
{
npc.AvailableActions.Sort(_comparer);
}
private class NpcActionComparer : Comparer
{
public override int Compare(IAiUtility? x, IAiUtility? y)
{
if (x == null || y == null) return 0;
return y.Bonus.CompareTo(x.Bonus);
}
}
}
}