Merge branch 'master' into 20-06-24-movement-prediction
This commit is contained in:
@@ -54,8 +54,9 @@ namespace Content.Client.Commands
|
||||
}
|
||||
|
||||
return !anyAction;
|
||||
#endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,9 @@ namespace Content.Client.Commands
|
||||
}
|
||||
|
||||
return !anyAction;
|
||||
#endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
Content.Client/Commands/ToggleOutlineCommand.cs
Normal file
24
Content.Client/Commands/ToggleOutlineCommand.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.Commands
|
||||
{
|
||||
public class ToggleOutlineCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "toggleoutline";
|
||||
|
||||
public string Description => "Toggles outline drawing on entities.";
|
||||
|
||||
public string Help => "";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
var _configurationManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
var old = _configurationManager.GetCVar<bool>("outline.enabled");
|
||||
_configurationManager.SetCVar("outline.enabled", !old);
|
||||
console.AddLine($"Draw outlines set to: {_configurationManager.GetCVar<bool>("outline.enabled")}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -41,6 +42,7 @@ namespace Content.Client
|
||||
[Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner;
|
||||
[Dependency] private readonly IGameController _gameController;
|
||||
[Dependency] private readonly IStateManager _stateManager;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Init()
|
||||
@@ -85,8 +87,6 @@ namespace Content.Client
|
||||
"Multitool",
|
||||
"Wrench",
|
||||
"Crowbar",
|
||||
"HitscanWeapon",
|
||||
"ProjectileWeapon",
|
||||
"Projectile",
|
||||
"MeleeWeapon",
|
||||
"Storeable",
|
||||
@@ -100,8 +100,8 @@ namespace Content.Client
|
||||
"LightBulb",
|
||||
"Healing",
|
||||
"Catwalk",
|
||||
"BallisticMagazine",
|
||||
"BallisticBullet",
|
||||
"RangedMagazine",
|
||||
"Ammo",
|
||||
"HitscanWeaponCapacitor",
|
||||
"PowerCell",
|
||||
"WeaponCapacitorCharger",
|
||||
@@ -144,6 +144,13 @@ namespace Content.Client
|
||||
"Bucket",
|
||||
"Puddle",
|
||||
"CanSpill",
|
||||
"SpeedLoader",
|
||||
"Hitscan",
|
||||
"BoltActionBarrel",
|
||||
"PumpBarrel",
|
||||
"RevolverBarrel",
|
||||
"ExplosiveProjectile",
|
||||
"StunnableProjectile",
|
||||
"RandomPottedPlant",
|
||||
"CommunicationsConsole",
|
||||
"BarSign",
|
||||
@@ -162,6 +169,10 @@ namespace Content.Client
|
||||
"SecureEntityStorage",
|
||||
"PresetIdCard",
|
||||
"SolarControlConsole",
|
||||
"BatteryBarrel",
|
||||
"FlashExplosive",
|
||||
"FlashProjectile",
|
||||
"Utensil",
|
||||
};
|
||||
|
||||
foreach (var ignoreName in registerIgnore)
|
||||
@@ -209,6 +220,8 @@ namespace Content.Client
|
||||
{
|
||||
IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace);
|
||||
};
|
||||
|
||||
_configurationManager.RegisterCVar("outline.enabled", true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -95,6 +95,9 @@ namespace Content.Client.GameObjects.Components.Doors
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
if (component.Owner.Deleted)
|
||||
return;
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
var animPlayer = component.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
if (!component.TryGetData(DoorVisuals.VisualState, out DoorVisualState state))
|
||||
|
||||
@@ -203,31 +203,30 @@ namespace Content.Client.GameObjects
|
||||
buttonDict.Add(slot, button);
|
||||
}
|
||||
|
||||
const int size = ButtonSize;
|
||||
const int sep = ButtonSeparation;
|
||||
const int rSep = RightSeparation;
|
||||
const int sizep = (ButtonSize + ButtonSeparation);
|
||||
|
||||
// Left column.
|
||||
AddButton(Slots.EYES, "glasses", (0, size + sep));
|
||||
AddButton(Slots.INNERCLOTHING, "uniform", (0, 2 * (size + sep)));
|
||||
AddButton(Slots.EXOSUITSLOT1, "suit_storage", (0, 3 * (size + sep)));
|
||||
AddButton(Slots.EYES, "glasses", (0, 0));
|
||||
AddButton(Slots.NECK, "neck", (0, sizep));
|
||||
AddButton(Slots.INNERCLOTHING, "uniform", (0, 2 * sizep));
|
||||
|
||||
// Middle column.
|
||||
AddButton(Slots.HEAD, "head", (size + sep, 0));
|
||||
AddButton(Slots.MASK, "mask", (size + sep, size + sep));
|
||||
AddButton(Slots.OUTERCLOTHING, "suit", (size + sep, 2 * (size + sep)));
|
||||
AddButton(Slots.SHOES, "shoes", (size + sep, 3 * (size + sep)));
|
||||
AddButton(Slots.HEAD, "head", (sizep, 0));
|
||||
AddButton(Slots.MASK, "mask", (sizep, sizep));
|
||||
AddButton(Slots.OUTERCLOTHING, "suit", (sizep, 2 * sizep));
|
||||
AddButton(Slots.SHOES, "shoes", (sizep, 3 * sizep));
|
||||
|
||||
// Right column
|
||||
AddButton(Slots.EARS, "ears", (2 * (size + sep), 0));
|
||||
AddButton(Slots.IDCARD, "id", (2 * (size + sep), size + sep));
|
||||
AddButton(Slots.GLOVES, "gloves", (2 * (size + sep), 2 * (size + sep)));
|
||||
AddButton(Slots.EARS, "ears", (2 * sizep, 0));
|
||||
AddButton(Slots.IDCARD, "id", (2 * sizep, sizep));
|
||||
AddButton(Slots.EXOSUITSLOT1, "suit_storage", (2 * sizep, 2 * sizep));
|
||||
AddButton(Slots.POCKET1, "pocket", (2 * sizep, 3 * sizep));
|
||||
|
||||
// Far right column.
|
||||
AddButton(Slots.BACKPACK, "back", (rSep + 3 * (size + sep), 0));
|
||||
AddButton(Slots.BELT, "belt", (rSep + 3 * (size + sep), size + sep));
|
||||
AddButton(Slots.POCKET1, "pocket", (rSep + 3 * (size + sep), 2 * (size + sep)));
|
||||
AddButton(Slots.POCKET2, "pocket", (rSep + 3 * (size + sep), 3 * (size + sep)));
|
||||
AddButton(Slots.BACKPACK, "back", (3 * sizep, 0));
|
||||
AddButton(Slots.BELT, "belt", (3 * sizep, sizep));
|
||||
AddButton(Slots.GLOVES, "gloves", (3 * sizep, 2 * sizep));
|
||||
AddButton(Slots.POCKET2, "pocket", (3 * sizep, 3 * sizep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Content.Client.GameObjects
|
||||
|
||||
public IEntity GetEntity(string index)
|
||||
{
|
||||
if (_hands.TryGetValue(index, out var entity))
|
||||
if (!string.IsNullOrEmpty(index) && _hands.TryGetValue(index, out var entity))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
@@ -166,6 +166,8 @@ namespace Content.Client.GameObjects
|
||||
{
|
||||
_hands.Add(slot, null);
|
||||
}
|
||||
|
||||
serializer.DataField(this, x => ActiveIndex, "defaultHand", _hands.Keys.LastOrDefault());
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent component)
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Content.Shared.GameObjects.Components.Weapons;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ClientFlashableComponent : SharedFlashableComponent
|
||||
{
|
||||
private CancellationTokenSource _cancelToken;
|
||||
private TimeSpan _startTime;
|
||||
private double _duration;
|
||||
private FlashOverlay _overlay;
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (curState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
if (playerManager.LocalPlayer.ControlledEntity != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newState = (FlashComponentState) curState;
|
||||
if (newState.Time == default)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Few things here:
|
||||
// 1. If a shorter duration flash is applied then don't do anything
|
||||
// 2. If the client-side time is later than when the flash should've ended don't do anything
|
||||
var currentTime = IoCManager.Resolve<IGameTiming>().CurTime.TotalSeconds;
|
||||
var newEndTime = newState.Time.TotalSeconds + newState.Duration;
|
||||
var currentEndTime = _startTime.TotalSeconds + _duration;
|
||||
|
||||
if (currentEndTime > newEndTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTime > newEndTime)
|
||||
{
|
||||
DisableOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
_startTime = newState.Time;
|
||||
_duration = newState.Duration;
|
||||
|
||||
EnableOverlay(newEndTime - currentTime);
|
||||
}
|
||||
|
||||
private void EnableOverlay(double duration)
|
||||
{
|
||||
// If the timer gets reset
|
||||
if (_overlay != null)
|
||||
{
|
||||
_overlay.Duration = _duration;
|
||||
_overlay.StartTime = _startTime;
|
||||
_cancelToken.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
_overlay = new FlashOverlay(_duration);
|
||||
overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
_cancelToken = new CancellationTokenSource();
|
||||
Timer.Spawn((int) duration * 1000, DisableOverlay, _cancelToken.Token);
|
||||
}
|
||||
|
||||
private void DisableOverlay()
|
||||
{
|
||||
if (_overlay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var overlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
overlayManager.RemoveOverlay(_overlay.ID);
|
||||
_overlay = null;
|
||||
_cancelToken.Cancel();
|
||||
_cancelToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FlashOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
private readonly IGameTiming _timer;
|
||||
private readonly IClyde _displayManager;
|
||||
public TimeSpan StartTime { get; set; }
|
||||
public double Duration { get; set; }
|
||||
public FlashOverlay(double duration) : base(nameof(FlashOverlay))
|
||||
{
|
||||
_timer = IoCManager.Resolve<IGameTiming>();
|
||||
_displayManager = IoCManager.Resolve<IClyde>();
|
||||
StartTime = _timer.CurTime;
|
||||
Duration = duration;
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle)
|
||||
{
|
||||
var elapsedTime = (_timer.CurTime - StartTime).TotalSeconds;
|
||||
if (elapsedTime > Duration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var screenHandle = (DrawingHandleScreen) handle;
|
||||
|
||||
screenHandle.DrawRect(
|
||||
new UIBox2(0.0f, 0.0f, _displayManager.ScreenSize.X, _displayManager.ScreenSize.Y),
|
||||
Color.White.WithAlpha(GetAlpha(elapsedTime / Duration))
|
||||
);
|
||||
}
|
||||
|
||||
private float GetAlpha(double ratio)
|
||||
{
|
||||
// Ideally you just want a smooth slope to finish it so it's not jarring at the end
|
||||
// By all means put in a better curve
|
||||
const float slope = -9.0f;
|
||||
const float exponent = 0.1f;
|
||||
const float yOffset = 9.0f;
|
||||
const float xOffset = 0.0f;
|
||||
|
||||
// Overkill but easy to adjust if you want to mess around with the design
|
||||
var result = (float) Math.Clamp(slope * (float) Math.Pow(ratio - xOffset, exponent) + yOffset, 0.0, 1.0);
|
||||
DebugTools.Assert(!float.IsNaN(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
public sealed class BallisticMagazineVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _baseState;
|
||||
private int _steps;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_baseState = node.GetNode("base_state").AsString();
|
||||
_steps = node.GetNode("steps").AsInt();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(BallisticMagazineVisuals.AmmoCapacity, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(BallisticMagazineVisuals.AmmoLeft, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
|
||||
|
||||
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
public sealed class BallisticMagazineWeaponVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _baseState;
|
||||
private int _steps;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_baseState = node.GetNode("base_state").AsString();
|
||||
_steps = node.GetNode("steps").AsInt();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
component.TryGetData(BallisticMagazineWeaponVisuals.MagazineLoaded, out bool loaded);
|
||||
|
||||
if (loaded)
|
||||
{
|
||||
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoCapacity, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoLeft, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// capacity is - 1 as normally a bullet is chambered so max state is virtually never hit.
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity - 1, _steps);
|
||||
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _baseState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,26 @@
|
||||
using System;
|
||||
using System;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Client.StaticIoC;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class BallisticMagazineWeaponComponent : Component, IItemStatus
|
||||
public class ClientMagazineBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
private static readonly Animation AlarmAnimationSmg = new Animation
|
||||
{
|
||||
@@ -70,8 +68,8 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
}
|
||||
};
|
||||
|
||||
public override string Name => "BallisticMagazineWeapon";
|
||||
public override uint? NetID => ContentNetIDs.BALLISTIC_MAGAZINE_WEAPON;
|
||||
public override string Name => "MagazineBarrel";
|
||||
public override uint? NetID => ContentNetIDs.MAGAZINE_BARREL;
|
||||
|
||||
private StatusControl _statusControl;
|
||||
|
||||
@@ -101,11 +99,11 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (!(curState is BallisticMagazineWeaponComponentState cast))
|
||||
if (!(curState is MagazineBarrelComponentState cast))
|
||||
return;
|
||||
|
||||
Chambered = cast.Chambered;
|
||||
MagazineCount = cast.MagazineCount;
|
||||
MagazineCount = cast.Magazine;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
@@ -115,7 +113,8 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case BmwComponentAutoEjectedMessage _:
|
||||
|
||||
case MagazineAutoEjectMessage _:
|
||||
_statusControl?.PlayAlarmAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -138,13 +137,13 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly BallisticMagazineWeaponComponent _parent;
|
||||
private readonly ClientMagazineBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsListTop;
|
||||
private readonly HBoxContainer _bulletsListBottom;
|
||||
private readonly TextureRect _chamberedBullet;
|
||||
private readonly Label _noMagazineLabel;
|
||||
|
||||
public StatusControl(BallisticMagazineWeaponComponent parent)
|
||||
public StatusControl(ClientMagazineBarrelComponent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand;
|
||||
@@ -181,7 +180,7 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
},
|
||||
(_chamberedBullet = new TextureRect
|
||||
{
|
||||
Texture = ResC.GetTexture("/Textures/UserInterface/status/bullets/chambered.png"),
|
||||
Texture = StaticIoC.ResC.GetTexture("/Textures/UserInterface/status/bullets/chambered.png"),
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill,
|
||||
})
|
||||
@@ -223,7 +222,7 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
texturePath = "/Textures/UserInterface/status/bullets/tiny.png";
|
||||
}
|
||||
|
||||
var texture = ResC.GetTexture(texturePath);
|
||||
var texture = StaticIoC.ResC.GetTexture(texturePath);
|
||||
|
||||
const int tinyMaxRow = 60;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BarrelBoltVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, $"bolt-open");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(BarrelBoltVisuals.BoltOpen, out bool boltOpen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (boltOpen)
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, "bolt-open");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, "bolt-closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
||||
using Content.Shared.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MagVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private bool _magLoaded;
|
||||
private string _magState;
|
||||
private int _magSteps;
|
||||
private bool _zeroVisible;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
_magState = node.GetNode("magState").AsString();
|
||||
_magSteps = node.GetNode("steps").AsInt();
|
||||
_zeroVisible = node.GetNode("zeroVisible").AsBool();
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Mag, $"{_magState}-{_magSteps-1}");
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.MagUnshaded, $"{_magState}-unshaded-{_magSteps-1}");
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
// tl;dr
|
||||
// 1.If no mag then hide it OR
|
||||
// 2. If step 0 isn't visible then hide it (mag or unshaded)
|
||||
// 3. Otherwise just do mag / unshaded as is
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
component.TryGetData(MagazineBarrelVisuals.MagLoaded, out _magLoaded);
|
||||
|
||||
if (_magLoaded)
|
||||
{
|
||||
if (!component.TryGetData(AmmoVisuals.AmmoMax, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(AmmoVisuals.AmmoCount, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity, _magSteps);
|
||||
|
||||
if (step == 0 && !_zeroVisible)
|
||||
{
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, true);
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Mag, $"{_magState}-{step}");
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, true);
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.MagUnshaded, $"{_magState}-unshaded-{step}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class SpentAmmoVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(AmmoVisuals.Spent, out bool spent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.LayerSetState(AmmoVisualLayers.Base, spent ? "spent" : "base");
|
||||
}
|
||||
}
|
||||
|
||||
public enum AmmoVisualLayers
|
||||
{
|
||||
Base,
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,35 @@ using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
// Yeah I put it all in the same enum, don't judge me
|
||||
public enum RangedBarrelVisualLayers
|
||||
{
|
||||
Base,
|
||||
BaseUnshaded,
|
||||
Bolt,
|
||||
Mag,
|
||||
MagUnshaded,
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class ClientRangedWeaponComponent : SharedRangedWeaponComponent
|
||||
{
|
||||
public FireRateSelector FireRateSelector { get; private set; } = FireRateSelector.Safety;
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (!(curState is RangedWeaponComponentState rangedState))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FireRateSelector = rangedState.FireRateSelector;
|
||||
}
|
||||
|
||||
public void SyncFirePos(GridCoordinates worldPos)
|
||||
{
|
||||
SendNetworkMessage(new SyncFirePosMessage(worldPos));
|
||||
SendNetworkMessage(new FirePosComponentMessage(worldPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class HitscanWeaponVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _prefix;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_prefix = node.GetNode("prefix").AsString();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData(PowerCellVisuals.ChargeLevel, out float fraction))
|
||||
{
|
||||
sprite.LayerSetState(0, $"{_prefix}_{ContentHelpers.RoundToLevels(fraction, 1, 5) * 25}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Client.GameObjects.Components.Weapons.Ranged;
|
||||
using System;
|
||||
using Content.Client.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
@@ -26,8 +28,8 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
|
||||
private InputSystem _inputSystem;
|
||||
private CombatModeSystem _combatModeSystem;
|
||||
private bool _isFirstShot;
|
||||
private bool _blocked;
|
||||
private int _shotCounter;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -47,17 +49,14 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
var canFireSemi = _isFirstShot;
|
||||
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
|
||||
if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down)
|
||||
{
|
||||
_isFirstShot = true;
|
||||
_shotCounter = 0;
|
||||
_blocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_isFirstShot = false;
|
||||
|
||||
var entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (entity == null || !entity.TryGetComponent(out IHandsComponent hands))
|
||||
{
|
||||
@@ -71,6 +70,25 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
switch (weapon.FireRateSelector)
|
||||
{
|
||||
case FireRateSelector.Safety:
|
||||
_blocked = true;
|
||||
return;
|
||||
case FireRateSelector.Single:
|
||||
if (_shotCounter >= 1)
|
||||
{
|
||||
_blocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case FireRateSelector.Automatic:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (_blocked)
|
||||
{
|
||||
return;
|
||||
@@ -81,10 +99,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
if (!_mapManager.TryFindGridAt(worldPos, out var grid))
|
||||
grid = _mapManager.GetDefaultGrid(worldPos.MapId);
|
||||
|
||||
if (weapon.Automatic || canFireSemi)
|
||||
{
|
||||
weapon.SyncFirePos(grid.MapToGrid(worldPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Content.Client.GameTicking
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public string ServerInfoBlob { get; private set; }
|
||||
[ViewVariables] public DateTime StartTime { get; private set; }
|
||||
[ViewVariables] public bool Paused { get; private set; }
|
||||
|
||||
public event Action InfoBlobUpdated;
|
||||
public event Action LobbyStatusUpdated;
|
||||
@@ -36,6 +37,7 @@ namespace Content.Client.GameTicking
|
||||
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame), JoinGame);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus), LobbyStatus);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyInfo>(nameof(MsgTickerLobbyInfo), LobbyInfo);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyCountdown>(nameof(MsgTickerLobbyCountdown), LobbyCountdown);
|
||||
_netManager.RegisterNetMessage<MsgRoundEndMessage>(nameof(MsgRoundEndMessage), RoundEnd);
|
||||
|
||||
_initialized = true;
|
||||
@@ -53,6 +55,7 @@ namespace Content.Client.GameTicking
|
||||
StartTime = message.StartTime;
|
||||
IsGameStarted = message.IsRoundStarted;
|
||||
AreWeReady = message.YouAreReady;
|
||||
Paused = message.Paused;
|
||||
|
||||
LobbyStatusUpdated?.Invoke();
|
||||
}
|
||||
@@ -69,6 +72,12 @@ namespace Content.Client.GameTicking
|
||||
_stateManager.RequestStateChange<GameScreen>();
|
||||
}
|
||||
|
||||
private void LobbyCountdown(MsgTickerLobbyCountdown message)
|
||||
{
|
||||
StartTime = message.StartTime;
|
||||
Paused = message.Paused;
|
||||
}
|
||||
|
||||
private void RoundEnd(MsgRoundEndMessage message)
|
||||
{
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Content.Client.Input
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveDown);
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveRight);
|
||||
ghost.AddFunction(EngineKeyFunctions.Run);
|
||||
ghost.AddFunction(EngineKeyFunctions.Walk);
|
||||
ghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||
|
||||
common.AddFunction(ContentKeyFunctions.OpenEntitySpawnWindow);
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Content.Client.Interfaces
|
||||
string ServerInfoBlob { get; }
|
||||
bool AreWeReady { get; }
|
||||
DateTime StartTime { get; }
|
||||
bool Paused { get; }
|
||||
|
||||
void Initialize();
|
||||
event Action InfoBlobUpdated;
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
@@ -36,6 +37,8 @@ namespace Content.Client.State
|
||||
[Dependency] private readonly IGameTiming _timing;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private IEntity _lastHoveredEntity;
|
||||
@@ -72,6 +75,15 @@ namespace Content.Client.State
|
||||
}
|
||||
|
||||
InteractionOutlineComponent outline;
|
||||
if(!_configurationManager.GetCVar<bool>("outline.enabled"))
|
||||
{
|
||||
if(entityToClick != null && entityToClick.TryGetComponent(out outline))
|
||||
{
|
||||
outline.OnMouseLeave(); //Prevent outline remains from persisting post command.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityToClick == _lastHoveredEntity)
|
||||
{
|
||||
if (entityToClick != null && entityToClick.TryGetComponent(out outline))
|
||||
|
||||
@@ -123,6 +123,13 @@ namespace Content.Client.State
|
||||
}
|
||||
|
||||
string text;
|
||||
|
||||
if (_clientGameTicker.Paused)
|
||||
{
|
||||
text = Loc.GetString("Paused");
|
||||
}
|
||||
else
|
||||
{
|
||||
var difference = _clientGameTicker.StartTime - DateTime.UtcNow;
|
||||
if (difference.Ticks < 0)
|
||||
{
|
||||
@@ -139,6 +146,7 @@ namespace Content.Client.State
|
||||
{
|
||||
text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}";
|
||||
}
|
||||
}
|
||||
|
||||
_lobby.StartTime.Text = Loc.GetString("Round Starts In: {0}", text);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Content.IntegrationTests
|
||||
{
|
||||
}
|
||||
|
||||
public void StartRound()
|
||||
public void StartRound(bool force = false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,12 +81,33 @@ namespace Content.IntegrationTests
|
||||
|
||||
public IEnumerable<GameRule> ActiveGameRules { get; } = Array.Empty<GameRule>();
|
||||
|
||||
public void SetStartPreset(Type type)
|
||||
public bool TryGetPreset(string name, out Type type)
|
||||
{
|
||||
type = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetStartPreset(Type type, bool force = false)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetStartPreset(string type)
|
||||
public void SetStartPreset(string name, bool force = false)
|
||||
{
|
||||
}
|
||||
|
||||
public bool DelayStart(TimeSpan time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PauseStart(bool pause = true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TogglePause()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Content.Server.AI.Operators
|
||||
{
|
||||
public abstract class AiOperator
|
||||
{
|
||||
public bool HasStartup => _hasStartup;
|
||||
private bool _hasStartup = false;
|
||||
private bool _hasShutdown = false;
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
|
||||
namespace Content.Server.AI.Operators.Combat.Ranged
|
||||
{
|
||||
public class ShootAtEntityOperator : AiOperator
|
||||
{
|
||||
private IEntity _owner;
|
||||
private IEntity _target;
|
||||
private float _accuracy;
|
||||
|
||||
private float _burstTime;
|
||||
|
||||
private float _elapsedTime;
|
||||
|
||||
public ShootAtEntityOperator(IEntity owner, IEntity target, float accuracy, float burstTime = 0.5f)
|
||||
{
|
||||
_owner = owner;
|
||||
_target = target;
|
||||
_accuracy = accuracy;
|
||||
_burstTime = burstTime;
|
||||
}
|
||||
|
||||
public override bool TryStartup()
|
||||
{
|
||||
if (!base.TryStartup())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!combatModeComponent.IsInCombatMode)
|
||||
{
|
||||
combatModeComponent.IsInCombatMode = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Shutdown(Outcome outcome)
|
||||
{
|
||||
base.Shutdown(outcome);
|
||||
if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
||||
{
|
||||
combatModeComponent.IsInCombatMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
// TODO: Probably just do all the checks on first try and then after that repeat the fire.
|
||||
if (_burstTime <= _elapsedTime)
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
_elapsedTime += frameTime;
|
||||
|
||||
if (_target.TryGetComponent(out DamageableComponent damageableComponent))
|
||||
{
|
||||
if (damageableComponent.IsDead())
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_owner.TryGetComponent(out HandsComponent hands) || hands.GetActiveHand == null)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
var equippedWeapon = hands.GetActiveHand.Owner;
|
||||
|
||||
if ((_target.Transform.GridPosition.Position - _owner.Transform.GridPosition.Position).Length >
|
||||
_owner.GetComponent<AiControllerComponent>().VisionRadius)
|
||||
{
|
||||
// Not necessarily a hard fail, more of a soft fail
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
// Unless RangedWeaponComponent is removed from hitscan weapons this shouldn't happen
|
||||
if (!equippedWeapon.TryGetComponent(out RangedWeaponComponent rangedWeaponComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
// TODO: Accuracy
|
||||
rangedWeaponComponent.AiFire(_owner, _target.Transform.GridPosition);
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Operators.Combat.Ranged
|
||||
{
|
||||
public class WaitForHitscanChargeOperator : AiOperator
|
||||
{
|
||||
private float _lastCharge = 0.0f;
|
||||
private float _lastFill = 0.0f;
|
||||
private HitscanWeaponComponent _hitscan;
|
||||
|
||||
public WaitForHitscanChargeOperator(IEntity entity)
|
||||
{
|
||||
if (!entity.TryGetComponent(out HitscanWeaponComponent hitscanWeaponComponent))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_hitscan = hitscanWeaponComponent;
|
||||
}
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (_hitscan.CapacitorComponent.Capacity - _hitscan.CapacitorComponent.Charge < 0.01f)
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
// If we're not charging then just stop
|
||||
_lastFill = _hitscan.CapacitorComponent.Charge - _lastCharge;
|
||||
_lastCharge = _hitscan.CapacitorComponent.Charge;
|
||||
|
||||
if (_lastFill == 0.0f)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
/// <returns></returns>
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (!_owner.TryGetComponent(out HandsComponent handsComponent))
|
||||
if (!_owner.TryGetComponent(out HandsComponent handsComponent) || handsComponent.FindHand(_entity) == null)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
@@ -24,16 +25,16 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (!InteractionChecks.InRangeUnobstructed(_owner, _target.Transform.MapPosition))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (!ContainerHelpers.TryGetContainer(_target, out var container))
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
if (!InteractionChecks.InRangeUnobstructed(_owner, container.Owner.Transform.MapPosition, ignoredEnt: container.Owner))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (!container.Owner.TryGetComponent(out EntityStorageComponent storageComponent) ||
|
||||
storageComponent.IsWeldedShut)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Content.Server.GameObjects.Components.Access;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
|
||||
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
|
||||
@@ -240,10 +241,11 @@ namespace Content.Server.AI.Operators.Movement
|
||||
|
||||
var startGrid = _mapManager.GetGrid(Owner.Transform.GridID).GetTileRef(Owner.Transform.GridPosition);
|
||||
var endGrid = _mapManager.GetGrid(TargetGrid.GridID).GetTileRef(TargetGrid);;
|
||||
// _routeCancelToken = new CancellationTokenSource();
|
||||
var access = AccessReader.FindAccessTags(Owner);
|
||||
|
||||
RouteJob = _pathfinder.RequestPath(new PathfindingArgs(
|
||||
Owner.Uid,
|
||||
access,
|
||||
collisionMask,
|
||||
startGrid,
|
||||
endGrid,
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Melee;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
@@ -41,9 +40,6 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new MeleeWeaponEquippedCon(
|
||||
new InverseBoolCurve()),
|
||||
// We'll prioritise equipping ranged weapons; If we try and score this then it'll just keep swapping between ranged and melee
|
||||
new RangedWeaponEquippedCon(
|
||||
new InverseBoolCurve()),
|
||||
new CanPutTargetInHandsCon(
|
||||
new BoolCurve()),
|
||||
new MeleeWeaponSpeedCon(
|
||||
|
||||
@@ -29,11 +29,17 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
var moveOperator = new MoveToEntityOperator(Owner, _entity);
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
MoveToEntityOperator moveOperator;
|
||||
if (equipped != null && equipped.TryGetComponent(out MeleeWeaponComponent meleeWeaponComponent))
|
||||
{
|
||||
moveOperator.DesiredRange = meleeWeaponComponent.Range - 0.01f;
|
||||
moveOperator = new MoveToEntityOperator(Owner, _entity, meleeWeaponComponent.Range - 0.01f);
|
||||
}
|
||||
// I think it's possible for this to happen given planning is time-sliced?
|
||||
// TODO: At this point we should abort
|
||||
else
|
||||
{
|
||||
moveOperator = new MoveToEntityOperator(Owner, _entity);
|
||||
}
|
||||
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
@@ -64,7 +70,7 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
|
||||
new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)),
|
||||
// Somewhat prioritise distance
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
|
||||
// Prefer weaker targets
|
||||
new TargetHealthCon(
|
||||
new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)),
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee
|
||||
new HasMeleeWeaponCon(
|
||||
new InverseBoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
|
||||
new MeleeWeaponDamageCon(
|
||||
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
|
||||
new MeleeWeaponSpeedCon(
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Combat.Ranged;
|
||||
using Content.Server.AI.Operators.Movement;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class BallisticAttackEntity : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
private MoveToEntityOperator _moveOperator;
|
||||
|
||||
public BallisticAttackEntity(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
if (_moveOperator != null)
|
||||
{
|
||||
_moveOperator.MovedATile -= InLos;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
_moveOperator = new MoveToEntityOperator(Owner, _entity);
|
||||
_moveOperator.MovedATile += InLos;
|
||||
|
||||
// TODO: Accuracy in blackboard
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
_moveOperator,
|
||||
new ShootAtEntityOperator(Owner, _entity, 0.7f),
|
||||
});
|
||||
|
||||
// We will do a quick check now to see if we even need to move which also saves a pathfind
|
||||
InLos();
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
context.GetState<WeaponEntityState>().SetValue(equipped);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
// Check if we have a weapon; easy-out
|
||||
new BallisticWeaponEquippedCon(
|
||||
new BoolCurve()),
|
||||
new BallisticAmmoCon(
|
||||
new QuadraticCurve(1.0f, 0.15f, 0.0f, 0.0f)),
|
||||
// Don't attack a dead target
|
||||
new TargetIsDeadCon(
|
||||
new InverseBoolCurve()),
|
||||
// Deprioritise a target in crit
|
||||
new TargetIsCritCon(
|
||||
new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)),
|
||||
// Somewhat prioritise distance
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.07f, 0.0f)),
|
||||
// Prefer weaker targets
|
||||
new TargetHealthCon(
|
||||
new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)),
|
||||
};
|
||||
|
||||
private void InLos()
|
||||
{
|
||||
// This should only be called if the movement operator is the current one;
|
||||
// if that turns out not to be the case we can just add a check here.
|
||||
if (Visibility.InLineOfSight(Owner, _entity))
|
||||
{
|
||||
_moveOperator.HaveArrived();
|
||||
var mover = ActionOperators.Dequeue();
|
||||
mover.Shutdown(Outcome.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class DropEmptyBallistic : UtilityAction
|
||||
{
|
||||
public sealed override float Bonus => 20.0f;
|
||||
private IEntity _entity;
|
||||
|
||||
public DropEmptyBallistic(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new DropEntityOperator(Owner, _entity)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new TargetInOurInventoryCon(
|
||||
new BoolCurve()),
|
||||
// Need to put in hands to drop
|
||||
new CanPutTargetInHandsCon(
|
||||
new BoolCurve()),
|
||||
// Drop that sucker
|
||||
new BallisticAmmoCon(
|
||||
new InverseBoolCurve()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Melee;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class EquipBallistic : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public EquipBallistic(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new EquippedBallisticCon(
|
||||
new InverseBoolCurve()),
|
||||
new MeleeWeaponEquippedCon(
|
||||
new QuadraticCurve(0.9f, 1.0f, 0.1f, 0.0f)),
|
||||
new CanPutTargetInHandsCon(
|
||||
new BoolCurve()),
|
||||
new BallisticAmmoCon(
|
||||
new QuadraticCurve(1.0f, 0.15f, 0.0f, 0.0f)),
|
||||
new RangedWeaponFireRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using Content.Server.AI.Operators.Sequences;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Containers;
|
||||
using Content.Server.AI.Utility.Considerations.Hands;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class PickUpAmmo : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public PickUpAmmo(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new GoPickupEntitySequence(Owner, _entity).Sequence;
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
//TODO: Consider ammo's type and what guns we have
|
||||
new TargetAccessibleCon(
|
||||
new BoolCurve()),
|
||||
new FreeHandCon(
|
||||
new BoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using Content.Server.AI.Operators.Sequences;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Considerations.Containers;
|
||||
using Content.Server.AI.Utility.Considerations.Hands;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class PickUpBallisticMagWeapon : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public PickUpBallisticMagWeapon(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new GoPickupEntitySequence(Owner, _entity).Sequence;
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new HeldRangedWeaponsCon(
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.0f, 0.0f)),
|
||||
new TargetAccessibleCon(
|
||||
new BoolCurve()),
|
||||
new FreeHandCon(
|
||||
new BoolCurve()),
|
||||
// For now don't grab empty guns - at least until we can start storing stuff in inventory
|
||||
new BallisticAmmoCon(
|
||||
new BoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new RangedWeaponFireRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
// TODO: Ballistic accuracy? Depends how the design transitions
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Operators.Movement;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class PutHitscanInCharger : UtilityAction
|
||||
{
|
||||
// Maybe a bad idea to not allow override
|
||||
public override bool CanOverride => false;
|
||||
private readonly IEntity _charger;
|
||||
|
||||
public PutHitscanInCharger(IEntity owner, IEntity charger, float weight) : base(owner)
|
||||
{
|
||||
_charger = charger;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
var weapon = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (weapon == null || _charger.GetComponent<WeaponCapacitorChargerComponent>().HeldItem != null)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>();
|
||||
return;
|
||||
}
|
||||
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new MoveToEntityOperator(Owner, _charger),
|
||||
new InteractWithEntityOperator(Owner, _charger),
|
||||
// Separate task will deal with picking it up
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<MoveTargetState>().SetValue(_charger);
|
||||
context.GetState<TargetEntityState>().SetValue(_charger);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } =
|
||||
{
|
||||
new HitscanWeaponEquippedCon(
|
||||
new BoolCurve()),
|
||||
new HitscanChargerFullCon(
|
||||
new InverseBoolCurve()),
|
||||
new HitscanChargerRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new HitscanChargeCon(
|
||||
new QuadraticCurve(-1.2f, 2.0f, 1.2f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class DropEmptyHitscan : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public DropEmptyHitscan(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new DropEntityOperator(Owner, _entity)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new TargetInOurInventoryCon(
|
||||
new BoolCurve()),
|
||||
// Need to put in hands to drop
|
||||
new CanPutTargetInHandsCon(
|
||||
new BoolCurve()),
|
||||
// If completely empty then drop that sucker
|
||||
new HitscanChargeCon(
|
||||
new InverseBoolCurve()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Melee;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class EquipHitscan : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public EquipHitscan(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new EquippedHitscanCon(
|
||||
new InverseBoolCurve()),
|
||||
new MeleeWeaponEquippedCon(
|
||||
new QuadraticCurve(0.9f, 1.0f, 0.1f, 0.0f)),
|
||||
new CanPutTargetInHandsCon(
|
||||
new BoolCurve()),
|
||||
new HitscanChargeCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.0f, 0.0f)),
|
||||
new RangedWeaponFireRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
new HitscanWeaponDamageCon(
|
||||
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Combat.Ranged;
|
||||
using Content.Server.AI.Operators.Movement;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanAttackEntity : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
private MoveToEntityOperator _moveOperator;
|
||||
|
||||
public HitscanAttackEntity(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
if (_moveOperator != null)
|
||||
{
|
||||
_moveOperator.MovedATile -= InLos;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
_moveOperator = new MoveToEntityOperator(Owner, _entity);
|
||||
_moveOperator.MovedATile += InLos;
|
||||
|
||||
// TODO: Accuracy in blackboard
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
_moveOperator,
|
||||
new ShootAtEntityOperator(Owner, _entity, 0.7f),
|
||||
});
|
||||
|
||||
InLos();
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
context.GetState<WeaponEntityState>().SetValue(equipped);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
// Check if we have a weapon; easy-out
|
||||
new HitscanWeaponEquippedCon(
|
||||
new BoolCurve()),
|
||||
new HitscanChargeCon(
|
||||
new QuadraticCurve(1.0f, 0.1f, 0.0f, 0.0f)),
|
||||
// Don't attack a dead target
|
||||
new TargetIsDeadCon(
|
||||
new InverseBoolCurve()),
|
||||
// Deprioritise a target in crit
|
||||
new TargetIsCritCon(
|
||||
new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)),
|
||||
// Somewhat prioritise distance
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.07f, 0.0f)),
|
||||
// Prefer weaker targets
|
||||
new TargetHealthCon(
|
||||
new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)),
|
||||
};
|
||||
|
||||
private void InLos()
|
||||
{
|
||||
// This should only be called if the movement operator is the current one;
|
||||
// if that turns out not to be the case we can just add a check here.
|
||||
if (Visibility.InLineOfSight(Owner, _entity))
|
||||
{
|
||||
_moveOperator.HaveArrived();
|
||||
var mover = ActionOperators.Dequeue();
|
||||
mover.Shutdown(Outcome.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Combat.Ranged;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Operators.Movement;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Containers;
|
||||
using Content.Server.AI.Utility.Considerations.Hands;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class PickUpHitscanFromCharger : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
private IEntity _charger;
|
||||
|
||||
public PickUpHitscanFromCharger(IEntity owner, IEntity entity, IEntity charger, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
_charger = charger;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new MoveToEntityOperator(Owner, _charger),
|
||||
new WaitForHitscanChargeOperator(_entity),
|
||||
new PickupEntityOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new HeldRangedWeaponsCon(
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.0f, 0.0f)),
|
||||
new TargetAccessibleCon(
|
||||
new BoolCurve()),
|
||||
new FreeHandCon(
|
||||
new BoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
// TODO: ChargerHasPower
|
||||
new RangedWeaponFireRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
new HitscanWeaponDamageCon(
|
||||
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using Content.Server.AI.Operators.Sequences;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.Considerations.Containers;
|
||||
using Content.Server.AI.Utility.Considerations.Hands;
|
||||
using Content.Server.AI.Utility.Considerations.Movement;
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.AI.WorldState.States.Movement;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class PickUpHitscanWeapon : UtilityAction
|
||||
{
|
||||
private IEntity _entity;
|
||||
|
||||
public PickUpHitscanWeapon(IEntity owner, IEntity entity, float weight) : base(owner)
|
||||
{
|
||||
_entity = entity;
|
||||
Bonus = weight;
|
||||
}
|
||||
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
ActionOperators = new GoPickupEntitySequence(Owner, _entity).Sequence;
|
||||
}
|
||||
|
||||
protected override void UpdateBlackboard(Blackboard context)
|
||||
{
|
||||
base.UpdateBlackboard(context);
|
||||
context.GetState<MoveTargetState>().SetValue(_entity);
|
||||
context.GetState<TargetEntityState>().SetValue(_entity);
|
||||
context.GetState<WeaponEntityState>().SetValue(_entity);
|
||||
}
|
||||
|
||||
protected override Consideration[] Considerations { get; } = {
|
||||
new HeldRangedWeaponsCon(
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.0f, 0.0f)),
|
||||
new TargetAccessibleCon(
|
||||
new BoolCurve()),
|
||||
new FreeHandCon(
|
||||
new BoolCurve()),
|
||||
// For now don't grab empty guns - at least until we can start storing stuff in inventory
|
||||
new HitscanChargeCon(
|
||||
new BoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
// TODO: Weapon charge level
|
||||
new RangedWeaponFireRateCon(
|
||||
new QuadraticCurve(1.0f, 0.5f, 0.0f, 0.0f)),
|
||||
new HitscanWeaponDamageCon(
|
||||
new QuadraticCurve(1.0f, 0.25f, 0.0f, 0.0f)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Idle
|
||||
new StoredStateIsNullCon<LastOpenedStorageState, IEntity>(
|
||||
new InverseBoolCurve()),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
|
||||
};
|
||||
public override void SetupOperators(Blackboard context)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Drink
|
||||
new ThirstCon(
|
||||
new LogisticCurve(1000f, 1.3f, -1.0f, 0.5f)),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
|
||||
new DrinkValueCon(
|
||||
new QuadraticCurve(1.0f, 0.4f, 0.0f, 0.0f)),
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Food
|
||||
new HungerCon(
|
||||
new LogisticCurve(1000f, 1.3f, -1.0f, 0.5f)),
|
||||
new DistanceCon(
|
||||
new QuadraticCurve(1.0f, 1.0f, 0.02f, 0.0f)),
|
||||
new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)),
|
||||
new FoodValueCon(
|
||||
new QuadraticCurve(1.0f, 0.4f, 0.0f, 0.0f)),
|
||||
};
|
||||
|
||||
@@ -126,6 +126,9 @@ namespace Content.Server.AI.Utility.AiLogic
|
||||
{
|
||||
damageableComponent.DamageThresholdPassed -= DeathHandle;
|
||||
}
|
||||
|
||||
var currentOp = CurrentAction?.ActionOperators.Peek();
|
||||
currentOp?.Shutdown(Outcome.Failed);
|
||||
}
|
||||
|
||||
private void DeathHandle(object sender, DamageThresholdPassedEventArgs eventArgs)
|
||||
@@ -153,6 +156,12 @@ namespace Content.Server.AI.Utility.AiLogic
|
||||
return;
|
||||
}
|
||||
|
||||
var currentOp = CurrentAction?.ActionOperators.Peek();
|
||||
if (currentOp != null && currentOp.HasStartup)
|
||||
{
|
||||
currentOp.Shutdown(Outcome.Failed);
|
||||
}
|
||||
|
||||
CurrentAction = action;
|
||||
action.SetupOperators(_blackboard);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utility.ExpandableActions.Combat;
|
||||
using Content.Server.AI.Utility.ExpandableActions.Combat.Melee;
|
||||
using Content.Server.AI.Utility.ExpandableActions.Combat.Ranged;
|
||||
using Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Hitscan;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.BehaviorSets
|
||||
@@ -18,19 +10,11 @@ namespace Content.Server.AI.Utility.BehaviorSets
|
||||
{
|
||||
Actions = new IAiUtility[]
|
||||
{
|
||||
new PickUpRangedExp(),
|
||||
// TODO: Reload Ballistic
|
||||
new DropEmptyBallisticExp(),
|
||||
// TODO: Ideally long-term we should just store the weapons in backpack
|
||||
new DropEmptyHitscanExp(),
|
||||
new EquipMeleeExp(),
|
||||
new EquipBallisticExp(),
|
||||
new EquipHitscanExp(),
|
||||
new PickUpHitscanFromChargersExp(),
|
||||
new ChargeEquippedHitscanExp(),
|
||||
new RangedAttackNearbySpeciesExp(),
|
||||
new PickUpMeleeWeaponExp(),
|
||||
new MeleeAttackNearbySpeciesExp(),
|
||||
new MeleeAttackNearbyPlayerExp(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic
|
||||
{
|
||||
public class BallisticAmmoCon : Consideration
|
||||
{
|
||||
public BallisticAmmoCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var weapon = context.GetState<WeaponEntityState>().GetValue();
|
||||
|
||||
if (weapon == null || !weapon.TryGetComponent(out BallisticMagazineWeaponComponent ballistic))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
var contained = ballistic.MagazineSlot.ContainedEntity;
|
||||
|
||||
if (contained == null)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
var mag = contained.GetComponent<BallisticMagazineComponent>();
|
||||
|
||||
if (mag.CountLoaded == 0)
|
||||
{
|
||||
// TODO: Do this better
|
||||
return ballistic.GetChambered(0) != null ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
return (float) mag.CountLoaded / mag.Capacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic
|
||||
{
|
||||
public class BallisticWeaponEquippedCon : Consideration
|
||||
{
|
||||
public BallisticWeaponEquippedCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (equipped == null)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Maybe change this to BallisticMagazineWeapon
|
||||
return equipped.HasComponent<BallisticMagazineWeaponComponent>() ? 1.0f : 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Ballistic
|
||||
{
|
||||
public class EquippedBallisticCon : Consideration
|
||||
{
|
||||
public EquippedBallisticCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (equipped == null || !equipped.HasComponent<BallisticMagazineWeaponComponent>())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged
|
||||
{
|
||||
public class HasTargetLosCon : Consideration
|
||||
{
|
||||
public HasTargetLosCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
if (target == null)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return Visibility.InLineOfSight(owner, target) ? 1.0f : 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Melee;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged
|
||||
{
|
||||
public sealed class HeldRangedWeaponsCon : Consideration
|
||||
{
|
||||
public HeldRangedWeaponsCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var count = 0;
|
||||
const int max = 3;
|
||||
|
||||
foreach (var item in context.GetState<InventoryState>().GetValue())
|
||||
{
|
||||
if (item.HasComponent<RangedWeaponComponent>())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (float) count / max;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class EquippedHitscanCon : Consideration
|
||||
{
|
||||
public EquippedHitscanCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (equipped == null || !equipped.HasComponent<HitscanWeaponComponent>())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanChargeCon : Consideration
|
||||
{
|
||||
public HitscanChargeCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var weapon = context.GetState<WeaponEntityState>().GetValue();
|
||||
|
||||
if (weapon == null || !weapon.TryGetComponent(out HitscanWeaponComponent hitscanWeaponComponent))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return hitscanWeaponComponent.CapacitorComponent.Charge / hitscanWeaponComponent.CapacitorComponent.Capacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanChargerFullCon : Consideration
|
||||
{
|
||||
public HitscanChargerFullCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
|
||||
if (target == null ||
|
||||
!target.TryGetComponent(out WeaponCapacitorChargerComponent chargerComponent) ||
|
||||
chargerComponent.HeldItem != null)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanChargerRateCon : Consideration
|
||||
{
|
||||
public HitscanChargerRateCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
if (target == null || !target.TryGetComponent(out WeaponCapacitorChargerComponent weaponCharger))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// AI don't care about efficiency, psfft!
|
||||
return weaponCharger.TransferRatio;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanWeaponDamageCon : Consideration
|
||||
{
|
||||
public HitscanWeaponDamageCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var weapon = context.GetState<WeaponEntityState>().GetValue();
|
||||
|
||||
if (weapon == null || !weapon.TryGetComponent(out HitscanWeaponComponent hitscanWeaponComponent))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Just went with max health
|
||||
return hitscanWeaponComponent.Damage / 300.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class HitscanWeaponEquippedCon : Consideration
|
||||
{
|
||||
public HitscanWeaponEquippedCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (equipped == null)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return equipped.HasComponent<HitscanWeaponComponent>() ? 1.0f : 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged
|
||||
{
|
||||
public sealed class RangedWeaponEquippedCon : Consideration
|
||||
{
|
||||
public RangedWeaponEquippedCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var equipped = context.GetState<EquippedEntityState>().GetValue();
|
||||
|
||||
if (equipped == null || !equipped.HasComponent<RangedWeaponComponent>())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.AI.Utility.Curves;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States.Combat;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Combat.Ranged
|
||||
{
|
||||
public class RangedWeaponFireRateCon : Consideration
|
||||
{
|
||||
public RangedWeaponFireRateCon(IResponseCurve curve) : base(curve) {}
|
||||
|
||||
public override float GetScore(Blackboard context)
|
||||
{
|
||||
var weapon = context.GetState<WeaponEntityState>().GetValue();
|
||||
|
||||
if (weapon == null || !weapon.TryGetComponent(out RangedWeaponComponent ranged))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return ranged.FireRate / 100.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,12 @@ namespace Content.Server.AI.Utility.Considerations.Containers
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're in a container (e.g. held or whatever) then we probably can't get it. Only exception
|
||||
// Is a locker / crate
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
|
||||
@@ -17,9 +17,8 @@ namespace Content.Server.AI.Utility.Considerations.Movement
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// TODO: Remove 1 -
|
||||
// Kind of just pulled a max distance out of nowhere. Add 0.01 just in case it's reaally far and we have no choice so it'll still be considered at least.
|
||||
return 1 - ((target.Transform.GridPosition.Position - self.Transform.GridPosition.Position).Length / 100 + 0.01f);
|
||||
return (target.Transform.GridPosition.Position - self.Transform.GridPosition.Position).Length / 100 + 0.01f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class DropEmptyBallisticExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<InventoryState>().GetValue())
|
||||
{
|
||||
if (entity.HasComponent<BallisticMagazineWeaponComponent>())
|
||||
{
|
||||
yield return new DropEmptyBallistic(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class EquipBallisticExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<InventoryState>().GetValue())
|
||||
{
|
||||
yield return new EquipBallistic(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Ballistic
|
||||
{
|
||||
public sealed class PickUpAmmoExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
if (!owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(BallisticMagazineComponent),
|
||||
controller.VisionRadius))
|
||||
{
|
||||
yield return new PickUpAmmo(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class ChargeEquippedHitscanExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
if (!owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(WeaponCapacitorChargerComponent),
|
||||
controller.VisionRadius))
|
||||
{
|
||||
yield return new PutHitscanInCharger(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public class DropEmptyHitscanExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<InventoryState>().GetValue())
|
||||
{
|
||||
if (entity.HasComponent<HitscanWeaponComponent>())
|
||||
{
|
||||
yield return new DropEmptyHitscan(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class EquipHitscanExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<InventoryState>().GetValue())
|
||||
{
|
||||
yield return new EquipHitscan(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged.Hitscan
|
||||
{
|
||||
public sealed class PickUpHitscanFromChargersExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
if (!owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(WeaponCapacitorChargerComponent),
|
||||
controller.VisionRadius))
|
||||
{
|
||||
var contained = entity.GetComponent<WeaponCapacitorChargerComponent>().HeldItem;
|
||||
|
||||
if (contained != null)
|
||||
{
|
||||
yield return new PickUpHitscanFromCharger(owner, entity, contained, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Combat.Nearby;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged
|
||||
{
|
||||
public sealed class PickUpRangedExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatPrepBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<NearbyRangedWeapons>().GetValue())
|
||||
{
|
||||
if (entity.HasComponent<HitscanWeaponComponent>())
|
||||
{
|
||||
yield return new PickUpHitscanWeapon(owner, entity, Bonus);
|
||||
}
|
||||
|
||||
if (entity.HasComponent<BallisticMagazineWeaponComponent>())
|
||||
{
|
||||
yield return new PickUpBallisticMagWeapon(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Ballistic;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Ranged.Hitscan;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Mobs;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Ranged
|
||||
{
|
||||
public sealed class RangedAttackNearbySpeciesExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<NearbySpeciesState>().GetValue())
|
||||
{
|
||||
yield return new HitscanAttackEntity(owner, entity, Bonus);
|
||||
yield return new BallisticAttackEntity(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Power.Chargers;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Nearby
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class NearbyLaserChargersState : StateData<List<IEntity>>
|
||||
{
|
||||
public override string Name => "NearbyLaserChargers";
|
||||
|
||||
public override List<IEntity> GetValue()
|
||||
{
|
||||
var nearby = new List<IEntity>();
|
||||
|
||||
if (!Owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
return nearby;
|
||||
}
|
||||
|
||||
foreach (var result in Visibility
|
||||
.GetNearestEntities(Owner.Transform.GridPosition, typeof(WeaponCapacitorChargerComponent), controller.VisionRadius))
|
||||
{
|
||||
nearby.Add(result);
|
||||
}
|
||||
|
||||
return nearby;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Nearby
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class NearbyLaserWeapons : StateData<List<IEntity>>
|
||||
{
|
||||
public override string Name => "NearbyLaserWeapons";
|
||||
|
||||
public override List<IEntity> GetValue()
|
||||
{
|
||||
var result = new List<IEntity>();
|
||||
|
||||
if (!Owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility
|
||||
.GetNearestEntities(Owner.Transform.GridPosition, typeof(HitscanWeaponComponent), controller.VisionRadius))
|
||||
{
|
||||
result.Add(entity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Nearby
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class NearbyRangedWeapons : CachedStateData<List<IEntity>>
|
||||
{
|
||||
public override string Name => "NearbyRangedWeapons";
|
||||
|
||||
protected override List<IEntity> GetTrueValue()
|
||||
{
|
||||
var result = new List<IEntity>();
|
||||
|
||||
if (!Owner.TryGetComponent(out AiControllerComponent controller))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility
|
||||
.GetNearestEntities(Owner.Transform.GridPosition, typeof(RangedWeaponComponent), controller.VisionRadius))
|
||||
{
|
||||
result.Add(entity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Ranged
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class Accuracy : StateData<float>
|
||||
{
|
||||
public override string Name => "Accuracy";
|
||||
|
||||
public override float GetValue()
|
||||
{
|
||||
// TODO: Maybe just make it a SetValue (maybe make a third type besides sensor / daemon called settablestate)
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Ranged
|
||||
{
|
||||
/// <summary>
|
||||
/// How long to wait between bursts
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class BurstCooldown : PlanningStateData<float>
|
||||
{
|
||||
public override string Name => "BurstCooldown";
|
||||
public override void Reset()
|
||||
{
|
||||
Value = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Ranged
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the discrete ammo count
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class EquippedRangedWeaponAmmo : StateData<int?>
|
||||
{
|
||||
public override string Name => "EquippedRangedWeaponAmmo";
|
||||
|
||||
public override int? GetValue()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out HandsComponent handsComponent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var equippedItem = handsComponent.GetActiveHand?.Owner;
|
||||
if (equippedItem == null) return null;
|
||||
|
||||
if (equippedItem.TryGetComponent(out HitscanWeaponComponent hitscanWeaponComponent))
|
||||
{
|
||||
return (int) hitscanWeaponComponent.CapacitorComponent.Charge / hitscanWeaponComponent.BaseFireCost;
|
||||
}
|
||||
|
||||
if (equippedItem.TryGetComponent(out BallisticMagazineWeaponComponent ballisticComponent))
|
||||
{
|
||||
return ballisticComponent.MagazineSlot.ContainedEntities.Count;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.AI.WorldState.States.Combat.Ranged
|
||||
{
|
||||
/// <summary>
|
||||
/// How many shots to take before cooling down
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class MaxBurstCount : PlanningStateData<int>
|
||||
{
|
||||
public override string Name => "BurstCount";
|
||||
public override void Reset()
|
||||
{
|
||||
Value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Access
|
||||
{
|
||||
public sealed class AccessReaderChangeMessage : EntitySystemMessage
|
||||
{
|
||||
public EntityUid Uid { get; }
|
||||
public bool Enabled { get; }
|
||||
|
||||
public AccessReaderChangeMessage(EntityUid uid, bool enabled)
|
||||
{
|
||||
Uid = uid;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
private static ICollection<string> FindAccessTags(IEntity entity)
|
||||
public static ICollection<string> FindAccessTags(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out IAccess accessComponent))
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Chemistry
|
||||
@@ -219,12 +220,15 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
|
||||
{
|
||||
if (user.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
if (hands.GetActiveHand != null)
|
||||
{
|
||||
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
|
||||
if (!ActionBlockerSystem.CanInteract(user) ||
|
||||
!user.TryGetComponent<HandsComponent>(out var hands) ||
|
||||
hands.GetActiveHand == null ||
|
||||
!hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((solution.Capabilities & SolutionCaps.PourOut) != 0 &&
|
||||
(component.Capabilities & SolutionCaps.PourIn) != 0)
|
||||
{
|
||||
@@ -234,9 +238,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
data.Text= $"Transfer liquid from [{heldEntityName}] to [{myName}].";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
}
|
||||
@@ -318,12 +319,15 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
|
||||
{
|
||||
if (user.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
if (hands.GetActiveHand != null)
|
||||
{
|
||||
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
|
||||
if (!ActionBlockerSystem.CanInteract(user) ||
|
||||
!user.TryGetComponent<HandsComponent>(out var hands) ||
|
||||
hands.GetActiveHand == null ||
|
||||
!hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((solution.Capabilities & SolutionCaps.PourIn) != 0 &&
|
||||
(component.Capabilities & SolutionCaps.PourOut) != 0)
|
||||
{
|
||||
@@ -333,9 +337,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
data.Text = $"Transfer liquid from [{myName}] to [{heldEntityName}].";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,8 @@ namespace Content.Server.GameObjects
|
||||
State = DoorState.Open;
|
||||
SetAppearance(DoorVisualState.Open);
|
||||
}, _cancellationTokenSource.Token);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner.Uid, false));
|
||||
}
|
||||
|
||||
public virtual bool CanClose()
|
||||
@@ -203,6 +205,7 @@ namespace Content.Server.GameObjects
|
||||
occluder.Enabled = true;
|
||||
}
|
||||
}, _cancellationTokenSource.Token);
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner.Uid, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using Content.Server.GameObjects.Components.Weapon;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Explosion
|
||||
{
|
||||
/// <summary>
|
||||
/// When triggered will flash in an area around the object and destroy itself
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class FlashExplosiveComponent : Component, ITimerTrigger, IDestroyAct
|
||||
{
|
||||
public override string Name => "FlashExplosive";
|
||||
|
||||
private float _range;
|
||||
private double _duration;
|
||||
private string _sound;
|
||||
private bool _deleteOnFlash;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _range, "range", 7.0f);
|
||||
serializer.DataField(ref _duration, "duration", 8.0);
|
||||
serializer.DataField(ref _sound, "sound", "/Audio/effects/flash_bang.ogg");
|
||||
serializer.DataField(ref _deleteOnFlash, "deleteOnFlash", true);
|
||||
}
|
||||
|
||||
public bool Explode()
|
||||
{
|
||||
// If we're in a locker or whatever then can't flash anything
|
||||
ContainerHelpers.TryGetContainer(Owner, out var container);
|
||||
if (container == null || !container.Owner.HasComponent<EntityStorageComponent>())
|
||||
{
|
||||
ServerFlashableComponent.FlashAreaHelper(Owner, _range, _duration);
|
||||
}
|
||||
|
||||
if (_sound != null)
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords(_sound, Owner.Transform.GridPosition);
|
||||
}
|
||||
|
||||
if (_deleteOnFlash && !Owner.Deleted)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ITimerTrigger.Trigger(TimerTriggerEventArgs eventArgs)
|
||||
{
|
||||
return Explode();
|
||||
}
|
||||
|
||||
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
@@ -21,7 +23,8 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
{
|
||||
protected override void GetData(IEntity user, CanSpillComponent component, VerbData data)
|
||||
{
|
||||
if (!component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
|
||||
if (!ActionBlockerSystem.CanInteract(user) ||
|
||||
!component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
|
||||
@@ -23,7 +23,7 @@ using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventory
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class InventoryComponent : SharedInventoryComponent
|
||||
public class InventoryComponent : SharedInventoryComponent, IExAct
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
@@ -397,5 +397,25 @@ namespace Content.Server.GameObjects
|
||||
}
|
||||
return new InventoryComponentState(list);
|
||||
}
|
||||
|
||||
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Severity < ExplosionSeverity.Heavy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var slot in SlotContainers.Values.ToList())
|
||||
{
|
||||
foreach (var entity in slot.ContainedEntities)
|
||||
{
|
||||
var exActs = entity.GetAllComponents<IExAct>();
|
||||
foreach (var exAct in exActs)
|
||||
{
|
||||
exAct.OnExplosion(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects;
|
||||
@@ -68,6 +69,8 @@ namespace Content.Server.GameObjects
|
||||
AddHand(handsname);
|
||||
}
|
||||
}
|
||||
|
||||
serializer.DataField(ref activeIndex, "defaultHand", orderedHands.LastOrDefault());
|
||||
}
|
||||
|
||||
public IEnumerable<ItemComponent> GetAllHeldItems()
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
@@ -240,6 +241,12 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
{
|
||||
protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Cell == null)
|
||||
{
|
||||
data.Text = "Eject cell (cell missing)";
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
@@ -338,8 +339,13 @@ namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data)
|
||||
{
|
||||
component.OpenVerbGetData(user, component, data);
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
component.OpenVerbGetData(user, component, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -351,6 +357,12 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
protected virtual void OpenVerbGetData(IEntity user, EntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsWeldedShut)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Disabled;
|
||||
|
||||
@@ -117,7 +117,9 @@ namespace Content.Server.GameObjects
|
||||
{
|
||||
protected override void GetData(IEntity user, ItemComponent component, VerbData data)
|
||||
{
|
||||
if (ContainerHelpers.IsInContainer(component.Owner) || !component.CanPickup(user))
|
||||
if (!ActionBlockerSystem.CanInteract(user) ||
|
||||
ContainerHelpers.IsInContainer(component.Owner) ||
|
||||
!component.CanPickup(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
@@ -139,7 +140,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
{
|
||||
protected override void GetData(IEntity user, SecureEntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (component.Open)
|
||||
if (!ActionBlockerSystem.CanInteract(user) || component.Open)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Content.Server.GameObjects
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IStorageComponent))]
|
||||
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct
|
||||
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
@@ -364,6 +364,24 @@ namespace Content.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Severity < ExplosionSeverity.Heavy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var storedEntities = storage.ContainedEntities.ToList();
|
||||
foreach (var entity in storedEntities)
|
||||
{
|
||||
var exActs = entity.GetAllComponents<IExAct>();
|
||||
foreach (var exAct in exActs)
|
||||
{
|
||||
exAct.OnExplosion(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an entity into the storage component from the players active hand.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,18 +2,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using NFluidsynth;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using SQLitePCL;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Markers
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Markers
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class TimedSpawnerComponent : Component
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IRobustRandom _robustRandom;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "TimedSpawner";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public List<string> Prototypes { get; set; } = new List<string>();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Chance { get; set; } = 1.0f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int IntervalSeconds { get; set; } = 60;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MinimumEntitiesSpawned { get; set; } = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaximumEntitiesSpawned { get; set; } = 1;
|
||||
|
||||
private CancellationTokenSource TokenSource;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SetupTimer();
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
TokenSource.Cancel();
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, x => Prototypes, "prototypes", new List<string>());
|
||||
serializer.DataField(this, x => Chance, "chance", 1.0f);
|
||||
serializer.DataField(this, x => IntervalSeconds, "intervalSeconds", 60);
|
||||
serializer.DataField(this, x => MinimumEntitiesSpawned, "minimumEntitiesSpawned", 1);
|
||||
serializer.DataField(this, x => MaximumEntitiesSpawned, "maximumEntitiesSpawned", 1);
|
||||
|
||||
if(MinimumEntitiesSpawned > MaximumEntitiesSpawned)
|
||||
throw new ArgumentException("MaximumEntitiesSpawned can't be lower than MinimumEntitiesSpawned!");
|
||||
}
|
||||
|
||||
private void SetupTimer()
|
||||
{
|
||||
TokenSource?.Cancel();
|
||||
TokenSource = new CancellationTokenSource();
|
||||
Timer.SpawnRepeating(TimeSpan.FromSeconds(IntervalSeconds), OnTimerFired, TokenSource.Token);
|
||||
}
|
||||
|
||||
private void OnTimerFired()
|
||||
{
|
||||
if (!_robustRandom.Prob(Chance))
|
||||
return;
|
||||
|
||||
var number = _robustRandom.Next(MinimumEntitiesSpawned, MaximumEntitiesSpawned);
|
||||
|
||||
for (int i = 0; i < number; i++)
|
||||
{
|
||||
var entity = _robustRandom.Pick(Prototypes);
|
||||
_entityManager.SpawnEntity(entity, Owner.Transform.GridPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
@@ -133,6 +134,12 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = "Enter";
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
}
|
||||
@@ -148,6 +155,12 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = "Eject";
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,17 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
_statusRemoveCancellation = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public void ResetStuns()
|
||||
{
|
||||
_stunnedTimer = 0f;
|
||||
_slowdownTimer = 0f;
|
||||
|
||||
if (KnockedDown)
|
||||
StandingStateHelper.Standing(Owner);
|
||||
|
||||
_knockdownTimer = 0f;
|
||||
}
|
||||
|
||||
public void Update(float delta)
|
||||
{
|
||||
if (Stunned)
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
Processor.Shutdown();
|
||||
Processor?.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -125,6 +125,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ViewVariables(VVAccess.ReadWrite)] public float StepSoundDistance { get; set; }
|
||||
|
||||
public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled) { }
|
||||
public void SetSprinting(ushort subTick, bool enabled) { }
|
||||
public void SetSprinting(ushort subTick, bool walking) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSprinting(ushort subTick, bool enabled)
|
||||
public void SetSprinting(ushort subTick, bool walking)
|
||||
{
|
||||
// Shuttles can't sprint.
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Server.GameObjects.Components.Sound;
|
||||
using Content.Server.GameObjects.Components.Utensil;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Utensil;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -10,6 +13,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -32,18 +36,28 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
private SolutionComponent _contents;
|
||||
[ViewVariables]
|
||||
private ReagentUnit _transferAmount;
|
||||
private UtensilType _utensilsNeeded;
|
||||
|
||||
public int UsesRemaining => _contents.CurrentVolume == 0
|
||||
?
|
||||
0 : Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float()));
|
||||
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _useSound, "useSound", "/Audio/items/eatfood.ogg");
|
||||
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5));
|
||||
serializer.DataField(ref _trashPrototype, "trash", "TrashPlate");
|
||||
serializer.DataField(ref _trashPrototype, "trash", null);
|
||||
|
||||
if (serializer.Reading)
|
||||
{
|
||||
var utensils = serializer.ReadDataField("utensils", new List<UtensilType>());
|
||||
foreach (var utensil in utensils)
|
||||
{
|
||||
_utensilsNeeded |= utensil;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -55,15 +69,27 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (_utensilsNeeded != UtensilType.None)
|
||||
{
|
||||
eventArgs.User.PopupMessage(eventArgs.User, Loc.GetString("You need to use a {0} to eat that!", _utensilsNeeded));
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryUseFood(eventArgs.User, null);
|
||||
}
|
||||
|
||||
// Feeding someone else
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryUseFood(eventArgs.User, eventArgs.Target);
|
||||
}
|
||||
|
||||
private bool TryUseFood(IEntity user, IEntity target)
|
||||
public bool TryUseFood(IEntity user, IEntity target, UtensilComponent utensilUsed = null)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
@@ -78,20 +104,65 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
|
||||
var trueTarget = target ?? user;
|
||||
|
||||
if (trueTarget.TryGetComponent(out StomachComponent stomachComponent))
|
||||
if (!trueTarget.TryGetComponent(out StomachComponent stomach))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var utensils = utensilUsed != null
|
||||
? new List<UtensilComponent> {utensilUsed}
|
||||
: null;
|
||||
|
||||
if (_utensilsNeeded != UtensilType.None)
|
||||
{
|
||||
utensils = new List<UtensilComponent>();
|
||||
var types = UtensilType.None;
|
||||
|
||||
if (user.TryGetComponent(out HandsComponent hands))
|
||||
{
|
||||
foreach (var item in hands.GetAllHeldItems())
|
||||
{
|
||||
if (!item.Owner.TryGetComponent(out UtensilComponent utensil))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
utensils.Add(utensil);
|
||||
types |= utensil.Types;
|
||||
}
|
||||
}
|
||||
|
||||
if (!types.HasFlag(_utensilsNeeded))
|
||||
{
|
||||
trueTarget.PopupMessage(user, Loc.GetString("You need to be holding a {0} to eat that!", _utensilsNeeded));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!InteractionChecks.InRangeUnobstructed(user, trueTarget.Transform.MapPosition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume);
|
||||
var split = _contents.SplitSolution(transferAmount);
|
||||
if (stomachComponent.TryTransferSolution(split))
|
||||
{
|
||||
_entitySystem.GetEntitySystem<AudioSystem>()
|
||||
.PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f));
|
||||
trueTarget.PopupMessage(user, Loc.GetString("Nom"));
|
||||
}
|
||||
else
|
||||
if (!stomach.TryTransferSolution(split))
|
||||
{
|
||||
_contents.TryAddSolution(split);
|
||||
trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
_entitySystem.GetEntitySystem<AudioSystem>()
|
||||
.PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f));
|
||||
trueTarget.PopupMessage(user, Loc.GetString("Nom"));
|
||||
|
||||
// If utensils were used
|
||||
if (utensils != null)
|
||||
{
|
||||
foreach (var utensil in utensils)
|
||||
{
|
||||
utensil.TryBreak(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,21 +171,35 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_trashPrototype))
|
||||
{
|
||||
Owner.Delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
//We're empty. Become trash.
|
||||
var position = Owner.Transform.GridPosition;
|
||||
Owner.Delete();
|
||||
var finisher = Owner.EntityManager.SpawnEntity(_trashPrototype, position);
|
||||
if (user.TryGetComponent(out HandsComponent handsComponent) && finisher.TryGetComponent(out ItemComponent itemComponent))
|
||||
{
|
||||
if (handsComponent.CanPutInHand(itemComponent))
|
||||
{
|
||||
handsComponent.PutInHand(itemComponent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finisher.Transform.GridPosition = user.Transform.GridPosition;
|
||||
return true;
|
||||
|
||||
// If the user is holding the item
|
||||
if (user.TryGetComponent(out HandsComponent handsComponent) &&
|
||||
handsComponent.IsHolding(Owner))
|
||||
{
|
||||
Owner.Delete();
|
||||
|
||||
// Put the trash in the user's hand
|
||||
if (finisher.TryGetComponent(out ItemComponent item) &&
|
||||
handsComponent.CanPutInHand(item))
|
||||
{
|
||||
handsComponent.PutInHand(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.PDA;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.PDA;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
@@ -250,6 +251,12 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Eject ID");
|
||||
data.Visibility = component.IdSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user