You feel a tiny prick (Hypospray) (#3034)
This commit is contained in:
committed by
GitHub
parent
72b5ae9468
commit
c947bb75e6
@@ -0,0 +1,68 @@
|
|||||||
|
using Content.Client.UserInterface.Stylesheets;
|
||||||
|
using Content.Client.Utility;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Content.Shared.GameObjects.Components.Chemistry;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Chemistry
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class HyposprayComponent : SharedHyposprayComponent, IItemStatus
|
||||||
|
{
|
||||||
|
[ViewVariables] private ReagentUnit CurrentVolume { get; set; }
|
||||||
|
[ViewVariables] private ReagentUnit TotalVolume { get; set; }
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||||
|
|
||||||
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
|
{
|
||||||
|
if (curState is not HyposprayComponentState cState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CurrentVolume = cState.CurVolume;
|
||||||
|
TotalVolume = cState.MaxVolume;
|
||||||
|
_uiUpdateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Control IItemStatus.MakeControl()
|
||||||
|
{
|
||||||
|
return new StatusControl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class StatusControl : Control
|
||||||
|
{
|
||||||
|
private readonly HyposprayComponent _parent;
|
||||||
|
private readonly RichTextLabel _label;
|
||||||
|
|
||||||
|
public StatusControl(HyposprayComponent parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
_label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
|
||||||
|
AddChild(_label);
|
||||||
|
|
||||||
|
parent._uiUpdateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.Update(args);
|
||||||
|
if (!_parent._uiUpdateNeeded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parent._uiUpdateNeeded = false;
|
||||||
|
|
||||||
|
_label.SetMarkup(Loc.GetString(
|
||||||
|
"Volume: [color=white]{0}/{1}[/color]",
|
||||||
|
_parent.CurrentVolume, _parent.TotalVolume));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs.State;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Content.Shared.GameObjects.Components.Chemistry;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Chemistry
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class HyposprayComponent : SharedHyposprayComponent, IAttack, ISolutionChange, IAfterInteract
|
||||||
|
{
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] public float ClumsyFailChance { get; set; }
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] public ReagentUnit TransferAmount { get; set; }
|
||||||
|
|
||||||
|
[ComponentDependency] private readonly SolutionContainerComponent? _solution = default!;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(this, x => x.ClumsyFailChance, "ClumsyFailChance", 0.5f);
|
||||||
|
serializer.DataField(this, x => x.TransferAmount, "TransferAmount", ReagentUnit.New(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IAttack.ClickAttack(AttackEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var target = eventArgs.TargetEntity;
|
||||||
|
var user = eventArgs.User;
|
||||||
|
|
||||||
|
return TryDoInject(target, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
TryDoInject(eventArgs.Target, eventArgs.User);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryDoInject(IEntity? target, IEntity user)
|
||||||
|
{
|
||||||
|
if (target == null || !EligibleEntity(target))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var msgFormat = "You inject {0:TheName}.";
|
||||||
|
|
||||||
|
if (target == user)
|
||||||
|
{
|
||||||
|
msgFormat = "You inject yourself.";
|
||||||
|
}
|
||||||
|
else if (EligibleEntity(user) && ClumsyComponent.TryRollClumsy(user, ClumsyFailChance))
|
||||||
|
{
|
||||||
|
msgFormat = "Oops! You injected yourself!";
|
||||||
|
target = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_solution == null || _solution.CurrentVolume == 0)
|
||||||
|
{
|
||||||
|
user.PopupMessageCursor(Loc.GetString("It's empty!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.PopupMessage(Loc.GetString(msgFormat, target));
|
||||||
|
if (target != user)
|
||||||
|
{
|
||||||
|
target.PopupMessage(Loc.GetString("You feel a tiny prick!"));
|
||||||
|
var meleeSys = EntitySystem.Get<MeleeWeaponSystem>();
|
||||||
|
var angle = new Angle(target.Transform.WorldPosition - user.Transform.WorldPosition);
|
||||||
|
meleeSys.SendLunge(angle, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/hypospray.ogg", user);
|
||||||
|
|
||||||
|
var targetSolution = target.GetComponent<SolutionContainerComponent>();
|
||||||
|
|
||||||
|
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||||
|
var realTransferAmount = ReagentUnit.Min(TransferAmount, targetSolution.EmptyVolume);
|
||||||
|
|
||||||
|
if (realTransferAmount <= 0)
|
||||||
|
{
|
||||||
|
user.PopupMessage(user, Loc.GetString("{0:TheName} is already full!", targetSolution.Owner));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move units from attackSolution to targetSolution
|
||||||
|
var removedSolution = _solution.SplitSolution(realTransferAmount);
|
||||||
|
|
||||||
|
if (!targetSolution.CanAddSolution(removedSolution))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
removedSolution.DoEntityReaction(target, ReactionMethod.Injection);
|
||||||
|
|
||||||
|
targetSolution.TryAddSolution(removedSolution);
|
||||||
|
|
||||||
|
static bool EligibleEntity(IEntity entity)
|
||||||
|
{
|
||||||
|
// TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag?
|
||||||
|
// In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else.
|
||||||
|
return entity.HasComponent<SolutionContainerComponent>() && entity.HasComponent<MobStateComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ComponentState GetComponentState()
|
||||||
|
{
|
||||||
|
if (_solution == null)
|
||||||
|
return new HyposprayComponentState(ReagentUnit.Zero, ReagentUnit.Zero);
|
||||||
|
|
||||||
|
return new HyposprayComponentState(_solution.CurrentVolume, _solution.MaxVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Chemistry
|
||||||
|
{
|
||||||
|
public abstract class SharedHyposprayComponent : Component
|
||||||
|
{
|
||||||
|
public sealed override string Name => "Hypospray";
|
||||||
|
public sealed override uint? NetID => ContentNetIDs.HYPOSPRAY;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
protected sealed class HyposprayComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public ReagentUnit CurVolume { get; }
|
||||||
|
public ReagentUnit MaxVolume { get; }
|
||||||
|
|
||||||
|
public HyposprayComponentState(ReagentUnit curVolume, ReagentUnit maxVolume) : base(ContentNetIDs.HYPOSPRAY)
|
||||||
|
{
|
||||||
|
CurVolume = curVolume;
|
||||||
|
MaxVolume = maxVolume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
// Starting from 1000 to avoid crossover with engine.
|
// Starting from 1000 to avoid crossover with engine.
|
||||||
public static class ContentNetIDs
|
public static class ContentNetIDs
|
||||||
{
|
{
|
||||||
// 1000
|
// As a CMO main I hereby declare the hypospray worthy of ID #1000.
|
||||||
|
public const uint HYPOSPRAY = 1000;
|
||||||
public const uint DESTRUCTIBLE = 1001;
|
public const uint DESTRUCTIBLE = 1001;
|
||||||
public const uint MAGAZINE_BARREL = 1002;
|
public const uint MAGAZINE_BARREL = 1002;
|
||||||
public const uint HANDS = 1003;
|
public const uint HANDS = 1003;
|
||||||
|
|||||||
BIN
Resources/Audio/Items/hypospray.ogg
Normal file
BIN
Resources/Audio/Items/hypospray.ogg
Normal file
Binary file not shown.
15
Resources/Prototypes/Entities/Objects/hypospray.yml
Normal file
15
Resources/Prototypes/Entities/Objects/hypospray.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
- type: entity
|
||||||
|
name: hypospray
|
||||||
|
parent: BaseItem
|
||||||
|
description: A sterile injector for rapid administration of drugs to patients.
|
||||||
|
id: Hypospray
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Specific/Medical/hypospray.rsi
|
||||||
|
state: hypo
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Specific/Medical/hypospray.rsi
|
||||||
|
- type: SolutionContainer
|
||||||
|
maxVol: 30
|
||||||
|
caps: AddTo, CanExamine
|
||||||
|
- type: Hypospray
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 429 B |
Binary file not shown.
|
After Width: | Height: | Size: 319 B |
Binary file not shown.
|
After Width: | Height: | Size: 319 B |
@@ -0,0 +1 @@
|
|||||||
|
{"version": 1, "size": {"x": 32, "y": 32}, "license": "Taken from https://github.com/tgstation/tgstation/tree/727eb0a445bccbdc2d472e158e96b87fc0e997a1", "copyright": "CC-BY-SA-3.0", "states": [{"name": "hypo", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
||||||
Reference in New Issue
Block a user