Data-driven NPC behaviors (#3271)

* Data-driven NPC behaviors

* Nuked AiLogicProcessor
* BehaviorSets are now all stored in yaml (might try making actions also yaml someday)
* Added a test to validate all BehaviorSets
* Might also try pooling actions in the future to reduce allocs but that'll be way down the line (cough physics).

* Forgot to re-add sorting nothing suss

* Remove last references

* Proper vector2i serialization for tile atmos (#3266)

* update map files

* update submodule

Co-authored-by: cyclowns <cyclowns@protonmail.ch>

* Remove weird "S" jumpsuit from existence (#3267)

* Change character names to use datasets prototypes (#3259)

* Remove old name lists in .txts

* Fix tests

* LATEST MASTER TECHNOLOGY

* Converts AdminMenu to partially use XAML (#3231)

* Cleans up Hydroponics content. (#3025)

* Adds to IgnoredComponents.cs

* Jackboots

* Half Done

* Moved to diff PR

* Everything functional

* Fixed Sprays

* Nice

* Fixed

* Update submodule

* Fix tests

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>

* Stacked sprite visualizer (#3096)

* Add Stack Visualizer

* Add cigarette pack resources

Adds transparent layers for visualizing cigarettes

* Add Bag Open/Close Visualizer

So storage opened in inventory can have different icons when opened
or closed.

* Create a component that only enumerates single item

Used for creating stuff like matchbox, or cigarettes. As a bonus.
It will only update stack visualizer for that particullar item.

* Refactoring stuff

* Fix other usage of stack in Resources

* Add docs

* Apply suggestions from code review

Apply metalgearsloth suggestions

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* Applied suggestions from metalgearsloth

* Changed SingleItemStorageComponent to StorageCounterComponent

Difference. New component doesn't spawn items, merely counts them.

* Refactored StackVisualizer

* Fix breakage with master

* Update Resources/Prototypes/Entities/Objects/Consumable/fancy.yml

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* Update with MGS suggestions

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* ApcNet updating fix (#3078)

* GridPowerComponent

* ApcNet Powered update bugfix

* PowerTest fix

* Add GridPower to Saltern

* test fix

* Update canceling cleanup

* code cleanup

* nullable & code cleanup for test

* undo power test nullable

* Replaces GridPowerSystem with ApcNetSystem

* build fix

* Update Content.Server/GameObjects/EntitySystems/ApcNetSystem.cs

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* Change all XAML to use spacestation14.io namespace (#3277)

* fix pizzaboxes (#3291)

Co-authored-by: cyclowns <cyclowns@protonmail.ch>

* Spikes fix reopened (#3203)

* DoAfter, dead and stun check, DragDropOn

* Not ignored anymore

* Copied comment deleted

* Herbert's an ass

* Woops

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>

* Make component states dependant on the player getting them (#3280)

* Make component states dependant on the player getting them

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>

* Updated submodule to v0.3.7.

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
Co-authored-by: Acruid <shatter66@gmail.com>

* Hoe fix (#3296)

* Initial (#3297)

* Sort reagent dispenser entries (#3272)

* Sort reagent dispenser entries

Saves manually doing it.

* zumzum's suggestion

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>

* Made firelocks damageable & destructible (#3303)

* Move job priority enum parity test ot unit tests (#3300)

* Spill hand contents when dropping them in a fall (#3304)

* Spill hand contents when dropping them due to falling down

* Better approach

* cleanup

* grammar

* stupid

* PauseManager moved to Shared (#3288)

* Namespace changes for moving IPauseManager to shared.

* Namespace changes for moving ITimerManager from Timers to Timing.

* Rebase Fixes.

* Update engine submodule to v0.3.8

* Improves kick, teleport and ban menus (#3312)

* Fix the admin panel not showing the account name (#3322)

* Fix name serialization for secret stashes (#3301)

* Fix name serialization for secret stashes

* Fix old usages of secret part name

* Separate ghost warp message into two (#3310)

* Separate ghost warp message into two

* Remove redundant arguments

* Address reviews

* Move properties up

* Add health overlay and a command to toggle it (#3278)

* Add health overlay bar and a command to toggle it

* Remove empty line

* Content PR for YAML hot reloading (#3319)

* Content PR for YAML hot reloading

* Add CanAdminReloadPrototypes (host permission)

* IndexedPrototype fixes

* Update RobustToolbox

* Update RobustToolbox

* Add an unconspicuous, meaningless and in no way motivated by any external force XML doc to buckle component

* Update RobustToolbox

* Update submodule to v0.3.12.

* Removed unused using statements that prevented compiling.
Removed references to IIndexedPrototype that does not exist anymore in the engine.

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
Co-authored-by: mirrorcult <notzombiedude@gmail.com>
Co-authored-by: cyclowns <cyclowns@protonmail.ch>
Co-authored-by: Visne <39844191+Visne@users.noreply.github.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: Leo <lzimann@users.noreply.github.com>
Co-authored-by: Swept <sweptwastaken@protonmail.com>
Co-authored-by: Ygg01 <y.laughing.man.y@gmail.com>
Co-authored-by: collinlunn <60152240+collinlunn@users.noreply.github.com>
Co-authored-by: komunre <49118681+komunre@users.noreply.github.com>
Co-authored-by: Acruid <shatter66@gmail.com>
Co-authored-by: Peptide90 <78795277+Peptide90@users.noreply.github.com>
Co-authored-by: Clyybber <darkmine956@gmail.com>
This commit is contained in:
metalgearsloth
2021-02-20 17:37:17 +11:00
committed by GitHub
parent 6f72f8f553
commit 4437fc7a1b
74 changed files with 548 additions and 707 deletions

View File

@@ -0,0 +1,172 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Server.AI.Utility.Actions;
using Content.Server.AI.Utility.AiLogic;
using Content.Server.GameObjects.EntitySystems.AI;
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);
}
/// <summary>
/// Handles BehaviorSets and adding / removing behaviors to NPCs
/// </summary>
internal sealed class NpcBehaviorManager : INpcBehaviorManager
{
[Dependency] private readonly IDynamicTypeFactory _typeFactory = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly NpcActionComparer _comparer = new();
private Dictionary<string, List<Type>> _behaviorSets = new();
public void Initialize()
{
IoCManager.InjectDependencies(this);
var protoManager = IoCManager.Resolve<IPrototypeManager>();
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
foreach (var bSet in protoManager.EnumeratePrototypes<BehaviorSetPrototype>())
{
var actions = new List<Type>();
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;
}
}
/// <summary>
/// Adds the BehaviorSet to the NPC.
/// </summary>
/// <param name="npc"></param>
/// <param name="behaviorSet"></param>
/// <param name="rebuild">Set to false if you want to manually rebuild it after bulk updates.</param>
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 && !EntitySystem.Get<AiSystem>().IsAwake(npc))
{
_entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(npc, false));
}
}
/// <summary>
/// Removes the BehaviorSet from the NPC.
/// </summary>
/// <param name="npc"></param>
/// <param name="behaviorSet"></param>
/// <param name="rebuild">Set to false if yo uwant to manually rebuild it after bulk updates.</param>
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 && EntitySystem.Get<AiSystem>().IsAwake(npc))
{
_entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(npc, true));
}
}
/// <summary>
/// Clear our actions and re-instantiate them from our BehaviorSets.
/// Will ensure each action is unique.
/// </summary>
/// <param name="npc"></param>
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<IAiUtility> GetActions(string behaviorSet)
{
foreach (var action in _behaviorSets[behaviorSet])
{
yield return (IAiUtility) _typeFactory.CreateInstance(action);
}
}
/// <summary>
/// Whenever the behavior sets are changed we'll re-sort the actions by bonus
/// </summary>
private void SortActions(UtilityAi npc)
{
npc.AvailableActions.Sort(_comparer);
}
private class NpcActionComparer : Comparer<IAiUtility>
{
public override int Compare(IAiUtility? x, IAiUtility? y)
{
if (x == null || y == null) return 0;
return y.Bonus.CompareTo(x.Bonus);
}
}
}
}