* 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>
211 lines
7.4 KiB
C#
211 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Content.Server.AI.Operators;
|
|
using Content.Server.AI.Utility.Actions;
|
|
using Content.Server.AI.WorldState;
|
|
using Content.Server.AI.WorldState.States.Utility;
|
|
using Content.Server.GameObjects.Components.Movement;
|
|
using Content.Server.GameObjects.EntitySystems.AI;
|
|
using Content.Server.GameObjects.EntitySystems.AI.LoadBalancer;
|
|
using Content.Server.GameObjects.EntitySystems.JobQueues;
|
|
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
using Content.Shared.GameObjects.Components.Movement;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Serialization;
|
|
|
|
namespace Content.Server.AI.Utility.AiLogic
|
|
{
|
|
// TODO: Need to split out the IMover stuff for NPC to a generic one that can be used for hoomans as well.
|
|
[RegisterComponent]
|
|
[ComponentReference(typeof(AiControllerComponent)), ComponentReference(typeof(IMoverComponent))]
|
|
internal sealed class UtilityAi : AiControllerComponent
|
|
{
|
|
public override string Name => "UtilityAI";
|
|
|
|
// TODO: Look at having ParallelOperators (probably no more than that as then you'd have a full-blown BT)
|
|
// Also RepeatOperators (e.g. if we're following an entity keep repeating MoveToEntity)
|
|
private AiActionSystem _planner;
|
|
public Blackboard Blackboard => _blackboard;
|
|
private Blackboard _blackboard;
|
|
|
|
/// <summary>
|
|
/// The sum of all BehaviorSets gives us what actions the AI can take
|
|
/// </summary>
|
|
public HashSet<string> BehaviorSets { get; } = new();
|
|
|
|
public List<IAiUtility> AvailableActions { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// The currently running action; most importantly are the operators.
|
|
/// </summary>
|
|
public UtilityAction CurrentAction { get; private set; }
|
|
|
|
/// <summary>
|
|
/// How frequently we can re-plan. If an AI's in combat you could decrease the cooldown,
|
|
/// or if there's no players nearby increase it.
|
|
/// </summary>
|
|
public float PlanCooldown { get; } = 0.5f;
|
|
private float _planCooldownRemaining;
|
|
|
|
/// <summary>
|
|
/// If we've requested a plan then wait patiently for the action
|
|
/// </summary>
|
|
private AiActionRequestJob _actionRequest;
|
|
|
|
private CancellationTokenSource _actionCancellation;
|
|
|
|
/// <summary>
|
|
/// If we can't do anything then stop thinking; should probably use ActionBlocker instead
|
|
/// </summary>
|
|
private bool _isDead;
|
|
|
|
public override void ExposeData(ObjectSerializer serializer)
|
|
{
|
|
base.ExposeData(serializer);
|
|
var bSets = serializer.ReadDataField("behaviorSets", new List<string>());
|
|
|
|
if (bSets.Count > 0)
|
|
{
|
|
var behaviorManager = IoCManager.Resolve<INpcBehaviorManager>();
|
|
|
|
foreach (var bSet in bSets)
|
|
{
|
|
behaviorManager.AddBehaviorSet(this, bSet, false);
|
|
}
|
|
|
|
behaviorManager.RebuildActions(this);
|
|
}
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
_planCooldownRemaining = PlanCooldown;
|
|
_blackboard = new Blackboard(Owner);
|
|
_planner = EntitySystem.Get<AiActionSystem>();
|
|
}
|
|
|
|
public override void OnRemove()
|
|
{
|
|
base.OnRemove();
|
|
var currentOp = CurrentAction?.ActionOperators.Peek();
|
|
currentOp?.Shutdown(Outcome.Failed);
|
|
CurrentAction?.Shutdown();
|
|
CurrentAction = null;
|
|
}
|
|
|
|
public void MobStateChanged(MobStateChangedMessage message)
|
|
{
|
|
var oldDeadState = _isDead;
|
|
_isDead = message.Component.IsIncapacitated();
|
|
|
|
if (oldDeadState != _isDead)
|
|
{
|
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
|
|
switch (_isDead)
|
|
{
|
|
case true:
|
|
entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(this, true));
|
|
break;
|
|
case false:
|
|
entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(this, false));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ReceivedAction()
|
|
{
|
|
switch (_actionRequest.Exception)
|
|
{
|
|
case null:
|
|
break;
|
|
default:
|
|
Logger.FatalS("ai", _actionRequest.Exception.ToString());
|
|
throw _actionRequest.Exception;
|
|
}
|
|
var action = _actionRequest.Result;
|
|
_actionRequest = null;
|
|
// Actions with lower scores should be implicitly dumped by GetAction
|
|
// If we're not allowed to replace the action with an action of the same type then dump.
|
|
if (action == null || !action.CanOverride && CurrentAction?.GetType() == action.GetType())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var currentOp = CurrentAction?.ActionOperators.Peek();
|
|
if (currentOp != null && currentOp.HasStartup)
|
|
{
|
|
currentOp.Shutdown(Outcome.Failed);
|
|
}
|
|
|
|
CurrentAction = action;
|
|
action.SetupOperators(_blackboard);
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
// If we asked for a new action we don't want to dump the existing one.
|
|
if (_actionRequest != null)
|
|
{
|
|
if (_actionRequest.Status != JobStatus.Finished)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ReceivedAction();
|
|
// Do something next tick
|
|
return;
|
|
}
|
|
|
|
_planCooldownRemaining -= frameTime;
|
|
|
|
// Might find a better action while we're doing one already
|
|
if (_planCooldownRemaining <= 0.0f)
|
|
{
|
|
_planCooldownRemaining = PlanCooldown;
|
|
_actionCancellation = new CancellationTokenSource();
|
|
_actionRequest = _planner.RequestAction(new AiActionRequest(Owner.Uid, _blackboard, AvailableActions), _actionCancellation);
|
|
|
|
return;
|
|
}
|
|
|
|
// When we spawn in we won't get an action for a bit
|
|
if (CurrentAction == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var outcome = CurrentAction.Execute(frameTime);
|
|
|
|
switch (outcome)
|
|
{
|
|
case Outcome.Success:
|
|
if (CurrentAction.ActionOperators.Count == 0)
|
|
{
|
|
CurrentAction.Shutdown();
|
|
CurrentAction = null;
|
|
// Nothing to compare new action to
|
|
_blackboard.GetState<LastUtilityScoreState>().SetValue(0.0f);
|
|
}
|
|
break;
|
|
case Outcome.Continuing:
|
|
break;
|
|
case Outcome.Failed:
|
|
CurrentAction.Shutdown();
|
|
CurrentAction = null;
|
|
_blackboard.GetState<LastUtilityScoreState>().SetValue(0.0f);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
}
|
|
}
|