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:
@@ -1,41 +1,41 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.AiLogic;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.AI;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles NPCs running every tick.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal class AiSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactory _typeFactory = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
||||
private readonly Dictionary<string, Type> _processorTypes = new();
|
||||
|
||||
/// <summary>
|
||||
/// To avoid iterating over dead AI continuously they can wake and sleep themselves when necessary.
|
||||
/// </summary>
|
||||
private readonly HashSet<AiLogicProcessor> _awakeAi = new();
|
||||
private readonly HashSet<AiControllerComponent> _awakeAi = new();
|
||||
|
||||
// To avoid modifying awakeAi while iterating over it.
|
||||
private readonly List<SleepAiMessage> _queuedSleepMessages = new();
|
||||
|
||||
private readonly List<MobStateChangedMessage> _queuedMobStateMessages = new();
|
||||
|
||||
public bool IsAwake(AiLogicProcessor processor) => _awakeAi.Contains(processor);
|
||||
public bool IsAwake(AiControllerComponent npc) => _awakeAi.Contains(npc);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
@@ -43,15 +43,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SleepAiMessage>(HandleAiSleep);
|
||||
SubscribeLocalEvent<MobStateChangedMessage>(MobStateChanged);
|
||||
|
||||
var processors = _reflectionManager.GetAllChildren<UtilityAi>();
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
var att = (AiLogicProcessorAttribute) Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute))!;
|
||||
// Tests should pick this up
|
||||
DebugTools.AssertNotNull(att);
|
||||
_processorTypes.Add(att.SerializeName, processor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -63,13 +54,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
|
||||
foreach (var message in _queuedMobStateMessages)
|
||||
{
|
||||
// TODO: Need to generecise this but that will be part of a larger cleanup later anyway.
|
||||
if (message.Entity.Deleted ||
|
||||
!message.Entity.TryGetComponent(out AiControllerComponent? controller))
|
||||
!message.Entity.TryGetComponent(out UtilityAi? controller))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
controller.Processor?.MobStateChanged(message);
|
||||
controller.MobStateChanged(message);
|
||||
}
|
||||
|
||||
_queuedMobStateMessages.Clear();
|
||||
@@ -79,14 +71,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
switch (message.Sleep)
|
||||
{
|
||||
case true:
|
||||
if (_awakeAi.Count == cvarMaxUpdates && _awakeAi.Contains(message.Processor))
|
||||
if (_awakeAi.Count == cvarMaxUpdates && _awakeAi.Contains(message.Component))
|
||||
{
|
||||
Logger.Warning($"Under AI limit again: {_awakeAi.Count - 1} / {cvarMaxUpdates}");
|
||||
}
|
||||
_awakeAi.Remove(message.Processor);
|
||||
_awakeAi.Remove(message.Component);
|
||||
break;
|
||||
case false:
|
||||
_awakeAi.Add(message.Processor);
|
||||
_awakeAi.Add(message.Component);
|
||||
|
||||
if (_awakeAi.Count > cvarMaxUpdates)
|
||||
{
|
||||
@@ -97,16 +89,17 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
}
|
||||
|
||||
_queuedSleepMessages.Clear();
|
||||
var toRemove = new List<AiLogicProcessor>();
|
||||
var toRemove = new List<AiControllerComponent>();
|
||||
var maxUpdates = Math.Min(_awakeAi.Count, cvarMaxUpdates);
|
||||
var count = 0;
|
||||
|
||||
foreach (var processor in _awakeAi)
|
||||
foreach (var npc in _awakeAi)
|
||||
{
|
||||
if (processor.SelfEntity.Deleted ||
|
||||
!processor.SelfEntity.HasComponent<AiControllerComponent>())
|
||||
if (npc.Paused) continue;
|
||||
|
||||
if (npc.Deleted)
|
||||
{
|
||||
toRemove.Add(processor);
|
||||
toRemove.Add(npc);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -115,7 +108,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
break;
|
||||
}
|
||||
|
||||
processor.Update(frameTime);
|
||||
npc.Update(frameTime);
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -139,32 +132,5 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
||||
|
||||
_queuedMobStateMessages.Add(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will start up the controller's processor if not already done so.
|
||||
/// Also add them to the awakeAi for updates.
|
||||
/// </summary>
|
||||
/// <param name="controller"></param>
|
||||
public void ProcessorInitialize(AiControllerComponent controller)
|
||||
{
|
||||
if (controller.Processor != null || controller.LogicName == null) return;
|
||||
controller.Processor = CreateProcessor(controller.LogicName);
|
||||
controller.Processor.SelfEntity = controller.Owner;
|
||||
controller.Processor.Setup();
|
||||
_awakeAi.Add(controller.Processor);
|
||||
}
|
||||
|
||||
private UtilityAi CreateProcessor(string name)
|
||||
{
|
||||
if (_processorTypes.TryGetValue(name, out var type))
|
||||
{
|
||||
return (UtilityAi)_typeFactory.CreateInstance(type);
|
||||
}
|
||||
|
||||
// processor needs to inherit AiLogicProcessor, and needs an AiLogicProcessorAttribute to define the YAML name
|
||||
throw new ArgumentException($"Processor type {name} could not be found.", nameof(name));
|
||||
}
|
||||
|
||||
public bool ProcessorTypeExists(string name) => _processorTypes.ContainsKey(name);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user