26
Content.Client/HotPotato/HotPotatoSystem.cs
Normal file
26
Content.Client/HotPotato/HotPotatoSystem.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Shared.HotPotato;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
if (!_timing.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var query = AllEntityQuery<ActiveHotPotatoComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp))
|
||||||
|
{
|
||||||
|
if (_timing.CurTime < comp.TargetTime)
|
||||||
|
continue;
|
||||||
|
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
|
||||||
|
Spawn("HotPotatoEffect", Transform(uid).MapPosition.Offset(_random.NextVector2(0.25f)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,12 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when timer trigger becomes active.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly record struct ActiveTimerTriggerEvent(EntityUid Triggered, EntityUid? User);
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed partial class TriggerSystem : EntitySystem
|
public sealed partial class TriggerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -184,6 +190,9 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
active.BeepInterval = beepInterval;
|
active.BeepInterval = beepInterval;
|
||||||
active.TimeUntilBeep = initialBeepDelay == null ? active.BeepInterval : initialBeepDelay.Value;
|
active.TimeUntilBeep = initialBeepDelay == null ? active.BeepInterval : initialBeepDelay.Value;
|
||||||
|
|
||||||
|
var ev = new ActiveTimerTriggerEvent(uid, user);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||||
_appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Primed, appearance);
|
_appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Primed, appearance);
|
||||||
}
|
}
|
||||||
|
|||||||
55
Content.Server/HotPotato/HotPotatoSystem.cs
Normal file
55
Content.Server/HotPotato/HotPotatoSystem.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
|
using Content.Shared.HotPotato;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
|
|
||||||
|
namespace Content.Server.HotPotato;
|
||||||
|
|
||||||
|
public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<HotPotatoComponent, ActiveTimerTriggerEvent>(OnActiveTimer);
|
||||||
|
SubscribeLocalEvent<HotPotatoComponent, MeleeHitEvent>(OnMeleeHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActiveTimer(EntityUid uid, HotPotatoComponent comp, ref ActiveTimerTriggerEvent args)
|
||||||
|
{
|
||||||
|
EnsureComp<ActiveHotPotatoComponent>(uid);
|
||||||
|
comp.CanTransfer = false;
|
||||||
|
Dirty(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMeleeHit(EntityUid uid, HotPotatoComponent comp, MeleeHitEvent args)
|
||||||
|
{
|
||||||
|
if (!HasComp<ActiveHotPotatoComponent>(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
comp.CanTransfer = true;
|
||||||
|
foreach (var hitEntity in args.HitEntities)
|
||||||
|
{
|
||||||
|
if (!TryComp<HandsComponent>(hitEntity, out var hands))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!_hands.IsHolding(hitEntity, uid, out _, hands) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("hot-potato-passed",
|
||||||
|
("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_popup.PopupEntity(Loc.GetString("hot-potato-failed",
|
||||||
|
("to", hitEntity)), uid, PopupType.Medium);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
comp.CanTransfer = false;
|
||||||
|
Dirty(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,6 +104,29 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to pick up an entity into any hand, forcing to drop an item if there are no free hands
|
||||||
|
/// By default it does check if it's possible to drop items
|
||||||
|
/// </summary>
|
||||||
|
public bool TryForcePickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
foreach (var hand in handsComp.Hands.Values)
|
||||||
|
{
|
||||||
|
if (TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp) &&
|
||||||
|
TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanPickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
public bool CanPickupAnyHand(EntityUid uid, EntityUid entity, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
|
|||||||
23
Content.Shared/HotPotato/ActiveHotPotatoComponent.cs
Normal file
23
Content.Shared/HotPotato/ActiveHotPotatoComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.HotPotato;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to an activated hot potato. Controls hot potato transfer on server / effect spawning on client.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access(typeof(SharedHotPotatoSystem))]
|
||||||
|
public sealed class ActiveHotPotatoComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hot potato effect spawn cooldown in seconds
|
||||||
|
/// </summary>
|
||||||
|
[DataField("effectCooldown"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float EffectCooldown = 0.3f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moment in time next effect will be spawned
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan TargetTime = TimeSpan.Zero;
|
||||||
|
}
|
||||||
20
Content.Shared/HotPotato/HotPotatoComponent.cs
Normal file
20
Content.Shared/HotPotato/HotPotatoComponent.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.HotPotato;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Similar to <see cref="Content.Shared.Interaction.Components.UnremoveableComponent"/>
|
||||||
|
/// except entities with this component can be removed in specific case: <see cref="CanTransfer"/>
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedHotPotatoSystem))]
|
||||||
|
public sealed partial class HotPotatoComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If set to true entity can be removed by hitting entities if they have hands
|
||||||
|
/// </summary>
|
||||||
|
[DataField("canTransfer"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[AutoNetworkedField]
|
||||||
|
public bool CanTransfer = true;
|
||||||
|
}
|
||||||
18
Content.Shared/HotPotato/SharedHotPotatoSystem.cs
Normal file
18
Content.Shared/HotPotato/SharedHotPotatoSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
namespace Content.Shared.HotPotato;
|
||||||
|
|
||||||
|
public abstract class SharedHotPotatoSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<HotPotatoComponent, ContainerGettingRemovedAttemptEvent>(OnRemoveAttempt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoveAttempt(EntityUid uid, HotPotatoComponent comp, ContainerGettingRemovedAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (!comp.CanTransfer)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Resources/Locale/en-US/hot-potato/hot-potato.ftl
Normal file
2
Resources/Locale/en-US/hot-potato/hot-potato.ftl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
hot-potato-passed = {$from} passed hot potato to {$to}!
|
||||||
|
hot-potato-failed = Can't pass the potato to {$to}!
|
||||||
@@ -157,6 +157,9 @@ uplink-revolver-cap-gun-fake-desc = Fool your enemy! It can use both cap and mag
|
|||||||
uplink-banana-peel-explosive-name = Explosive Banana Peel
|
uplink-banana-peel-explosive-name = Explosive Banana Peel
|
||||||
uplink-banana-peel-explosive-desc = They will burst into laughter when they slip on it!
|
uplink-banana-peel-explosive-desc = They will burst into laughter when they slip on it!
|
||||||
|
|
||||||
|
uplink-hot-potato-name = Hot Potato
|
||||||
|
uplink-hot-potato-desc = Once activated, this time bomb can't be dropped - only passed to someone else!
|
||||||
|
|
||||||
# Armor
|
# Armor
|
||||||
uplink-chameleon-name = Chameleon Kit
|
uplink-chameleon-name = Chameleon Kit
|
||||||
uplink-chameleon-desc = A backpack full of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more!
|
uplink-chameleon-desc = A backpack full of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more!
|
||||||
|
|||||||
@@ -620,6 +620,23 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
- Clown
|
- Clown
|
||||||
|
|
||||||
|
- type: listing
|
||||||
|
id: uplinkHotPotato
|
||||||
|
name: uplink-hot-potato-name
|
||||||
|
description: uplink-hot-potato-desc
|
||||||
|
productEntity: HotPotato
|
||||||
|
cost:
|
||||||
|
Telecrystal: 4
|
||||||
|
categories:
|
||||||
|
- UplinkJob
|
||||||
|
conditions:
|
||||||
|
- !type:BuyerJobCondition
|
||||||
|
whitelist:
|
||||||
|
- Chef
|
||||||
|
- Botanist
|
||||||
|
- Clown
|
||||||
|
- Mime
|
||||||
|
|
||||||
# Armor
|
# Armor
|
||||||
|
|
||||||
- type: listing
|
- type: listing
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
- type: entity
|
||||||
|
name: hot potato
|
||||||
|
description: Once activated, this time bomb can't be dropped - only passed to someone else!
|
||||||
|
parent: BaseItem
|
||||||
|
id: HotPotato
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Bombs/hot_potato.rsi
|
||||||
|
state: icon
|
||||||
|
netsync: false
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Weapons/Bombs/hot_potato.rsi
|
||||||
|
size: 5
|
||||||
|
- type: MeleeWeapon
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Blunt: 5
|
||||||
|
- type: OnUseTimerTrigger
|
||||||
|
delay: 180
|
||||||
|
beepSound: /Audio/Machines/Nuke/general_beep.ogg
|
||||||
|
- type: ExplodeOnTrigger
|
||||||
|
- type: Explosive
|
||||||
|
explosionType: Default
|
||||||
|
maxIntensity: 8
|
||||||
|
intensitySlope: 5
|
||||||
|
totalIntensity: 20
|
||||||
|
canCreateVacuum: false
|
||||||
|
- type: DeleteOnTrigger
|
||||||
|
- type: HotPotato
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: GenericEnumVisualizer
|
||||||
|
key: enum.Trigger.TriggerVisuals.VisualState
|
||||||
|
states:
|
||||||
|
enum.Trigger.TriggerVisualState.Primed: activated
|
||||||
|
enum.Trigger.TriggerVisualState.Unprimed: complete
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: HotPotatoEffect
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 0.6
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
noRot: true
|
||||||
|
drawdepth: Effects
|
||||||
|
sprite: Effects/chemsmoke.rsi
|
||||||
|
state: chemsmoke
|
||||||
|
scale: "0.15, 0.15"
|
||||||
|
- type: EffectVisuals
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- HideContextMenu
|
||||||
|
- type: AnimationPlayer
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png
Normal file
BIN
Resources/Textures/Objects/Weapons/Bombs/hot_potato.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 533 B |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "activated",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user