item toggling giga rework + full ninja refactor (#28039)
* item toggle refactoring and some new systems * add ToggleClothing component/system * unhardcode magboots gravity logic * make magboots and speedboots use ItemToggle and stuff * remove now useless clothing components * update client/server magboots systems * add note to use ItemToggledEvent in ToggleActionEvent doc * refactor PowerCellDraw to use ItemToggle for ui open/close control * add TryUseCharges, refactor charges system * update magboot trigger code * make borg use ItemToggle, network SelectedModule instead of now removed Activated * add AccessToggle for borg * the giga ninja refactor * update ninja yml * update ItemToggle usage for some stuff * fix activatableui requires power * random fixing * yaml fixing * nuke ItemToggleDisarmMalus * make defib use ItemToggle * make things that use power not turn on if missing use charge * pro * fix sound prediction * bruh * proximity detector use ItemToggle * oop * big idiot syndrome * fix ninja spawn rule and make it generic * fix ninja spawn rule yml * move loading profiles into AntagLoadProfileRule * more ninja refactor * ninja yml fixes * the dreaded copy paste ops * remove useless NinjaRuleComponent and ue AntagSelection for greeting * fix invisibility * move IsCompleted to SharedObjectivesSystem * ability fixes * oop fix powercell instantly draining itself * sentient speedboots gaming * make reflect use ItemToggle * fix other test * loadprofilerule moved into its own pr * remove conflict with dragon refactor * remove all GenericAntag code from ninja * ) * probably * remove old enabled * great language bravo vince * GREAT LANGUAGE * who made this language * because it stinks * reparent blood-red magboots to magboots probbbly works * most of the review stuff * hasGrav doesnt mean what i thought it did * make health analyzer use itemtoggle, not fail test * fix mag/speed boots being wacky * UNTROLL * add ItemToggle to the random health analyzers * a * remove unused obsolete borg func * untrolling * :trollface: * fix test * fix * g * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
using Content.Shared.Item.ItemToggle;
|
|
||||||
|
|
||||||
namespace Content.Shared.Item;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public sealed class ItemToggleSystem : SharedItemToggleSystem
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
5
Content.Client/Ninja/Systems/ItemCreatorSystem.cs
Normal file
5
Content.Client/Ninja/Systems/ItemCreatorSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Ninja.Systems;
|
||||||
|
|
||||||
|
public sealed class ItemCreatorSystem : SharedItemCreatorSystem;
|
||||||
@@ -2,9 +2,4 @@ using Content.Shared.Ninja.Systems;
|
|||||||
|
|
||||||
namespace Content.Client.Ninja.Systems;
|
namespace Content.Client.Ninja.Systems;
|
||||||
|
|
||||||
/// <summary>
|
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem;
|
||||||
/// Does nothing special, only exists to provide a client implementation.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,24 +1,5 @@
|
|||||||
using Content.Shared.Clothing.EntitySystems;
|
|
||||||
using Content.Shared.Ninja.Components;
|
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
namespace Content.Client.Ninja.Systems;
|
namespace Content.Client.Ninja.Systems;
|
||||||
|
|
||||||
/// <summary>
|
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem;
|
||||||
/// Disables cloak prediction since client has no knowledge of battery power.
|
|
||||||
/// Cloak will still be enabled after server tells it.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, AttemptStealthEvent>(OnAttemptStealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args)
|
|
||||||
{
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,11 +2,4 @@ using Content.Shared.Ninja.Systems;
|
|||||||
|
|
||||||
namespace Content.Client.Ninja.Systems;
|
namespace Content.Client.Ninja.Systems;
|
||||||
|
|
||||||
/// <summary>
|
public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem;
|
||||||
/// Currently does nothing special clientside.
|
|
||||||
/// All functionality is in shared and server.
|
|
||||||
/// Only exists to prevent crashing.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
5
Content.Client/Ninja/Systems/SpiderChargeSystem.cs
Normal file
5
Content.Client/Ninja/Systems/SpiderChargeSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Ninja.Systems;
|
||||||
|
|
||||||
|
public sealed class SpiderChargeSystem : SharedSpiderChargeSystem;
|
||||||
@@ -171,7 +171,7 @@ public abstract partial class InteractionTest
|
|||||||
// turn on welders
|
// turn on welders
|
||||||
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
|
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
|
||||||
{
|
{
|
||||||
Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle));
|
Assert.That(ItemToggleSys.TryActivate((item, itemToggle), user: playerEnt));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public abstract partial class InteractionTest
|
|||||||
protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
|
protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
|
||||||
protected SharedDoAfterSystem DoAfterSys = default!;
|
protected SharedDoAfterSystem DoAfterSys = default!;
|
||||||
protected ToolSystem ToolSys = default!;
|
protected ToolSystem ToolSys = default!;
|
||||||
protected SharedItemToggleSystem ItemToggleSys = default!;
|
protected ItemToggleSystem ItemToggleSys = default!;
|
||||||
protected InteractionTestSystem STestSystem = default!;
|
protected InteractionTestSystem STestSystem = default!;
|
||||||
protected SharedTransformSystem Transform = default!;
|
protected SharedTransformSystem Transform = default!;
|
||||||
protected SharedMapSystem MapSystem = default!;
|
protected SharedMapSystem MapSystem = default!;
|
||||||
@@ -165,7 +165,7 @@ public abstract partial class InteractionTest
|
|||||||
HandSys = SEntMan.System<HandsSystem>();
|
HandSys = SEntMan.System<HandsSystem>();
|
||||||
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
||||||
ToolSys = SEntMan.System<ToolSystem>();
|
ToolSys = SEntMan.System<ToolSystem>();
|
||||||
ItemToggleSys = SEntMan.System<SharedItemToggleSystem>();
|
ItemToggleSys = SEntMan.System<ItemToggleSystem>();
|
||||||
DoAfterSys = SEntMan.System<SharedDoAfterSystem>();
|
DoAfterSys = SEntMan.System<SharedDoAfterSystem>();
|
||||||
Transform = SEntMan.System<SharedTransformSystem>();
|
Transform = SEntMan.System<SharedTransformSystem>();
|
||||||
MapSystem = SEntMan.System<SharedMapSystem>();
|
MapSystem = SEntMan.System<SharedMapSystem>();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Content.Server.Charges.Components;
|
|||||||
/// Something with limited charges that can be recharged automatically.
|
/// Something with limited charges that can be recharged automatically.
|
||||||
/// Requires LimitedChargesComponent to function.
|
/// Requires LimitedChargesComponent to function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
// TODO: no reason this cant be predicted and server system deleted
|
||||||
[RegisterComponent, AutoGenerateComponentPause]
|
[RegisterComponent, AutoGenerateComponentPause]
|
||||||
[Access(typeof(ChargesSystem))]
|
[Access(typeof(ChargesSystem))]
|
||||||
public sealed partial class AutoRechargeComponent : Component
|
public sealed partial class AutoRechargeComponent : Component
|
||||||
|
|||||||
@@ -37,15 +37,17 @@ public sealed class ChargesSystem : SharedChargesSystem
|
|||||||
args.PushMarkup(Loc.GetString("limited-charges-recharging", ("seconds", timeRemaining)));
|
args.PushMarkup(Loc.GetString("limited-charges-recharging", ("seconds", timeRemaining)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null)
|
public override void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp, false))
|
if (!Query.Resolve(uid, ref comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var startRecharge = comp.Charges == comp.MaxCharges;
|
var startRecharge = comp.Charges == comp.MaxCharges;
|
||||||
base.UseCharge(uid, comp);
|
base.AddCharges(uid, change, comp);
|
||||||
// start the recharge time after first use at full charge
|
|
||||||
if (startRecharge && TryComp<AutoRechargeComponent>(uid, out var recharge))
|
// if a charge was just used from full, start the recharge timer
|
||||||
|
// TODO: probably make this an event instead of having le server system that just does this
|
||||||
|
if (change < 0 && startRecharge && TryComp<AutoRechargeComponent>(uid, out var recharge))
|
||||||
recharge.NextChargeTime = _timing.CurTime + recharge.RechargeDuration;
|
recharge.NextChargeTime = _timing.CurTime + recharge.RechargeDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using Content.Server.Ninja.Systems;
|
|
||||||
using Content.Shared.Communications;
|
|
||||||
using Content.Shared.Random;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.Rules.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores some configuration used by the ninja system.
|
|
||||||
/// Objectives and roundend summary are handled by <see cref="GenericAntagRuleComponent"/>.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, Access(typeof(SpaceNinjaSystem))]
|
|
||||||
public sealed partial class NinjaRuleComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// List of threats that can be called in. Copied onto <see cref="CommsHackerComponent"/> when gloves are enabled.
|
|
||||||
/// </summary>
|
|
||||||
[DataField(required: true)]
|
|
||||||
public ProtoId<WeightedRandomPrototype> Threats = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sound played when making the player a ninja via antag control or ghost role
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/ninja_greeting.ogg");
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
namespace Content.Server.Item;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles whether this item applies a disarm malus when active.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class ItemToggleDisarmMalusComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Item has this modifier to the chance to disarm when activated.
|
|
||||||
/// If null, the value will be inferred from the current malus just before the malus is first deactivated.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField]
|
|
||||||
public float? ActivatedDisarmMalus = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier.
|
|
||||||
/// If null, the value will be inferred from the current malus just before the malus is first activated.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField]
|
|
||||||
public float? DeactivatedDisarmMalus = null;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Content.Server.Item;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles whether this item is sharp when toggled on.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class ItemToggleSharpComponent : Component
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using Content.Server.CombatMode.Disarm;
|
|
||||||
using Content.Server.Kitchen.Components;
|
|
||||||
using Content.Shared.Item.ItemToggle;
|
|
||||||
using Content.Shared.Item.ItemToggle.Components;
|
|
||||||
|
|
||||||
namespace Content.Server.Item;
|
|
||||||
|
|
||||||
public sealed class ItemToggleSystem : SharedItemToggleSystem
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<ItemToggleSharpComponent, ItemToggledEvent>(ToggleSharp);
|
|
||||||
SubscribeLocalEvent<ItemToggleDisarmMalusComponent, ItemToggledEvent>(ToggleMalus);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleSharp(Entity<ItemToggleSharpComponent> ent, ref ItemToggledEvent args)
|
|
||||||
{
|
|
||||||
// TODO generalize this into a "ToggleComponentComponent", though probably with a better name
|
|
||||||
if (args.Activated)
|
|
||||||
EnsureComp<SharpComponent>(ent);
|
|
||||||
else
|
|
||||||
RemCompDeferred<SharpComponent>(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleMalus(Entity<ItemToggleDisarmMalusComponent> ent, ref ItemToggledEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<DisarmMalusComponent>(ent, out var malus))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (args.Activated)
|
|
||||||
{
|
|
||||||
ent.Comp.DeactivatedDisarmMalus ??= malus.Malus;
|
|
||||||
if (ent.Comp.ActivatedDisarmMalus is {} activatedMalus)
|
|
||||||
malus.Malus = activatedMalus;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ent.Comp.ActivatedDisarmMalus ??= malus.Malus;
|
|
||||||
if (ent.Comp.DeactivatedDisarmMalus is {} deactivatedMalus)
|
|
||||||
malus.Malus = deactivatedMalus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,9 @@ namespace Content.Server.Medical.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// After scanning, retrieves the target Uid to use with its related UI.
|
/// After scanning, retrieves the target Uid to use with its related UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Requires <c>ItemToggleComponent</c>.
|
||||||
|
/// </remarks>
|
||||||
[RegisterComponent, AutoGenerateComponentPause]
|
[RegisterComponent, AutoGenerateComponentPause]
|
||||||
[Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))]
|
[Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))]
|
||||||
public sealed partial class HealthAnalyzerComponent : Component
|
public sealed partial class HealthAnalyzerComponent : Component
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Content.Shared.DoAfter;
|
|||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Components;
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Medical;
|
using Content.Shared.Medical;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
@@ -37,6 +38,7 @@ public sealed class DefibrillatorSystem : EntitySystem
|
|||||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
||||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly RottingSystem _rotting = default!;
|
[Dependency] private readonly RottingSystem _rotting = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||||
@@ -50,30 +52,10 @@ public sealed class DefibrillatorSystem : EntitySystem
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<DefibrillatorComponent, UseInHandEvent>(OnUseInHand);
|
|
||||||
SubscribeLocalEvent<DefibrillatorComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
|
||||||
SubscribeLocalEvent<DefibrillatorComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<DefibrillatorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
SubscribeLocalEvent<DefibrillatorComponent, DefibrillatorZapDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<DefibrillatorComponent, DefibrillatorZapDoAfterEvent>(OnDoAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryToggle(uid, component, args.User))
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
_useDelay.TryResetDelay((uid, useDelay));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args)
|
|
||||||
{
|
|
||||||
if (!TerminatingOrDeleted(uid))
|
|
||||||
TryDisable(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args)
|
private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled || args.Target is not { } target)
|
if (args.Handled || args.Target is not { } target)
|
||||||
@@ -96,54 +78,12 @@ public sealed class DefibrillatorSystem : EntitySystem
|
|||||||
Zap(uid, target, args.User, component);
|
Zap(uid, target, args.User, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryToggle(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return component.Enabled
|
|
||||||
? TryDisable(uid, component)
|
|
||||||
: TryEnable(uid, component, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryEnable(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (component.Enabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_powerCell.HasActivatableCharge(uid))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
component.Enabled = true;
|
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, true);
|
|
||||||
_audio.PlayPvs(component.PowerOnSound, uid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryDisable(EntityUid uid, DefibrillatorComponent? component = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!component.Enabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
component.Enabled = false;
|
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, false);
|
|
||||||
|
|
||||||
_audio.PlayPvs(component.PowerOffSound, uid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null)
|
public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!component.Enabled)
|
if (!_toggle.IsActivated(uid))
|
||||||
{
|
{
|
||||||
if (user != null)
|
if (user != null)
|
||||||
_popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value);
|
_popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value);
|
||||||
@@ -257,7 +197,7 @@ public sealed class DefibrillatorSystem : EntitySystem
|
|||||||
|
|
||||||
// if we don't have enough power left for another shot, turn it off
|
// if we don't have enough power left for another shot, turn it off
|
||||||
if (!_powerCell.HasActivatableCharge(uid))
|
if (!_powerCell.HasActivatableCharge(uid))
|
||||||
TryDisable(uid, component);
|
_toggle.TryDeactivate(uid);
|
||||||
|
|
||||||
// TODO clean up this clown show above
|
// TODO clean up this clown show above
|
||||||
var ev = new TargetDefibrillatedEvent(user, (uid, component));
|
var ev = new TargetDefibrillatedEvent(user, (uid, component));
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ using Content.Shared.DoAfter;
|
|||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.MedicalScanner;
|
using Content.Shared.MedicalScanner;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -26,6 +28,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
[Dependency] private readonly PowerCellSystem _cell = default!;
|
[Dependency] private readonly PowerCellSystem _cell = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||||
@@ -36,7 +39,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
|
||||||
SubscribeLocalEvent<HealthAnalyzerComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
SubscribeLocalEvent<HealthAnalyzerComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||||
SubscribeLocalEvent<HealthAnalyzerComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
SubscribeLocalEvent<HealthAnalyzerComponent, ItemToggledEvent>(OnToggled);
|
||||||
SubscribeLocalEvent<HealthAnalyzerComponent, DroppedEvent>(OnDropped);
|
SubscribeLocalEvent<HealthAnalyzerComponent, DroppedEvent>(OnDropped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,16 +114,16 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
private void OnInsertedIntoContainer(Entity<HealthAnalyzerComponent> uid, ref EntGotInsertedIntoContainerMessage args)
|
private void OnInsertedIntoContainer(Entity<HealthAnalyzerComponent> uid, ref EntGotInsertedIntoContainerMessage args)
|
||||||
{
|
{
|
||||||
if (uid.Comp.ScannedEntity is { } patient)
|
if (uid.Comp.ScannedEntity is { } patient)
|
||||||
StopAnalyzingEntity(uid, patient);
|
_toggle.TryDeactivate(uid.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disable continuous updates once battery is dead
|
/// Disable continuous updates once turned off
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnPowerCellSlotEmpty(Entity<HealthAnalyzerComponent> uid, ref PowerCellSlotEmptyEvent args)
|
private void OnToggled(Entity<HealthAnalyzerComponent> ent, ref ItemToggledEvent args)
|
||||||
{
|
{
|
||||||
if (uid.Comp.ScannedEntity is { } patient)
|
if (!args.Activated && ent.Comp.ScannedEntity is { } patient)
|
||||||
StopAnalyzingEntity(uid, patient);
|
StopAnalyzingEntity(ent, patient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -129,7 +132,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
private void OnDropped(Entity<HealthAnalyzerComponent> uid, ref DroppedEvent args)
|
private void OnDropped(Entity<HealthAnalyzerComponent> uid, ref DroppedEvent args)
|
||||||
{
|
{
|
||||||
if (uid.Comp.ScannedEntity is { } patient)
|
if (uid.Comp.ScannedEntity is { } patient)
|
||||||
StopAnalyzingEntity(uid, patient);
|
_toggle.TryDeactivate(uid.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenUserInterface(EntityUid user, EntityUid analyzer)
|
private void OpenUserInterface(EntityUid user, EntityUid analyzer)
|
||||||
@@ -150,7 +153,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
//Link the health analyzer to the scanned entity
|
//Link the health analyzer to the scanned entity
|
||||||
healthAnalyzer.Comp.ScannedEntity = target;
|
healthAnalyzer.Comp.ScannedEntity = target;
|
||||||
|
|
||||||
_cell.SetPowerCellDrawEnabled(healthAnalyzer, true);
|
_toggle.TryActivate(healthAnalyzer.Owner);
|
||||||
|
|
||||||
UpdateScannedUser(healthAnalyzer, target, true);
|
UpdateScannedUser(healthAnalyzer, target, true);
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
|
|||||||
//Unlink the analyzer
|
//Unlink the analyzer
|
||||||
healthAnalyzer.Comp.ScannedEntity = null;
|
healthAnalyzer.Comp.ScannedEntity = null;
|
||||||
|
|
||||||
_cell.SetPowerCellDrawEnabled(target, false);
|
_toggle.TryDeactivate(healthAnalyzer.Owner);
|
||||||
|
|
||||||
UpdateScannedUser(healthAnalyzer, target, false);
|
UpdateScannedUser(healthAnalyzer, target, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace Content.Server.Ninja.Events;
|
namespace Content.Server.Ninja.Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised on the ninja when the suit has its powercell changed.
|
/// Raised on the ninja and suit when the suit has its powercell changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct NinjaBatteryChangedEvent(EntityUid Battery, EntityUid BatteryHolder);
|
public record struct NinjaBatteryChangedEvent(EntityUid Battery, EntityUid BatteryHolder);
|
||||||
|
|||||||
@@ -33,16 +33,17 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
|||||||
/// Start do after for draining a power source.
|
/// Start do after for draining a power source.
|
||||||
/// Can't predict PNBC existing so only done on server.
|
/// Can't predict PNBC existing so only done on server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, BeforeInteractHandEvent args)
|
private void OnBeforeInteractHand(Entity<BatteryDrainerComponent> ent, ref BeforeInteractHandEvent args)
|
||||||
{
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
var target = args.Target;
|
var target = args.Target;
|
||||||
if (args.Handled || comp.BatteryUid == null || !HasComp<PowerNetworkBatteryComponent>(target))
|
if (args.Handled || comp.BatteryUid is not {} battery || !HasComp<PowerNetworkBatteryComponent>(target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// handles even if battery is full so you can actually see the poup
|
// handles even if battery is full so you can actually see the poup
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
||||||
if (_battery.IsFull(comp.BatteryUid.Value))
|
if (_battery.IsFull(battery))
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("battery-drainer-full"), uid, uid, PopupType.Medium);
|
_popup.PopupEntity(Loc.GetString("battery-drainer-full"), uid, uid, PopupType.Medium);
|
||||||
return;
|
return;
|
||||||
@@ -59,23 +60,24 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
|||||||
_doAfter.TryStartDoAfter(doAfterArgs);
|
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryChanged(EntityUid uid, BatteryDrainerComponent comp, ref NinjaBatteryChangedEvent args)
|
private void OnBatteryChanged(Entity<BatteryDrainerComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||||
{
|
{
|
||||||
SetBattery(uid, args.Battery, comp);
|
SetBattery((ent, ent.Comp), args.Battery);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
protected override void OnDoAfterAttempt(Entity<BatteryDrainerComponent> ent, ref DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
||||||
{
|
{
|
||||||
base.OnDoAfterAttempt(uid, comp, args);
|
base.OnDoAfterAttempt(ent, ref args);
|
||||||
|
|
||||||
if (comp.BatteryUid == null || _battery.IsFull(comp.BatteryUid.Value))
|
if (ent.Comp.BatteryUid is not {} battery || _battery.IsFull(battery))
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target)
|
protected override bool TryDrainPower(Entity<BatteryDrainerComponent> ent, EntityUid target)
|
||||||
{
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
if (comp.BatteryUid == null || !TryComp<BatteryComponent>(comp.BatteryUid.Value, out var battery))
|
if (comp.BatteryUid == null || !TryComp<BatteryComponent>(comp.BatteryUid.Value, out var battery))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -98,6 +100,7 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
|||||||
|
|
||||||
var output = input * comp.DrainEfficiency;
|
var output = input * comp.DrainEfficiency;
|
||||||
_battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery);
|
_battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery);
|
||||||
|
// TODO: create effect message or something
|
||||||
Spawn("EffectSparks", Transform(target).Coordinates);
|
Spawn("EffectSparks", Transform(target).Coordinates);
|
||||||
_audio.PlayPvs(comp.SparkSound, target);
|
_audio.PlayPvs(comp.SparkSound, target);
|
||||||
_popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid);
|
_popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid);
|
||||||
|
|||||||
57
Content.Server/Ninja/Systems/ItemCreatorSystem.cs
Normal file
57
Content.Server/Ninja/Systems/ItemCreatorSystem.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using Content.Server.Ninja.Events;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
|
using Content.Shared.Ninja.Components;
|
||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
|
||||||
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|
||||||
|
public sealed class ItemCreatorSystem : SharedItemCreatorSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly BatterySystem _battery = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ItemCreatorComponent, CreateItemEvent>(OnCreateItem);
|
||||||
|
SubscribeLocalEvent<ItemCreatorComponent, NinjaBatteryChangedEvent>(OnBatteryChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreateItem(Entity<ItemCreatorComponent> ent, ref CreateItemEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
if (comp.Battery is not {} battery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
|
||||||
|
var user = args.Performer;
|
||||||
|
if (!_battery.TryUseCharge(battery, comp.Charge))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString(comp.NoPowerPopup), user, user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ev = new CreateItemAttemptEvent(user);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
if (ev.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// try to put throwing star in hand, otherwise it goes on the ground
|
||||||
|
var star = Spawn(comp.SpawnedPrototype, Transform(user).Coordinates);
|
||||||
|
_hands.TryPickupAnyHand(user, star);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBatteryChanged(Entity<ItemCreatorComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Battery == args.Battery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Battery = args.Battery;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
using Content.Server.Communications;
|
|
||||||
using Content.Server.Mind;
|
|
||||||
using Content.Server.Ninja.Events;
|
using Content.Server.Ninja.Events;
|
||||||
using Content.Server.Objectives.Systems;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Communications;
|
using Content.Shared.Objectives.Systems;
|
||||||
using Content.Shared.CriminalRecords.Components;
|
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Research.Components;
|
|
||||||
using Content.Shared.Toggleable;
|
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|
||||||
@@ -16,89 +11,44 @@ namespace Content.Server.Ninja.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EmagProviderSystem _emagProvider = default!;
|
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||||
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||||
[Dependency] private readonly CommsHackerSystem _commsHacker = default!;
|
|
||||||
[Dependency] private readonly SharedStunProviderSystem _stunProvider = default!;
|
|
||||||
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
protected override void EnableGloves(Entity<NinjaGlovesComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.EnableGloves(ent, user);
|
||||||
|
|
||||||
SubscribeLocalEvent<NinjaGlovesComponent, ToggleActionEvent>(OnToggleAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggle gloves, if the user is a ninja wearing a ninja suit.
|
|
||||||
/// </summary>
|
|
||||||
private void OnToggleAction(EntityUid uid, NinjaGlovesComponent comp, ToggleActionEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
|
|
||||||
var user = args.Performer;
|
|
||||||
// need to wear suit to enable gloves
|
|
||||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja)
|
|
||||||
|| ninja.Suit == null
|
|
||||||
|| !HasComp<NinjaSuitComponent>(ninja.Suit.Value))
|
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-gloves-not-wearing-suit"), user, user);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// show its state to the user
|
|
||||||
var enabling = comp.User == null;
|
|
||||||
Appearance.SetData(uid, ToggleVisuals.Toggled, enabling);
|
|
||||||
var message = Loc.GetString(enabling ? "ninja-gloves-on" : "ninja-gloves-off");
|
|
||||||
Popup.PopupEntity(message, user, user);
|
|
||||||
|
|
||||||
if (enabling)
|
|
||||||
{
|
|
||||||
EnableGloves(uid, comp, user, ninja);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DisableGloves(uid, comp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnableGloves(EntityUid uid, NinjaGlovesComponent comp, EntityUid user, SpaceNinjaComponent ninja)
|
|
||||||
{
|
|
||||||
// can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability
|
// can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability
|
||||||
if (ninja.Suit == null)
|
if (user.Comp.Suit is not {} suit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.User = user;
|
if (!_mind.TryGetMind(user, out var mindId, out var mind))
|
||||||
Dirty(uid, comp);
|
return;
|
||||||
_ninja.AssignGloves(user, uid, ninja);
|
|
||||||
|
|
||||||
var drainer = EnsureComp<BatteryDrainerComponent>(user);
|
foreach (var ability in ent.Comp.Abilities)
|
||||||
var stun = EnsureComp<StunProviderComponent>(user);
|
{
|
||||||
_stunProvider.SetNoPowerPopup(user, "ninja-no-power", stun);
|
// non-objective abilities are added in shared already
|
||||||
|
if (ability.Objective is not {} objId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// prevent doing an objective multiple times by toggling gloves after doing them
|
||||||
|
// if it's not tied to an objective always add them anyway
|
||||||
|
if (!_mind.TryFindObjective((mindId, mind), objId, out var obj))
|
||||||
|
{
|
||||||
|
Log.Error($"Ninja glove ability of {ent} referenced missing objective {ability.Objective} of {_mind.MindOwnerLoggingString(mind)}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_objectives.IsCompleted(obj.Value, (mindId, mind)))
|
||||||
|
EntityManager.AddComponents(user, ability.Components);
|
||||||
|
}
|
||||||
|
|
||||||
|
// let abilities that use battery power work
|
||||||
if (_ninja.GetNinjaBattery(user, out var battery, out var _))
|
if (_ninja.GetNinjaBattery(user, out var battery, out var _))
|
||||||
{
|
{
|
||||||
var ev = new NinjaBatteryChangedEvent(battery.Value, ninja.Suit.Value);
|
var ev = new NinjaBatteryChangedEvent(battery.Value, suit);
|
||||||
RaiseLocalEvent(user, ref ev);
|
RaiseLocalEvent(user, ref ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
var emag = EnsureComp<EmagProviderComponent>(user);
|
|
||||||
_emagProvider.SetWhitelist(user, comp.DoorjackWhitelist, emag);
|
|
||||||
|
|
||||||
EnsureComp<ResearchStealerComponent>(user);
|
|
||||||
// prevent calling in multiple threats by toggling gloves after
|
|
||||||
if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective))
|
|
||||||
{
|
|
||||||
var hacker = EnsureComp<CommsHackerComponent>(user);
|
|
||||||
var rule = _ninja.NinjaRule(user);
|
|
||||||
if (rule != null)
|
|
||||||
_commsHacker.SetThreats(user, rule.Threats, hacker);
|
|
||||||
}
|
|
||||||
if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective))
|
|
||||||
{
|
|
||||||
EnsureComp<CriminalRecordsHackerComponent>(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Emp;
|
|||||||
using Content.Server.Ninja.Events;
|
using Content.Server.Ninja.Events;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
@@ -29,15 +28,13 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, AttemptStealthEvent>(OnAttemptStealth);
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, CreateThrowingStarEvent>(OnCreateThrowingStar);
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja)
|
protected override void NinjaEquipped(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
{
|
{
|
||||||
base.NinjaEquippedSuit(uid, comp, user, ninja);
|
base.NinjaEquipped(ent, user);
|
||||||
|
|
||||||
_ninja.SetSuitPowerAlert(user);
|
_ninja.SetSuitPowerAlert(user);
|
||||||
}
|
}
|
||||||
@@ -57,16 +54,15 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
|
|
||||||
// can only upgrade power cell, not swap to recharge instantly otherwise ninja could just swap batteries with flashlights in maints for easy power
|
// can only upgrade power cell, not swap to recharge instantly otherwise ninja could just swap batteries with flashlights in maints for easy power
|
||||||
if (!TryComp<BatteryComponent>(args.EntityUid, out var inserting) || inserting.MaxCharge <= battery.MaxCharge)
|
if (!TryComp<BatteryComponent>(args.EntityUid, out var inserting) || inserting.MaxCharge <= battery.MaxCharge)
|
||||||
{
|
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
|
||||||
|
|
||||||
// tell ninja abilities that use battery to update it so they don't use charge from the old one
|
// tell ninja abilities that use battery to update it so they don't use charge from the old one
|
||||||
var user = Transform(uid).ParentUid;
|
var user = Transform(uid).ParentUid;
|
||||||
if (!HasComp<SpaceNinjaComponent>(user))
|
if (!_ninja.IsNinja(user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new NinjaBatteryChangedEvent(args.EntityUid, uid);
|
var ev = new NinjaBatteryChangedEvent(args.EntityUid, uid);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
RaiseLocalEvent(user, ref ev);
|
RaiseLocalEvent(user, ref ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,64 +73,22 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user)
|
protected override void UserUnequippedSuit(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
{
|
{
|
||||||
base.UserUnequippedSuit(uid, comp, user);
|
base.UserUnequippedSuit(ent, user);
|
||||||
|
|
||||||
// remove power indicator
|
// remove power indicator
|
||||||
_ninja.SetSuitPowerAlert(user);
|
_ninja.SetSuitPowerAlert(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args)
|
private void OnRecallKatana(Entity<NinjaSuitComponent> ent, ref RecallKatanaEvent args)
|
||||||
{
|
{
|
||||||
var user = args.User;
|
var (uid, comp) = ent;
|
||||||
// need 1 second of charge to turn on stealth
|
|
||||||
var chargeNeeded = SuitWattage(uid, comp);
|
|
||||||
// being attacked while cloaked gives no power message since it overloads the power supply or something
|
|
||||||
if (!_ninja.GetNinjaBattery(user, out _, out var battery) || battery.CurrentCharge < chargeNeeded)
|
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
|
|
||||||
args.Cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
|
||||||
args.Cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StealthClothing.SetEnabled(uid, user, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCreateThrowingStar(EntityUid uid, NinjaSuitComponent comp, CreateThrowingStarEvent args)
|
|
||||||
{
|
|
||||||
args.Handled = true;
|
|
||||||
var user = args.Performer;
|
var user = args.Performer;
|
||||||
if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge))
|
if (!_ninja.NinjaQuery.TryComp(user, out var ninja) || ninja.Katana == null)
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to put throwing star in hand, otherwise it goes on the ground
|
|
||||||
var star = Spawn(comp.ThrowingStarPrototype, Transform(user).Coordinates);
|
|
||||||
_hands.TryPickupAnyHand(user, star);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatanaEvent args)
|
|
||||||
{
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
var user = args.Performer;
|
|
||||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja) || ninja.Katana == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var katana = ninja.Katana.Value;
|
var katana = ninja.Katana.Value;
|
||||||
var coords = _transform.GetWorldPosition(katana);
|
var coords = _transform.GetWorldPosition(katana);
|
||||||
@@ -146,11 +100,8 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
if (CheckDisabled(ent, user))
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: teleporting into belt slot
|
// TODO: teleporting into belt slot
|
||||||
var message = _hands.TryPickupAnyHand(user, katana)
|
var message = _hands.TryPickupAnyHand(user, katana)
|
||||||
@@ -159,9 +110,11 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
Popup.PopupEntity(Loc.GetString(message), user, user);
|
Popup.PopupEntity(Loc.GetString(message), user, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args)
|
private void OnEmp(Entity<NinjaSuitComponent> ent, ref NinjaEmpEvent args)
|
||||||
{
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
||||||
var user = args.Performer;
|
var user = args.Performer;
|
||||||
if (!_ninja.TryUseCharge(user, comp.EmpCharge))
|
if (!_ninja.TryUseCharge(user, comp.EmpCharge))
|
||||||
{
|
{
|
||||||
@@ -169,13 +122,9 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comp.DisableCooldown > GameTiming.CurTime)
|
if (CheckDisabled(ent, user))
|
||||||
{
|
|
||||||
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// I don't think this affects the suit battery, but if it ever does in the future add a blacklist for it
|
|
||||||
var coords = _transform.GetMapCoordinates(user);
|
var coords = _transform.GetMapCoordinates(user);
|
||||||
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
|
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Communications;
|
|||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
using Content.Server.CriminalRecords.Systems;
|
using Content.Server.CriminalRecords.Systems;
|
||||||
using Content.Server.GameTicking.Rules.Components;
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
using Content.Server.GenericAntag;
|
|
||||||
using Content.Server.Objectives.Components;
|
using Content.Server.Objectives.Components;
|
||||||
using Content.Server.Objectives.Systems;
|
using Content.Server.Objectives.Systems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
@@ -11,7 +10,6 @@ using Content.Server.PowerCell;
|
|||||||
using Content.Server.Research.Systems;
|
using Content.Server.Research.Systems;
|
||||||
using Content.Server.Roles;
|
using Content.Server.Roles;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
@@ -26,11 +24,6 @@ using Robust.Shared.Audio.Systems;
|
|||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|
||||||
// TODO: when syndiborgs are a thing have a borg converter with 6 second doafter
|
|
||||||
// engi -> saboteur
|
|
||||||
// medi -> idk reskin it
|
|
||||||
// other -> assault
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use.
|
/// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -44,13 +37,11 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
[Dependency] private readonly RoleSystem _role = default!;
|
[Dependency] private readonly RoleSystem _role = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||||
[Dependency] private readonly StealthClothingSystem _stealthClothing = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, GenericAntagCreatedEvent>(OnNinjaCreated);
|
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, EmaggedSomethingEvent>(OnDoorjack);
|
SubscribeLocalEvent<SpaceNinjaComponent, EmaggedSomethingEvent>(OnDoorjack);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, ResearchStolenEvent>(OnResearchStolen);
|
SubscribeLocalEvent<SpaceNinjaComponent, ResearchStolenEvent>(OnResearchStolen);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, ThreatCalledInEvent>(OnThreatCalledIn);
|
SubscribeLocalEvent<SpaceNinjaComponent, ThreatCalledInEvent>(OnThreatCalledIn);
|
||||||
@@ -62,7 +53,7 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
var query = EntityQueryEnumerator<SpaceNinjaComponent>();
|
var query = EntityQueryEnumerator<SpaceNinjaComponent>();
|
||||||
while (query.MoveNext(out var uid, out var ninja))
|
while (query.MoveNext(out var uid, out var ninja))
|
||||||
{
|
{
|
||||||
UpdateNinja(uid, ninja, frameTime);
|
SetSuitPowerAlert((uid, ninja));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,31 +71,13 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
return newCount - oldCount;
|
return newCount - oldCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a ninja's gamerule config data.
|
|
||||||
/// If the gamerule was not started then it will be started automatically.
|
|
||||||
/// </summary>
|
|
||||||
public NinjaRuleComponent? NinjaRule(EntityUid uid, GenericAntagComponent? comp = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref comp))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// mind not added yet so no rule
|
|
||||||
if (comp.RuleEntity == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return CompOrNull<NinjaRuleComponent>(comp.RuleEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: can probably copy paste borg code here
|
// TODO: can probably copy paste borg code here
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the alert for the ninja's suit power indicator.
|
/// Update the alert for the ninja's suit power indicator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null)
|
public void SetSuitPowerAlert(Entity<SpaceNinjaComponent> ent)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp, false))
|
var (uid, comp) = ent;
|
||||||
return;
|
|
||||||
|
|
||||||
if (comp.Deleted || comp.Suit == null)
|
if (comp.Deleted || comp.Suit == null)
|
||||||
{
|
{
|
||||||
_alerts.ClearAlert(uid, comp.SuitPowerAlert);
|
_alerts.ClearAlert(uid, comp.SuitPowerAlert);
|
||||||
@@ -145,53 +118,6 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery);
|
return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set up everything for ninja to work and send the greeting message/sound.
|
|
||||||
/// Objectives are added by <see cref="GenericAntagSystem"/>.
|
|
||||||
/// </summary>
|
|
||||||
private void OnNinjaCreated(EntityUid uid, SpaceNinjaComponent comp, ref GenericAntagCreatedEvent args)
|
|
||||||
{
|
|
||||||
var mindId = args.MindId;
|
|
||||||
var mind = args.Mind;
|
|
||||||
|
|
||||||
if (mind.Session == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var config = NinjaRule(uid);
|
|
||||||
if (config == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var role = new NinjaRoleComponent
|
|
||||||
{
|
|
||||||
PrototypeId = "SpaceNinja"
|
|
||||||
};
|
|
||||||
_role.MindAddRole(mindId, role, mind);
|
|
||||||
_role.MindPlaySound(mindId, config.GreetingSound, mind);
|
|
||||||
|
|
||||||
var session = mind.Session;
|
|
||||||
_audio.PlayGlobal(config.GreetingSound, Filter.Empty().AddPlayer(session), false, AudioParams.Default);
|
|
||||||
_chatMan.DispatchServerMessage(session, Loc.GetString("ninja-role-greeting"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: PowerCellDraw, modify when cloak enabled
|
|
||||||
/// <summary>
|
|
||||||
/// Handle constant power drains from passive usage and cloak.
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateNinja(EntityUid uid, SpaceNinjaComponent ninja, float frameTime)
|
|
||||||
{
|
|
||||||
if (ninja.Suit == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float wattage = Suit.SuitWattage(ninja.Suit.Value);
|
|
||||||
|
|
||||||
SetSuitPowerAlert(uid, ninja);
|
|
||||||
if (!TryUseCharge(uid, wattage * frameTime))
|
|
||||||
{
|
|
||||||
// ran out of power, uncloak ninja
|
|
||||||
_stealthClothing.SetEnabled(ninja.Suit.Value, uid, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Increment greentext when emagging a door.
|
/// Increment greentext when emagging a door.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Server.Roles;
|
|||||||
using Content.Server.Sticky.Events;
|
using Content.Server.Sticky.Events;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
@@ -14,7 +15,7 @@ namespace Content.Server.Ninja.Systems;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prevents planting a spider charge outside of its location and handles greentext.
|
/// Prevents planting a spider charge outside of its location and handles greentext.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SpiderChargeSystem : EntitySystem
|
public sealed class SpiderChargeSystem : SharedSpiderChargeSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly MindSystem _mind = default!;
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ using Content.Shared.Ninja.Components;
|
|||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
using Robust.Shared.Prototypes;
|
using Content.Shared.Timing;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Content.Shared.Whitelist;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|
||||||
@@ -20,12 +21,12 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly BatterySystem _battery = default!;
|
[Dependency] private readonly BatterySystem _battery = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -38,16 +39,18 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stun clicked mobs on the whitelist, if there is enough power.
|
/// Stun clicked mobs on the whitelist, if there is enough power.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, BeforeInteractHandEvent args)
|
private void OnBeforeInteractHand(Entity<StunProviderComponent> ent, ref BeforeInteractHandEvent args)
|
||||||
{
|
{
|
||||||
// TODO: generic check
|
// TODO: generic check
|
||||||
|
var (uid, comp) = ent;
|
||||||
if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target))
|
if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (target == uid || _whitelistSystem.IsWhitelistFail(comp.Whitelist, target))
|
if (target == uid || _whitelist.IsWhitelistFail(comp.Whitelist, target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_timing.CurTime < comp.NextStun)
|
var useDelay = EnsureComp<UseDelayComponent>(uid);
|
||||||
|
if (_useDelay.IsDelayed((uid, useDelay), id: comp.DelayId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// take charge from battery
|
// take charge from battery
|
||||||
@@ -63,13 +66,14 @@ public sealed class StunProviderSystem : SharedStunProviderSystem
|
|||||||
_stun.TryParalyze(target, comp.StunTime, refresh: false);
|
_stun.TryParalyze(target, comp.StunTime, refresh: false);
|
||||||
|
|
||||||
// short cooldown to prevent instant stunlocking
|
// short cooldown to prevent instant stunlocking
|
||||||
comp.NextStun = _timing.CurTime + comp.Cooldown;
|
_useDelay.SetLength((uid, useDelay), comp.Cooldown, id: comp.DelayId);
|
||||||
|
_useDelay.TryResetDelay((uid, useDelay), id: comp.DelayId);
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryChanged(EntityUid uid, StunProviderComponent comp, ref NinjaBatteryChangedEvent args)
|
private void OnBatteryChanged(Entity<StunProviderComponent> ent, ref NinjaBatteryChangedEvent args)
|
||||||
{
|
{
|
||||||
SetBattery(uid, args.Battery, comp);
|
SetBattery((ent, ent.Comp), args.Battery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,20 +35,6 @@ public sealed class CodeConditionSystem : EntitySystem
|
|||||||
return ent.Comp.Completed;
|
return ent.Comp.Completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if a mob's objective with a certain prototype is completed.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCompleted(Entity<MindContainerComponent?> mob, string prototype)
|
|
||||||
{
|
|
||||||
if (_mind.GetMind(mob, mob.Comp) is not {} mindId)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_mind.TryFindObjective(mindId, prototype, out var obj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return IsCompleted(obj.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets an objective's completed field.
|
/// Sets an objective's completed field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
|
|
||||||
@@ -10,22 +11,20 @@ public sealed partial class PowerCellSystem
|
|||||||
* Handles PowerCellDraw
|
* Handles PowerCellDraw
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1);
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent>();
|
var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent, ItemToggleComponent>();
|
||||||
|
|
||||||
while (query.MoveNext(out var uid, out var comp, out var slot))
|
while (query.MoveNext(out var uid, out var comp, out var slot, out var toggle))
|
||||||
{
|
{
|
||||||
if (!comp.Drawing)
|
if (!comp.Enabled || !toggle.Activated)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Timing.CurTime < comp.NextUpdateTime)
|
if (Timing.CurTime < comp.NextUpdateTime)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
comp.NextUpdateTime += Delay;
|
comp.NextUpdateTime += comp.Delay;
|
||||||
|
|
||||||
if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
|
if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
|
||||||
continue;
|
continue;
|
||||||
@@ -33,7 +32,8 @@ public sealed partial class PowerCellSystem
|
|||||||
if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
|
if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
comp.Drawing = false;
|
Toggle.TryDeactivate((uid, toggle));
|
||||||
|
|
||||||
var ev = new PowerCellSlotEmptyEvent();
|
var ev = new PowerCellSlotEmptyEvent();
|
||||||
RaiseLocalEvent(uid, ref ev);
|
RaiseLocalEvent(uid, ref ev);
|
||||||
}
|
}
|
||||||
@@ -42,26 +42,9 @@ public sealed partial class PowerCellSystem
|
|||||||
private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args)
|
private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args)
|
||||||
{
|
{
|
||||||
// Update the bools for client prediction.
|
// Update the bools for client prediction.
|
||||||
bool canDraw;
|
var canUse = component.UseRate <= 0f || args.Charge > component.UseRate;
|
||||||
bool canUse;
|
|
||||||
|
|
||||||
if (component.UseRate > 0f)
|
var canDraw = component.DrawRate <= 0f || args.Charge > 0f;
|
||||||
{
|
|
||||||
canUse = args.Charge > component.UseRate;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
canUse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.DrawRate > 0f)
|
|
||||||
{
|
|
||||||
canDraw = args.Charge > 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
canDraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
||||||
{
|
{
|
||||||
@@ -76,6 +59,9 @@ public sealed partial class PowerCellSystem
|
|||||||
var canDraw = !args.Ejected && HasCharge(uid, float.MinValue);
|
var canDraw = !args.Ejected && HasCharge(uid, float.MinValue);
|
||||||
var canUse = !args.Ejected && HasActivatableCharge(uid, component);
|
var canUse = !args.Ejected && HasActivatableCharge(uid, component);
|
||||||
|
|
||||||
|
if (!canDraw)
|
||||||
|
Toggle.TryDeactivate(uid);
|
||||||
|
|
||||||
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
||||||
{
|
{
|
||||||
component.CanDraw = canDraw;
|
component.CanDraw = canDraw;
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
|||||||
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
||||||
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
||||||
|
|
||||||
// funny
|
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
|
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
|
||||||
|
// funny
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
|
SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public sealed partial class BorgSystem
|
|||||||
|
|
||||||
if (!TryComp<BorgChassisComponent>(chassis, out var chassisComp) ||
|
if (!TryComp<BorgChassisComponent>(chassis, out var chassisComp) ||
|
||||||
args.Container != chassisComp.ModuleContainer ||
|
args.Container != chassisComp.ModuleContainer ||
|
||||||
!chassisComp.Activated)
|
!Toggle.IsActivated(chassis))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_powerCell.HasDrawCharge(uid))
|
if (!_powerCell.HasDrawCharge(uid))
|
||||||
@@ -143,6 +143,7 @@ public sealed partial class BorgSystem
|
|||||||
var ev = new BorgModuleSelectedEvent(chassis);
|
var ev = new BorgModuleSelectedEvent(chassis);
|
||||||
RaiseLocalEvent(moduleUid, ref ev);
|
RaiseLocalEvent(moduleUid, ref ev);
|
||||||
chassisComp.SelectedModule = moduleUid;
|
chassisComp.SelectedModule = moduleUid;
|
||||||
|
Dirty(chassis, chassisComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -162,6 +163,7 @@ public sealed partial class BorgSystem
|
|||||||
var ev = new BorgModuleUnselectedEvent(chassis);
|
var ev = new BorgModuleUnselectedEvent(chassis);
|
||||||
RaiseLocalEvent(chassisComp.SelectedModule.Value, ref ev);
|
RaiseLocalEvent(chassisComp.SelectedModule.Value, ref ev);
|
||||||
chassisComp.SelectedModule = null;
|
chassisComp.SelectedModule = null;
|
||||||
|
Dirty(chassis, chassisComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args)
|
private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Content.Shared.Alert;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
@@ -73,6 +74,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
SubscribeLocalEvent<BorgChassisComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||||
SubscribeLocalEvent<BorgChassisComponent, GetCharactedDeadIcEvent>(OnGetDeadIC);
|
SubscribeLocalEvent<BorgChassisComponent, GetCharactedDeadIcEvent>(OnGetDeadIC);
|
||||||
|
SubscribeLocalEvent<BorgChassisComponent, ItemToggledEvent>(OnToggled);
|
||||||
|
|
||||||
SubscribeLocalEvent<BorgBrainComponent, MindAddedMessage>(OnBrainMindAdded);
|
SubscribeLocalEvent<BorgBrainComponent, MindAddedMessage>(OnBrainMindAdded);
|
||||||
SubscribeLocalEvent<BorgBrainComponent, PointAttemptEvent>(OnBrainPointAttempt);
|
SubscribeLocalEvent<BorgBrainComponent, PointAttemptEvent>(OnBrainPointAttempt);
|
||||||
@@ -173,11 +175,11 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
if (args.NewMobState == MobState.Alive)
|
if (args.NewMobState == MobState.Alive)
|
||||||
{
|
{
|
||||||
if (_mind.TryGetMind(uid, out _, out _))
|
if (_mind.TryGetMind(uid, out _, out _))
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, true);
|
_powerCell.SetDrawEnabled(uid, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, false);
|
_powerCell.SetDrawEnabled(uid, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,24 +187,10 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
{
|
{
|
||||||
UpdateBatteryAlert((uid, component));
|
UpdateBatteryAlert((uid, component));
|
||||||
|
|
||||||
if (!TryComp<PowerCellDrawComponent>(uid, out var draw))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// if we eject the battery or run out of charge, then disable
|
|
||||||
if (args.Ejected || !_powerCell.HasDrawCharge(uid))
|
|
||||||
{
|
|
||||||
DisableBorgAbilities(uid, component);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we aren't drawing and suddenly get enough power to draw again, reeanble.
|
// if we aren't drawing and suddenly get enough power to draw again, reeanble.
|
||||||
if (_powerCell.HasDrawCharge(uid, draw))
|
if (_powerCell.HasDrawCharge(uid))
|
||||||
{
|
{
|
||||||
// only reenable the powerdraw if a player has the role.
|
Toggle.TryActivate(uid);
|
||||||
if (!draw.Drawing && _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(uid))
|
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, true);
|
|
||||||
|
|
||||||
EnableBorgAbilities(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateUI(uid, component);
|
UpdateUI(uid, component);
|
||||||
@@ -210,7 +198,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
|
|
||||||
private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args)
|
private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args)
|
||||||
{
|
{
|
||||||
DisableBorgAbilities(uid, component);
|
Toggle.TryDeactivate(uid);
|
||||||
UpdateUI(uid, component);
|
UpdateUI(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +207,23 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
args.Dead = true;
|
args.Dead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<BorgChassisComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
if (args.Activated)
|
||||||
|
InstallAllModules(uid, comp);
|
||||||
|
else
|
||||||
|
DisableAllModules(uid, comp);
|
||||||
|
|
||||||
|
// only enable the powerdraw if there is a player in the chassis
|
||||||
|
var drawing = _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(ent);
|
||||||
|
_powerCell.SetDrawEnabled(uid, drawing);
|
||||||
|
|
||||||
|
UpdateUI(uid, comp);
|
||||||
|
|
||||||
|
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args)
|
private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args)
|
||||||
{
|
{
|
||||||
if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container))
|
if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container))
|
||||||
@@ -271,44 +276,14 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
_alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent);
|
_alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Activates the borg, enabling all of its modules.
|
|
||||||
/// </summary>
|
|
||||||
public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component, PowerCellDrawComponent? powerCell = null)
|
|
||||||
{
|
|
||||||
if (component.Activated)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.Activated = true;
|
|
||||||
InstallAllModules(uid, component);
|
|
||||||
Dirty(uid, component);
|
|
||||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deactivates the borg, disabling all of its modules and decreasing its speed.
|
|
||||||
/// </summary>
|
|
||||||
public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component)
|
|
||||||
{
|
|
||||||
if (!component.Activated)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.Activated = false;
|
|
||||||
DisableAllModules(uid, component);
|
|
||||||
Dirty(uid, component);
|
|
||||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Activates a borg when a player occupies it
|
/// Activates a borg when a player occupies it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BorgActivate(EntityUid uid, BorgChassisComponent component)
|
public void BorgActivate(EntityUid uid, BorgChassisComponent component)
|
||||||
{
|
{
|
||||||
Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid);
|
Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid);
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, true);
|
Toggle.TryActivate(uid);
|
||||||
_access.SetAccessEnabled(uid, true);
|
|
||||||
_appearance.SetData(uid, BorgVisuals.HasPlayer, true);
|
_appearance.SetData(uid, BorgVisuals.HasPlayer, true);
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -317,10 +292,8 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
|||||||
public void BorgDeactivate(EntityUid uid, BorgChassisComponent component)
|
public void BorgDeactivate(EntityUid uid, BorgChassisComponent component)
|
||||||
{
|
{
|
||||||
Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid);
|
Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid);
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, false);
|
Toggle.TryDeactivate(uid);
|
||||||
_access.SetAccessEnabled(uid, false);
|
|
||||||
_appearance.SetData(uid, BorgVisuals.HasPlayer, false);
|
_appearance.SetData(uid, BorgVisuals.HasPlayer, false);
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using Content.Server.StationEvents.Events;
|
|
||||||
|
|
||||||
namespace Content.Server.StationEvents.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configuration component for the Space Ninja antag.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, Access(typeof(NinjaSpawnRule))]
|
|
||||||
public sealed partial class NinjaSpawnRuleComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Distance that the ninja spawns from the station's half AABB radius
|
|
||||||
/// </summary>
|
|
||||||
[DataField("spawnDistance")]
|
|
||||||
public float SpawnDistance = 20f;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Server.StationEvents.Events;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.StationEvents.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component for spawning antags in space around a station.
|
||||||
|
/// Requires <c>AntagSelectionComponent</c>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(SpaceSpawnRule))]
|
||||||
|
public sealed partial class SpaceSpawnRuleComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Distance that the entity spawns from the station's half AABB radius
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float SpawnDistance = 20f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Location that was picked.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public MapCoordinates? Coords;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
using Content.Server.Antag;
|
||||||
using Content.Server.GameTicking.Rules.Components;
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
using Content.Server.Ninja.Systems;
|
|
||||||
using Content.Server.Station.Components;
|
using Content.Server.Station.Components;
|
||||||
using Content.Server.StationEvents.Components;
|
using Content.Server.StationEvents.Components;
|
||||||
using Content.Shared.GameTicking.Components;
|
using Content.Shared.GameTicking.Components;
|
||||||
@@ -9,18 +9,28 @@ using Robust.Shared.Map.Components;
|
|||||||
namespace Content.Server.StationEvents.Events;
|
namespace Content.Server.StationEvents.Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event for spawning a Space Ninja mid-game.
|
/// Station event component for spawning this rules antags in space around a station.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class NinjaSpawnRule : StationEventSystem<NinjaSpawnRuleComponent>
|
public sealed class SpaceSpawnRule : StationEventSystem<SpaceSpawnRuleComponent>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Started(uid, comp, gameRule, args);
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SpaceSpawnRuleComponent, AntagSelectLocationEvent>(OnSelectLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Added(EntityUid uid, SpaceSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||||
|
{
|
||||||
|
base.Added(uid, comp, gameRule, args);
|
||||||
|
|
||||||
if (!TryGetRandomStation(out var station))
|
if (!TryGetRandomStation(out var station))
|
||||||
|
{
|
||||||
|
ForceEndSelf(uid, gameRule);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var stationData = Comp<StationDataComponent>(station.Value);
|
var stationData = Comp<StationDataComponent>(station.Value);
|
||||||
|
|
||||||
@@ -28,22 +38,28 @@ public sealed class NinjaSpawnRule : StationEventSystem<NinjaSpawnRuleComponent>
|
|||||||
var gridUid = StationSystem.GetLargestGrid(stationData);
|
var gridUid = StationSystem.GetLargestGrid(stationData);
|
||||||
if (gridUid == null || !TryComp<MapGridComponent>(gridUid, out var grid))
|
if (gridUid == null || !TryComp<MapGridComponent>(gridUid, out var grid))
|
||||||
{
|
{
|
||||||
Sawmill.Warning("Chosen station has no grids, cannot spawn space ninja!");
|
Sawmill.Warning("Chosen station has no grids, cannot pick location for {ToPrettyString(uid):rule}");
|
||||||
|
ForceEndSelf(uid, gameRule);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure out its AABB size and use that as a guide to how far ninja should be
|
// figure out its AABB size and use that as a guide to how far the spawner should be
|
||||||
var size = grid.LocalAABB.Size.Length() / 2;
|
var size = grid.LocalAABB.Size.Length() / 2;
|
||||||
var distance = size + comp.SpawnDistance;
|
var distance = size + comp.SpawnDistance;
|
||||||
var angle = RobustRandom.NextAngle();
|
var angle = RobustRandom.NextAngle();
|
||||||
// position relative to station center
|
// position relative to station center
|
||||||
var location = angle.ToVec() * distance;
|
var location = angle.ToVec() * distance;
|
||||||
|
|
||||||
// create the spawner, the ninja will appear when a ghost has picked the role
|
// create the spawner!
|
||||||
var xform = Transform(gridUid.Value);
|
var xform = Transform(gridUid.Value);
|
||||||
var position = _transform.GetWorldPosition(xform) + location;
|
var position = _transform.GetWorldPosition(xform) + location;
|
||||||
var coords = new MapCoordinates(position, xform.MapID);
|
comp.Coords = new MapCoordinates(position, xform.MapID);
|
||||||
Sawmill.Info($"Creating ninja spawnpoint at {coords}");
|
Sawmill.Info($"Picked location {comp.Coords} for {ToPrettyString(uid):rule}");
|
||||||
Spawn("SpawnPointGhostSpaceNinja", coords);
|
}
|
||||||
|
|
||||||
|
private void OnSelectLocation(Entity<SpaceSpawnRuleComponent> ent, ref AntagSelectLocationEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Coords is {} coords)
|
||||||
|
args.Coordinates.Add(coords);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace Content.Server.Stunnable.Systems
|
|||||||
[Dependency] private readonly RiggableSystem _riggableSystem = default!;
|
[Dependency] private readonly RiggableSystem _riggableSystem = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly BatterySystem _battery = default!;
|
[Dependency] private readonly BatterySystem _battery = default!;
|
||||||
[Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
|
[Dependency] private readonly ItemToggleSystem _itemToggle = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Content.Shared.Weapons.Misc;
|
using Content.Shared.Weapons.Misc;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
@@ -8,6 +9,7 @@ namespace Content.Server.Weapons.Misc;
|
|||||||
public sealed class TetherGunSystem : SharedTetherGunSystem
|
public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly PowerCellSystem _cell = default!;
|
[Dependency] private readonly PowerCellSystem _cell = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -36,12 +38,12 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
|||||||
PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null)
|
PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null)
|
||||||
{
|
{
|
||||||
base.StartTether(gunUid, component, target, user, targetPhysics, targetXform);
|
base.StartTether(gunUid, component, target, user, targetPhysics, targetXform);
|
||||||
_cell.SetPowerCellDrawEnabled(gunUid, true);
|
_toggle.TryActivate(gunUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false)
|
protected override void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false)
|
||||||
{
|
{
|
||||||
base.StopTether(gunUid, component, land, transfer);
|
base.StopTether(gunUid, component, land, transfer);
|
||||||
_cell.SetPowerCellDrawEnabled(gunUid, false);
|
_toggle.TryDeactivate(gunUid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Linq;
|
|||||||
using Content.Server.Salvage;
|
using Content.Server.Salvage;
|
||||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
|
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
|
||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
|
||||||
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
|
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
|
||||||
|
|
||||||
@@ -29,11 +30,11 @@ public sealed class ArtifactMagnetTriggerSystem : EntitySystem
|
|||||||
|
|
||||||
_toActivate.Clear();
|
_toActivate.Clear();
|
||||||
|
|
||||||
//assume that there's more instruments than artifacts
|
//assume that there's more magboots than artifacts
|
||||||
var query = EntityQueryEnumerator<MagbootsComponent, TransformComponent>();
|
var query = EntityQueryEnumerator<MagbootsComponent, TransformComponent, ItemToggleComponent>();
|
||||||
while (query.MoveNext(out _, out var magboot, out var magXform))
|
while (query.MoveNext(out _, out var magboot, out var magXform, out var toggle))
|
||||||
{
|
{
|
||||||
if (!magboot.On)
|
if (!toggle.Activated)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var artiQuery = EntityQueryEnumerator<ArtifactMagnetTriggerComponent, TransformComponent>();
|
var artiQuery = EntityQueryEnumerator<ArtifactMagnetTriggerComponent, TransformComponent>();
|
||||||
|
|||||||
11
Content.Shared/Access/Components/AccessToggleComponent.cs
Normal file
11
Content.Shared/Access/Components/AccessToggleComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.Access.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Access.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles an access provider with <c>ItemToggle</c>.
|
||||||
|
/// Requires <see cref="AccessComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(AccessToggleSystem))]
|
||||||
|
public sealed partial class AccessToggleComponent : Component;
|
||||||
21
Content.Shared/Access/Systems/AccessToggleSystem.cs
Normal file
21
Content.Shared/Access/Systems/AccessToggleSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Access.Systems;
|
||||||
|
|
||||||
|
public sealed class AccessToggleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedAccessSystem _access = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<AccessToggleComponent, ItemToggledEvent>(OnToggled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<AccessToggleComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
_access.SetAccessEnabled(ent, args.Activated);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,15 +10,12 @@ namespace Content.Shared.Beeper.Components;
|
|||||||
/// This is used for an item that beeps based on
|
/// This is used for an item that beeps based on
|
||||||
/// proximity to a specified component.
|
/// proximity to a specified component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Requires <c>ItemToggleComponent</c> to control it.
|
||||||
|
/// </remarks>
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(BeeperSystem)), AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, Access(typeof(BeeperSystem)), AutoGenerateComponentState]
|
||||||
public sealed partial class BeeperComponent : Component
|
public sealed partial class BeeperComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether or not it's on.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public bool Enabled = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much to scale the interval by (< 0 = min, > 1 = max)
|
/// How much to scale the interval by (< 0 = min, > 1 = max)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,7 +53,7 @@ public sealed partial class BeeperComponent : Component
|
|||||||
/// Is the beep muted
|
/// Is the beep muted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
public bool IsMuted = false;
|
public bool IsMuted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sound played when the locator beeps.
|
/// The sound played when the locator beeps.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Content.Shared.Beeper.Components;
|
using Content.Shared.Beeper.Components;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
@@ -11,34 +13,20 @@ namespace Content.Shared.Beeper.Systems;
|
|||||||
public sealed class BeeperSystem : EntitySystem
|
public sealed class BeeperSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly INetManager _net = default!;
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
public override void Initialize()
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<BeeperComponent>();
|
var query = EntityQueryEnumerator<BeeperComponent, ItemToggleComponent>();
|
||||||
while (query.MoveNext(out var uid, out var beeper))
|
while (query.MoveNext(out var uid, out var beeper, out var toggle))
|
||||||
{
|
{
|
||||||
if (!beeper.Enabled)
|
if (toggle.Activated)
|
||||||
continue;
|
|
||||||
RunUpdate_Internal(uid, beeper);
|
RunUpdate_Internal(uid, beeper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEnable(EntityUid owner, bool isEnabled, BeeperComponent? beeper = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(owner, ref beeper) || beeper.Enabled == isEnabled)
|
|
||||||
return;
|
|
||||||
beeper.Enabled = isEnabled;
|
|
||||||
|
|
||||||
RunUpdate_Internal(owner, beeper);
|
|
||||||
Dirty(owner, beeper);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetIntervalScaling(EntityUid owner, BeeperComponent beeper, FixedPoint2 newScaling)
|
public void SetIntervalScaling(EntityUid owner, BeeperComponent beeper, FixedPoint2 newScaling)
|
||||||
{
|
{
|
||||||
newScaling = FixedPoint2.Clamp(newScaling, 0, 1);
|
newScaling = FixedPoint2.Clamp(newScaling, 0, 1);
|
||||||
@@ -70,6 +58,7 @@ public sealed class BeeperSystem : EntitySystem
|
|||||||
if (!Resolve(owner, ref comp))
|
if (!Resolve(owner, ref comp))
|
||||||
return;
|
return;
|
||||||
comp.IsMuted = isMuted;
|
comp.IsMuted = isMuted;
|
||||||
|
Dirty(owner, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateBeepInterval(EntityUid owner, BeeperComponent beeper)
|
private void UpdateBeepInterval(EntityUid owner, BeeperComponent beeper)
|
||||||
@@ -91,19 +80,17 @@ public sealed class BeeperSystem : EntitySystem
|
|||||||
|
|
||||||
private void RunUpdate_Internal(EntityUid owner, BeeperComponent beeper)
|
private void RunUpdate_Internal(EntityUid owner, BeeperComponent beeper)
|
||||||
{
|
{
|
||||||
if (!beeper.Enabled)
|
if (!_toggle.IsActivated(owner))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
UpdateBeepInterval(owner, beeper);
|
UpdateBeepInterval(owner, beeper);
|
||||||
if (beeper.NextBeep >= _timing.CurTime)
|
if (beeper.NextBeep >= _timing.CurTime)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var beepEvent = new BeepPlayedEvent(beeper.IsMuted);
|
var beepEvent = new BeepPlayedEvent(beeper.IsMuted);
|
||||||
RaiseLocalEvent(owner, ref beepEvent);
|
RaiseLocalEvent(owner, ref beepEvent);
|
||||||
if (!beeper.IsMuted && _net.IsServer)
|
if (!beeper.IsMuted && _net.IsServer)
|
||||||
{
|
|
||||||
_audio.PlayPvs(beeper.BeepSound, owner);
|
_audio.PlayPvs(beeper.BeepSound, owner);
|
||||||
}
|
|
||||||
beeper.LastBeepTime = _timing.CurTime;
|
beeper.LastBeepTime = _timing.CurTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.Beeper.Components;
|
using Content.Shared.Beeper.Components;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Pinpointer;
|
using Content.Shared.Pinpointer;
|
||||||
using Content.Shared.PowerCell;
|
|
||||||
using Content.Shared.ProximityDetection;
|
using Content.Shared.ProximityDetection;
|
||||||
using Content.Shared.ProximityDetection.Components;
|
using Content.Shared.ProximityDetection.Components;
|
||||||
using Content.Shared.ProximityDetection.Systems;
|
using Content.Shared.ProximityDetection.Systems;
|
||||||
@@ -9,20 +8,17 @@ using Content.Shared.ProximityDetection.Systems;
|
|||||||
namespace Content.Shared.Beeper.Systems;
|
namespace Content.Shared.Beeper.Systems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This handles logic for implementing proximity beeper as a handheld tool />
|
/// This handles controlling a beeper from proximity detector events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ProximityBeeperSystem : EntitySystem
|
public sealed class ProximityBeeperSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedPowerCellSystem _powerCell = default!;
|
|
||||||
[Dependency] private readonly ProximityDetectionSystem _proximity = default!;
|
[Dependency] private readonly ProximityDetectionSystem _proximity = default!;
|
||||||
[Dependency] private readonly BeeperSystem _beeper = default!;
|
[Dependency] private readonly BeeperSystem _beeper = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<ProximityBeeperComponent, UseInHandEvent>(OnUseInHand);
|
|
||||||
SubscribeLocalEvent<ProximityBeeperComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
|
||||||
SubscribeLocalEvent<ProximityBeeperComponent, NewProximityTargetEvent>(OnNewProximityTarget);
|
SubscribeLocalEvent<ProximityBeeperComponent, NewProximityTargetEvent>(OnNewProximityTarget);
|
||||||
SubscribeLocalEvent<ProximityBeeperComponent, ProximityTargetUpdatedEvent>(OnProximityTargetUpdate);
|
SubscribeLocalEvent<ProximityBeeperComponent, ProximityTargetUpdatedEvent>(OnProximityTargetUpdate);
|
||||||
}
|
}
|
||||||
@@ -33,82 +29,16 @@ public sealed class ProximityBeeperSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
if (args.Target == null)
|
if (args.Target == null)
|
||||||
{
|
{
|
||||||
_beeper.SetEnable(owner, false, beeper);
|
_beeper.SetMute(owner, true, beeper);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_beeper.SetIntervalScaling(owner,args.Distance/args.Detector.Range, beeper);
|
|
||||||
_beeper.SetEnable(owner, true, beeper);
|
_beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Range, beeper);
|
||||||
|
_beeper.SetMute(owner, false, beeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args)
|
private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args)
|
||||||
{
|
{
|
||||||
_beeper.SetEnable(owner, args.Target != null);
|
_beeper.SetMute(owner, args.Target != null);
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUseInHand(EntityUid uid, ProximityBeeperComponent proxBeeper, UseInHandEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled)
|
|
||||||
return;
|
|
||||||
args.Handled = TryToggle(uid, proxBeeper, user: args.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent beeper, ref PowerCellSlotEmptyEvent args)
|
|
||||||
{
|
|
||||||
if (_proximity.GetEnable(uid))
|
|
||||||
TryDisable(uid);
|
|
||||||
}
|
|
||||||
public bool TryEnable(EntityUid owner, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null,
|
|
||||||
PowerCellDrawComponent? draw = null,EntityUid? user = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(owner, ref beeper, ref detector))
|
|
||||||
return false;
|
|
||||||
if (Resolve(owner, ref draw, false) && !_powerCell.HasActivatableCharge(owner, battery: draw, user: user))
|
|
||||||
return false;
|
|
||||||
Enable(owner, beeper, detector, draw);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private void Enable(EntityUid owner, BeeperComponent beeper,
|
|
||||||
ProximityDetectorComponent detector, PowerCellDrawComponent? draw)
|
|
||||||
{
|
|
||||||
_proximity.SetEnable(owner, true, detector);
|
|
||||||
_appearance.SetData(owner, ProximityBeeperVisuals.Enabled, true);
|
|
||||||
_powerCell.SetPowerCellDrawEnabled(owner, true, draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disables the proximity beeper
|
|
||||||
/// </summary>
|
|
||||||
public bool TryDisable(EntityUid owner,BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, PowerCellDrawComponent? draw = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(owner, ref beeper, ref detector))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!detector.Enabled)
|
|
||||||
return false;
|
|
||||||
Disable(owner, beeper, detector, draw);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private void Disable(EntityUid owner, BeeperComponent beeper,
|
|
||||||
ProximityDetectorComponent detector, PowerCellDrawComponent? draw)
|
|
||||||
{
|
|
||||||
_proximity.SetEnable(owner, false, detector);
|
|
||||||
_appearance.SetData(owner, ProximityBeeperVisuals.Enabled, false);
|
|
||||||
_beeper.SetEnable(owner, false, beeper);
|
|
||||||
_powerCell.SetPowerCellDrawEnabled(owner, false, draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// toggles the proximity beeper
|
|
||||||
/// </summary>
|
|
||||||
public bool TryToggle(EntityUid owner, ProximityBeeperComponent? proxBeeper = null, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null,
|
|
||||||
PowerCellDrawComponent? draw = null, EntityUid? user = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(owner, ref proxBeeper, ref beeper, ref detector))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return detector.Enabled
|
|
||||||
? TryDisable(owner, beeper, detector, draw)
|
|
||||||
: TryEnable(owner, beeper, detector, draw,user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,14 @@ namespace Content.Shared.Charges.Systems;
|
|||||||
|
|
||||||
public abstract class SharedChargesSystem : EntitySystem
|
public abstract class SharedChargesSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
protected EntityQuery<LimitedChargesComponent> Query;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
Query = GetEntityQuery<LimitedChargesComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<LimitedChargesComponent, ExaminedEvent>(OnExamine);
|
SubscribeLocalEvent<LimitedChargesComponent, ExaminedEvent>(OnExamine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +34,9 @@ public abstract class SharedChargesSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to add a number of charges. If it over or underflows it will be clamped, wasting the extra charges.
|
/// Tries to add a number of charges. If it over or underflows it will be clamped, wasting the extra charges.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null)
|
public virtual void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp, false))
|
if (!Query.Resolve(uid, ref comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var old = comp.Charges;
|
var old = comp.Charges;
|
||||||
@@ -47,7 +51,7 @@ public abstract class SharedChargesSystem : EntitySystem
|
|||||||
public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null)
|
public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null)
|
||||||
{
|
{
|
||||||
// can't be empty if there are no limited charges
|
// can't be empty if there are no limited charges
|
||||||
if (!Resolve(uid, ref comp, false))
|
if (!Query.Resolve(uid, ref comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return comp.Charges <= 0;
|
return comp.Charges <= 0;
|
||||||
@@ -56,12 +60,26 @@ public abstract class SharedChargesSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses a single charge. Must check IsEmpty beforehand to prevent using with 0 charge.
|
/// Uses a single charge. Must check IsEmpty beforehand to prevent using with 0 charge.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null)
|
public void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null)
|
||||||
{
|
{
|
||||||
if (Resolve(uid, ref comp, false))
|
|
||||||
AddCharges(uid, -1, comp);
|
AddCharges(uid, -1, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks IsEmpty and uses a charge if it isn't empty.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryUseCharge(Entity<LimitedChargesComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Query.Resolve(ent, ref ent.Comp, false))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (IsEmpty(ent, ent.Comp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UseCharge(ent, ent.Comp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the limited charges component and returns true if the number of charges remaining is less than the specified value.
|
/// Gets the limited charges component and returns true if the number of charges remaining is less than the specified value.
|
||||||
/// Will return false if there is no limited charges component.
|
/// Will return false if there is no limited charges component.
|
||||||
@@ -80,7 +98,6 @@ public abstract class SharedChargesSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void UseCharges(EntityUid uid, int chargesUsed, LimitedChargesComponent? comp = null)
|
public virtual void UseCharges(EntityUid uid, int chargesUsed, LimitedChargesComponent? comp = null)
|
||||||
{
|
{
|
||||||
if (Resolve(uid, ref comp, false))
|
|
||||||
AddCharges(uid, -chargesUsed, comp);
|
AddCharges(uid, -chargesUsed, comp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,18 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Clothing;
|
namespace Content.Shared.Clothing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modifies speed when worn and activated.
|
||||||
|
/// Supports <c>ItemToggleComponent</c>.
|
||||||
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem))]
|
[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem))]
|
||||||
public sealed partial class ClothingSpeedModifierComponent : Component
|
public sealed partial class ClothingSpeedModifierComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float WalkModifier = 1.0f;
|
public float WalkModifier = 1.0f;
|
||||||
|
|
||||||
[DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float SprintModifier = 1.0f;
|
public float SprintModifier = 1.0f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is this clothing item currently 'actively' slowing you down?
|
|
||||||
/// e.g. magboots can be turned on and off.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("enabled")] public bool Enabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -25,12 +23,9 @@ public sealed class ClothingSpeedModifierComponentState : ComponentState
|
|||||||
public float WalkModifier;
|
public float WalkModifier;
|
||||||
public float SprintModifier;
|
public float SprintModifier;
|
||||||
|
|
||||||
public bool Enabled;
|
public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier)
|
||||||
|
|
||||||
public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier, bool enabled)
|
|
||||||
{
|
{
|
||||||
WalkModifier = walkModifier;
|
WalkModifier = walkModifier;
|
||||||
SprintModifier = sprintModifier;
|
SprintModifier = sprintModifier;
|
||||||
Enabled = enabled;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Content.Shared.Toggleable;
|
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -15,12 +14,12 @@ namespace Content.Shared.Clothing;
|
|||||||
|
|
||||||
public sealed class ClothingSpeedModifierSystem : EntitySystem
|
public sealed class ClothingSpeedModifierSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!;
|
[Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!;
|
||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
|
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SharedPowerCellSystem _powerCell = default!;
|
[Dependency] private readonly SharedPowerCellSystem _powerCell = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -31,39 +30,12 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, ComponentHandleState>(OnHandleState);
|
SubscribeLocalEvent<ClothingSpeedModifierComponent, ComponentHandleState>(OnHandleState);
|
||||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent>>(OnRefreshMoveSpeed);
|
SubscribeLocalEvent<ClothingSpeedModifierComponent, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent>>(OnRefreshMoveSpeed);
|
||||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, GetVerbsEvent<ExamineVerb>>(OnClothingVerbExamine);
|
SubscribeLocalEvent<ClothingSpeedModifierComponent, GetVerbsEvent<ExamineVerb>>(OnClothingVerbExamine);
|
||||||
|
SubscribeLocalEvent<ClothingSpeedModifierComponent, ItemToggledEvent>(OnToggled);
|
||||||
SubscribeLocalEvent<ToggleClothingSpeedComponent, GetVerbsEvent<ActivationVerb>>(AddToggleVerb);
|
|
||||||
SubscribeLocalEvent<ToggleClothingSpeedComponent, GetItemActionsEvent>(OnGetActions);
|
|
||||||
SubscribeLocalEvent<ToggleClothingSpeedComponent, ToggleClothingSpeedEvent>(OnToggleSpeed);
|
|
||||||
SubscribeLocalEvent<ToggleClothingSpeedComponent, MapInitEvent>(OnMapInit);
|
|
||||||
SubscribeLocalEvent<ToggleClothingSpeedComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API
|
|
||||||
|
|
||||||
public void SetClothingSpeedModifierEnabled(EntityUid uid, bool enabled, ClothingSpeedModifierComponent? component = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (component.Enabled != enabled)
|
|
||||||
{
|
|
||||||
component.Enabled = enabled;
|
|
||||||
Dirty(uid, component);
|
|
||||||
|
|
||||||
// inventory system will automatically hook into the event raised by this and update accordingly
|
|
||||||
if (_container.TryGetContainingContainer(uid, out var container))
|
|
||||||
{
|
|
||||||
_movementSpeed.RefreshMovementSpeedModifiers(container.Owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event handlers
|
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier, component.Enabled);
|
args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentHandleState args)
|
private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentHandleState args)
|
||||||
@@ -71,13 +43,11 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||||||
if (args.Current is not ClothingSpeedModifierComponentState state)
|
if (args.Current is not ClothingSpeedModifierComponentState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var diff = component.Enabled != state.Enabled ||
|
var diff = !MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) ||
|
||||||
!MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) ||
|
|
||||||
!MathHelper.CloseTo(component.WalkModifier, state.WalkModifier);
|
!MathHelper.CloseTo(component.WalkModifier, state.WalkModifier);
|
||||||
|
|
||||||
component.WalkModifier = state.WalkModifier;
|
component.WalkModifier = state.WalkModifier;
|
||||||
component.SprintModifier = state.SprintModifier;
|
component.SprintModifier = state.SprintModifier;
|
||||||
component.Enabled = state.Enabled;
|
|
||||||
|
|
||||||
// Avoid raising the event for the container if nothing changed.
|
// Avoid raising the event for the container if nothing changed.
|
||||||
// We'll still set the values in case they're slightly different but within tolerance.
|
// We'll still set the values in case they're slightly different but within tolerance.
|
||||||
@@ -89,9 +59,7 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args)
|
private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args)
|
||||||
{
|
{
|
||||||
if (!component.Enabled)
|
if (_toggle.IsActivated(uid))
|
||||||
return;
|
|
||||||
|
|
||||||
args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier);
|
args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,60 +110,15 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||||||
_examine.AddDetailedExamineVerb(args, component, msg, Loc.GetString("clothing-speed-examinable-verb-text"), "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png", Loc.GetString("clothing-speed-examinable-verb-message"));
|
_examine.AddDetailedExamineVerb(args, component, msg, Loc.GetString("clothing-speed-examinable-verb-text"), "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png", Loc.GetString("clothing-speed-examinable-verb-message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(Entity<ToggleClothingSpeedComponent> uid, ref MapInitEvent args)
|
private void OnToggled(Entity<ClothingSpeedModifierComponent> ent, ref ItemToggledEvent args)
|
||||||
{
|
{
|
||||||
_actions.AddAction(uid, ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction);
|
// make sentient boots slow or fast too
|
||||||
|
_movementSpeed.RefreshMovementSpeedModifiers(ent);
|
||||||
|
|
||||||
|
if (_container.TryGetContainingContainer(ent.Owner, out var container))
|
||||||
|
{
|
||||||
|
// inventory system will automatically hook into the event raised by this and update accordingly
|
||||||
|
_movementSpeed.RefreshMovementSpeedModifiers(container.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnToggleSpeed(Entity<ToggleClothingSpeedComponent> uid, ref ToggleClothingSpeedEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, args.Performer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetSpeedToggleEnabled(Entity<ToggleClothingSpeedComponent> uid, bool value, EntityUid? user)
|
|
||||||
{
|
|
||||||
if (uid.Comp.Enabled == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TryComp<PowerCellDrawComponent>(uid, out var draw);
|
|
||||||
if (value && !_powerCell.HasDrawCharge(uid, draw, user: user))
|
|
||||||
return;
|
|
||||||
|
|
||||||
uid.Comp.Enabled = value;
|
|
||||||
|
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, uid.Comp.Enabled);
|
|
||||||
_actions.SetToggled(uid.Comp.ToggleActionEntity, uid.Comp.Enabled);
|
|
||||||
_clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid.Owner, uid.Comp.Enabled);
|
|
||||||
_powerCell.SetPowerCellDrawEnabled(uid, uid.Comp.Enabled, draw);
|
|
||||||
Dirty(uid, uid.Comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddToggleVerb(Entity<ToggleClothingSpeedComponent> uid, ref GetVerbsEvent<ActivationVerb> args)
|
|
||||||
{
|
|
||||||
if (!args.CanAccess || !args.CanInteract)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var user = args.User;
|
|
||||||
ActivationVerb verb = new()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("toggle-clothing-verb-text",
|
|
||||||
("entity", Identity.Entity(uid, EntityManager))),
|
|
||||||
Act = () => SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, user)
|
|
||||||
};
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetActions(Entity<ToggleClothingSpeedComponent> uid, ref GetItemActionsEvent args)
|
|
||||||
{
|
|
||||||
args.AddAction(ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPowerCellSlotEmpty(Entity<ToggleClothingSpeedComponent> uid, ref PowerCellSlotEmptyEvent args)
|
|
||||||
{
|
|
||||||
SetSpeedToggleEnabled(uid, false, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds StealthComponent to the user when enabled, either by an action or the system's SetEnabled method.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(StealthClothingSystem))]
|
|
||||||
public sealed partial class StealthClothingComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether stealth effect is enabled.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
|
||||||
public bool Enabled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number added to MinVisibility when stealthed, to make the user not fully invisible.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("visibility"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
|
||||||
public float Visibility;
|
|
||||||
|
|
||||||
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
|
||||||
public string ToggleAction = "ActionTogglePhaseCloak";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The action for enabling and disabling stealth.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField] public EntityUid? ToggleActionEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When stealth is enabled, disables it.
|
|
||||||
/// When it is disabled, raises <see cref="AttemptStealthEvent"/> before enabling.
|
|
||||||
/// Put any checks in a handler for that event to cancel it.
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class ToggleStealthEvent : InstantActionEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clothing that can be enabled and disabled with an action.
|
||||||
|
/// Requires <see cref="ItemToggleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Not to be confused with <see cref="ToggleableClothingComponent"/> for hardsuit helmets and such.
|
||||||
|
/// </remarks>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(ToggleClothingSystem))]
|
||||||
|
public sealed partial class ToggleClothingComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The action to add when equipped, even if not worn.
|
||||||
|
/// This must raise <see cref="ToggleActionEvent"/> to then get handled.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntProtoId<InstantActionComponent> Action = string.Empty;
|
||||||
|
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? ActionEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, automatically disable the clothing after unequipping it.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool DisableOnUnequip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the clothing when being equipped to see if it should add the action.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct ToggleClothingCheckEvent(EntityUid User, bool Cancelled = false);
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is used for a clothing item that gives a speed modification that is toggleable.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem)), AutoGenerateComponentState]
|
|
||||||
public sealed partial class ToggleClothingSpeedComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The action for toggling the clothing.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public EntProtoId ToggleAction = "ActionToggleSpeedBoots";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The action entity
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public EntityUid? ToggleActionEntity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The state of the toggle.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public bool Enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class ToggleClothingSpeedEvent : InstantActionEvent
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Clothing.Components;
|
|
||||||
using Content.Shared.Inventory.Events;
|
|
||||||
using Content.Shared.Stealth;
|
|
||||||
using Content.Shared.Stealth.Components;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing.EntitySystems;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the toggle action and disables stealth when clothing is unequipped.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class StealthClothingSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedStealthSystem _stealth = default!;
|
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<StealthClothingComponent, GetItemActionsEvent>(OnGetItemActions);
|
|
||||||
SubscribeLocalEvent<StealthClothingComponent, ToggleStealthEvent>(OnToggleStealth);
|
|
||||||
SubscribeLocalEvent<StealthClothingComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
|
||||||
SubscribeLocalEvent<StealthClothingComponent, GotUnequippedEvent>(OnUnequipped);
|
|
||||||
SubscribeLocalEvent<StealthClothingComponent, MapInitEvent>(OnMapInit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, StealthClothingComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
_actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
|
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the clothing's stealth effect for the user.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if it was changed, false otherwise</returns>
|
|
||||||
public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthClothingComponent? comp = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref comp) || comp.Enabled == enabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// TODO remove this when clothing unequip on delete is less sus
|
|
||||||
// prevent debug assert when ending round and its disabled
|
|
||||||
if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
comp.Enabled = enabled;
|
|
||||||
Dirty(uid, comp);
|
|
||||||
|
|
||||||
var stealth = EnsureComp<StealthComponent>(user);
|
|
||||||
// slightly visible, but doesn't change when moving so it's ok
|
|
||||||
var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility;
|
|
||||||
_stealth.SetVisibility(user, visibility, stealth);
|
|
||||||
_stealth.SetEnabled(user, enabled, stealth);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raise <see cref="AddStealthActionEvent"/> then add the toggle action if it was not cancelled.
|
|
||||||
/// </summary>
|
|
||||||
private void OnGetItemActions(EntityUid uid, StealthClothingComponent comp, GetItemActionsEvent args)
|
|
||||||
{
|
|
||||||
var ev = new AddStealthActionEvent(args.User);
|
|
||||||
RaiseLocalEvent(uid, ev);
|
|
||||||
if (ev.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raises <see cref="AttemptStealthEvent"/> if enabling.
|
|
||||||
/// </summary>
|
|
||||||
private void OnToggleStealth(EntityUid uid, StealthClothingComponent comp, ToggleStealthEvent args)
|
|
||||||
{
|
|
||||||
args.Handled = true;
|
|
||||||
var user = args.Performer;
|
|
||||||
if (comp.Enabled)
|
|
||||||
{
|
|
||||||
SetEnabled(uid, user, false, comp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ev = new AttemptStealthEvent(user);
|
|
||||||
RaiseLocalEvent(uid, ev);
|
|
||||||
if (ev.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SetEnabled(uid, user, true, comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calls <see cref="SetEnabled"/> when server sends new state.
|
|
||||||
/// </summary>
|
|
||||||
private void OnHandleState(EntityUid uid, StealthClothingComponent comp, ref AfterAutoHandleStateEvent args)
|
|
||||||
{
|
|
||||||
// SetEnabled checks if it is the same, so change it to before state was received from the server
|
|
||||||
var enabled = comp.Enabled;
|
|
||||||
comp.Enabled = !enabled;
|
|
||||||
var user = Transform(uid).ParentUid;
|
|
||||||
SetEnabled(uid, user, enabled, comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Force unstealths the user, doesnt remove StealthComponent since other things might use it
|
|
||||||
/// </summary>
|
|
||||||
private void OnUnequipped(EntityUid uid, StealthClothingComponent comp, GotUnequippedEvent args)
|
|
||||||
{
|
|
||||||
SetEnabled(uid, args.Equipee, false, comp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised on the stealth clothing when attempting to add an action.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class AddStealthActionEvent : CancellableEntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// User that equipped the stealth clothing.
|
|
||||||
/// </summary>
|
|
||||||
public EntityUid User;
|
|
||||||
|
|
||||||
public AddStealthActionEvent(EntityUid user)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised on the stealth clothing when the user is attemping to enable it.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class AttemptStealthEvent : CancellableEntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// User that is attempting to enable the stealth clothing.
|
|
||||||
/// </summary>
|
|
||||||
public EntityUid User;
|
|
||||||
|
|
||||||
public AttemptStealthEvent(EntityUid user)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Clothing;
|
||||||
|
using Content.Shared.Clothing.Components;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Toggleable;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles adding and using a toggle action for <see cref="ToggleClothingComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ToggleClothingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ToggleClothingComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<ToggleClothingComponent, GetItemActionsEvent>(OnGetActions);
|
||||||
|
SubscribeLocalEvent<ToggleClothingComponent, ToggleActionEvent>(OnToggleAction);
|
||||||
|
SubscribeLocalEvent<ToggleClothingComponent, ClothingGotUnequippedEvent>(OnUnequipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(Entity<ToggleClothingComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
// test funny
|
||||||
|
if (string.IsNullOrEmpty(comp.Action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_actions.AddAction(uid, ref comp.ActionEntity, comp.Action);
|
||||||
|
_actions.SetToggled(comp.ActionEntity, _toggle.IsActivated(ent.Owner));
|
||||||
|
Dirty(uid, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetActions(Entity<ToggleClothingComponent> ent, ref GetItemActionsEvent args)
|
||||||
|
{
|
||||||
|
var ev = new ToggleClothingCheckEvent(args.User);
|
||||||
|
RaiseLocalEvent(ent, ref ev);
|
||||||
|
if (!ev.Cancelled)
|
||||||
|
args.AddAction(ent.Comp.ActionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToggleAction(Entity<ToggleClothingComponent> ent, ref ToggleActionEvent args)
|
||||||
|
{
|
||||||
|
args.Handled = _toggle.Toggle(ent.Owner, args.Performer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnequipped(Entity<ToggleClothingComponent> ent, ref ClothingGotUnequippedEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.DisableOnUnequip)
|
||||||
|
_toggle.TryDeactivate(ent.Owner, args.Wearer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,13 @@
|
|||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing;
|
namespace Content.Shared.Clothing;
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent(), AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent]
|
||||||
[Access(typeof(SharedMagbootsSystem))]
|
[Access(typeof(SharedMagbootsSystem))]
|
||||||
public sealed partial class MagbootsComponent : Component
|
public sealed partial class MagbootsComponent : Component
|
||||||
{
|
{
|
||||||
[DataField]
|
|
||||||
public EntProtoId ToggleAction = "ActionToggleMagboots";
|
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public EntityUid? ToggleActionEntity;
|
|
||||||
|
|
||||||
[DataField("on"), AutoNetworkedField]
|
|
||||||
public bool On;
|
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public ProtoId<AlertPrototype> MagbootsAlert = "Magboots";
|
public ProtoId<AlertPrototype> MagbootsAlert = "Magboots";
|
||||||
|
|
||||||
@@ -26,4 +16,10 @@ public sealed partial class MagbootsComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool RequiresGrid = true;
|
public bool RequiresGrid = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Slot the clothing has to be worn in to work.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string Slot = "shoes";
|
||||||
}
|
}
|
||||||
|
|||||||
90
Content.Shared/Clothing/MagbootsSystem.cs
Normal file
90
Content.Shared/Clothing/MagbootsSystem.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
|
using Content.Shared.Gravity;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing;
|
||||||
|
|
||||||
|
public sealed class SharedMagbootsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||||
|
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||||
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
|
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
||||||
|
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<MagbootsComponent, ItemToggledEvent>(OnToggled);
|
||||||
|
SubscribeLocalEvent<MagbootsComponent, ClothingGotEquippedEvent>(OnGotEquipped);
|
||||||
|
SubscribeLocalEvent<MagbootsComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
|
||||||
|
SubscribeLocalEvent<MagbootsComponent, IsWeightlessEvent>(OnIsWeightless);
|
||||||
|
SubscribeLocalEvent<MagbootsComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<MagbootsComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
// only stick to the floor if being worn in the correct slot
|
||||||
|
if (_container.TryGetContainingContainer(uid, out var container) &&
|
||||||
|
_inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn)
|
||||||
|
&& uid == worn)
|
||||||
|
{
|
||||||
|
UpdateMagbootEffects(container.Owner, ent, args.Activated);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = args.Activated ? "on" : null;
|
||||||
|
_item.SetHeldPrefix(ent, prefix);
|
||||||
|
_clothing.SetEquippedPrefix(ent, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGotUnequipped(Entity<MagbootsComponent> ent, ref ClothingGotUnequippedEvent args)
|
||||||
|
{
|
||||||
|
UpdateMagbootEffects(args.Wearer, ent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGotEquipped(Entity<MagbootsComponent> ent, ref ClothingGotEquippedEvent args)
|
||||||
|
{
|
||||||
|
UpdateMagbootEffects(args.Wearer, ent, _toggle.IsActivated(ent.Owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMagbootEffects(EntityUid user, Entity<MagbootsComponent> ent, bool state)
|
||||||
|
{
|
||||||
|
// TODO: public api for this and add access
|
||||||
|
if (TryComp<MovedByPressureComponent>(user, out var moved))
|
||||||
|
moved.Enabled = !state;
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
_alerts.ShowAlert(user, ent.Comp.MagbootsAlert);
|
||||||
|
else
|
||||||
|
_alerts.ClearAlert(user, ent.Comp.MagbootsAlert);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIsWeightless(Entity<MagbootsComponent> ent, ref IsWeightlessEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || !_toggle.IsActivated(ent.Owner))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not cancel weightlessness if the person is in off-grid.
|
||||||
|
if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner))
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.IsWeightless = false;
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIsWeightless(Entity<MagbootsComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args)
|
||||||
|
{
|
||||||
|
OnIsWeightless(ent, ref args.Args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Atmos.Components;
|
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
|
||||||
using Content.Shared.Gravity;
|
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Slippery;
|
|
||||||
using Content.Shared.Toggleable;
|
|
||||||
using Content.Shared.Verbs;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing;
|
|
||||||
|
|
||||||
public sealed class SharedMagbootsSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
|
||||||
[Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!;
|
|
||||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
|
||||||
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
||||||
[Dependency] private readonly SharedActionsSystem _sharedActions = default!;
|
|
||||||
[Dependency] private readonly SharedActionsSystem _actionContainer = default!;
|
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
||||||
[Dependency] private readonly SharedContainerSystem _sharedContainer = default!;
|
|
||||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, GetVerbsEvent<ActivationVerb>>(AddToggleVerb);
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, InventoryRelayedEvent<SlipAttemptEvent>>(OnSlipAttempt);
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, GetItemActionsEvent>(OnGetActions);
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, ToggleMagbootsEvent>(OnToggleMagboots);
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, MapInitEvent>(OnMapInit);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, ClothingGotEquippedEvent>(OnGotEquipped);
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<MagbootsComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, MagbootsComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
_actionContainer.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
|
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGotUnequipped(EntityUid uid, MagbootsComponent component, ref ClothingGotUnequippedEvent args)
|
|
||||||
{
|
|
||||||
UpdateMagbootEffects(args.Wearer, uid, false, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGotEquipped(EntityUid uid, MagbootsComponent component, ref ClothingGotEquippedEvent args)
|
|
||||||
{
|
|
||||||
UpdateMagbootEffects(args.Wearer, uid, true, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnToggleMagboots(EntityUid uid, MagbootsComponent component, ToggleMagbootsEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
|
|
||||||
ToggleMagboots(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleMagboots(EntityUid uid, MagbootsComponent magboots)
|
|
||||||
{
|
|
||||||
magboots.On = !magboots.On;
|
|
||||||
|
|
||||||
if (_sharedContainer.TryGetContainingContainer((uid, Transform(uid)), out var container) &&
|
|
||||||
_inventory.TryGetSlotEntity(container.Owner, "shoes", out var entityUid) && entityUid == uid)
|
|
||||||
{
|
|
||||||
UpdateMagbootEffects(container.Owner, uid, true, magboots);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryComp<ItemComponent>(uid, out var item))
|
|
||||||
{
|
|
||||||
_item.SetHeldPrefix(uid, magboots.On ? "on" : null, component: item);
|
|
||||||
_clothing.SetEquippedPrefix(uid, magboots.On ? "on" : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, magboots.On);
|
|
||||||
OnChanged(uid, magboots);
|
|
||||||
Dirty(uid, magboots);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
return;
|
|
||||||
state = state && component.On;
|
|
||||||
|
|
||||||
if (TryComp(parent, out MovedByPressureComponent? movedByPressure))
|
|
||||||
{
|
|
||||||
movedByPressure.Enabled = !state;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
{
|
|
||||||
_alerts.ShowAlert(parent, component.MagbootsAlert);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_alerts.ClearAlert(parent, component.MagbootsAlert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChanged(EntityUid uid, MagbootsComponent component)
|
|
||||||
{
|
|
||||||
_sharedActions.SetToggled(component.ToggleActionEntity, component.On);
|
|
||||||
_clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid, component.On);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddToggleVerb(EntityUid uid, MagbootsComponent component, GetVerbsEvent<ActivationVerb> args)
|
|
||||||
{
|
|
||||||
if (!args.CanAccess || !args.CanInteract)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActivationVerb verb = new()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("toggle-magboots-verb-get-data-text"),
|
|
||||||
Act = () => ToggleMagboots(uid, component),
|
|
||||||
// TODO VERB ICON add toggle icon? maybe a computer on/off symbol?
|
|
||||||
};
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSlipAttempt(EntityUid uid, MagbootsComponent component, InventoryRelayedEvent<SlipAttemptEvent> args)
|
|
||||||
{
|
|
||||||
if (component.On)
|
|
||||||
args.Args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetActions(EntityUid uid, MagbootsComponent component, GetItemActionsEvent args)
|
|
||||||
{
|
|
||||||
args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnIsWeightless(Entity<MagbootsComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args)
|
|
||||||
{
|
|
||||||
if (args.Args.Handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!ent.Comp.On)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// do not cancel weightlessness if the person is in off-grid.
|
|
||||||
if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner))
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Args.IsWeightless = false;
|
|
||||||
args.Args.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class ToggleMagbootsEvent : InstantActionEvent;
|
|
||||||
26
Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs
Normal file
26
Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Item.ItemToggle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles <see cref="ComponentTogglerComponent"/> component manipulation.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ComponentTogglerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ComponentTogglerComponent, ItemToggledEvent>(OnToggled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<ComponentTogglerComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
var target = ent.Comp.Parent ? Transform(ent).ParentUid : ent.Owner;
|
||||||
|
|
||||||
|
if (args.Activated)
|
||||||
|
EntityManager.AddComponents(target, ent.Comp.Components);
|
||||||
|
else
|
||||||
|
EntityManager.RemoveComponents(target, ent.Comp.RemoveComponents ?? ent.Comp.Components);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Item.ItemToggle.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or removes components when toggled.
|
||||||
|
/// Requires <see cref="ItemToggleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(ComponentTogglerSystem))]
|
||||||
|
public sealed partial class ComponentTogglerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The components to add when activated.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ComponentRegistry Components = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The components to remove when deactivated.
|
||||||
|
/// If this is null <see cref="Components"/> is reused.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ComponentRegistry? RemoveComponents;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, adds components on the entity's parent instead of the entity itself.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Parent;
|
||||||
|
}
|
||||||
@@ -12,12 +12,12 @@ public sealed partial class ItemToggleActiveSoundComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The continuous noise this item makes when it's activated (like an e-sword's hum).
|
/// The continuous noise this item makes when it's activated (like an e-sword's hum).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
[DataField(required: true), AutoNetworkedField]
|
||||||
public SoundSpecifier? ActiveSound;
|
public SoundSpecifier? ActiveSound;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used when the item emits sound while active.
|
/// Used when the item emits sound while active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField]
|
[DataField]
|
||||||
public EntityUid? PlayingStream;
|
public EntityUid? PlayingStream;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Content.Shared.Item.ItemToggle.Components;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If you need extended functionality (e.g. requiring power) then add a new component and use events:
|
/// If you need extended functionality (e.g. requiring power) then add a new component and use events:
|
||||||
/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent or ItemToggleForceToggleEvent.
|
/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent, ItemToggledEvent.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class ItemToggleComponent : Component
|
public sealed partial class ItemToggleComponent : Component
|
||||||
@@ -19,6 +19,13 @@ public sealed partial class ItemToggleComponent : Component
|
|||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public bool Activated = false;
|
public bool Activated = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If this is set to false then the item can't be toggled by pressing Z.
|
||||||
|
/// Use another system to do it then.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool OnUse = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the item's toggle can be predicted by the client.
|
/// Whether the item's toggle can be predicted by the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Item.ItemToggle.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a verb for toggling something, requires <see cref="ItemToggleComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(ToggleVerbSystem))]
|
||||||
|
public sealed partial class ToggleVerbComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Text the verb will have.
|
||||||
|
/// Gets passed "entity" as the entity's identity string.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId Text = string.Empty;
|
||||||
|
}
|
||||||
@@ -15,12 +15,12 @@ namespace Content.Shared.Item.ItemToggle;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If you need extended functionality (e.g. requiring power) then add a new component and use events.
|
/// If you need extended functionality (e.g. requiring power) then add a new component and use events.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public abstract class SharedItemToggleSystem : EntitySystem
|
public sealed class ItemToggleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
||||||
[Dependency] private readonly SharedPointLightSystem _light = default!;
|
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedPointLightSystem _light = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -28,8 +28,9 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<ItemToggleComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<ItemToggleComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<ItemToggleComponent, ItemUnwieldedEvent>(TurnOffonUnwielded);
|
SubscribeLocalEvent<ItemToggleComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<ItemToggleComponent, ItemWieldedEvent>(TurnOnonWielded);
|
SubscribeLocalEvent<ItemToggleComponent, ItemUnwieldedEvent>(TurnOffOnUnwielded);
|
||||||
|
SubscribeLocalEvent<ItemToggleComponent, ItemWieldedEvent>(TurnOnOnWielded);
|
||||||
SubscribeLocalEvent<ItemToggleComponent, UseInHandEvent>(OnUseInHand);
|
SubscribeLocalEvent<ItemToggleComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
|
||||||
SubscribeLocalEvent<ItemToggleHotComponent, IsHotEvent>(OnIsHotEvent);
|
SubscribeLocalEvent<ItemToggleHotComponent, IsHotEvent>(OnIsHotEvent);
|
||||||
@@ -42,57 +43,76 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
|||||||
UpdateVisuals(ent);
|
UpdateVisuals(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args)
|
private void OnMapInit(Entity<ItemToggleComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
if (!ent.Comp.Activated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new ItemToggledEvent(Predicted: ent.Comp.Predictable, Activated: ent.Comp.Activated, User: null);
|
||||||
|
RaiseLocalEvent(ent, ref ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUseInHand(Entity<ItemToggleComponent> ent, ref UseInHandEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || !ent.Comp.OnUse)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
||||||
Toggle(uid, args.User, predicted: itemToggle.Predictable, itemToggle: itemToggle);
|
Toggle((ent, ent.Comp), args.User, predicted: ent.Comp.Predictable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used when an item is attempted to be toggled.
|
/// Used when an item is attempted to be toggled.
|
||||||
|
/// Sets its state to the opposite of what it is.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Toggle(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
|
/// <returns>Same as <see cref="TrySetActive"/></returns>
|
||||||
|
public bool Toggle(Entity<ItemToggleComponent?> ent, EntityUid? user = null, bool predicted = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref itemToggle))
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (itemToggle.Activated)
|
return TrySetActive(ent, !ent.Comp.Activated, user, predicted);
|
||||||
{
|
|
||||||
TryDeactivate(uid, user, itemToggle: itemToggle, predicted: predicted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to set the activated bool from a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>false if the attempt fails for any reason</returns>
|
||||||
|
public bool TrySetActive(Entity<ItemToggleComponent?> ent, bool active, EntityUid? user = null, bool predicted = true)
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
return TryActivate(ent, user, predicted: predicted);
|
||||||
else
|
else
|
||||||
{
|
return TryDeactivate(ent, user, predicted: predicted);
|
||||||
TryActivate(uid, user, itemToggle: itemToggle, predicted: predicted);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used when an item is attempting to be activated. It returns false if the attempt fails any reason, interrupting the activation.
|
/// Used when an item is attempting to be activated. It returns false if the attempt fails any reason, interrupting the activation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
|
public bool TryActivate(Entity<ItemToggleComponent?> ent, EntityUid? user = null, bool predicted = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref itemToggle))
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (itemToggle.Activated)
|
var uid = ent.Owner;
|
||||||
|
var comp = ent.Comp;
|
||||||
|
if (comp.Activated)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!itemToggle.Predictable && _netManager.IsClient)
|
if (!comp.Predictable && _netManager.IsClient)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var attempt = new ItemToggleActivateAttemptEvent(user);
|
var attempt = new ItemToggleActivateAttemptEvent(user);
|
||||||
RaiseLocalEvent(uid, ref attempt);
|
RaiseLocalEvent(uid, ref attempt);
|
||||||
|
|
||||||
|
if (!comp.Predictable) predicted = false;
|
||||||
if (attempt.Cancelled)
|
if (attempt.Cancelled)
|
||||||
{
|
{
|
||||||
if (predicted)
|
if (predicted)
|
||||||
_audio.PlayPredicted(itemToggle.SoundFailToActivate, uid, user);
|
_audio.PlayPredicted(comp.SoundFailToActivate, uid, user);
|
||||||
else
|
else
|
||||||
_audio.PlayPvs(itemToggle.SoundFailToActivate, uid);
|
_audio.PlayPvs(comp.SoundFailToActivate, uid);
|
||||||
|
|
||||||
if (attempt.Popup != null && user != null)
|
if (attempt.Popup != null && user != null)
|
||||||
{
|
{
|
||||||
@@ -105,7 +125,7 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Activate(uid, itemToggle, predicted, user);
|
Activate((uid, comp), predicted, user);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -113,75 +133,65 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used when an item is attempting to be deactivated. It returns false if the attempt fails any reason, interrupting the deactivation.
|
/// Used when an item is attempting to be deactivated. It returns false if the attempt fails any reason, interrupting the deactivation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDeactivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null)
|
public bool TryDeactivate(Entity<ItemToggleComponent?> ent, EntityUid? user = null, bool predicted = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref itemToggle))
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!itemToggle.Predictable && _netManager.IsClient)
|
var uid = ent.Owner;
|
||||||
|
var comp = ent.Comp;
|
||||||
|
if (!comp.Activated)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!itemToggle.Activated)
|
if (!comp.Predictable && _netManager.IsClient)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var attempt = new ItemToggleDeactivateAttemptEvent(user);
|
var attempt = new ItemToggleDeactivateAttemptEvent(user);
|
||||||
RaiseLocalEvent(uid, ref attempt);
|
RaiseLocalEvent(uid, ref attempt);
|
||||||
|
|
||||||
if (attempt.Cancelled)
|
if (attempt.Cancelled)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Deactivate(uid, itemToggle, predicted, user);
|
if (!comp.Predictable) predicted = false;
|
||||||
|
Deactivate((uid, comp), predicted, user);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Activate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null)
|
private void Activate(Entity<ItemToggleComponent> ent, bool predicted, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
// TODO: Fix this hardcoding
|
var (uid, comp) = ent;
|
||||||
TryComp(uid, out AppearanceComponent? appearance);
|
var soundToPlay = comp.SoundActivate;
|
||||||
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, true, appearance);
|
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance);
|
|
||||||
|
|
||||||
if (_light.TryGetLight(uid, out var light))
|
|
||||||
{
|
|
||||||
_light.SetEnabled(uid, true, light);
|
|
||||||
}
|
|
||||||
|
|
||||||
var soundToPlay = itemToggle.SoundActivate;
|
|
||||||
if (predicted)
|
if (predicted)
|
||||||
_audio.PlayPredicted(soundToPlay, uid, user);
|
_audio.PlayPredicted(soundToPlay, uid, user);
|
||||||
else
|
else
|
||||||
_audio.PlayPvs(soundToPlay, uid);
|
_audio.PlayPvs(soundToPlay, uid);
|
||||||
|
|
||||||
// END FIX HARDCODING
|
comp.Activated = true;
|
||||||
|
UpdateVisuals((uid, comp));
|
||||||
|
Dirty(uid, comp);
|
||||||
|
|
||||||
var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user);
|
var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user);
|
||||||
RaiseLocalEvent(uid, ref toggleUsed);
|
RaiseLocalEvent(uid, ref toggleUsed);
|
||||||
|
|
||||||
itemToggle.Activated = true;
|
|
||||||
UpdateVisuals((uid, itemToggle));
|
|
||||||
Dirty(uid, itemToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to make the actual changes to the item's components on deactivation.
|
/// Used to make the actual changes to the item's components on deactivation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null)
|
private void Deactivate(Entity<ItemToggleComponent> ent, bool predicted, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
var soundToPlay = itemToggle.SoundDeactivate;
|
var (uid, comp) = ent;
|
||||||
|
var soundToPlay = comp.SoundDeactivate;
|
||||||
if (predicted)
|
if (predicted)
|
||||||
_audio.PlayPredicted(soundToPlay, uid, user);
|
_audio.PlayPredicted(soundToPlay, uid, user);
|
||||||
else
|
else
|
||||||
_audio.PlayPvs(soundToPlay, uid);
|
_audio.PlayPvs(soundToPlay, uid);
|
||||||
// END FIX HARDCODING
|
|
||||||
|
comp.Activated = false;
|
||||||
|
UpdateVisuals((uid, comp));
|
||||||
|
Dirty(uid, comp);
|
||||||
|
|
||||||
var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user);
|
var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user);
|
||||||
RaiseLocalEvent(uid, ref toggleUsed);
|
RaiseLocalEvent(uid, ref toggleUsed);
|
||||||
|
|
||||||
itemToggle.Activated = false;
|
|
||||||
UpdateVisuals((uid, itemToggle));
|
|
||||||
Dirty(uid, itemToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateVisuals(Entity<ItemToggleComponent> ent)
|
private void UpdateVisuals(Entity<ItemToggleComponent> ent)
|
||||||
@@ -204,55 +214,56 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for items that require to be wielded in both hands to activate. For instance the dual energy sword will turn off if not wielded.
|
/// Used for items that require to be wielded in both hands to activate. For instance the dual energy sword will turn off if not wielded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void TurnOffonUnwielded(EntityUid uid, ItemToggleComponent itemToggle, ItemUnwieldedEvent args)
|
private void TurnOffOnUnwielded(Entity<ItemToggleComponent> ent, ref ItemUnwieldedEvent args)
|
||||||
{
|
{
|
||||||
if (itemToggle.Activated)
|
TryDeactivate((ent, ent.Comp), args.User);
|
||||||
TryDeactivate(uid, args.User, itemToggle: itemToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wieldable items will automatically turn on when wielded.
|
/// Wieldable items will automatically turn on when wielded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void TurnOnonWielded(EntityUid uid, ItemToggleComponent itemToggle, ref ItemWieldedEvent args)
|
private void TurnOnOnWielded(Entity<ItemToggleComponent> ent, ref ItemWieldedEvent args)
|
||||||
{
|
{
|
||||||
if (!itemToggle.Activated)
|
// FIXME: for some reason both client and server play sound
|
||||||
TryActivate(uid, itemToggle: itemToggle);
|
TryActivate((ent, ent.Comp));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsActivated(EntityUid uid, ItemToggleComponent? comp = null)
|
public bool IsActivated(Entity<ItemToggleComponent?> ent)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return true; // assume always activated if no component
|
return true; // assume always activated if no component
|
||||||
|
|
||||||
return comp.Activated;
|
return ent.Comp.Activated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to make the item hot when activated.
|
/// Used to make the item hot when activated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args)
|
private void OnIsHotEvent(Entity<ItemToggleHotComponent> ent, ref IsHotEvent args)
|
||||||
{
|
{
|
||||||
args.IsHot |= IsActivated(uid);
|
args.IsHot |= IsActivated(ent.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to update the looping active sound linked to the entity.
|
/// Used to update the looping active sound linked to the entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggledEvent args)
|
private void UpdateActiveSound(Entity<ItemToggleActiveSoundComponent> ent, ref ItemToggledEvent args)
|
||||||
{
|
{
|
||||||
if (args.Activated)
|
var (uid, comp) = ent;
|
||||||
|
if (!args.Activated)
|
||||||
{
|
{
|
||||||
if (activeSound.ActiveSound != null && activeSound.PlayingStream == null)
|
comp.PlayingStream = _audio.Stop(comp.PlayingStream);
|
||||||
{
|
return;
|
||||||
if (args.Predicted)
|
|
||||||
activeSound.PlayingStream = _audio.PlayPredicted(activeSound.ActiveSound, uid, args.User, AudioParams.Default.WithLoop(true)).Value.Entity;
|
|
||||||
else
|
|
||||||
activeSound.PlayingStream = _audio.PlayPvs(activeSound.ActiveSound, uid, AudioParams.Default.WithLoop(true)).Value.Entity;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (comp.ActiveSound != null && comp.PlayingStream == null)
|
||||||
{
|
{
|
||||||
activeSound.PlayingStream = _audio.Stop(activeSound.PlayingStream);
|
var loop = AudioParams.Default.WithLoop(true);
|
||||||
|
var stream = args.Predicted
|
||||||
|
? _audio.PlayPredicted(comp.ActiveSound, uid, args.User, loop)
|
||||||
|
: _audio.PlayPvs(comp.ActiveSound, uid, loop);
|
||||||
|
if (stream?.Entity is {} entity)
|
||||||
|
comp.PlayingStream = entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
34
Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs
Normal file
34
Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
|
||||||
|
namespace Content.Shared.Item.ItemToggle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a verb for toggling something with <see cref="ToggleVerbComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ToggleVerbSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ToggleVerbComponent, GetVerbsEvent<ActivationVerb>>(OnGetVerbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetVerbs(Entity<ToggleVerbComponent> ent, ref GetVerbsEvent<ActivationVerb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanAccess || !args.CanInteract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var name = Identity.Entity(ent, EntityManager);
|
||||||
|
var user = args.User;
|
||||||
|
args.Verbs.Add(new ActivationVerb()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString(ent.Comp.Text, ("entity", name)),
|
||||||
|
Act = () => _toggle.Toggle(ent.Owner, user)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,16 +10,11 @@ namespace Content.Shared.Medical;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is used for defibrillators; a machine that shocks a dead
|
/// This is used for defibrillators; a machine that shocks a dead
|
||||||
/// person back into the world of the living.
|
/// person back into the world of the living.
|
||||||
|
/// Uses <c>ItemToggleComponent</c>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
|
||||||
public sealed partial class DefibrillatorComponent : Component
|
public sealed partial class DefibrillatorComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether or not it's turned on and able to be used.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("enabled"), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Enabled;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which the zap cooldown will be completed
|
/// The time at which the zap cooldown will be completed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -72,15 +67,6 @@ public sealed partial class DefibrillatorComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite), DataField("zapSound")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("zapSound")]
|
||||||
public SoundSpecifier? ZapSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_zap.ogg");
|
public SoundSpecifier? ZapSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_zap.ogg");
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sound when the defib is powered on.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("powerOnSound")]
|
|
||||||
public SoundSpecifier? PowerOnSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_on.ogg");
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("powerOffSound")]
|
|
||||||
public SoundSpecifier? PowerOffSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_off.ogg");
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("chargeSound")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("chargeSound")]
|
||||||
public SoundSpecifier? ChargeSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_charge.ogg");
|
public SoundSpecifier? ChargeSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_charge.ogg");
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
@@ -7,32 +8,33 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Component for draining power from APCs/substations/SMESes, when ProviderUid is set to a battery cell.
|
/// Component for draining power from APCs/substations/SMESes, when ProviderUid is set to a battery cell.
|
||||||
/// Does not rely on relay, simply being on the user and having BatteryUid set is enough.
|
/// Does not rely on relay, simply being on the user and having BatteryUid set is enough.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, Access(typeof(SharedBatteryDrainerSystem))]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedBatteryDrainerSystem))]
|
||||||
public sealed partial class BatteryDrainerComponent : Component
|
public sealed partial class BatteryDrainerComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The powercell entity to drain power into.
|
/// The powercell entity to drain power into.
|
||||||
/// Determines whether draining is possible.
|
/// Determines whether draining is possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("batteryUid"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? BatteryUid;
|
public EntityUid? BatteryUid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Conversion rate between joules in a device and joules added to battery.
|
/// Conversion rate between joules in a device and joules added to battery.
|
||||||
/// Should be very low since powercells store nothing compared to even an APC.
|
/// Should be very low since powercells store nothing compared to even an APC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("drainEfficiency"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float DrainEfficiency = 0.001f;
|
public float DrainEfficiency = 0.001f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time that the do after takes to drain charge from a battery, in seconds
|
/// Time that the do after takes to drain charge from a battery, in seconds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("drainTime"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float DrainTime = 1f;
|
public float DrainTime = 1f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound played after the doafter ends.
|
/// Sound played after the doafter ends.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("sparkSound")]
|
[DataField]
|
||||||
public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks");
|
public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,4 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Makes this warp point a valid bombing target for ninja's spider charge.
|
/// Makes this warp point a valid bombing target for ninja's spider charge.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed partial class BombingTargetComponent : Component
|
public sealed partial class BombingTargetComponent : Component;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds an action to dash, teleport to clicked position, when this item is held.
|
/// Adds an action to dash, teleport to clicked position, when this item is held.
|
||||||
|
/// Cancel <see cref="CheckDashEvent"/> to prevent using it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(DashAbilitySystem)), AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, Access(typeof(DashAbilitySystem)), AutoGenerateComponentState]
|
||||||
public sealed partial class DashAbilityComponent : Component
|
public sealed partial class DashAbilityComponent : Component
|
||||||
@@ -16,19 +17,10 @@ public sealed partial class DashAbilityComponent : Component
|
|||||||
/// The action id for dashing.
|
/// The action id for dashing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public EntProtoId DashAction = "ActionEnergyKatanaDash";
|
public EntProtoId<WorldTargetActionComponent> DashAction = "ActionEnergyKatanaDash";
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? DashActionEntity;
|
public EntityUid? DashActionEntity;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sound played when using dash action.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("blinkSound"), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public SoundSpecifier BlinkSound = new SoundPathSpecifier("/Audio/Magic/blink.ogg")
|
|
||||||
{
|
|
||||||
Params = AudioParams.Default.WithVolume(5f)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class DashEvent : WorldTargetActionEvent { }
|
public sealed partial class DashEvent : WorldTargetActionEvent;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using Content.Shared.Ninja.Systems;
|
|||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
@@ -10,19 +10,18 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Component for emagging things on click.
|
/// Component for emagging things on click.
|
||||||
/// No charges but checks against a whitelist.
|
/// No charges but checks against a whitelist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, Access(typeof(EmagProviderSystem))]
|
||||||
[Access(typeof(EmagProviderSystem))]
|
|
||||||
public sealed partial class EmagProviderComponent : Component
|
public sealed partial class EmagProviderComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tag that marks an entity as immune to emagging.
|
/// The tag that marks an entity as immune to emagging.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("emagImmuneTag", customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
|
[DataField]
|
||||||
public string EmagImmuneTag = "EmagImmune";
|
public ProtoId<TagPrototype> EmagImmuneTag = "EmagImmune";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whitelist that entities must be on to work.
|
/// Whitelist that entities must be on to work.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("whitelist"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField]
|
||||||
public EntityWhitelist? Whitelist = null;
|
public EntityWhitelist? Whitelist;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,4 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Requires a ninja with a suit for abilities to work.
|
/// Requires a ninja with a suit for abilities to work.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class EnergyKatanaComponent : Component
|
public sealed partial class EnergyKatanaComponent : Component;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
52
Content.Shared/Ninja/Components/ItemCreatorComponent.cs
Normal file
52
Content.Shared/Ninja/Components/ItemCreatorComponent.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses battery charge to spawn an item and place it in the user's hands.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedItemCreatorSystem))]
|
||||||
|
public sealed partial class ItemCreatorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The battery entity to use charge from
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? Battery;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The action id for creating an item.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntProtoId<InstantActionComponent> Action = string.Empty;
|
||||||
|
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? ActionEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Battery charge used to create an item.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public float Charge = 14.4f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Item to create with the action
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntProtoId SpawnedPrototype = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Popup shown to the user when there isn't enough power to create an item.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId NoPowerPopup = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Action event to use an <see cref="ItemCreator"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class CreateItemEvent : InstantActionEvent;
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
using Content.Shared.DoAfter;
|
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Toggleable;
|
using Content.Shared.Objectives.Components;
|
||||||
using Content.Shared.Whitelist;
|
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component for toggling glove powers.
|
/// Component for toggling glove powers.
|
||||||
/// Powers being enabled is controlled by User not being null.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Requires <c>ItemToggleComponent</c>.
|
||||||
|
/// </remarks>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[Access(typeof(SharedNinjaGlovesSystem))]
|
[Access(typeof(SharedNinjaGlovesSystem))]
|
||||||
public sealed partial class NinjaGlovesComponent : Component
|
public sealed partial class NinjaGlovesComponent : Component
|
||||||
@@ -22,24 +19,33 @@ public sealed partial class NinjaGlovesComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entity of the ninja using these gloves, usually means enabled
|
/// Entity of the ninja using these gloves, usually means enabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("user"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? User;
|
public EntityUid? User;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action id for toggling ninja gloves abilities
|
/// Abilities to give to the user when enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField(required: true)]
|
||||||
public string ToggleAction = "ActionToggleNinjaGloves";
|
public List<NinjaGloveAbility> Abilities = new();
|
||||||
|
}
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
/// <summary>
|
||||||
public EntityUid? ToggleActionEntity;
|
/// An ability that adds components to the user when the gloves are enabled.
|
||||||
|
/// </summary>
|
||||||
|
[DataRecord]
|
||||||
|
public record struct NinjaGloveAbility()
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If not null, checks if an objective with this prototype has been completed.
|
||||||
|
/// If it has, the ability components are skipped to prevent doing the objective twice.
|
||||||
|
/// The objective must have <c>CodeConditionComponent</c> to be checked.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId<ObjectiveComponent>? Objective;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The whitelist used for the emag provider to emag airlocks only (not regular doors).
|
/// Components to add and remove.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("doorjackWhitelist")]
|
[DataField(required: true)]
|
||||||
public EntityWhitelist DoorjackWhitelist = new()
|
public ComponentRegistry Components = new();
|
||||||
{
|
|
||||||
Components = new[] {"Airlock"}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ using Content.Shared.Ninja.Systems;
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
@@ -14,68 +11,27 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Component for ninja suit abilities and power consumption.
|
/// Component for ninja suit abilities and power consumption.
|
||||||
/// As an implementation detail, dashing with katana is a suit action which isn't ideal.
|
/// As an implementation detail, dashing with katana is a suit action which isn't ideal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedNinjaSuitSystem)), AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[AutoGenerateComponentPause]
|
[Access(typeof(SharedNinjaSuitSystem))]
|
||||||
public sealed partial class NinjaSuitComponent : Component
|
public sealed partial class NinjaSuitComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Battery charge used passively, in watts. Will last 1000 seconds on a small-capacity power cell.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("passiveWattage")]
|
|
||||||
public float PassiveWattage = 0.36f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Battery charge used while cloaked, stacks with passive. Will last 200 seconds while cloaked on a small-capacity power cell.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("cloakWattage")]
|
|
||||||
public float CloakWattage = 1.44f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound played when a ninja is hit while cloaked.
|
/// Sound played when a ninja is hit while cloaked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("revealSound")]
|
[DataField]
|
||||||
public SoundSpecifier RevealSound = new SoundPathSpecifier("/Audio/Effects/chime.ogg");
|
public SoundSpecifier RevealSound = new SoundPathSpecifier("/Audio/Effects/chime.ogg");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long to disable all abilities when revealed.
|
/// ID of the use delay to disable all ninja abilities.
|
||||||
/// Normally, ninjas are revealed when attacking or getting damaged.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("disableTime")]
|
[DataField]
|
||||||
public TimeSpan DisableTime = TimeSpan.FromSeconds(5);
|
public string DisableDelayId = "suit_powers";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Time at which we will be able to use our abilities again
|
|
||||||
/// </summary>
|
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
|
||||||
[AutoPausedField]
|
|
||||||
public TimeSpan DisableCooldown;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The action id for creating throwing stars.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("createThrowingStarAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
|
||||||
public string CreateThrowingStarAction = "ActionCreateThrowingStar";
|
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public EntityUid? CreateThrowingStarActionEntity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Battery charge used to create a throwing star. Can do it 25 times on a small-capacity power cell.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("throwingStarCharge")]
|
|
||||||
public float ThrowingStarCharge = 14.4f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Throwing star item to create with the action
|
|
||||||
/// </summary>
|
|
||||||
[DataField("throwingStarPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
|
||||||
public string ThrowingStarPrototype = "ThrowingStarNinja";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action id for recalling a bound energy katana
|
/// The action id for recalling a bound energy katana
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("recallKatanaAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string RecallKatanaAction = "ActionRecallKatana";
|
public EntProtoId RecallKatanaAction = "ActionRecallKatana";
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? RecallKatanaActionEntity;
|
public EntityUid? RecallKatanaActionEntity;
|
||||||
@@ -84,14 +40,14 @@ public sealed partial class NinjaSuitComponent : Component
|
|||||||
/// Battery charge used per tile the katana teleported.
|
/// Battery charge used per tile the katana teleported.
|
||||||
/// Uses 1% of a default battery per tile.
|
/// Uses 1% of a default battery per tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("recallCharge")]
|
[DataField]
|
||||||
public float RecallCharge = 3.6f;
|
public float RecallCharge = 3.6f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action id for creating an EMP burst
|
/// The action id for creating an EMP burst
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("empAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string EmpAction = "ActionNinjaEmp";
|
public EntProtoId EmpAction = "ActionNinjaEmp";
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? EmpActionEntity;
|
public EntityUid? EmpActionEntity;
|
||||||
@@ -99,36 +55,29 @@ public sealed partial class NinjaSuitComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Battery charge used to create an EMP burst. Can do it 2 times on a small-capacity power cell.
|
/// Battery charge used to create an EMP burst. Can do it 2 times on a small-capacity power cell.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("empCharge")]
|
[DataField]
|
||||||
public float EmpCharge = 180f;
|
public float EmpCharge = 180f;
|
||||||
|
|
||||||
|
// TODO: EmpOnTrigger bruh
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Range of the EMP in tiles.
|
/// Range of the EMP in tiles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("empRange")]
|
[DataField]
|
||||||
public float EmpRange = 6f;
|
public float EmpRange = 6f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Power consumed from batteries by the EMP
|
/// Power consumed from batteries by the EMP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("empConsumption")]
|
[DataField]
|
||||||
public float EmpConsumption = 100000f;
|
public float EmpConsumption = 100000f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long the EMP effects last for, in seconds
|
/// How long the EMP effects last for, in seconds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("empDuration")]
|
[DataField]
|
||||||
public float EmpDuration = 60f;
|
public float EmpDuration = 60f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class CreateThrowingStarEvent : InstantActionEvent
|
public sealed partial class RecallKatanaEvent : InstantActionEvent;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class RecallKatanaEvent : InstantActionEvent
|
public sealed partial class NinjaEmpEvent : InstantActionEvent;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class NinjaEmpEvent : InstantActionEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,34 +7,28 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component placed on a mob to make it a space ninja, able to use suit and glove powers.
|
/// Component placed on a mob to make it a space ninja, able to use suit and glove powers.
|
||||||
/// Contains ids of all ninja equipment and the game rule.
|
/// Contains ids of all ninja equipment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[Access(typeof(SharedSpaceNinjaSystem))]
|
[Access(typeof(SharedSpaceNinjaSystem))]
|
||||||
public sealed partial class SpaceNinjaComponent : Component
|
public sealed partial class SpaceNinjaComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The ninja game rule that spawned this ninja.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("rule")]
|
|
||||||
public EntityUid? Rule;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Currently worn suit
|
/// Currently worn suit
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("suit"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? Suit;
|
public EntityUid? Suit;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Currently worn gloves
|
/// Currently worn gloves, if enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("gloves"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? Gloves;
|
public EntityUid? Gloves;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bound katana, set once picked up and never removed
|
/// Bound katana, set once picked up and never removed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("katana"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? Katana;
|
public EntityUid? Katana;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,6 +49,9 @@ public sealed partial class SpaceNinjaComponent : Component
|
|||||||
[DataField]
|
[DataField]
|
||||||
public EntProtoId SpiderChargeObjective = "SpiderChargeObjective";
|
public EntProtoId SpiderChargeObjective = "SpiderChargeObjective";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Alert to show for suit power.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public ProtoId<AlertPrototype> SuitPowerAlert = "SuitPower";
|
public ProtoId<AlertPrototype> SuitPowerAlert = "SuitPower";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
@@ -6,14 +7,14 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Component for the Space Ninja's unique Spider Charge.
|
/// Component for the Space Ninja's unique Spider Charge.
|
||||||
/// Only this component detonating can trigger the ninja's objective.
|
/// Only this component detonating can trigger the ninja's objective.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSpiderChargeSystem))]
|
||||||
public sealed partial class SpiderChargeComponent : Component
|
public sealed partial class SpiderChargeComponent : Component
|
||||||
{
|
{
|
||||||
/// Range for planting within the target area
|
/// Range for planting within the target area
|
||||||
[DataField("range")]
|
[DataField]
|
||||||
public float Range = 10f;
|
public float Range = 10f;
|
||||||
|
|
||||||
/// The ninja that planted this charge
|
/// The ninja that planted this charge
|
||||||
[DataField("planter")]
|
[DataField]
|
||||||
public EntityUid? Planter = null;
|
public EntityUid? Planter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Content.Shared.Ninja.Systems;
|
|||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
@@ -11,32 +10,33 @@ namespace Content.Shared.Ninja.Components;
|
|||||||
/// Component for stunning mobs on click outside of harm mode.
|
/// Component for stunning mobs on click outside of harm mode.
|
||||||
/// Knocks them down for a bit and deals shock damage.
|
/// Knocks them down for a bit and deals shock damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStunProviderSystem))]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedStunProviderSystem))]
|
||||||
public sealed partial class StunProviderComponent : Component
|
public sealed partial class StunProviderComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The powercell entity to take power from.
|
/// The powercell entity to take power from.
|
||||||
/// Determines whether stunning is possible.
|
/// Determines whether stunning is possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? BatteryUid;
|
public EntityUid? BatteryUid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound played when stunning someone.
|
/// Sound played when stunning someone.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public SoundSpecifier Sound = new SoundCollectionSpecifier("sparks");
|
public SoundSpecifier Sound = new SoundCollectionSpecifier("sparks");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Joules required in the battery to stun someone. Defaults to 10 uses on a small battery.
|
/// Joules required in the battery to stun someone. Defaults to 10 uses on a small battery.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float StunCharge = 36f;
|
public float StunCharge = 36f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Damage dealt when stunning someone
|
/// Damage dealt when stunning someone
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public DamageSpecifier StunDamage = new()
|
public DamageSpecifier StunDamage = new()
|
||||||
{
|
{
|
||||||
DamageDict = new()
|
DamageDict = new()
|
||||||
@@ -48,34 +48,30 @@ public sealed partial class StunProviderComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time that someone is stunned for, stacks if done multiple times.
|
/// Time that someone is stunned for, stacks if done multiple times.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public TimeSpan StunTime = TimeSpan.FromSeconds(5);
|
public TimeSpan StunTime = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long stunning is disabled after stunning something.
|
/// How long stunning is disabled after stunning something.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(2);
|
public TimeSpan Cooldown = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID of the cooldown use delay.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string DelayId = "stun_cooldown";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Locale string to popup when there is no power
|
/// Locale string to popup when there is no power
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
|
[DataField(required: true)]
|
||||||
public string NoPowerPopup = string.Empty;
|
public LocId NoPowerPopup = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whitelist for what counts as a mob.
|
/// Whitelist for what counts as a mob.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField(required: true)]
|
||||||
public EntityWhitelist Whitelist = new()
|
public EntityWhitelist Whitelist = new();
|
||||||
{
|
|
||||||
Components = new[] {"Stamina"}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When someone can next be stunned.
|
|
||||||
/// Essentially a UseDelay unique to this component.
|
|
||||||
/// </summary>
|
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan NextStun = TimeSpan.Zero;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Content.Shared.Ninja.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class DashAbilitySystem : EntitySystem
|
public sealed class DashAbilitySystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||||
@@ -23,48 +24,40 @@ public sealed class DashAbilitySystem : EntitySystem
|
|||||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DashAbilityComponent, GetItemActionsEvent>(OnGetItemActions);
|
SubscribeLocalEvent<DashAbilityComponent, GetItemActionsEvent>(OnGetActions);
|
||||||
SubscribeLocalEvent<DashAbilityComponent, DashEvent>(OnDash);
|
SubscribeLocalEvent<DashAbilityComponent, DashEvent>(OnDash);
|
||||||
SubscribeLocalEvent<DashAbilityComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<DashAbilityComponent, MapInitEvent>(OnMapInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, DashAbilityComponent component, MapInitEvent args)
|
private void OnMapInit(Entity<DashAbilityComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
_actionContainer.EnsureAction(uid, ref component.DashActionEntity, component.DashAction);
|
var (uid, comp) = ent;
|
||||||
Dirty(uid, component);
|
_actionContainer.EnsureAction(uid, ref comp.DashActionEntity, comp.DashAction);
|
||||||
|
Dirty(uid, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetItemActions(EntityUid uid, DashAbilityComponent comp, GetItemActionsEvent args)
|
private void OnGetActions(Entity<DashAbilityComponent> ent, ref GetItemActionsEvent args)
|
||||||
{
|
{
|
||||||
var ev = new AddDashActionEvent(args.User);
|
if (CheckDash(ent, args.User))
|
||||||
RaiseLocalEvent(uid, ev);
|
args.AddAction(ent.Comp.DashActionEntity);
|
||||||
|
|
||||||
if (ev.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.AddAction(ref comp.DashActionEntity, comp.DashAction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle charges and teleport to a visible location.
|
/// Handle charges and teleport to a visible location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args)
|
private void OnDash(Entity<DashAbilityComponent> ent, ref DashEvent args)
|
||||||
{
|
{
|
||||||
if (!_timing.IsFirstTimePredicted)
|
if (!_timing.IsFirstTimePredicted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var (uid, comp) = ent;
|
||||||
var user = args.Performer;
|
var user = args.Performer;
|
||||||
args.Handled = true;
|
if (!CheckDash(uid, user))
|
||||||
|
|
||||||
var ev = new DashAttemptEvent(user);
|
|
||||||
RaiseLocalEvent(uid, ev);
|
|
||||||
if (ev.Cancelled)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_hands.IsHolding(user, uid, out var _))
|
if (!_hands.IsHolding(user, uid, out var _))
|
||||||
@@ -73,15 +66,8 @@ public sealed class DashAbilitySystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TryComp<LimitedChargesComponent>(uid, out var charges);
|
|
||||||
if (_charges.IsEmpty(uid, charges))
|
|
||||||
{
|
|
||||||
_popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var origin = _transform.GetMapCoordinates(user);
|
var origin = _transform.GetMapCoordinates(user);
|
||||||
var target = args.Target.ToMap(EntityManager, _transform);
|
var target = args.Target.ToMap(EntityManager, _transform);
|
||||||
// prevent collision with the user duh
|
|
||||||
if (!_examine.InRangeUnOccluded(origin, target, SharedInteractionSystem.MaxRaycastRange, null))
|
if (!_examine.InRangeUnOccluded(origin, target, SharedInteractionSystem.MaxRaycastRange, null))
|
||||||
{
|
{
|
||||||
// can only dash if the destination is visible on screen
|
// can only dash if the destination is visible on screen
|
||||||
@@ -89,36 +75,28 @@ public sealed class DashAbilitySystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_transform.SetCoordinates(user, args.Target);
|
if (!_charges.TryUseCharge(uid))
|
||||||
_transform.AttachToGridOrMap(user);
|
{
|
||||||
_audio.PlayPredicted(comp.BlinkSound, user, user);
|
_popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user);
|
||||||
if (charges != null)
|
return;
|
||||||
_charges.UseCharge(uid, charges);
|
}
|
||||||
|
|
||||||
|
var xform = Transform(user);
|
||||||
|
_transform.SetCoordinates(user, xform, args.Target);
|
||||||
|
_transform.AttachToGridOrMap(user, xform);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckDash(EntityUid uid, EntityUid user)
|
||||||
|
{
|
||||||
|
var ev = new CheckDashEvent(user);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
return !ev.Cancelled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised on the item before adding the dash action
|
/// Raised on the item before adding the dash action and when using the action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddDashActionEvent : CancellableEntityEventArgs
|
[ByRefEvent]
|
||||||
{
|
public record struct CheckDashEvent(EntityUid User, bool Cancelled = false);
|
||||||
public EntityUid User;
|
|
||||||
|
|
||||||
public AddDashActionEvent(EntityUid user)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised on the item before dashing is done.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class DashAttemptEvent : CancellableEntityEventArgs
|
|
||||||
{
|
|
||||||
public EntityUid User;
|
|
||||||
|
|
||||||
public DashAttemptEvent(EntityUid user)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Emag.Systems;
|
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Emag.Systems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
@@ -14,10 +14,10 @@ namespace Content.Shared.Ninja.Systems;
|
|||||||
public sealed class EmagProviderSystem : EntitySystem
|
public sealed class EmagProviderSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EmagSystem _emag = default!;
|
[Dependency] private readonly EmagSystem _emag = default!;
|
||||||
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
||||||
[Dependency] private readonly TagSystem _tags = default!;
|
[Dependency] private readonly TagSystem _tag = default!;
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -29,18 +29,20 @@ public sealed class EmagProviderSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Emag clicked entities that are on the whitelist.
|
/// Emag clicked entities that are on the whitelist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, BeforeInteractHandEvent args)
|
private void OnBeforeInteractHand(Entity<EmagProviderComponent> ent, ref BeforeInteractHandEvent args)
|
||||||
{
|
{
|
||||||
// TODO: change this into a generic check event thing
|
// TODO: change this into a generic check event thing
|
||||||
if (args.Handled || !_gloves.AbilityCheck(uid, args, out var target))
|
if (args.Handled || !_gloves.AbilityCheck(ent, args, out var target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
|
||||||
// only allowed to emag entities on the whitelist
|
// only allowed to emag entities on the whitelist
|
||||||
if (_whitelistSystem.IsWhitelistFail(comp.Whitelist, target))
|
if (_whitelist.IsWhitelistFail(comp.Whitelist, target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// only allowed to emag non-immune entities
|
// only allowed to emag non-immune entities
|
||||||
if (_tags.HasTag(target, comp.EmagImmuneTag))
|
if (_tag.HasTag(target, comp.EmagImmuneTag))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var handled = _emag.DoEmagEffect(uid, target);
|
var handled = _emag.DoEmagEffect(uid, target);
|
||||||
@@ -52,18 +54,6 @@ public sealed class EmagProviderSystem : EntitySystem
|
|||||||
RaiseLocalEvent(uid, ref ev);
|
RaiseLocalEvent(uid, ref ev);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the whitelist for emagging something outside of yaml.
|
|
||||||
/// </summary>
|
|
||||||
public void SetWhitelist(EntityUid uid, EntityWhitelist? whitelist, EmagProviderComponent? comp = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref comp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
comp.Whitelist = whitelist;
|
|
||||||
Dirty(uid, comp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -15,33 +15,20 @@ public sealed class EnergyKatanaSystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<EnergyKatanaComponent, GotEquippedEvent>(OnEquipped);
|
SubscribeLocalEvent<EnergyKatanaComponent, GotEquippedEvent>(OnEquipped);
|
||||||
SubscribeLocalEvent<EnergyKatanaComponent, AddDashActionEvent>(OnAddDashAction);
|
SubscribeLocalEvent<EnergyKatanaComponent, CheckDashEvent>(OnCheckDash);
|
||||||
SubscribeLocalEvent<EnergyKatanaComponent, DashAttemptEvent>(OnDashAttempt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When equipped by a ninja, try to bind it.
|
/// When equipped by a ninja, try to bind it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnEquipped(EntityUid uid, EnergyKatanaComponent comp, GotEquippedEvent args)
|
private void OnEquipped(Entity<EnergyKatanaComponent> ent, ref GotEquippedEvent args)
|
||||||
{
|
{
|
||||||
// check if user isnt a ninja or already has a katana bound
|
_ninja.BindKatana(args.Equipee, ent);
|
||||||
var user = args.Equipee;
|
|
||||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja) || ninja.Katana != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// bind it since its unbound
|
|
||||||
_ninja.BindKatana(user, uid, ninja);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAddDashAction(EntityUid uid, EnergyKatanaComponent comp, AddDashActionEvent args)
|
private void OnCheckDash(Entity<EnergyKatanaComponent> ent, ref CheckDashEvent args)
|
||||||
{
|
{
|
||||||
if (!HasComp<SpaceNinjaComponent>(args.User))
|
if (!_ninja.IsNinja(args.User))
|
||||||
args.Cancel();
|
args.Cancelled = true;
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDashAttempt(EntityUid uid, EnergyKatanaComponent comp, DashAttemptEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<SpaceNinjaComponent>(args.User, out var ninja) || ninja.Katana != uid)
|
|
||||||
args.Cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
56
Content.Shared/Ninja/Systems/ItemCreatorSystem.cs
Normal file
56
Content.Shared/Ninja/Systems/ItemCreatorSystem.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles predicting that the action exists, creating items is done serverside.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedItemCreatorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ItemCreatorComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<ItemCreatorComponent, GetItemActionsEvent>(OnGetActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(Entity<ItemCreatorComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
// test funny dont mind me
|
||||||
|
if (string.IsNullOrEmpty(comp.Action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_actionContainer.EnsureAction(uid, ref comp.ActionEntity, comp.Action);
|
||||||
|
Dirty(uid, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetActions(Entity<ItemCreatorComponent> ent, ref GetItemActionsEvent args)
|
||||||
|
{
|
||||||
|
if (CheckItemCreator(ent, args.User))
|
||||||
|
args.AddAction(ent.Comp.ActionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckItemCreator(EntityUid uid, EntityUid user)
|
||||||
|
{
|
||||||
|
var ev = new CheckItemCreatorEvent(user);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
return !ev.Cancelled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the item creator before adding the action.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct CheckItemCreatorEvent(EntityUid User, bool Cancelled = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the item creator before creating an item.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct CreateItemAttemptEvent(EntityUid User, bool Cancelled = false);
|
||||||
@@ -18,34 +18,32 @@ public abstract class SharedBatteryDrainerSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cancel any drain doafters if the battery is removed or gets filled.
|
/// Cancel any drain doafters if the battery is removed or, on the server, gets filled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
protected virtual void OnDoAfterAttempt(Entity<BatteryDrainerComponent> ent, ref DoAfterAttemptEvent<DrainDoAfterEvent> args)
|
||||||
{
|
|
||||||
if (comp.BatteryUid == null)
|
|
||||||
{
|
{
|
||||||
|
if (ent.Comp.BatteryUid == null)
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drain power from a power source (on server) and repeat if it succeeded.
|
/// Drain power from a power source (on server) and repeat if it succeeded.
|
||||||
/// Client will predict always succeeding since power is serverside.
|
/// Client will predict always succeeding since power is serverside.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDoAfter(EntityUid uid, BatteryDrainerComponent comp, DrainDoAfterEvent args)
|
private void OnDoAfter(Entity<BatteryDrainerComponent> ent, ref DrainDoAfterEvent args)
|
||||||
{
|
{
|
||||||
if (args.Cancelled || args.Handled || args.Target == null)
|
if (args.Cancelled || args.Handled || args.Target is not {} target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// repeat if there is still power to drain
|
// repeat if there is still power to drain
|
||||||
args.Repeat = TryDrainPower(uid, comp, args.Target.Value);
|
args.Repeat = TryDrainPower(ent, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to drain as much power as possible into the powercell.
|
/// Attempt to drain as much power as possible into the powercell.
|
||||||
/// Client always predicts this as succeeding since power is serverside and it can only fail once, when the powercell is filled or the target is emptied.
|
/// Client always predicts this as succeeding since power is serverside and it can only fail once, when the powercell is filled or the target is emptied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target)
|
protected virtual bool TryDrainPower(Entity<BatteryDrainerComponent> ent, EntityUid target)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -53,12 +51,13 @@ public abstract class SharedBatteryDrainerSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the battery field on the drainer.
|
/// Sets the battery field on the drainer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetBattery(EntityUid uid, EntityUid? battery, BatteryDrainerComponent? comp = null)
|
public void SetBattery(Entity<BatteryDrainerComponent?> ent, EntityUid? battery)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp))
|
if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.BatteryUid = battery;
|
ent.Comp.BatteryUid = battery;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,4 +65,4 @@ public abstract class SharedBatteryDrainerSystem : EntitySystem
|
|||||||
/// DoAfter event for <see cref="BatteryDrainerComponent"/>.
|
/// DoAfter event for <see cref="BatteryDrainerComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent { }
|
public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent;
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Communications;
|
|
||||||
using Content.Shared.CriminalRecords.Components;
|
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Research.Components;
|
|
||||||
using Content.Shared.Toggleable;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Systems;
|
namespace Content.Shared.Ninja.Systems;
|
||||||
@@ -20,85 +18,105 @@ namespace Content.Shared.Ninja.Systems;
|
|||||||
public abstract class SharedNinjaGlovesSystem : EntitySystem
|
public abstract class SharedNinjaGlovesSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
|
||||||
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
||||||
[Dependency] protected readonly SharedInteractionSystem Interaction = default!;
|
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<NinjaGlovesComponent, GetItemActionsEvent>(OnGetItemActions);
|
SubscribeLocalEvent<NinjaGlovesComponent, ToggleClothingCheckEvent>(OnToggleCheck);
|
||||||
|
SubscribeLocalEvent<NinjaGlovesComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
||||||
|
SubscribeLocalEvent<NinjaGlovesComponent, ItemToggledEvent>(OnToggled);
|
||||||
SubscribeLocalEvent<NinjaGlovesComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<NinjaGlovesComponent, ExaminedEvent>(OnExamined);
|
||||||
SubscribeLocalEvent<NinjaGlovesComponent, GotUnequippedEvent>(OnUnequipped);
|
|
||||||
SubscribeLocalEvent<NinjaGlovesComponent, MapInitEvent>(OnMapInit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, NinjaGlovesComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
_actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
|
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disable glove abilities and show the popup if they were enabled previously.
|
/// Disable glove abilities and show the popup if they were enabled previously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DisableGloves(EntityUid uid, NinjaGlovesComponent? comp = null)
|
private void DisableGloves(Entity<NinjaGlovesComponent> ent)
|
||||||
{
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
|
||||||
// already disabled?
|
// already disabled?
|
||||||
if (!Resolve(uid, ref comp) || comp.User == null)
|
if (comp.User is not {} user)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var user = comp.User.Value;
|
|
||||||
comp.User = null;
|
comp.User = null;
|
||||||
Dirty(uid, comp);
|
Dirty(uid, comp);
|
||||||
|
|
||||||
Appearance.SetData(uid, ToggleVisuals.Toggled, false);
|
foreach (var ability in comp.Abilities)
|
||||||
Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user);
|
{
|
||||||
|
EntityManager.RemoveComponents(user, ability.Components);
|
||||||
RemComp<BatteryDrainerComponent>(user);
|
}
|
||||||
RemComp<EmagProviderComponent>(user);
|
|
||||||
RemComp<StunProviderComponent>(user);
|
|
||||||
RemComp<ResearchStealerComponent>(user);
|
|
||||||
RemComp<CommsHackerComponent>(user);
|
|
||||||
RemComp<CriminalRecordsHackerComponent>(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the toggle action when equipped.
|
/// Adds the toggle action when equipped by a ninja only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnGetItemActions(EntityUid uid, NinjaGlovesComponent comp, GetItemActionsEvent args)
|
private void OnToggleCheck(Entity<NinjaGlovesComponent> ent, ref ToggleClothingCheckEvent args)
|
||||||
{
|
{
|
||||||
if (HasComp<SpaceNinjaComponent>(args.User))
|
if (!_ninja.IsNinja(args.User))
|
||||||
args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction);
|
args.Cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show if the gloves are enabled when examining.
|
/// Show if the gloves are enabled when examining.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnExamined(EntityUid uid, NinjaGlovesComponent comp, ExaminedEvent args)
|
private void OnExamined(Entity<NinjaGlovesComponent> ent, ref ExaminedEvent args)
|
||||||
{
|
{
|
||||||
if (!args.IsInDetailsRange)
|
if (!args.IsInDetailsRange)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.PushText(Loc.GetString(comp.User != null ? "ninja-gloves-examine-on" : "ninja-gloves-examine-off"));
|
var on = _toggle.IsActivated(ent.Owner) ? "on" : "off";
|
||||||
|
args.PushText(Loc.GetString($"ninja-gloves-examine-{on}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void OnActivateAttempt(Entity<NinjaGlovesComponent> ent, ref ItemToggleActivateAttemptEvent args)
|
||||||
/// Disable gloves when unequipped and clean up ninja's gloves reference
|
|
||||||
/// </summary>
|
|
||||||
private void OnUnequipped(EntityUid uid, NinjaGlovesComponent comp, GotUnequippedEvent args)
|
|
||||||
{
|
{
|
||||||
if (comp.User != null)
|
if (args.User is not {} user
|
||||||
|
|| !_ninja.NinjaQuery.TryComp(user, out var ninja)
|
||||||
|
// need to wear suit to enable gloves
|
||||||
|
|| !HasComp<NinjaSuitComponent>(ninja.Suit))
|
||||||
{
|
{
|
||||||
var user = comp.User.Value;
|
args.Cancelled = true;
|
||||||
Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user);
|
args.Popup = Loc.GetString("ninja-gloves-not-wearing-suit");
|
||||||
DisableGloves(uid, comp);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<NinjaGlovesComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
if ((args.User ?? ent.Comp.User) is not {} user)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = Loc.GetString(args.Activated ? "ninja-gloves-on" : "ninja-gloves-off");
|
||||||
|
_popup.PopupClient(message, user, user);
|
||||||
|
|
||||||
|
if (args.Activated && _ninja.NinjaQuery.TryComp(user, out var ninja))
|
||||||
|
EnableGloves(ent, (user, ninja));
|
||||||
|
else
|
||||||
|
DisableGloves(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void EnableGloves(Entity<NinjaGlovesComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
comp.User = user;
|
||||||
|
Dirty(uid, comp);
|
||||||
|
_ninja.AssignGloves(user, uid);
|
||||||
|
|
||||||
|
// yeah this is just ComponentToggler but with objective checking
|
||||||
|
foreach (var ability in comp.Abilities)
|
||||||
|
{
|
||||||
|
// can't predict the objective related abilities
|
||||||
|
if (ability.Objective == null)
|
||||||
|
EntityManager.AddComponents(user, ability.Components);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: generic event thing
|
// TODO: generic event thing
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,6 +130,6 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
|||||||
&& !_combatMode.IsInCombatMode(uid)
|
&& !_combatMode.IsInCombatMode(uid)
|
||||||
&& TryComp<HandsComponent>(uid, out var hands)
|
&& TryComp<HandsComponent>(uid, out var hands)
|
||||||
&& hands.ActiveHandEntity == null
|
&& hands.ActiveHandEntity == null
|
||||||
&& Interaction.InRangeUnobstructed(uid, target);
|
&& _interaction.InRangeUnobstructed(uid, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Clothing;
|
||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Timing;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Systems;
|
namespace Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
@@ -14,137 +17,158 @@ namespace Content.Shared.Ninja.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SharedNinjaSuitSystem : EntitySystem
|
public abstract class SharedNinjaSuitSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
|
||||||
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
|
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
[Dependency] protected readonly StealthClothingSystem StealthClothing = default!;
|
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
|
||||||
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<NinjaSuitComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<NinjaSuitComponent, ClothingGotEquippedEvent>(OnEquipped);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, GotEquippedEvent>(OnEquipped);
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, GetItemActionsEvent>(OnGetItemActions);
|
SubscribeLocalEvent<NinjaSuitComponent, GetItemActionsEvent>(OnGetItemActions);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, AddStealthActionEvent>(OnAddStealthAction);
|
SubscribeLocalEvent<NinjaSuitComponent, ToggleClothingCheckEvent>(OnCloakCheck);
|
||||||
|
SubscribeLocalEvent<NinjaSuitComponent, CheckItemCreatorEvent>(OnStarCheck);
|
||||||
|
SubscribeLocalEvent<NinjaSuitComponent, CreateItemAttemptEvent>(OnCreateStarAttempt);
|
||||||
|
SubscribeLocalEvent<NinjaSuitComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, GotUnequippedEvent>(OnUnequipped);
|
SubscribeLocalEvent<NinjaSuitComponent, GotUnequippedEvent>(OnUnequipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, NinjaSuitComponent component, MapInitEvent args)
|
private void OnEquipped(Entity<NinjaSuitComponent> ent, ref ClothingGotEquippedEvent args)
|
||||||
{
|
{
|
||||||
_actionContainer.EnsureAction(uid, ref component.RecallKatanaActionEntity, component.RecallKatanaAction);
|
var user = args.Wearer;
|
||||||
_actionContainer.EnsureAction(uid, ref component.CreateThrowingStarActionEntity, component.CreateThrowingStarAction);
|
if (_ninja.NinjaQuery.TryComp(user, out var ninja))
|
||||||
_actionContainer.EnsureAction(uid, ref component.EmpActionEntity, component.EmpAction);
|
NinjaEquipped(ent, (user, ninja));
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
protected virtual void NinjaEquipped(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
/// Call the shared and serverside code for when a ninja equips the suit.
|
|
||||||
/// </summary>
|
|
||||||
private void OnEquipped(EntityUid uid, NinjaSuitComponent comp, GotEquippedEvent args)
|
|
||||||
{
|
{
|
||||||
var user = args.Equipee;
|
// mark the user as wearing this suit, used when being attacked among other things
|
||||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja))
|
_ninja.AssignSuit(user, ent);
|
||||||
return;
|
}
|
||||||
|
|
||||||
NinjaEquippedSuit(uid, comp, user, ninja);
|
private void OnMapInit(Entity<NinjaSuitComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
_actionContainer.EnsureAction(uid, ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction);
|
||||||
|
_actionContainer.EnsureAction(uid, ref comp.EmpActionEntity, comp.EmpAction);
|
||||||
|
Dirty(uid, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add all the actions when a suit is equipped by a ninja.
|
/// Add all the actions when a suit is equipped by a ninja.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnGetItemActions(EntityUid uid, NinjaSuitComponent comp, GetItemActionsEvent args)
|
private void OnGetItemActions(Entity<NinjaSuitComponent> ent, ref GetItemActionsEvent args)
|
||||||
{
|
{
|
||||||
if (!HasComp<SpaceNinjaComponent>(args.User))
|
if (!_ninja.IsNinja(args.User))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var comp = ent.Comp;
|
||||||
args.AddAction(ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction);
|
args.AddAction(ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction);
|
||||||
args.AddAction(ref comp.CreateThrowingStarActionEntity, comp.CreateThrowingStarAction);
|
|
||||||
args.AddAction(ref comp.EmpActionEntity, comp.EmpAction);
|
args.AddAction(ref comp.EmpActionEntity, comp.EmpAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only add stealth clothing's toggle action when equipped by a ninja.
|
/// Only add toggle cloak action when equipped by a ninja.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnAddStealthAction(EntityUid uid, NinjaSuitComponent comp, AddStealthActionEvent args)
|
private void OnCloakCheck(Entity<NinjaSuitComponent> ent, ref ToggleClothingCheckEvent args)
|
||||||
{
|
{
|
||||||
if (!HasComp<SpaceNinjaComponent>(args.User))
|
if (!_ninja.IsNinja(args.User))
|
||||||
args.Cancel();
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStarCheck(Entity<NinjaSuitComponent> ent, ref CheckItemCreatorEvent args)
|
||||||
|
{
|
||||||
|
if (!_ninja.IsNinja(args.User))
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreateStarAttempt(Entity<NinjaSuitComponent> ent, ref CreateItemAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (CheckDisabled(ent, args.User))
|
||||||
|
args.Cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call the shared and serverside code for when anyone unequips a suit.
|
/// Call the shared and serverside code for when anyone unequips a suit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnUnequipped(EntityUid uid, NinjaSuitComponent comp, GotUnequippedEvent args)
|
private void OnUnequipped(Entity<NinjaSuitComponent> ent, ref GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
UserUnequippedSuit(uid, comp, args.Equipee);
|
var user = args.Equipee;
|
||||||
}
|
if (_ninja.NinjaQuery.TryComp(user, out var ninja))
|
||||||
|
UserUnequippedSuit(ent, (user, ninja));
|
||||||
/// <summary>
|
|
||||||
/// Called when a suit is equipped by a space ninja.
|
|
||||||
/// In the future it might be changed to an explicit activation toggle/verb like gloves are.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja)
|
|
||||||
{
|
|
||||||
// mark the user as wearing this suit, used when being attacked among other things
|
|
||||||
_ninja.AssignSuit(user, uid, ninja);
|
|
||||||
|
|
||||||
// initialize phase cloak, but keep it off
|
|
||||||
StealthClothing.SetEnabled(uid, user, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force uncloaks the user and disables suit abilities.
|
/// Force uncloaks the user and disables suit abilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RevealNinja(EntityUid uid, EntityUid user, bool disable = true, NinjaSuitComponent? comp = null, StealthClothingComponent? stealthClothing = null)
|
public void RevealNinja(Entity<NinjaSuitComponent?> ent, EntityUid user, bool disable = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp, ref stealthClothing))
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!StealthClothing.SetEnabled(uid, user, false, stealthClothing))
|
var uid = ent.Owner;
|
||||||
return;
|
var comp = ent.Comp;
|
||||||
|
if (_toggle.TryDeactivate(uid, user) || !disable)
|
||||||
if (!disable)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// previously cloaked, disable abilities for a short time
|
// previously cloaked, disable abilities for a short time
|
||||||
_audio.PlayPredicted(comp.RevealSound, uid, user);
|
_audio.PlayPredicted(comp.RevealSound, uid, user);
|
||||||
Popup.PopupClient(Loc.GetString("ninja-revealed"), user, user, PopupType.MediumCaution);
|
Popup.PopupClient(Loc.GetString("ninja-revealed"), user, user, PopupType.MediumCaution);
|
||||||
comp.DisableCooldown = GameTiming.CurTime + comp.DisableTime;
|
_useDelay.TryResetDelay(uid, id: comp.DisableDelayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: modify PowerCellDrain
|
private void OnActivateAttempt(Entity<NinjaSuitComponent> ent, ref ItemToggleActivateAttemptEvent args)
|
||||||
/// <summary>
|
|
||||||
/// Returns the power used by a suit
|
|
||||||
/// </summary>
|
|
||||||
public float SuitWattage(EntityUid uid, NinjaSuitComponent? suit = null)
|
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref suit))
|
if (!_ninja.IsNinja(args.User))
|
||||||
return 0f;
|
{
|
||||||
|
args.Cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
float wattage = suit.PassiveWattage;
|
if (IsDisabled((ent, ent.Comp, null)))
|
||||||
if (TryComp<StealthClothingComponent>(uid, out var stealthClothing) && stealthClothing.Enabled)
|
{
|
||||||
wattage += suit.CloakWattage;
|
args.Cancelled = true;
|
||||||
return wattage;
|
args.Popup = Loc.GetString("ninja-suit-cooldown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the suit is currently disabled
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDisabled(Entity<NinjaSuitComponent?, UseDelayComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _useDelay.IsDelayed((ent, ent.Comp2), ent.Comp1.DisableDelayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool CheckDisabled(Entity<NinjaSuitComponent> ent, EntityUid user)
|
||||||
|
{
|
||||||
|
if (IsDisabled((ent, ent.Comp, null)))
|
||||||
|
{
|
||||||
|
Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a suit is unequipped, not necessarily by a space ninja.
|
/// Called when a suit is unequipped, not necessarily by a space ninja.
|
||||||
/// In the future it might be changed to also have explicit deactivation via toggle.
|
/// In the future it might be changed to also have explicit deactivation via toggle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user)
|
protected virtual void UserUnequippedSuit(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
{
|
{
|
||||||
if (!TryComp<SpaceNinjaComponent>(user, out var ninja))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// mark the user as not wearing a suit
|
// mark the user as not wearing a suit
|
||||||
_ninja.AssignSuit(user, null, ninja);
|
_ninja.AssignSuit(user, null);
|
||||||
// disable glove abilities
|
// disable glove abilities
|
||||||
if (ninja.Gloves != null && TryComp<NinjaGlovesComponent>(ninja.Gloves.Value, out var gloves))
|
if (user.Comp.Gloves is {} uid)
|
||||||
_gloves.DisableGloves(ninja.Gloves.Value, gloves);
|
_toggle.TryDeactivate(uid, user: user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.Ninja.Components;
|
|||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Systems;
|
namespace Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
@@ -14,49 +15,59 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem
|
|||||||
[Dependency] protected readonly SharedNinjaSuitSystem Suit = default!;
|
[Dependency] protected readonly SharedNinjaSuitSystem Suit = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
|
|
||||||
|
public EntityQuery<SpaceNinjaComponent> NinjaQuery;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
NinjaQuery = GetEntityQuery<SpaceNinjaComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, AttackedEvent>(OnNinjaAttacked);
|
SubscribeLocalEvent<SpaceNinjaComponent, AttackedEvent>(OnNinjaAttacked);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, MeleeAttackEvent>(OnNinjaAttack);
|
SubscribeLocalEvent<SpaceNinjaComponent, MeleeAttackEvent>(OnNinjaAttack);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, ShotAttemptedEvent>(OnShotAttempted);
|
SubscribeLocalEvent<SpaceNinjaComponent, ShotAttemptedEvent>(OnShotAttempted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsNinja([NotNullWhen(true)] EntityUid? uid)
|
||||||
|
{
|
||||||
|
return NinjaQuery.HasComp(uid);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the ninja's worn suit entity
|
/// Set the ninja's worn suit entity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AssignSuit(EntityUid uid, EntityUid? suit, SpaceNinjaComponent? comp = null)
|
public void AssignSuit(Entity<SpaceNinjaComponent> ent, EntityUid? suit)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp) || comp.Suit == suit)
|
if (ent.Comp.Suit == suit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.Suit = suit;
|
ent.Comp.Suit = suit;
|
||||||
Dirty(uid, comp);
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the ninja's worn gloves entity
|
/// Set the ninja's worn gloves entity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AssignGloves(EntityUid uid, EntityUid? gloves, SpaceNinjaComponent? comp = null)
|
public void AssignGloves(Entity<SpaceNinjaComponent> ent, EntityUid? gloves)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp) || comp.Gloves == gloves)
|
if (ent.Comp.Gloves == gloves)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.Gloves = gloves;
|
ent.Comp.Gloves = gloves;
|
||||||
Dirty(uid, comp);
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bind a katana entity to a ninja, letting it be recalled and dash.
|
/// Bind a katana entity to a ninja, letting it be recalled and dash.
|
||||||
|
/// Does nothing if the player is not a ninja or already has a katana bound.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BindKatana(EntityUid uid, EntityUid? katana, SpaceNinjaComponent? comp = null)
|
public void BindKatana(Entity<SpaceNinjaComponent?> ent, EntityUid katana)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp) || comp.Katana == katana)
|
if (!NinjaQuery.Resolve(ent, ref ent.Comp) || ent.Comp.Katana != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.Katana = katana;
|
ent.Comp.Katana = katana;
|
||||||
Dirty(uid, comp);
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -71,32 +82,32 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle revealing ninja if cloaked when attacked.
|
/// Handle revealing ninja if cloaked when attacked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnNinjaAttacked(EntityUid uid, SpaceNinjaComponent comp, AttackedEvent args)
|
private void OnNinjaAttacked(Entity<SpaceNinjaComponent> ent, ref AttackedEvent args)
|
||||||
{
|
{
|
||||||
if (comp.Suit != null && TryComp<StealthClothingComponent>(comp.Suit, out var stealthClothing) && stealthClothing.Enabled)
|
TryRevealNinja(ent, disable: true);
|
||||||
{
|
|
||||||
Suit.RevealNinja(comp.Suit.Value, uid, true, null, stealthClothing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle revealing ninja if cloaked when attacking.
|
/// Handle revealing ninja if cloaked when attacking.
|
||||||
/// Only reveals, there is no cooldown.
|
/// Only reveals, there is no cooldown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnNinjaAttack(EntityUid uid, SpaceNinjaComponent comp, ref MeleeAttackEvent args)
|
private void OnNinjaAttack(Entity<SpaceNinjaComponent> ent, ref MeleeAttackEvent args)
|
||||||
{
|
{
|
||||||
if (comp.Suit != null && TryComp<StealthClothingComponent>(comp.Suit, out var stealthClothing) && stealthClothing.Enabled)
|
TryRevealNinja(ent, disable: false);
|
||||||
{
|
|
||||||
Suit.RevealNinja(comp.Suit.Value, uid, false, null, stealthClothing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TryRevealNinja(Entity<SpaceNinjaComponent> ent, bool disable)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Suit is {} uid && TryComp<NinjaSuitComponent>(ent.Comp.Suit, out var suit))
|
||||||
|
Suit.RevealNinja((uid, suit), ent, disable: disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Require ninja to fight with HONOR, no guns!
|
/// Require ninja to fight with HONOR, no guns!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnShotAttempted(EntityUid uid, SpaceNinjaComponent comp, ref ShotAttemptedEvent args)
|
private void OnShotAttempted(Entity<SpaceNinjaComponent> ent, ref ShotAttemptedEvent args)
|
||||||
{
|
{
|
||||||
Popup.PopupClient(Loc.GetString("gun-disabled"), uid, uid);
|
Popup.PopupClient(Loc.GetString("gun-disabled"), ent, ent);
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs
Normal file
6
Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Content.Shared.Ninja.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sticking triggering and exploding are all in server so this is just for access.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedSpiderChargeSystem : EntitySystem;
|
||||||
@@ -11,22 +11,12 @@ public abstract class SharedStunProviderSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the battery field on the stun provider.
|
/// Set the battery field on the stun provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetBattery(EntityUid uid, EntityUid? battery, StunProviderComponent? comp = null)
|
public void SetBattery(Entity<StunProviderComponent?> ent, EntityUid? battery)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp))
|
if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.BatteryUid = battery;
|
ent.Comp.BatteryUid = battery;
|
||||||
}
|
Dirty(ent, ent.Comp);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the no power popup field on the stun provider.
|
|
||||||
/// </summary>
|
|
||||||
public void SetNoPowerPopup(EntityUid uid, string popup, StunProviderComponent? comp = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref comp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
comp.NoPowerPopup = popup;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public abstract class SharedObjectivesSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the title, description, icon and progress of an objective using <see cref="ObjectiveGetInfoEvent"/>.
|
/// Get the title, description, icon and progress of an objective using <see cref="ObjectiveGetProgressEvent"/>.
|
||||||
/// If any of them are null it is logged and null is returned.
|
/// If any of them are null it is logged and null is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uid"/>ID of the condition entity</param>
|
/// <param name="uid"/>ID of the condition entity</param>
|
||||||
@@ -103,20 +103,43 @@ public abstract class SharedObjectivesSystem : EntitySystem
|
|||||||
if (!Resolve(mindId, ref mind))
|
if (!Resolve(mindId, ref mind))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var ev = new ObjectiveGetProgressEvent(mindId, mind);
|
if (GetProgress(uid, (mindId, mind)) is not {} progress)
|
||||||
RaiseLocalEvent(uid, ref ev);
|
return null;
|
||||||
|
|
||||||
var comp = Comp<ObjectiveComponent>(uid);
|
var comp = Comp<ObjectiveComponent>(uid);
|
||||||
var meta = MetaData(uid);
|
var meta = MetaData(uid);
|
||||||
var title = meta.EntityName;
|
var title = meta.EntityName;
|
||||||
var description = meta.EntityDescription;
|
var description = meta.EntityDescription;
|
||||||
if (comp.Icon == null || ev.Progress == null)
|
if (comp.Icon == null)
|
||||||
{
|
{
|
||||||
Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing icon or progress ({ev.Progress})");
|
Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing an icon!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ObjectiveInfo(title, description, comp.Icon, ev.Progress.Value);
|
return new ObjectiveInfo(title, description, comp.Icon, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the progress of an objective using <see cref="ObjectiveGetProgressEvent"/>.
|
||||||
|
/// Returning null is a programmer error.
|
||||||
|
/// </summary>
|
||||||
|
public float? GetProgress(EntityUid uid, Entity<MindComponent> mind)
|
||||||
|
{
|
||||||
|
var ev = new ObjectiveGetProgressEvent(mind, mind.Comp);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
if (ev.Progress != null)
|
||||||
|
return ev.Progress;
|
||||||
|
|
||||||
|
Log.Error($"Objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind.Comp)} didn't set a progress value!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if an objective is completed.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted(EntityUid uid, Entity<MindComponent> mind)
|
||||||
|
{
|
||||||
|
return (GetProgress(uid, mind) ?? 0f) >= 0.999f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Pinpointer;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum ProximityBeeperVisuals : byte
|
|
||||||
{
|
|
||||||
Enabled
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,10 @@ namespace Content.Shared.PowerCell;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the entity's ActivatableUI requires power or else it closes.
|
/// Indicates that the entity's ActivatableUI requires power or else it closes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// With ActivatableUI it will activate and deactivate when the ui is opened and closed, drawing power inbetween.
|
||||||
|
/// Requires <see cref="ItemToggleComponent"/> to work.
|
||||||
|
/// </remarks>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||||
public sealed partial class PowerCellDrawComponent : Component
|
public sealed partial class PowerCellDrawComponent : Component
|
||||||
{
|
{
|
||||||
@@ -26,10 +30,12 @@ public sealed partial class PowerCellDrawComponent : Component
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is this power cell currently drawing power every tick.
|
/// Whether drawing is enabled, regardless of ItemToggle.
|
||||||
|
/// Having no cell will still disable it.
|
||||||
|
/// Only use this if you really don't want it to use power for some time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("enabled")]
|
[DataField, AutoNetworkedField]
|
||||||
public bool Drawing;
|
public bool Enabled = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much the entity draws while the UI is open.
|
/// How much the entity draws while the UI is open.
|
||||||
@@ -51,4 +57,10 @@ public sealed partial class PowerCellDrawComponent : Component
|
|||||||
[DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
[DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
[AutoPausedField]
|
[AutoPausedField]
|
||||||
public TimeSpan NextUpdateTime;
|
public TimeSpan NextUpdateTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long to wait between power drawing.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan Delay = TimeSpan.FromSeconds(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -11,14 +13,19 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
|||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] protected readonly ItemToggleSystem Toggle = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, RejuvenateEvent>(OnRejuvenate);
|
SubscribeLocalEvent<PowerCellSlotComponent, RejuvenateEvent>(OnRejuvenate);
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellInserted);
|
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellInserted);
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellRemoved);
|
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellRemoved);
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, ContainerIsInsertingAttemptEvent>(OnCellInsertAttempt);
|
SubscribeLocalEvent<PowerCellSlotComponent, ContainerIsInsertingAttemptEvent>(OnCellInsertAttempt);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PowerCellDrawComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
||||||
|
SubscribeLocalEvent<PowerCellDrawComponent, ItemToggledEvent>(OnToggled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRejuvenate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args)
|
private void OnRejuvenate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args)
|
||||||
@@ -63,13 +70,25 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
|||||||
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPowerCellDrawEnabled(EntityUid uid, bool enabled, PowerCellDrawComponent? component = null)
|
private void OnActivateAttempt(Entity<PowerCellDrawComponent> ent, ref ItemToggleActivateAttemptEvent args)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component, false) || enabled == component.Drawing)
|
if (!HasDrawCharge(ent, ent.Comp, user: args.User)
|
||||||
|
|| !HasActivatableCharge(ent, ent.Comp, user: args.User))
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToggled(Entity<PowerCellDrawComponent> ent, ref ItemToggledEvent args)
|
||||||
|
{
|
||||||
|
ent.Comp.NextUpdateTime = Timing.CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDrawEnabled(Entity<PowerCellDrawComponent?> ent, bool enabled)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Drawing = enabled;
|
ent.Comp.Enabled = enabled;
|
||||||
component.NextUpdateTime = Timing.CurTime;
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ namespace Content.Shared.ProximityDetection.Components;
|
|||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))]
|
||||||
public sealed partial class ProximityDetectorComponent : Component
|
public sealed partial class ProximityDetectorComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether or not it's on.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Enabled = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The criteria used to filter entities
|
/// The criteria used to filter entities
|
||||||
/// Note: RequireAll is only supported for tags, all components are required to count as a match!
|
/// Note: RequireAll is only supported for tags, all components are required to count as a match!
|
||||||
@@ -35,13 +29,13 @@ public sealed partial class ProximityDetectorComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
public FixedPoint2 Distance = -1;
|
public FixedPoint2 Distance = -1;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The farthest distance to search for targets
|
/// The farthest distance to search for targets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
public FixedPoint2 Range = 10f;
|
public FixedPoint2 Range = 10f;
|
||||||
|
|
||||||
|
// TODO: use timespans not this
|
||||||
public float AccumulatedFrameTime;
|
public float AccumulatedFrameTime;
|
||||||
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Content.Shared.ProximityDetection.Components;
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
|
using Content.Shared.ProximityDetection.Components;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
@@ -9,6 +11,7 @@ namespace Content.Shared.ProximityDetection.Systems;
|
|||||||
public sealed class ProximityDetectionSystem : EntitySystem
|
public sealed class ProximityDetectionSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
[Dependency] private readonly INetManager _net = default!;
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
@@ -17,10 +20,10 @@ public sealed class ProximityDetectionSystem : EntitySystem
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<ProximityDetectorComponent, EntityPausedEvent>(OnPaused);
|
base.Initialize();
|
||||||
SubscribeLocalEvent<ProximityDetectorComponent, EntityUnpausedEvent>(OnUnpaused);
|
|
||||||
SubscribeLocalEvent<ProximityDetectorComponent, ComponentInit>(OnCompInit);
|
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ProximityDetectorComponent, ComponentInit>(OnCompInit);
|
||||||
|
SubscribeLocalEvent<ProximityDetectorComponent, ItemToggledEvent>(OnToggled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args)
|
private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args)
|
||||||
@@ -30,57 +33,39 @@ public sealed class ProximityDetectionSystem : EntitySystem
|
|||||||
Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!");
|
Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPaused(EntityUid owner, ProximityDetectorComponent component, EntityPausedEvent args)
|
|
||||||
{
|
|
||||||
SetEnable_Internal(owner,component,false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnpaused(EntityUid owner, ProximityDetectorComponent detector, ref EntityUnpausedEvent args)
|
|
||||||
{
|
|
||||||
SetEnable_Internal(owner, detector,true);
|
|
||||||
}
|
|
||||||
public void SetEnable(EntityUid owner, bool enabled, ProximityDetectorComponent? detector = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(owner, ref detector) || detector.Enabled == enabled)
|
|
||||||
return;
|
|
||||||
SetEnable_Internal(owner ,detector, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
if (_net.IsClient)
|
if (_net.IsClient)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<ProximityDetectorComponent>();
|
var query = EntityQueryEnumerator<ProximityDetectorComponent>();
|
||||||
while (query.MoveNext(out var owner, out var detector))
|
while (query.MoveNext(out var owner, out var detector))
|
||||||
{
|
{
|
||||||
if (!detector.Enabled)
|
if (!_toggle.IsActivated(owner))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
detector.AccumulatedFrameTime += frameTime;
|
detector.AccumulatedFrameTime += frameTime;
|
||||||
if (detector.AccumulatedFrameTime < detector.UpdateRate)
|
if (detector.AccumulatedFrameTime < detector.UpdateRate)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
detector.AccumulatedFrameTime -= detector.UpdateRate;
|
detector.AccumulatedFrameTime -= detector.UpdateRate;
|
||||||
RunUpdate_Internal(owner, detector);
|
RunUpdate_Internal(owner, detector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetEnable(EntityUid owner, ProximityDetectorComponent? detector = null)
|
private void OnToggled(Entity<ProximityDetectorComponent> ent, ref ItemToggledEvent args)
|
||||||
{
|
{
|
||||||
return Resolve(owner, ref detector, false) && detector.Enabled;
|
if (args.Activated)
|
||||||
}
|
|
||||||
|
|
||||||
private void SetEnable_Internal(EntityUid owner,ProximityDetectorComponent detector, bool enabled)
|
|
||||||
{
|
{
|
||||||
detector.Enabled = enabled;
|
RunUpdate_Internal(ent, ent.Comp);
|
||||||
var noDetectEvent = new ProximityTargetUpdatedEvent(detector, detector.TargetEnt, detector.Distance);
|
|
||||||
RaiseLocalEvent(owner, ref noDetectEvent);
|
|
||||||
if (!enabled)
|
|
||||||
{
|
|
||||||
detector.AccumulatedFrameTime = 0;
|
|
||||||
RunUpdate_Internal(owner, detector);
|
|
||||||
Dirty(owner, detector);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RunUpdate_Internal(owner, detector);
|
|
||||||
|
var noDetectEvent = new ProximityTargetUpdatedEvent(ent.Comp, Target: null, ent.Comp.Distance);
|
||||||
|
RaiseLocalEvent(ent, ref noDetectEvent);
|
||||||
|
|
||||||
|
ent.Comp.AccumulatedFrameTime = 0;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null)
|
public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null)
|
||||||
@@ -90,11 +75,31 @@ public sealed class ProximityDetectionSystem : EntitySystem
|
|||||||
RunUpdate_Internal(owner, detector);
|
RunUpdate_Internal(owner, detector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ClearTarget(Entity<ProximityDetectorComponent> ent)
|
||||||
|
{
|
||||||
|
var (uid, comp) = ent;
|
||||||
|
if (comp.TargetEnt == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
comp.Distance = -1;
|
||||||
|
comp.TargetEnt = null;
|
||||||
|
var noDetectEvent = new ProximityTargetUpdatedEvent(comp, null, -1);
|
||||||
|
RaiseLocalEvent(uid, ref noDetectEvent);
|
||||||
|
var newTargetEvent = new NewProximityTargetEvent(comp, null);
|
||||||
|
RaiseLocalEvent(uid, ref newTargetEvent);
|
||||||
|
Dirty(uid, comp);
|
||||||
|
}
|
||||||
|
|
||||||
private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector)
|
private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector)
|
||||||
{
|
{
|
||||||
if (!_net.IsServer) //only run detection checks on the server!
|
if (!_net.IsServer) //only run detection checks on the server!
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (Deleted(detector.TargetEnt))
|
||||||
|
{
|
||||||
|
ClearTarget((owner, detector));
|
||||||
|
}
|
||||||
|
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
var xform = xformQuery.GetComponent(owner);
|
var xform = xformQuery.GetComponent(owner);
|
||||||
List<(EntityUid TargetEnt, float Distance)> detections = new();
|
List<(EntityUid TargetEnt, float Distance)> detections = new();
|
||||||
@@ -173,15 +178,7 @@ public sealed class ProximityDetectionSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (detections.Count == 0)
|
if (detections.Count == 0)
|
||||||
{
|
{
|
||||||
if (detector.TargetEnt == null)
|
ClearTarget((owner, detector));
|
||||||
return;
|
|
||||||
detector.Distance = -1;
|
|
||||||
detector.TargetEnt = null;
|
|
||||||
var noDetectEvent = new ProximityTargetUpdatedEvent(detector, null, -1);
|
|
||||||
RaiseLocalEvent(owner, ref noDetectEvent);
|
|
||||||
var newTargetEvent = new NewProximityTargetEvent(detector, null);
|
|
||||||
RaiseLocalEvent(owner, ref newTargetEvent);
|
|
||||||
Dirty(owner, detector);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var closestDistance = detections[0].Distance;
|
var closestDistance = detections[0].Distance;
|
||||||
@@ -198,6 +195,7 @@ public sealed class ProximityDetectionSystem : EntitySystem
|
|||||||
var newData = newTarget || detector.Distance != closestDistance;
|
var newData = newTarget || detector.Distance != closestDistance;
|
||||||
detector.TargetEnt = closestEnt;
|
detector.TargetEnt = closestEnt;
|
||||||
detector.Distance = closestDistance;
|
detector.Distance = closestDistance;
|
||||||
|
Dirty(owner, detector);
|
||||||
if (newTarget)
|
if (newTarget)
|
||||||
{
|
{
|
||||||
var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt);
|
var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt);
|
||||||
|
|||||||
@@ -15,12 +15,6 @@ namespace Content.Shared.Silicons.Borgs.Components;
|
|||||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState]
|
||||||
public sealed partial class BorgChassisComponent : Component
|
public sealed partial class BorgChassisComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed
|
|
||||||
/// </summary>
|
|
||||||
[DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
|
||||||
public bool Activated;
|
|
||||||
|
|
||||||
#region Brain
|
#region Brain
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A whitelist for which entities count as valid brains
|
/// A whitelist for which entities count as valid brains
|
||||||
@@ -68,7 +62,7 @@ public sealed partial class BorgChassisComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently selected module
|
/// The currently selected module
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("selectedModule")]
|
[DataField("selectedModule"), AutoNetworkedField]
|
||||||
public EntityUid? SelectedModule;
|
public EntityUid? SelectedModule;
|
||||||
|
|
||||||
#region Visuals
|
#region Visuals
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -18,6 +19,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] protected readonly SharedContainerSystem Container = default!;
|
[Dependency] protected readonly SharedContainerSystem Container = default!;
|
||||||
[Dependency] protected readonly ItemSlotsSystem ItemSlots = default!;
|
[Dependency] protected readonly ItemSlotsSystem ItemSlots = default!;
|
||||||
|
[Dependency] protected readonly ItemToggleSystem Toggle = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -96,7 +98,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args)
|
private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||||
{
|
{
|
||||||
if (component.Activated)
|
if (Toggle.IsActivated(uid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
|
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ namespace Content.Shared.Toggleable;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic action-event for toggle-able components.
|
/// Generic action-event for toggle-able components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class ToggleActionEvent : InstantActionEvent { }
|
/// <remarks>
|
||||||
|
/// If you are using <c>ItemToggleComponent</c> subscribe to <c>ItemToggledEvent</c> instead.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed partial class ToggleActionEvent : InstantActionEvent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic enum keys for toggle-visualizer appearance data & sprite layers.
|
/// Generic enum keys for toggle-visualizer appearance data & sprite layers.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public abstract partial class SharedToolSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||||
[Dependency] protected readonly SharedInteractionSystem InteractionSystem = default!;
|
[Dependency] protected readonly SharedInteractionSystem InteractionSystem = default!;
|
||||||
[Dependency] protected readonly SharedItemToggleSystem ItemToggle = default!;
|
[Dependency] protected readonly ItemToggleSystem ItemToggle = default!;
|
||||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] protected readonly SharedSolutionContainerSystem SolutionContainerSystem = default!;
|
[Dependency] protected readonly SharedSolutionContainerSystem SolutionContainerSystem = default!;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
@@ -5,6 +7,7 @@ namespace Content.Shared.UserInterface;
|
|||||||
|
|
||||||
public sealed partial class ActivatableUISystem
|
public sealed partial class ActivatableUISystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SharedPowerCellSystem _cell = default!;
|
[Dependency] private readonly SharedPowerCellSystem _cell = default!;
|
||||||
|
|
||||||
private void InitializePower()
|
private void InitializePower()
|
||||||
@@ -12,27 +15,22 @@ public sealed partial class ActivatableUISystem
|
|||||||
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, ActivatableUIOpenAttemptEvent>(OnBatteryOpenAttempt);
|
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, ActivatableUIOpenAttemptEvent>(OnBatteryOpenAttempt);
|
||||||
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIOpenedEvent>(OnBatteryOpened);
|
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIOpenedEvent>(OnBatteryOpened);
|
||||||
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIClosedEvent>(OnBatteryClosed);
|
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIClosedEvent>(OnBatteryClosed);
|
||||||
|
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, ItemToggledEvent>(OnToggled);
|
||||||
SubscribeLocalEvent<PowerCellDrawComponent, EntRemovedFromContainerMessage>(OnPowerCellRemoved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPowerCellRemoved(EntityUid uid, PowerCellDrawComponent component, EntRemovedFromContainerMessage args)
|
private void OnToggled(Entity<ActivatableUIRequiresPowerCellComponent> ent, ref ItemToggledEvent args)
|
||||||
{
|
|
||||||
_cell.SetPowerCellDrawEnabled(uid, false);
|
|
||||||
|
|
||||||
if (!HasComp<ActivatableUIRequiresPowerCellComponent>(uid) ||
|
|
||||||
!TryComp(uid, out ActivatableUIComponent? activatable))
|
|
||||||
{
|
{
|
||||||
|
// only close ui when losing power
|
||||||
|
if (!TryComp<ActivatableUIComponent>(ent, out var activatable) || args.Activated)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (activatable.Key == null)
|
if (activatable.Key == null)
|
||||||
{
|
{
|
||||||
Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(uid)}");
|
Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiSystem.CloseUi(uid, activatable.Key);
|
_uiSystem.CloseUi(ent.Owner, activatable.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIOpenedEvent args)
|
private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIOpenedEvent args)
|
||||||
@@ -42,7 +40,7 @@ public sealed partial class ActivatableUISystem
|
|||||||
if (!args.UiKey.Equals(activatable.Key))
|
if (!args.UiKey.Equals(activatable.Key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_cell.SetPowerCellDrawEnabled(uid, true);
|
_toggle.TryActivate(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIClosedEvent args)
|
private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIClosedEvent args)
|
||||||
@@ -54,7 +52,7 @@ public sealed partial class ActivatableUISystem
|
|||||||
|
|
||||||
// Stop drawing power if this was the last person with the UI open.
|
// Stop drawing power if this was the last person with the UI open.
|
||||||
if (!_uiSystem.IsUiOpen(uid, activatable.Key))
|
if (!_uiSystem.IsUiOpen(uid, activatable.Key))
|
||||||
_cell.SetPowerCellDrawEnabled(uid, false);
|
_toggle.TryDeactivate(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -5,16 +5,11 @@ namespace Content.Shared.Weapons.Reflect;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entities with this component have a chance to reflect projectiles and hitscan shots
|
/// Entities with this component have a chance to reflect projectiles and hitscan shots
|
||||||
|
/// Uses <c>ItemToggleComponent</c> to control reflection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class ReflectComponent : Component
|
public sealed partial class ReflectComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Can only reflect when enabled
|
|
||||||
/// </summary>
|
|
||||||
[DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
|
||||||
public bool Enabled = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// What we reflect.
|
/// What we reflect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Item.ItemToggle.Components;
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
@@ -27,10 +28,11 @@ namespace Content.Shared.Weapons.Reflect;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ReflectSystem : EntitySystem
|
public sealed class ReflectSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
@@ -93,7 +95,7 @@ public sealed class ReflectSystem : EntitySystem
|
|||||||
private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null)
|
private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(reflector, ref reflect, false) ||
|
if (!Resolve(reflector, ref reflect, false) ||
|
||||||
!reflect.Enabled ||
|
!_toggle.IsActivated(reflector) ||
|
||||||
!TryComp<ReflectiveComponent>(projectile, out var reflective) ||
|
!TryComp<ReflectiveComponent>(projectile, out var reflective) ||
|
||||||
(reflect.Reflects & reflective.Reflective) == 0x0 ||
|
(reflect.Reflects & reflective.Reflective) == 0x0 ||
|
||||||
!_random.Prob(reflect.ReflectProb) ||
|
!_random.Prob(reflect.ReflectProb) ||
|
||||||
@@ -162,7 +164,7 @@ public sealed class ReflectSystem : EntitySystem
|
|||||||
[NotNullWhen(true)] out Vector2? newDirection)
|
[NotNullWhen(true)] out Vector2? newDirection)
|
||||||
{
|
{
|
||||||
if (!TryComp<ReflectComponent>(reflector, out var reflect) ||
|
if (!TryComp<ReflectComponent>(reflector, out var reflect) ||
|
||||||
!reflect.Enabled ||
|
!_toggle.IsActivated(reflector) ||
|
||||||
!_random.Prob(reflect.ReflectProb))
|
!_random.Prob(reflect.ReflectProb))
|
||||||
{
|
{
|
||||||
newDirection = null;
|
newDirection = null;
|
||||||
@@ -214,8 +216,8 @@ public sealed class ReflectSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args)
|
private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args)
|
||||||
{
|
{
|
||||||
comp.Enabled = args.Activated;
|
if (args.User is {} user)
|
||||||
Dirty(uid, comp);
|
RefreshReflectUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -225,7 +227,7 @@ public sealed class ReflectSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET))
|
foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET))
|
||||||
{
|
{
|
||||||
if (!HasComp<ReflectComponent>(ent))
|
if (!HasComp<ReflectComponent>(ent) || !_toggle.IsActivated(ent))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
EnsureComp<ReflectUserComponent>(user);
|
EnsureComp<ReflectUserComponent>(user);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: ActionToggleNinjaGloves
|
id: ActionToggleNinjaGloves
|
||||||
name: Toggle ninja gloves
|
name: Toggle ninja gloves
|
||||||
description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies, downloading research and calling in a threat.
|
description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies and hacking certain computers.
|
||||||
components:
|
components:
|
||||||
- type: InstantAction
|
- type: InstantAction
|
||||||
priority: -13
|
priority: -13
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
state: icon
|
state: icon
|
||||||
itemIconStyle: NoItem
|
itemIconStyle: NoItem
|
||||||
priority: -10
|
priority: -10
|
||||||
event: !type:CreateThrowingStarEvent {}
|
event: !type:CreateItemEvent {}
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: ActionRecallKatana
|
id: ActionRecallKatana
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
# have to plan (un)cloaking ahead of time
|
# have to plan (un)cloaking ahead of time
|
||||||
useDelay: 5
|
useDelay: 5
|
||||||
priority: -9
|
priority: -9
|
||||||
event: !type:ToggleStealthEvent
|
event: !type:ToggleActionEvent
|
||||||
|
|
||||||
# katana
|
# katana
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -72,6 +72,10 @@
|
|||||||
sprite: Objects/Magic/magicactions.rsi
|
sprite: Objects/Magic/magicactions.rsi
|
||||||
state: blink
|
state: blink
|
||||||
itemIconStyle: NoItem
|
itemIconStyle: NoItem
|
||||||
|
sound:
|
||||||
|
path: /Audio/Magic/blink.ogg
|
||||||
|
params:
|
||||||
|
volume: 5
|
||||||
priority: -12
|
priority: -12
|
||||||
event: !type:DashEvent
|
event: !type:DashEvent
|
||||||
checkCanAccess: false
|
checkCanAccess: false
|
||||||
|
|||||||
@@ -206,7 +206,7 @@
|
|||||||
- type: FingerprintMask
|
- type: FingerprintMask
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingHandsBase
|
parent: [ClothingHandsBase, BaseToggleClothing]
|
||||||
id: ClothingHandsGlovesSpaceNinja
|
id: ClothingHandsGlovesSpaceNinja
|
||||||
name: space ninja gloves
|
name: space ninja gloves
|
||||||
description: These black nano-enhanced gloves insulate from electricity and provide fire resistance.
|
description: These black nano-enhanced gloves insulate from electricity and provide fire resistance.
|
||||||
@@ -234,7 +234,31 @@
|
|||||||
- type: Thieving
|
- type: Thieving
|
||||||
stripTimeReduction: 1
|
stripTimeReduction: 1
|
||||||
stealthy: true
|
stealthy: true
|
||||||
|
- type: ToggleClothing
|
||||||
|
action: ActionToggleNinjaGloves
|
||||||
- type: NinjaGloves
|
- type: NinjaGloves
|
||||||
|
abilities:
|
||||||
|
- components:
|
||||||
|
- type: BatteryDrainer
|
||||||
|
- type: StunProvider
|
||||||
|
noPowerPopup: ninja-no-power
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- Stamina
|
||||||
|
- type: EmagProvider
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- Airlock
|
||||||
|
- objective: StealResearchObjective
|
||||||
|
components:
|
||||||
|
- type: ResearchStealer
|
||||||
|
- objective: TerrorObjective
|
||||||
|
components:
|
||||||
|
- type: CommsHacker
|
||||||
|
threats: NinjaThreats
|
||||||
|
- objective: MassArrestObjective
|
||||||
|
components:
|
||||||
|
- type: CriminalRecordsHacker
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingHandsGlovesColorBlack
|
parent: ClothingHandsGlovesColorBlack
|
||||||
|
|||||||
@@ -183,6 +183,36 @@
|
|||||||
- type: AddAccentClothing
|
- type: AddAccentClothing
|
||||||
accent: OwOAccent
|
accent: OwOAccent
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: [ClothingHeadHatCatEars, BaseToggleClothing]
|
||||||
|
id: ClothingHeadHatCatEarsValid
|
||||||
|
suffix: Valid, DO NOT MAP
|
||||||
|
components:
|
||||||
|
- type: ToggleClothing
|
||||||
|
action: ActionBecomeValid
|
||||||
|
disableOnUnequip: true
|
||||||
|
- type: ComponentToggler
|
||||||
|
parent: true
|
||||||
|
components:
|
||||||
|
- type: KillSign
|
||||||
|
- type: Tag
|
||||||
|
tags: [] # ignore "WhitelistChameleon" tag
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Clothing/Head/Hats/catears.rsi
|
||||||
|
- type: Clothing
|
||||||
|
sprite: Clothing/Head/Hats/catears.rsi
|
||||||
|
- type: AddAccentClothing
|
||||||
|
accent: OwOAccent
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
noSpawn: true
|
||||||
|
id: ActionBecomeValid
|
||||||
|
name: Become Valid
|
||||||
|
description: "*notices your killsign* owo whats this"
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
event: !type:ToggleActionEvent
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingHeadBase
|
parent: ClothingHeadBase
|
||||||
id: ClothingHeadHatDogEars
|
id: ClothingHeadHatDogEars
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
slots: WITHOUT_POCKET
|
slots: WITHOUT_POCKET
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing]
|
parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing, BaseToggleClothing]
|
||||||
id: ClothingOuterSuitSpaceNinja
|
id: ClothingOuterSuitSpaceNinja
|
||||||
name: space ninja suit
|
name: space ninja suit
|
||||||
description: This black technologically advanced, cybernetically-enhanced suit provides many abilities like invisibility or teleportation.
|
description: This black technologically advanced, cybernetically-enhanced suit provides many abilities like invisibility or teleportation.
|
||||||
@@ -136,9 +136,7 @@
|
|||||||
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
|
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
|
sprite: Clothing/OuterClothing/Suits/spaceninja.rsi
|
||||||
- type: StealthClothing
|
# hardsuit stuff
|
||||||
visibility: 1.1
|
|
||||||
toggleAction: ActionTogglePhaseCloak
|
|
||||||
- type: PressureProtection
|
- type: PressureProtection
|
||||||
highPressureMultiplier: 0.6
|
highPressureMultiplier: 0.6
|
||||||
lowPressureMultiplier: 1000
|
lowPressureMultiplier: 1000
|
||||||
@@ -151,7 +149,27 @@
|
|||||||
Slash: 0.8
|
Slash: 0.8
|
||||||
Piercing: 0.8
|
Piercing: 0.8
|
||||||
Heat: 0.8
|
Heat: 0.8
|
||||||
|
# phase cloak
|
||||||
|
- type: ToggleClothing
|
||||||
|
action: ActionTogglePhaseCloak
|
||||||
|
- type: ComponentToggler
|
||||||
|
parent: true
|
||||||
|
components:
|
||||||
|
- type: Stealth
|
||||||
|
minVisibility: 0.1
|
||||||
|
lastVisibility: 0.1
|
||||||
|
- type: PowerCellDraw
|
||||||
|
drawRate: 1.8 # 200 seconds on the default cell
|
||||||
|
# throwing star ability
|
||||||
|
- type: ItemCreator
|
||||||
|
action: ActionCreateThrowingStar
|
||||||
|
charge: 14.4
|
||||||
|
spawnedPrototype: ThrowingStarNinja
|
||||||
|
noPowerPopup: ninja-no-power
|
||||||
|
# core ninja suit stuff
|
||||||
- type: NinjaSuit
|
- type: NinjaSuit
|
||||||
|
- type: UseDelay
|
||||||
|
delay: 5 # disable time
|
||||||
- type: PowerCellSlot
|
- type: PowerCellSlot
|
||||||
cellSlotId: cell_slot
|
cellSlotId: cell_slot
|
||||||
# throwing in a recharger would bypass glove charging mechanic
|
# throwing in a recharger would bypass glove charging mechanic
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingShoesBase
|
parent: [ClothingShoesBase, BaseToggleClothing]
|
||||||
id: ClothingShoesBootsMag
|
id: ClothingShoesBootsMag
|
||||||
name: magboots
|
name: magboots
|
||||||
description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle.
|
description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle.
|
||||||
@@ -11,11 +11,17 @@
|
|||||||
map: [ "enum.ToggleVisuals.Layer" ]
|
map: [ "enum.ToggleVisuals.Layer" ]
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/magboots.rsi
|
sprite: Clothing/Shoes/Boots/magboots.rsi
|
||||||
|
- type: ToggleClothing
|
||||||
|
action: ActionToggleMagboots
|
||||||
|
- type: ToggleVerb
|
||||||
|
text: toggle-magboots-verb-get-data-text
|
||||||
|
- type: ComponentToggler
|
||||||
|
components:
|
||||||
|
- type: NoSlip
|
||||||
- type: Magboots
|
- type: Magboots
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 0.85
|
walkModifier: 0.85
|
||||||
sprintModifier: 0.8
|
sprintModifier: 0.8
|
||||||
enabled: false
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: GenericVisualizer
|
- type: GenericVisualizer
|
||||||
visuals:
|
visuals:
|
||||||
@@ -40,13 +46,9 @@
|
|||||||
state: icon
|
state: icon
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/magboots-advanced.rsi
|
sprite: Clothing/Shoes/Boots/magboots-advanced.rsi
|
||||||
- type: Magboots
|
|
||||||
toggleAction: ActionToggleMagbootsAdvanced
|
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 1
|
walkModifier: 1
|
||||||
sprintModifier: 1
|
sprintModifier: 1
|
||||||
enabled: false
|
|
||||||
- type: NoSlip
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- WhitelistChameleon
|
- WhitelistChameleon
|
||||||
@@ -64,8 +66,6 @@
|
|||||||
sprite: Clothing/Shoes/Boots/magboots-science.rsi
|
sprite: Clothing/Shoes/Boots/magboots-science.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/magboots-science.rsi
|
sprite: Clothing/Shoes/Boots/magboots-science.rsi
|
||||||
- type: Magboots
|
|
||||||
toggleAction: ActionToggleMagbootsSci
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingShoesBootsMag
|
parent: ClothingShoesBootsMag
|
||||||
@@ -76,7 +76,6 @@
|
|||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 1.10 #PVS isn't too much of an issue when you are blind...
|
walkModifier: 1.10 #PVS isn't too much of an issue when you are blind...
|
||||||
sprintModifier: 1.10
|
sprintModifier: 1.10
|
||||||
enabled: false
|
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 3000
|
price: 3000
|
||||||
|
|
||||||
@@ -91,12 +90,9 @@
|
|||||||
state: icon
|
state: icon
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi
|
sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi
|
||||||
- type: Magboots
|
|
||||||
toggleAction: ActionToggleMagbootsSyndie
|
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 0.95
|
walkModifier: 0.95
|
||||||
sprintModifier: 0.9
|
sprintModifier: 0.9
|
||||||
enabled: false
|
|
||||||
- type: GasTank
|
- type: GasTank
|
||||||
outputPressure: 42.6
|
outputPressure: 42.6
|
||||||
air:
|
air:
|
||||||
@@ -111,42 +107,10 @@
|
|||||||
size: Normal
|
size: Normal
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: ActionBaseToggleMagboots
|
id: ActionToggleMagboots
|
||||||
name: Toggle Magboots
|
name: Toggle Magboots
|
||||||
description: Toggles the magboots on and off.
|
description: Toggles the magboots on and off.
|
||||||
components:
|
components:
|
||||||
- type: InstantAction
|
- type: InstantAction
|
||||||
itemIconStyle: NoItem
|
itemIconStyle: BigItem
|
||||||
event: !type:ToggleMagbootsEvent
|
event: !type:ToggleActionEvent
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: ActionToggleMagboots
|
|
||||||
parent: ActionBaseToggleMagboots
|
|
||||||
components:
|
|
||||||
- type: InstantAction
|
|
||||||
icon: { sprite: Clothing/Shoes/Boots/magboots.rsi, state: icon }
|
|
||||||
iconOn: { sprite : Clothing/Shoes/Boots/magboots.rsi, state: icon-on }
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: ActionToggleMagbootsAdvanced
|
|
||||||
parent: ActionBaseToggleMagboots
|
|
||||||
components:
|
|
||||||
- type: InstantAction
|
|
||||||
icon: { sprite: Clothing/Shoes/Boots/magboots-advanced.rsi, state: icon }
|
|
||||||
iconOn: Clothing/Shoes/Boots/magboots-advanced.rsi/icon-on.png
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: ActionToggleMagbootsSci
|
|
||||||
parent: ActionBaseToggleMagboots
|
|
||||||
components:
|
|
||||||
- type: InstantAction
|
|
||||||
icon: { sprite: Clothing/Shoes/Boots/magboots-science.rsi, state: icon }
|
|
||||||
iconOn: Clothing/Shoes/Boots/magboots-science.rsi/icon-on.png
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: ActionToggleMagbootsSyndie
|
|
||||||
parent: ActionBaseToggleMagboots
|
|
||||||
components:
|
|
||||||
- type: InstantAction
|
|
||||||
icon: { sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi, state: icon }
|
|
||||||
iconOn: Clothing/Shoes/Boots/magboots-syndicate.rsi/icon-on.png
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
- type: NoSlip
|
- type: NoSlip
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [ClothingShoesBase, PowerCellSlotSmallItem]
|
parent: [ClothingShoesBase, PowerCellSlotSmallItem, BaseToggleClothing]
|
||||||
id: ClothingShoesBootsSpeed
|
id: ClothingShoesBootsSpeed
|
||||||
name: speed boots
|
name: speed boots
|
||||||
description: High-tech boots woven with quantum fibers, able to convert electricity into pure speed!
|
description: High-tech boots woven with quantum fibers, able to convert electricity into pure speed!
|
||||||
@@ -100,12 +100,11 @@
|
|||||||
map: [ "enum.ToggleVisuals.Layer" ]
|
map: [ "enum.ToggleVisuals.Layer" ]
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/speedboots.rsi
|
sprite: Clothing/Shoes/Boots/speedboots.rsi
|
||||||
- type: ToggleClothingSpeed
|
- type: ToggleClothing
|
||||||
toggleAction: ActionToggleSpeedBoots
|
action: ActionToggleSpeedBoots
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 1.5
|
walkModifier: 1.5
|
||||||
sprintModifier: 1.5
|
sprintModifier: 1.5
|
||||||
enabled: false
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: GenericVisualizer
|
- type: GenericVisualizer
|
||||||
visuals:
|
visuals:
|
||||||
@@ -130,10 +129,8 @@
|
|||||||
description: Toggles the speed boots on and off.
|
description: Toggles the speed boots on and off.
|
||||||
components:
|
components:
|
||||||
- type: InstantAction
|
- type: InstantAction
|
||||||
itemIconStyle: NoItem
|
itemIconStyle: BigItem
|
||||||
event: !type:ToggleClothingSpeedEvent
|
event: !type:ToggleActionEvent
|
||||||
icon: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon }
|
|
||||||
iconOn: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon-on }
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingShoesBase
|
parent: ClothingShoesBase
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user