Files
tbd-station-14/Content.Server/Light/Components/HandheldLightComponent.cs
Leon Friedrich 6cb58e608b ECS verbs and update context menu (#4594)
* Functioning ECS verbs

Currently only ID card console works.

* Changed verb types and allow ID card insertions

* Verb GUI sorting and verb networking

* More networking, and shared components

* Clientside verbs work now.

* Verb enums changed to bitmask flags

* Verb Categories redo

* Fix range check

* GasTank Verb

* Remove unnecessary bodypart verb

* Buckle Verb

* buckle & unbuckle verbs

* Updated range checks

* Item cabinet verbs

* Add range user override

* construction verb

* Chemistry machine verbs

* Climb Verb

* Generalise pulled entity verbs

* ViewVariables Verb

* rejuvenate, delete, sentient, control verbs

* Outfit verb

* inrangeunoccluded and tubedirection verbs

* attach-to verbs

* remove unused verbs and move VV

* Rename DebugVerbSystem

* Ghost role and pointing verbs

* Remove global verbs

* Allow verbs to raise events

* Changing categories and simplifying debug verbs

* Add rotate and flip verbs

* fix rejuvenate test

* redo context menu

* new Add Gas debug verb

* Add Set Temperature debug verb

* Uncuff verb

* Disposal unit verbs

* Add pickup verb

* lock/unlock verb

* Remove verb type, add specific verb events

* rename verb messages -> events

* Context menu displays verbs by interaction type

* Updated context menu HandleMove

previously, checked if entities moved 1 tile from click location.

Now checks if entities moved out of view.

Now you can actually right-click interact with yourself while walking!

* Misc Verb menu GUI changes

* Fix non-human/ghost verbs

* Update types and categories

* Allow non-ghost/human to open context menu

* configuration verb

* tagger verb

* Morgue Verbs

* Medical Scanner Verbs

* Fix solution refactor merge issues

* Fix context menu in-view check

* Remove prepare GUI

* Redo verb restrictions

* Fix context menu UI

* Disposal Verbs

* Spill verb

* Light verb

* Hand Held light verb

* power cell verbs

* storage verbs

and adding names to insert/eject

* Pulling verb

* Close context menu on verb execution

* Strip verb

* AmmoBox verb

* fix pull verb

* gun barrel verbs

revolver verb
energy weapon verbs
Bolt action verb

* Magazine gun barrel  verbs

* Add charger verbs

* PDA verbs

* Transfer amount verb

* Add reagent verb

* make alt-click use ECS verbs

* Delete old verb files

* Magboot verb

* finalising tweaks

* context menu visibility changes

* code cleanup

* Update AdminAddReagentUI.cs

* Remove HasFlag

* Consistent verb keys

* Remove Linq, add comment

* Fix in-inventory check

* Update GUI text alignment and padding

* Added close-menu option

* Changed some "interaction" verbs to "activation"

* Remove verb keys, use sorted sets

* fix master merge

* update some verb text

* Undo Changes

Remove some new verbs that can be added later

undid some .ftl bugfixes, can and should be done separately

* fix merge

* Undo file rename

* fix merge

* Misc Cleanup

* remove contraction

* Fix keybinding issue

* fix comment

* merge fix

* fix merge

* fix merge

* fix merge

* fix merge

* fix open-close verbs

* adjust uncuff verb

* fix merge

and undo the renaming of SharedPullableComponent to PullableComponent. I'm tired of all of those merge conflicts
2021-10-04 20:29:03 -07:00

280 lines
9.5 KiB
C#

using System.Threading.Tasks;
using Content.Server.Clothing.Components;
using Content.Server.Items;
using Content.Server.PowerCell.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
using Content.Shared.Actions.Behaviors.Item;
using Content.Shared.Actions.Components;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Light.Component;
using Content.Shared.Popups;
using Content.Shared.Rounding;
using Content.Shared.Sound;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.Light.Components
{
/// <summary>
/// Component that represents a powered handheld light source which can be toggled on and off.
/// </summary>
[RegisterComponent]
internal sealed class HandheldLightComponent : SharedHandheldLightComponent, IUse, IExamine, IInteractUsing
{
[ViewVariables(VVAccess.ReadWrite)] [DataField("wattage")] public float Wattage { get; set; } = 3f;
[ViewVariables] private PowerCellSlotComponent _cellSlot = default!;
private PowerCellComponent? Cell => _cellSlot.Cell;
/// <summary>
/// Status of light, whether or not it is emitting light.
/// </summary>
[ViewVariables]
public bool Activated { get; private set; }
[ViewVariables] protected override bool HasCell => _cellSlot.HasCell;
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOnSound")] public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Items/flashlight_on.ogg");
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOnFailSound")] public SoundSpecifier TurnOnFailSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
[ViewVariables(VVAccess.ReadWrite)] [DataField("turnOffSound")] public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/Items/flashlight_off.ogg");
[ComponentDependency] private readonly ItemActionsComponent? _itemActions = null;
/// <summary>
/// Client-side ItemStatus level
/// </summary>
private byte? _lastLevel;
protected override void Initialize()
{
base.Initialize();
Owner.EnsureComponent<PointLightComponent>();
_cellSlot = Owner.EnsureComponent<PowerCellSlotComponent>();
Dirty();
}
protected override void OnRemove()
{
base.OnRemove();
Owner.EntityManager.EventBus.QueueEvent(EventSource.Local, new DeactivateHandheldLightMessage(this));
}
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User)) return false;
if (!_cellSlot.InsertCell(eventArgs.Using)) return false;
Dirty();
return true;
}
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
{
if (Activated)
{
message.AddMarkup(Loc.GetString("handheld-light-component-on-examine-is-on-message"));
}
else
{
message.AddMarkup(Loc.GetString("handheld-light-component-on-examine-is-off-message"));
}
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{
return ToggleStatus(eventArgs.User);
}
/// <summary>
/// Illuminates the light if it is not active, extinguishes it if it is active.
/// </summary>
/// <returns>True if the light's status was toggled, false otherwise.</returns>
public bool ToggleStatus(IEntity user)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanUse(user)) return false;
return Activated ? TurnOff() : TurnOn(user);
}
public bool TurnOff(bool makeNoise = true)
{
if (!Activated)
{
return false;
}
SetState(false);
Activated = false;
UpdateLightAction();
Owner.EntityManager.EventBus.QueueEvent(EventSource.Local, new DeactivateHandheldLightMessage(this));
if (makeNoise)
{
SoundSystem.Play(Filter.Pvs(Owner), TurnOffSound.GetSound(), Owner);
}
return true;
}
public bool TurnOn(IEntity user)
{
if (Activated)
{
return false;
}
if (Cell == null)
{
SoundSystem.Play(Filter.Pvs(Owner), TurnOnFailSound.GetSound(), Owner);
Owner.PopupMessage(user, Loc.GetString("handheld-light-component-cell-missing-message"));
UpdateLightAction();
return false;
}
// To prevent having to worry about frame time in here.
// Let's just say you need a whole second of charge before you can turn it on.
// Simple enough.
if (Wattage > Cell.CurrentCharge)
{
SoundSystem.Play(Filter.Pvs(Owner), TurnOnFailSound.GetSound(), Owner);
Owner.PopupMessage(user, Loc.GetString("handheld-light-component-cell-dead-message"));
UpdateLightAction();
return false;
}
Activated = true;
UpdateLightAction();
SetState(true);
Owner.EntityManager.EventBus.QueueEvent(EventSource.Local, new ActivateHandheldLightMessage(this));
SoundSystem.Play(Filter.Pvs(Owner), TurnOnSound.GetSound(), Owner);
return true;
}
private void SetState(bool on)
{
if (Owner.TryGetComponent(out SpriteComponent? sprite))
{
sprite.LayerSetVisible(1, on);
}
if (Owner.TryGetComponent(out PointLightComponent? light))
{
light.Enabled = on;
}
if (Owner.TryGetComponent(out ClothingComponent? clothing))
{
clothing.ClothingEquippedPrefix = Loc.GetString(on ? "on" : "off");
}
if (Owner.TryGetComponent(out ItemComponent? item))
{
item.EquippedPrefix = Loc.GetString(on ? "on" : "off");
}
}
private void UpdateLightAction()
{
_itemActions?.Toggle(ItemActionType.ToggleLight, Activated);
}
public void OnUpdate(float frameTime)
{
if (Cell == null)
{
TurnOff(false);
return;
}
var appearanceComponent = Owner.GetComponent<AppearanceComponent>();
if (Cell.MaxCharge - Cell.CurrentCharge < Cell.MaxCharge * 0.70)
{
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.FullPower);
}
else if (Cell.MaxCharge - Cell.CurrentCharge < Cell.MaxCharge * 0.90)
{
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.LowPower);
}
else
{
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.Dying);
}
if (Activated && !Cell.TryUseCharge(Wattage * frameTime)) TurnOff(false);
var level = GetLevel();
if (level != _lastLevel)
{
_lastLevel = level;
Dirty();
}
}
// Curently every single flashlight has the same number of levels for status and that's all it uses the charge for
// Thus we'll just check if the level changes.
private byte? GetLevel()
{
if (Cell == null)
return null;
var currentCharge = Cell.CurrentCharge;
if (MathHelper.CloseToPercent(currentCharge, 0) || Wattage > currentCharge)
return 0;
return (byte?) ContentHelpers.RoundToNearestLevels(currentCharge / Cell.MaxCharge * 255, 255, StatusLevels);
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new HandheldLightComponentState(GetLevel());
}
}
[UsedImplicitly]
[DataDefinition]
public class ToggleLightAction : IToggleItemAction
{
public bool DoToggleAction(ToggleItemActionEventArgs args)
{
if (!args.Item.TryGetComponent<HandheldLightComponent>(out var lightComponent)) return false;
if (lightComponent.Activated == args.ToggledOn) return false;
return lightComponent.ToggleStatus(args.Performer);
}
}
internal sealed class ActivateHandheldLightMessage : EntityEventArgs
{
public HandheldLightComponent Component { get; }
public ActivateHandheldLightMessage(HandheldLightComponent component)
{
Component = component;
}
}
internal sealed class DeactivateHandheldLightMessage : EntityEventArgs
{
public HandheldLightComponent Component { get; }
public DeactivateHandheldLightMessage(HandheldLightComponent component)
{
Component = component;
}
}
}