using Content.Client.Clothing;
using Content.Client.Examine;
using Content.Client.Verbs.UI;
-using Content.Shared.Clothing.Components;
using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Storage;
_equipEventsQueue.Enqueue((comp, args)));
SubscribeLocalEvent<InventorySlotsComponent, DidUnequipEvent>((_, comp, args) =>
_equipEventsQueue.Enqueue((comp, args)));
-
- SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
}
public override void Update(float frameTime)
}
}
- private void OnUseInHand(EntityUid uid, ClothingComponent component, UseInHandEvent args)
- {
- if (args.Handled || !component.QuickEquip)
- return;
-
- QuickEquip(uid, component, args);
- }
-
private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args)
{
UpdateSlot(args.Equipee, component, args.Slot);
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Hands.Controls;
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
-using Content.Shared.Cooldown;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
-using Robust.Client.GameObjects;
+using Content.Shared.Timing;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
if (HandsGui != null &&
_playerHandsComponent != null &&
- _player.LocalPlayer?.ControlledEntity is { } playerEntity &&
+ _player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand(playerEntity, handName, out var hand, _playerHandsComponent))
{
HandsGui.UpdatePanelEntity(hand.HeldEntity);
private bool RemoveHand(string handName, out HandButton? handButton)
{
- handButton = null;
if (!_handLookup.TryGetValue(handName, out handButton))
return false;
if (handButton.Parent is HandsContainer handContainer)
{
foreach (var hand in container.GetButtons())
{
- if (!_entities.TryGetComponent(hand.Entity, out ItemCooldownComponent? cooldown) ||
- cooldown is not { CooldownStart: { } start, CooldownEnd: { } end})
+
+ if (!_entities.TryGetComponent(hand.Entity, out UseDelayComponent? useDelay) ||
+ useDelay is not { DelayStartTime: var start, DelayEndTime: var end })
{
hand.CooldownDisplay.Visible = false;
return;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
-using Content.Server.Explosion.EntitySystems;
using Content.Server.IgnitionSource;
using Content.Server.Stunnable;
using Content.Server.Temperature.Components;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
using Content.Shared.Physics;
using Content.Shared.Popups;
using Content.Shared.Rejuvenate;
using Content.Shared.Toggleable;
using Content.Shared.Weapons.Melee.Events;
using Robust.Server.Audio;
-using Robust.Server.GameObjects;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
[Dependency] private readonly IgnitionSourceSystem _ignitionSourceSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
- [Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly FixtureSystem _fixture = default!;
- [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
private float _timer;
private readonly Dictionary<Entity<FlammableComponent>, float> _fireEvents = new();
- private readonly List<EntityUid> _toRemove = new();
public override void Initialize()
{
args.Handled = true;
- if (!_useDelay.BeginDelay(uid))
+ if (!TryComp(uid, out UseDelayComponent? useDelay) || !_useDelay.TryResetDelay((uid, useDelay), true))
return;
_audio.PlayPvs(component.ExtinguishAttemptSound, uid);
+
if (_random.Prob(component.Probability))
{
AdjustFireStacks(uid, component.StackDelta, flammable);
_popup.PopupEntity(Loc.GetString(component.ExtinguishFailed), uid);
}
}
+
private void OnCollide(EntityUid uid, FlammableComponent flammable, ref StartCollideEvent args)
{
var otherUid = args.OtherEntity;
// TODO: This needs cleanup to take off the crust from TemperatureComponent and shit.
var query = EntityQueryEnumerator<FlammableComponent, TransformComponent>();
- while (query.MoveNext(out var uid, out var flammable, out var transform))
+ while (query.MoveNext(out var uid, out var flammable, out _))
{
// Slowly dry ourselves off if wet.
if (flammable.FireStacks < 0)
EnsureComp<IgnitionSourceComponent>(uid);
_ignitionSourceSystem.SetIgnited(uid);
- var damageScale = MathF.Min(flammable.FireStacks, 5);
+ var damageScale = MathF.Min( flammable.FireStacks, 5);
if (TryComp(uid, out TemperatureComponent? temp))
_temperatureSystem.ChangeHeat(uid, 12500 * damageScale, false, temp);
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly UseDelaySystem _delay = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
{
base.Update(frameTime);
- foreach(var entity in _addQueue)
+ foreach (var entity in _addQueue)
{
EnsureComp<SummonableRespawningComponent>(entity);
}
_addQueue.Clear();
- foreach(var entity in _remQueue)
+ foreach (var entity in _remQueue)
{
RemComp<SummonableRespawningComponent>(entity);
}
_remQueue.Clear();
var query = EntityQueryEnumerator<SummonableRespawningComponent, SummonableComponent>();
- while (query.MoveNext(out var uid, out var respawning, out var summonableComp))
+ while (query.MoveNext(out var uid, out var _, out var summonableComp))
{
summonableComp.Accumulator += frameTime;
if (summonableComp.Accumulator < summonableComp.RespawnTime)
summonableComp.Summon = null;
}
summonableComp.AlreadySummoned = false;
- _popupSystem.PopupEntity(Loc.GetString("bible-summon-respawn-ready", ("book", summonableComp.Owner)), summonableComp.Owner, PopupType.Medium);
- _audio.PlayPvs("/Audio/Effects/radpulse9.ogg", summonableComp.Owner, AudioParams.Default.WithVolume(-4f));
+ _popupSystem.PopupEntity(Loc.GetString("bible-summon-respawn-ready", ("book", uid)), uid, PopupType.Medium);
+ _audio.PlayPvs("/Audio/Effects/radpulse9.ogg", uid, AudioParams.Default.WithVolume(-4f));
// Clean up the accumulator and respawn tracking component
summonableComp.Accumulator = 0;
_remQueue.Enqueue(uid);
if (!args.CanReach)
return;
- UseDelayComponent? delay = null;
-
- if (_delay.ActiveDelay(uid, delay))
+ if (!TryComp(uid, out UseDelayComponent? useDelay) || _delay.IsDelayed((uid, useDelay)))
return;
if (args.Target == null || args.Target == args.User || !_mobStateSystem.IsAlive(args.Target.Value))
_audio.PlayPvs(component.SizzleSoundPath, args.User);
_damageableSystem.TryChangeDamage(args.User, component.DamageOnUntrainedUse, true, origin: uid);
- _delay.BeginDelay(uid, delay);
+ _delay.TryResetDelay((uid, useDelay));
return;
}
{
if (_random.Prob(component.FailChance))
{
- var othersFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-others", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var othersFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(othersFailMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.SmallCaution);
- var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(selfFailMessage, args.User, args.User, PopupType.MediumCaution);
_audio.PlayPvs("/Audio/Effects/hit_kick.ogg", args.User);
_damageableSystem.TryChangeDamage(args.Target.Value, component.DamageOnFail, true, origin: uid);
- _delay.BeginDelay(uid, delay);
+ _delay.TryResetDelay((uid, useDelay));
return;
}
}
if (damage == null || damage.Empty)
{
- var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-none-others", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-none-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.Medium);
- var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-none-self", ("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-none-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(selfMessage, args.User, args.User, PopupType.Large);
}
else
{
- var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-others", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.Medium);
- var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-self", ("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
+ var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(selfMessage, args.User, args.User, PopupType.Large);
_audio.PlayPvs(component.HealSoundPath, args.User);
- _delay.BeginDelay(uid, delay);
+ _delay.TryResetDelay((uid, useDelay));
}
}
{
Act = () =>
{
- if (!TryComp<TransformComponent>(args.User, out var userXform)) return;
+ if (!TryComp<TransformComponent>(args.User, out var userXform))
+ return;
AttemptSummon((uid, component), args.User, userXform);
},
// Make this familiar the component's summon
var familiar = EntityManager.SpawnEntity(component.SpecialItemPrototype, position.Coordinates);
- component.Summon = familiar;
+ component.Summon = familiar;
// If this is going to use a ghost role mob spawner, attach it to the bible.
if (HasComp<GhostRoleMobSpawnerComponent>(familiar))
{
_popupSystem.PopupEntity(Loc.GetString("bible-summon-requested"), user, PopupType.Medium);
- Transform(familiar).AttachParent(uid);
+ _transform.SetParent(familiar, uid);
}
component.AlreadySummoned = true;
_actionsSystem.RemoveAction(user, component.SummonActionEntity);
using Content.Shared.Interaction;
using Content.Shared.Timing;
using Content.Shared.Verbs;
-using Robust.Shared.Player;
namespace Content.Server.Cargo.Systems;
private void OnUtilityVerb(EntityUid uid, PriceGunComponent component, GetVerbsEvent<UtilityVerb> args)
{
-
- if (!args.CanAccess || !args.CanInteract)
+ if (!args.CanAccess || !args.CanInteract || args.Using == null)
return;
- if (TryComp(args.Using, out UseDelayComponent? useDelay) && useDelay.ActiveDelay)
+ if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
return;
var price = _pricingSystem.GetPrice(args.Target);
Act = () =>
{
_popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target, EntityManager)), ("price", $"{price:F2}")), args.User, args.User);
- _useDelay.BeginDelay(uid, useDelay);
+ _useDelay.TryResetDelay((uid, useDelay));
},
Text = Loc.GetString("price-gun-verb-text"),
Message = Loc.GetString("price-gun-verb-message", ("object", Identity.Entity(args.Target, EntityManager)))
args.Verbs.Add(verb);
}
+
private void OnAfterInteract(EntityUid uid, PriceGunComponent component, AfterInteractEvent args)
{
if (!args.CanReach || args.Target == null || args.Handled)
return;
- if (TryComp(args.Used, out UseDelayComponent? useDelay) && useDelay.ActiveDelay)
+ if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
return;
var price = _pricingSystem.GetPrice(args.Target.Value);
_popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target.Value, EntityManager)), ("price", $"{price:F2}")), args.User, args.User);
- _useDelay.BeginDelay(uid, useDelay);
+ _useDelay.TryResetDelay((uid, useDelay));
args.Handled = true;
}
}
if (!EligibleEntity(target, _entMan, component))
return false;
- if (TryComp(uid, out UseDelayComponent? delayComp) && _useDelay.ActiveDelay(uid, delayComp))
- return false;
+ if (TryComp(uid, out UseDelayComponent? delayComp))
+ {
+ if (_useDelay.IsDelayed((uid, delayComp)))
+ return false;
+ }
+
string? msgFormat = null;
// Medipens and such use this system and don't have a delay, requiring extra checks
// BeginDelay function returns if item is already on delay
- if (delayComp is not null)
- _useDelay.BeginDelay(uid, delayComp);
+ if (delayComp != null)
+ _useDelay.TryResetDelay((uid, delayComp));
// Get transfer amount. May be smaller than component.TransferAmount if not enough room
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
+++ /dev/null
-using Content.Shared.Cooldown;
-
-namespace Content.Server.Cooldown
-{
- public sealed class ItemCooldownSystem : EntitySystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<ItemCooldownComponent, RefreshItemCooldownEvent>(OnItemCooldownRefreshed);
- }
-
- public void OnItemCooldownRefreshed(EntityUid uid, ItemCooldownComponent comp, RefreshItemCooldownEvent args)
- {
- comp.CooldownStart = args.LastAttackTime;
- comp.CooldownEnd = args.CooldownEnd;
- }
- }
-
- public sealed class RefreshItemCooldownEvent : EntityEventArgs
- {
- public TimeSpan LastAttackTime { get; }
- public TimeSpan CooldownEnd { get; }
-
- public RefreshItemCooldownEvent(TimeSpan lastAttackTime, TimeSpan cooldownEnd)
- {
- LastAttackTime = lastAttackTime;
- CooldownEnd = cooldownEnd;
- }
- }
-}
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Timing;
-using Content.Shared.Tools;
using Content.Shared.Tools.Systems;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
return;
// no sound spamming
- if (TryComp<UseDelayComponent>(uid, out var useDelay) && _useDelay.ActiveDelay(uid, useDelay))
+ if (TryComp<UseDelayComponent>(uid, out var useDelay)
+ && !_useDelay.TryResetDelay((uid, useDelay), true))
return;
// cycle through possible gates
var msg = Loc.GetString("logic-gate-cycle", ("gate", comp.Gate.ToString().ToUpper()));
_popup.PopupEntity(msg, uid, args.User);
_appearance.SetData(uid, LogicGateVisuals.Gate, comp.Gate);
-
- _useDelay.BeginDelay(uid, useDelay);
}
private void OnSignalReceived(EntityUid uid, LogicGateComponent comp, ref SignalReceivedEvent args)
using Content.Server.DeviceLinking.Components;
-using Content.Server.DeviceNetwork;
using Content.Server.NodeContainer;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.Nodes;
using Content.Shared.Popups;
using Content.Shared.Power.Generator;
using Content.Shared.Timing;
-using Content.Shared.Tools;
using Content.Shared.Tools.Systems;
using Robust.Shared.Audio.Systems;
-using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
namespace Content.Server.DeviceLinking.Systems;
{
[Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
[Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly PowerNetSystem _powerNet = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
return;
// no sound spamming
- if (TryComp<UseDelayComponent>(uid, out var useDelay) && _useDelay.ActiveDelay(uid, useDelay))
+ if (TryComp<UseDelayComponent>(uid, out var useDelay)
+ && !_useDelay.TryResetDelay((uid, useDelay), true))
return;
// switch between input and output mode.
_audio.PlayPvs(comp.SwitchSound, uid);
var msg = Loc.GetString("power-sensor-switch", ("output", comp.Output));
_popup.PopupEntity(msg, uid, args.User);
-
- _useDelay.BeginDelay(uid, useDelay);
}
private void UpdateOutputs(EntityUid uid, PowerSensorComponent comp)
// update state based on the power stats retrieved from the selected power network
var xform = _xformQuery.GetComponent(uid);
- _mapManager.TryGetGrid(xform.GridUid, out var grid);
+ if (!TryComp(xform.GridUid, out MapGridComponent? grid))
+ return;
+
var cables = deviceNode.GetReachableNodes(xform, _nodeQuery, _xformQuery, grid, EntityManager);
foreach (var node in cables)
{
private void OnTrigger(EntityUid uid, SignallerComponent component, TriggerEvent args)
{
- // if on cooldown, do nothing
- var hasUseDelay = TryComp<UseDelayComponent>(uid, out var useDelay);
- if (hasUseDelay && _useDelay.ActiveDelay(uid, useDelay))
+ if (!TryComp(uid, out UseDelayComponent? useDelay)
+ // if on cooldown, do nothing
+ // and set cooldown to prevent clocks
+ || !_useDelay.TryResetDelay((uid, useDelay), true))
return;
- // set cooldown to prevent clocks
- if (hasUseDelay)
- _useDelay.BeginDelay(uid, useDelay);
-
_link.InvokePort(uid, component.Port);
args.Handled = true;
}
using Content.Shared.Timing;
using Content.Shared.Weapons.Melee;
using Robust.Server.Audio;
-using Robust.Shared.Map;
+using Robust.Server.GameObjects;
+using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
/// <inheritdoc/>
public sealed class AbsorbentSystem : SharedAbsorbentSystem
{
- [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly PopupSystem _popups = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
+ [Dependency] private readonly MapSystem _mapSystem = default!;
public override void Initialize()
{
if (!_solutionContainerSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
return;
- if (_useDelay.ActiveDelay(used))
+ if (TryComp<UseDelayComponent>(used, out var useDelay)
+ && _useDelay.IsDelayed((used, useDelay)))
return;
// If it's a puddle try to grab from
- if (!TryPuddleInteract(user, used, target, component, absorberSoln.Value))
+ if (!TryPuddleInteract(user, used, target, component, useDelay, absorberSoln.Value))
{
// If it's refillable try to transfer
- if (!TryRefillableInteract(user, used, target, component, absorberSoln.Value))
+ if (!TryRefillableInteract(user, used, target, component, useDelay, absorberSoln.Value))
return;
}
}
/// <summary>
/// Logic for an absorbing entity interacting with a refillable.
/// </summary>
- private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Entity<SolutionComponent> absorbentSoln)
+ private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, UseDelayComponent? useDelay, Entity<SolutionComponent> absorbentSoln)
{
if (!TryComp(target, out RefillableSolutionComponent? refillable))
return false;
}
_audio.PlayPvs(component.TransferSound, target);
- _useDelay.BeginDelay(used);
+ if (useDelay != null)
+ _useDelay.TryResetDelay((used, useDelay));
return true;
}
/// <summary>
/// Logic for an absorbing entity interacting with a puddle.
/// </summary>
- private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, Entity<SolutionComponent> absorberSoln)
+ private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, UseDelayComponent? useDelay, Entity<SolutionComponent> absorberSoln)
{
if (!TryComp(target, out PuddleComponent? puddle))
return false;
var absorberSplit = absorberSolution.SplitSolutionWithOnly(puddleSplit.Volume, PuddleSystem.EvaporationReagents);
// Do tile reactions first
- var coordinates = Transform(target).Coordinates;
- if (_mapManager.TryGetGrid(coordinates.GetGridUid(EntityManager), out var mapGrid))
+ var transform = Transform(target);
+ var gridUid = transform.GridUid;
+ if (TryComp(gridUid, out MapGridComponent? mapGrid))
{
- _puddleSystem.DoTileReactions(mapGrid.GetTileRef(coordinates), absorberSplit);
+ var tileRef = _mapSystem.GetTileRef(gridUid.Value, mapGrid, transform.Coordinates);
+ _puddleSystem.DoTileReactions(tileRef, absorberSplit);
}
_solutionContainerSystem.AddSolution(puddle.Solution.Value, absorberSplit);
_solutionContainerSystem.AddSolution(absorberSoln, puddleSplit);
_audio.PlayPvs(absorber.PickupSound, target);
- _useDelay.BeginDelay(used);
+ if (useDelay != null)
+ _useDelay.TryResetDelay((used, useDelay));
var userXform = Transform(user);
var targetPos = _transform.GetWorldPosition(target);
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.EntitySystems;
-using Content.Server.Cooldown;
using Content.Server.Extinguisher;
using Content.Server.Fluids.Components;
using Content.Server.Gravity;
using Content.Server.Popups;
-using Content.Shared.Cooldown;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
+using Content.Shared.Timing;
using Content.Shared.Vapor;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
using System.Numerics;
namespace Content.Server.Fluids.EntitySystems;
public sealed class SpraySystem : EntitySystem
{
- [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly GravitySystem _gravity = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
+ [Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly VaporSystem _vapor = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly TransformSystem _transform = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
if (ev.Cancelled)
return;
- var curTime = _gameTiming.CurTime;
- if (TryComp<ItemCooldownComponent>(entity, out var cooldown)
- && curTime < cooldown.CooldownEnd)
- {
+ if (!TryComp<UseDelayComponent>(entity, out var useDelay)
+ || _useDelay.IsDelayed((entity, useDelay)))
return;
- }
if (solution.Volume <= 0)
{
var xformQuery = GetEntityQuery<TransformComponent>();
var userXform = xformQuery.GetComponent(args.User);
- var userMapPos = userXform.MapPosition;
+ var userMapPos = _transform.GetMapCoordinates(userXform);
var clickMapPos = args.ClickLocation.ToMap(EntityManager, _transform);
var diffPos = clickMapPos.Position - userMapPos.Position;
_audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f));
- RaiseLocalEvent(entity, new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
+ _useDelay.SetDelay((entity, useDelay), TimeSpan.FromSeconds(cooldownTime));
+ _useDelay.TryResetDelay((entity, useDelay));
}
}
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Timing;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Timing;
private void OnTrigger(Entity<IgniteOnTriggerComponent> ent, ref TriggerEvent args)
{
// prevent spamming sound and ignition
- TryComp<UseDelayComponent>(ent, out var delay);
- if (_useDelay.ActiveDelay(ent, delay))
+ if (!TryComp(ent.Owner, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((ent.Owner, useDelay)))
return;
_source.SetIgnited(ent.Owner);
_audio.PlayPvs(ent.Comp.IgniteSound, ent);
- _useDelay.BeginDelay(ent, delay);
+ _useDelay.TryResetDelay((ent.Owner, useDelay));
ent.Comp.IgnitedUntil = _timing.CurTime + ent.Comp.IgnitedTime;
}
}
using Content.Server.Storage.EntitySystems;
-using Content.Shared.Clothing.Components;
using Content.Shared.Explosion;
-using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Storage;
base.Initialize();
SubscribeLocalEvent<InventoryComponent, BeforeExplodeEvent>(OnExploded);
-
- SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
-
SubscribeNetworkEvent<OpenSlotStorageNetworkMessage>(OnOpenSlotStorage);
}
}
}
- private void OnUseInHand(EntityUid uid, ClothingComponent component, UseInHandEvent args)
- {
- if (args.Handled || !component.QuickEquip)
- return;
-
- QuickEquip(uid, component, args);
- }
-
private void OnOpenSlotStorage(OpenSlotStorageNetworkMessage ev, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { Valid: true } uid)
private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args)
{
- if (args.Handled || _useDelay.ActiveDelay(uid))
+ if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
return;
if (!TryToggle(uid, component, args.User))
return;
+
args.Handled = true;
- _useDelay.BeginDelay(uid);
+ _useDelay.TryResetDelay((uid, useDelay));
}
private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args)
/// <summary>
/// Key that contains the target entity.
/// </summary>
- [DataField("targetKey", required: true)]
+ [DataField(required: true)]
public string TargetKey = default!;
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
{
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
- if (_entManager.System<UseDelaySystem>().ActiveDelay(owner) ||
+ if (!_entManager.TryGetComponent<UseDelayComponent>(owner, out var useDelay) ||
+ _entManager.System<UseDelaySystem>().IsDelayed((owner, useDelay)) ||
!blackboard.TryGetValue<EntityUid>(TargetKey, out var moveTarget, _entManager) ||
!_entManager.TryGetComponent<TransformComponent>(moveTarget, out var targetXform))
{
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.PowerCell;
-using Content.Shared.Actions;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Ninja.Components;
using Content.Shared.Ninja.Systems;
-using Content.Shared.Popups;
using Content.Shared.PowerCell.Components;
+using Content.Shared.Timing;
using Robust.Shared.Containers;
namespace Content.Server.Ninja.Systems;
// 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 var _, out var battery) || battery.CurrentCharge < chargeNeeded || UseDelay.ActiveDelay(user))
+ if (!_ninja.GetNinjaBattery(user, out var _, out var battery) || battery.CurrentCharge < chargeNeeded
+ || !TryComp(user, out UseDelayComponent? useDelay) || UseDelay.IsDelayed((user, useDelay)))
{
_popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
args.Cancel();
{
args.Handled = true;
var user = args.Performer;
- if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge) || UseDelay.ActiveDelay(user))
+ if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge)
+ || !TryComp(user, out UseDelayComponent? useDelay) || UseDelay.IsDelayed((user, useDelay)))
{
_popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
return;
var coords = _transform.GetWorldPosition(katana);
var distance = (_transform.GetWorldPosition(user) - coords).Length();
var chargeNeeded = (float) distance * comp.RecallCharge;
- if (!_ninja.TryUseCharge(user, chargeNeeded) || UseDelay.ActiveDelay(user))
+ if (!_ninja.TryUseCharge(user, chargeNeeded)
+ || !TryComp(user, out UseDelayComponent? useDelay) || UseDelay.IsDelayed((user, useDelay)))
{
_popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
return;
{
args.Handled = true;
var user = args.Performer;
- if (!_ninja.TryUseCharge(user, comp.EmpCharge) || UseDelay.ActiveDelay(user))
+ if (!_ninja.TryUseCharge(user, comp.EmpCharge)
+ || !TryComp(user, out UseDelayComponent? useDelay) || UseDelay.IsDelayed((user, useDelay)))
{
_popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user);
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(user).MapPosition;
+ var coords = _transform.GetMapCoordinates(user);
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
}
}
return;
// no sound spamming
- if (TryComp<UseDelayComponent>(uid, out var useDelay) && _useDelay.ActiveDelay(uid))
+ if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay)))
return;
comp.ActiveIndex = NextIndex(uid, comp);
_audio.PlayPvs(comp.SwitchSound, uid);
- _useDelay.BeginDelay(uid, useDelay);
+ _useDelay.TryResetDelay((uid, useDelay));
}
}
/// <summary>
/// How much joules will collector generate for each rad.
/// </summary>
- [DataField("chargeModifier")]
+ [DataField]
[ViewVariables(VVAccess.ReadWrite)]
public float ChargeModifier = 30000f;
/// <summary>
- /// Cooldown time between users interaction.
+ /// Is the machine enabled.
/// </summary>
- [DataField("cooldown")]
- [ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan Cooldown = TimeSpan.FromSeconds(0.81f);
-
- /// <summary>
- /// Was machine activated by user?
- /// </summary>
- [ViewVariables(VVAccess.ReadOnly)]
+ [DataField]
+ [ViewVariables]
public bool Enabled;
- /// <summary>
- /// Timestamp when machine can be deactivated again.
- /// </summary>
- public TimeSpan CoolDownEnd;
-
/// <summary>
/// List of gases that will react to the radiation passing through the collector
/// </summary>
- [DataField("radiationReactiveGases")]
+ [DataField]
[ViewVariables(VVAccess.ReadWrite)]
public List<RadiationReactiveGas>? RadiationReactiveGases;
}
public sealed partial class RadiationReactiveGas
{
/// <summary>
- /// The reactant gas
+ /// The reactant gas
/// </summary>
- [DataField("reactantPrototype", required: true)]
- public Gas Reactant = Gas.Plasma;
+ [DataField(required: true)]
+ public Gas ReactantPrototype;
/// <summary>
/// Multipier for the amount of power produced by the radiation collector when using this gas
/// </summary>
- [DataField("powerGenerationEfficiency")]
+ [DataField]
public float PowerGenerationEfficiency = 1f;
/// <summary>
/// /// <remarks>
/// Set to zero if the reactant does not deplete
/// </remarks>
- [DataField("reactantBreakdownRate")]
+ [DataField]
public float ReactantBreakdownRate = 1f;
/// <summary>
/// <remarks>
/// Leave null if the reactant no byproduct gas is to be formed
/// </remarks>
- [DataField("byproductPrototype")]
- public Gas? Byproduct = null;
+ [DataField]
+ public Gas? Byproduct;
/// <summary>
/// The molar ratio of the byproduct gas generated from the reactant gas
/// </summary>
- [DataField("molarRatio")]
+ [DataField]
public float MolarRatio = 1f;
}
-using Content.Server.Singularity.Components;
-using Content.Shared.Interaction;
-using Content.Shared.Singularity.Components;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Content.Server.Atmos;
+using Content.Server.Atmos.Components;
using Content.Server.Popups;
using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
+using Content.Server.Singularity.Components;
+using Content.Shared.Atmos;
+using Content.Shared.Examine;
+using Content.Shared.Interaction;
using Content.Shared.Radiation.Events;
-using Robust.Shared.Timing;
+using Content.Shared.Singularity.Components;
+using Content.Shared.Timing;
using Robust.Shared.Containers;
-using Content.Server.Atmos.Components;
-using Content.Shared.Examine;
-using Content.Server.Atmos;
-using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Atmos;
+using Robust.Shared.Timing;
namespace Content.Server.Singularity.EntitySystems;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+ [Dependency] private readonly UseDelaySystem _useDelay = default!;
+ [Dependency] private readonly BatterySystem _batterySystem = default!;
+
+ private const string GasTankContainer = "gas_tank";
public override void Initialize()
{
private bool TryGetLoadedGasTank(EntityUid uid, [NotNullWhen(true)] out GasTankComponent? gasTankComponent)
{
gasTankComponent = null;
- var container = _containerSystem.EnsureContainer<ContainerSlot>(uid, "GasTank");
- if (container.ContainedEntity == null)
+ if (!_containerSystem.TryGetContainer(uid, GasTankContainer, out var container) || container.ContainedEntities.Count == 0)
return false;
- if (!EntityManager.TryGetComponent(container.ContainedEntity, out gasTankComponent))
+ if (!EntityManager.TryGetComponent(container.ContainedEntities.First(), out gasTankComponent))
return false;
return true;
private void OnInteractHand(EntityUid uid, RadiationCollectorComponent component, InteractHandEvent args)
{
- var curTime = _gameTiming.CurTime;
-
- if (curTime < component.CoolDownEnd)
+ if (TryComp(uid, out UseDelayComponent? useDelay) && !_useDelay.TryResetDelay((uid, useDelay), true))
return;
ToggleCollector(uid, args.User, component);
- component.CoolDownEnd = curTime + component.Cooldown;
}
private void OnRadiation(EntityUid uid, RadiationCollectorComponent component, OnIrradiatedEvent args)
foreach (var gas in component.RadiationReactiveGases)
{
- float reactantMol = gasTankComponent.Air.GetMoles(gas.Reactant);
+ float reactantMol = gasTankComponent.Air.GetMoles(gas.ReactantPrototype);
float delta = args.TotalRads * reactantMol * gas.ReactantBreakdownRate;
// We need to offset the huge power gains possible when using very cold gases
if (delta > 0)
{
- gasTankComponent.Air.AdjustMoles(gas.Reactant, -Math.Min(delta, reactantMol));
+ gasTankComponent.Air.AdjustMoles(gas.ReactantPrototype, -Math.Min(delta, reactantMol));
}
if (gas.Byproduct != null)
// This still won't stop things being potentially hilariously unbalanced though.
if (TryComp<BatteryComponent>(uid, out var batteryComponent))
{
- batteryComponent.CurrentCharge += charge;
+ _batterySystem.SetCharge(uid, charge, batteryComponent);
}
// Update appearance
using Content.Shared.Timing;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
+using Robust.Shared.Audio.Systems;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Player;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly UseDelaySystem _useDelay = default!;
public override void Initialize()
{
return;
// prevent spamming bag open / honkerton honk sound
- silent |= TryComp<UseDelayComponent>(uid, out var useDelay) && UseDelay.ActiveDelay(uid, useDelay);
+ silent |= TryComp<UseDelayComponent>(uid, out var useDelay) && _useDelay.IsDelayed((uid, useDelay));
if (!silent)
{
- Audio.PlayPvs(storageComp.StorageOpenSound, uid);
+ _audio.PlayPvs(storageComp.StorageOpenSound, uid);
if (useDelay != null)
- UseDelay.BeginDelay(uid, useDelay);
+ _useDelay.TryResetDelay((uid, useDelay));
}
Log.Debug($"Storage (UID {uid}) \"used\" by player session (UID {player.PlayerSession.AttachedEntity}).");
using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Shared.Interaction;
using Content.Shared.Timing;
-using Robust.Shared.Player;
namespace Content.Server.Xenoarchaeology.Equipment.Systems;
args.Handled = true;
var target = args.Target.Value;
- _useDelay.BeginDelay(uid);
+
+ if (TryComp(uid, out UseDelayComponent? useDelay)
+ && !_useDelay.TryResetDelay((uid, useDelay), true))
+ return;
+
_popupSystem.PopupEntity(Loc.GetString("node-scan-popup",
("id", $"{artifact.CurrentNodeId}")), target);
}
using Content.Shared.Clothing.Components;
+using Content.Shared.Hands.Components;
+using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
+using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Tag;
+using Content.Shared.Timing;
using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.EntitySystems;
[Dependency] private readonly SharedItemSystem _itemSys = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
+ [Dependency] private readonly InventorySystem _invSystem = default!;
+ [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[ValidatePrototypeId<TagPrototype>]
private const string HairTag = "HidesHair";
{
base.Initialize();
+ SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ClothingComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<ClothingComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
+ private void OnUseInHand(Entity<ClothingComponent> ent, ref UseInHandEvent args)
+ {
+ if (args.Handled || !ent.Comp.QuickEquip)
+ return;
+
+ var user = args.User;
+ if (!TryComp(user, out InventoryComponent? inv) ||
+ !TryComp(user, out HandsComponent? hands))
+ return;
+
+ QuickEquip(ent, (user, inv, hands));
+ args.Handled = true;
+ args.ApplyDelay = false;
+ }
+
+ private void QuickEquip(
+ Entity<ClothingComponent> toEquipEnt,
+ Entity<InventoryComponent, HandsComponent> userEnt)
+ {
+ foreach (var slotDef in userEnt.Comp1.Slots)
+ {
+ if (!_invSystem.CanEquip(userEnt, toEquipEnt, slotDef.Name, out _, slotDef, userEnt, toEquipEnt))
+ continue;
+
+ if (_invSystem.TryGetSlotEntity(userEnt, slotDef.Name, out var slotEntity, userEnt))
+ {
+ // Item in slot has to be quick equipable as well
+ if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
+ continue;
+
+ if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
+ continue;
+
+ if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
+ continue;
+
+ _handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
+ }
+ else
+ {
+ if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt))
+ continue;
+ }
+
+ break;
+ }
+ }
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
{
clothing.EquippedPrefix = prefix;
_itemSys.VisualsChanged(uid);
- Dirty(clothing);
+ Dirty(uid, clothing);
}
public void SetSlots(EntityUid uid, SlotFlags slots, ClothingComponent? clothing = null)
return;
clothing.Slots = slots;
- Dirty(clothing);
+ Dirty(uid, clothing);
}
/// <summary>
clothing.FemaleMask = otherClothing.FemaleMask;
_itemSys.VisualsChanged(uid);
- Dirty(clothing);
+ Dirty(uid, clothing);
}
#endregion
+++ /dev/null
-using Robust.Shared.Timing;
-
-namespace Content.Shared.Cooldown
-{
- /// <summary>
- /// Utilities for working with cooldowns.
- /// </summary>
- public static class Cooldowns
- {
- /// <param name="gameTiming">game timing to use, otherwise will resolve using IoCManager.</param>
- /// <returns>a cooldown interval starting at GameTiming.Curtime and ending at (offset) from CurTime.
- /// For example, passing TimeSpan.FromSeconds(5) will create an interval
- /// from now to 5 seconds from now.</returns>
- public static (TimeSpan start, TimeSpan end) FromNow(TimeSpan offset, IGameTiming? gameTiming = null)
- {
- var now = (gameTiming ?? IoCManager.Resolve<IGameTiming>()).CurTime;
- return (now, now + offset);
- }
-
- /// <see cref="FromNow"/>
- public static (TimeSpan start, TimeSpan end) SecondsFromNow(double seconds, IGameTiming? gameTiming = null)
- {
- return FromNow(TimeSpan.FromSeconds(seconds), gameTiming);
- }
- }
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Cooldown
-{
- /// <summary>
- /// Stores a visual "cooldown" for items, that gets displayed in the hands GUI.
- /// </summary>
- [RegisterComponent, NetworkedComponent]
- [AutoGenerateComponentState]
- public sealed partial class ItemCooldownComponent : Component
- {
- // TODO: access and system setting and dirtying not this funny stuff
- private TimeSpan? _cooldownEnd;
- private TimeSpan? _cooldownStart;
-
- /// <summary>
- /// The time when this cooldown ends.
- /// </summary>
- /// <remarks>
- /// If null, no cooldown is displayed.
- /// </remarks>
- [ViewVariables, AutoNetworkedField]
- public TimeSpan? CooldownEnd
- {
- get => _cooldownEnd;
- set
- {
- _cooldownEnd = value;
- Dirty();
- }
- }
-
- /// <summary>
- /// The time when this cooldown started.
- /// </summary>
- /// <remarks>
- /// If null, no cooldown is displayed.
- /// </remarks>
- [ViewVariables, AutoNetworkedField]
- public TimeSpan? CooldownStart
- {
- get => _cooldownStart;
- set
- {
- _cooldownStart = value;
- Dirty();
- }
- }
- }
-}
+using Content.Shared.Clothing.EntitySystems;
+using Content.Shared.Timing;
using JetBrains.Annotations;
namespace Content.Shared.Interaction.Events;
/// <summary>
/// Entity holding the item in their hand.
/// </summary>
- public EntityUid User { get; }
+ public EntityUid User;
+
+ /// <summary>
+ /// Whether or not to apply a UseDelay when used.
+ /// Mostly used by the <see cref="ClothingSystem"/> quick-equip to not apply the delay to entities that have the <see cref="UseDelayComponent"/>.
+ /// </summary>
+ public bool ApplyDelay = true;
public UseInHandEvent(EntityUid user)
{
using Content.Shared.Administration.Managers;
using Content.Shared.CombatMode;
using Content.Shared.Database;
-using Content.Shared.DragDrop;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Content.Shared.Pulling;
using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
-using Content.Shared.Throwing;
using Content.Shared.Timing;
using Content.Shared.Verbs;
using Content.Shared.Wall;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
- private const CollisionGroup InRangeUnobstructedMask
- = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
+ private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
public const float InteractionRange = 1.5f;
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
QueueDel(uid);
}
-
private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (!ValidateClientInput(session, coords, uid, out var userEntity))
UseDelayComponent? delayComponent = null;
if (checkUseDelay
&& TryComp(used, out delayComponent)
- && delayComponent.ActiveDelay)
+ && _useDelay.IsDelayed((used, delayComponent)))
return false;
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
return false;
DoContactInteraction(user, used, activateMsg);
- _useDelay.BeginDelay(used, delayComponent);
+ if (delayComponent != null)
+ _useDelay.TryResetDelay((used, delayComponent));
if (!activateMsg.WasLogged)
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
return true;
if (checkUseDelay
&& TryComp(used, out delayComponent)
- && delayComponent.ActiveDelay)
+ && _useDelay.IsDelayed((used, delayComponent)))
return true; // if the item is on cooldown, we consider this handled.
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
if (useMsg.Handled)
{
DoContactInteraction(user, used, useMsg);
- _useDelay.BeginDelay(used, delayComponent);
+ if (delayComponent != null && useMsg.ApplyDelay)
+ _useDelay.TryResetDelay((used, delayComponent));
return true;
}
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Systems;
using Content.Shared.Popups;
using Content.Shared.Strip.Components;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
-using Robust.Shared.Network;
-using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[ValidatePrototypeId<ItemSizePrototype>]
SubscribeAllEvent<UseSlotNetworkMessage>(OnUseSlot);
}
- protected void QuickEquip(EntityUid uid, ClothingComponent component, UseInHandEvent args)
- {
- if (!TryComp(args.User, out InventoryComponent? inv) || !HasComp<HandsComponent>(args.User))
- return;
-
- foreach (var slotDef in inv.Slots)
- {
- if (!CanEquip(args.User, uid, slotDef.Name, out _, slotDef, inv))
- continue;
-
- if (TryGetSlotEntity(args.User, slotDef.Name, out var slotEntity, inv))
- {
- // Item in slot has to be quick equipable as well
- if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
- continue;
-
- if (!TryUnequip(args.User, slotDef.Name, true, inventory: inv))
- continue;
-
- if (!TryEquip(args.User, uid, slotDef.Name, true, inventory: inv))
- continue;
-
- _handsSystem.PickupOrDrop(args.User, slotEntity.Value);
- }
- else
- {
- if (!TryEquip(args.User, uid, slotDef.Name, true, inventory: inv))
- continue;
- }
-
- args.Handled = true;
- break;
- }
- }
-
private void OnEntRemoved(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
{
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
-using Content.Shared.Mech;
+using System.Linq;
using Content.Shared.Mech.Equipment.Components;
-using Content.Shared.Mech.Equipment.Systems;
using Content.Shared.Timing;
-using Robust.Shared.Audio;
-using System.Linq;
using Robust.Shared.Audio.Systems;
namespace Content.Shared.Mech.Equipment.Systems;
if (msg.Sound >= comp.Sounds.Count)
return;
- if (_useDelay.ActiveDelay(uid))
+ if (TryComp(uid, out UseDelayComponent? useDelay)
+ && !_useDelay.TryResetDelay((uid, useDelay), true))
return;
// honk!!!!!
- var mech = equipment.EquipmentOwner.Value;
- _useDelay.BeginDelay(uid);
_audio.PlayPvs(comp.Sounds[msg.Sound], uid);
}
}
using Content.Shared.Ninja.Components;
using Content.Shared.Popups;
using Content.Shared.Research.Components;
-using Content.Shared.Timing;
using Content.Shared.Toggleable;
using Robust.Shared.Timing;
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
[Dependency] protected readonly SharedInteractionSystem Interaction = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
- [Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
public override void Initialize()
target = args.Target;
return _timing.IsFirstTimePredicted
&& !_combatMode.IsInCombatMode(uid)
- && !_useDelay.ActiveDelay(uid)
&& TryComp<HandsComponent>(uid, out var hands)
&& hands.ActiveHandEntity == null
&& Interaction.InRangeUnobstructed(uid, target);
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
- [Dependency] protected readonly SharedSpaceNinjaSystem _ninja = default!;
+ [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
[Dependency] protected readonly StealthClothingSystem StealthClothing = default!;
[Dependency] protected readonly UseDelaySystem UseDelay = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
_audio.PlayPredicted(comp.RevealSound, uid, user);
// all abilities check for a usedelay on the ninja
var useDelay = EnsureComp<UseDelayComponent>(user);
- useDelay.Delay = comp.DisableTime;
- UseDelay.BeginDelay(user, useDelay);
+ UseDelay.SetDelay((user, useDelay), comp.DisableTime);
+ UseDelay.TryResetDelay((user, useDelay));
}
// TODO: modify PowerCellDrain
using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Timing;
/// <summary>
/// Timer that creates a cooldown each time an object is activated/used
/// </summary>
-[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+/// <remarks>
+/// Currently it only supports a single delay per entity, this means that for things that have two delay interactions they will share one timer, so this can cause issues. For example, the bible has a delay when opening the storage UI and when applying it's interaction effect, and they share the same delay.
+/// </remarks>
+[RegisterComponent]
+[NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(UseDelaySystem))]
public sealed partial class UseDelayComponent : Component
{
- [AutoNetworkedField]
- public TimeSpan LastUseTime;
-
- [AutoNetworkedField]
- public TimeSpan? DelayEndTime;
+ /// <summary>
+ /// When the delay starts.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
+ public TimeSpan DelayStartTime;
- [DataField, AutoNetworkedField]
- [ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan Delay = TimeSpan.FromSeconds(1);
+ /// <summary>
+ /// When the delay ends.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
+ public TimeSpan DelayEndTime;
/// <summary>
- /// Stores remaining delay pausing (and eventually, serialization).
+ /// Default delay time
/// </summary>
[DataField]
- public TimeSpan? RemainingDelay;
-
- public bool ActiveDelay => DelayEndTime != null;
+ [ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ public TimeSpan Delay = TimeSpan.FromSeconds(1);
}
-using Content.Shared.Cooldown;
using Robust.Shared.Timing;
-using Robust.Shared.Utility;
namespace Content.Shared.Timing;
public sealed class UseDelaySystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
-
- private HashSet<UseDelayComponent> _activeDelays = new();
+ [Dependency] private readonly MetaDataSystem _metadata = default!;
public override void Initialize()
{
- base.Initialize();
-
- SubscribeLocalEvent<UseDelayComponent, AfterAutoHandleStateEvent>(OnHandleState);
-
- SubscribeLocalEvent<UseDelayComponent, EntityPausedEvent>(OnPaused);
SubscribeLocalEvent<UseDelayComponent, EntityUnpausedEvent>(OnUnpaused);
}
- private void OnPaused(EntityUid uid, UseDelayComponent component, ref EntityPausedEvent args)
+ private void OnUnpaused(Entity<UseDelayComponent> ent, ref EntityUnpausedEvent args)
{
- // This entity just got paused, but wasn't before
- if (component.DelayEndTime != null)
- component.RemainingDelay = _gameTiming.CurTime - component.DelayEndTime;
-
- _activeDelays.Remove(component);
- Dirty(component);
+ // We got unpaused, resume the delay
+ ent.Comp.DelayStartTime += args.PausedTime;
+ ent.Comp.DelayEndTime += args.PausedTime;
+ Dirty(ent);
}
- private void OnUnpaused(EntityUid uid, UseDelayComponent component, ref EntityUnpausedEvent args)
+ public void SetDelay(Entity<UseDelayComponent> ent, TimeSpan delay)
{
- if (component.RemainingDelay == null)
+ if (ent.Comp.Delay == delay)
return;
- // We got unpaused, resume the delay/cooldown. Currently this takes for granted that ItemCooldownComponent
- // handles the pausing on its own. I'm not even gonna check, because I CBF fixing it if it doesn't.
- component.DelayEndTime = _gameTiming.CurTime + component.RemainingDelay;
- Dirty(component);
- _activeDelays.Add(component);
+ ent.Comp.Delay += delay;
+ Dirty(ent);
}
- private void OnHandleState(EntityUid uid, UseDelayComponent component, ref AfterAutoHandleStateEvent args)
+ /// <summary>
+ /// Returns true if the entity has a currently active UseDelay.
+ /// </summary>
+ public bool IsDelayed(Entity<UseDelayComponent> ent)
{
- if (component.DelayEndTime == null)
- _activeDelays.Remove(component);
- else
- _activeDelays.Add(component);
+ return ent.Comp.DelayEndTime >= _gameTiming.CurTime;
}
- public override void Update(float frameTime)
+ /// <summary>
+ /// Cancels the current delay.
+ /// </summary>
+ public void CancelDelay(Entity<UseDelayComponent> ent)
{
- base.Update(frameTime);
-
- var toRemove = new RemQueue<UseDelayComponent>();
- var curTime = _gameTiming.CurTime;
- var mQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
-
- // TODO refactor this to use active components
- foreach (var delay in _activeDelays)
- {
- if (delay.DelayEndTime == null ||
- curTime > delay.DelayEndTime ||
- Deleted(delay.Owner, mQuery))
- {
- toRemove.Add(delay);
- }
- }
-
- foreach (var delay in toRemove)
- {
- delay.DelayEndTime = null;
- _activeDelays.Remove(delay);
- Dirty(delay);
- }
+ ent.Comp.DelayEndTime = _gameTiming.CurTime;
+ Dirty(ent);
}
/// <summary>
- /// Attempts tp start a use-delay for some entity. Returns true unless there is already an active delay.
+ /// Resets the UseDelay entirely for this entity if possible.
/// </summary>
- /// <remarks>
- /// Note that this will always return true if the entity does not have a use delay component, as in that case there
- /// is no reason to block/prevent an interaction.
- /// </remarks>
- public bool BeginDelay(EntityUid uid, UseDelayComponent? component = null)
+ /// <param name="checkDelayed">Check if the entity has an ongoing delay, return false if it does, return true if it does not.</param>
+ public bool TryResetDelay(Entity<UseDelayComponent> ent, bool checkDelayed = false)
{
- if (!Resolve(uid, ref component, false))
- return true;
-
- if (component.ActiveDelay)
+ if (checkDelayed && IsDelayed(ent))
return false;
- DebugTools.Assert(!_activeDelays.Contains(component));
- _activeDelays.Add(component);
-
- var currentTime = _gameTiming.CurTime;
- component.LastUseTime = currentTime;
- component.DelayEndTime = currentTime + component.Delay;
- Dirty(uid, component);
-
- var cooldown = EnsureComp<ItemCooldownComponent>(uid);
- cooldown.CooldownStart = currentTime;
- cooldown.CooldownEnd = component.DelayEndTime;
+ var curTime = _gameTiming.CurTime;
+ ent.Comp.DelayStartTime = curTime;
+ ent.Comp.DelayEndTime = curTime - _metadata.GetPauseTime(ent) + ent.Comp.Delay;
+ Dirty(ent);
return true;
}
-
- public bool ActiveDelay(EntityUid uid, UseDelayComponent? component = null)
- {
- return Resolve(uid, ref component, false) && component.ActiveDelay;
- }
-
- public void Cancel(UseDelayComponent component)
- {
- component.DelayEndTime = null;
- _activeDelays.Remove(component);
- Dirty(component);
-
- if (TryComp<ItemCooldownComponent>(component.Owner, out var cooldown))
- {
- cooldown.CooldownEnd = _gameTiming.CurTime;
- }
- }
}
using Content.Shared.Movement.Events;
using Content.Shared.Physics;
using Content.Shared.Projectiles;
-using Content.Shared.Timing;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Physics;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedJointSystem _joints = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
- [Dependency] private readonly UseDelaySystem _delay = default!;
public const string GrapplingJoint = "grappling";
private void OnGunActivate(EntityUid uid, GrapplingGunComponent component, ActivateInWorldEvent args)
{
- if (!Timing.IsFirstTimePredicted || _delay.ActiveDelay(uid))
+ if (!Timing.IsFirstTimePredicted || args.Handled)
return;
- _delay.BeginDelay(uid);
_audio.PlayPredicted(component.CycleSound, uid, args.User);
TryComp<AppearanceComponent>(uid, out var appearance);
component.Projectile = null;
Dirty(uid, component);
}
+
+ args.Handled = true;
}
private void SetReeling(EntityUid uid, GrapplingGunComponent component, bool value, EntityUid? user)
private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args)
{
- _delay.BeginDelay(uid);
+ if (TryComp(uid, out UseDelayComponent? useDelay))
+ _delay.TryResetDelay((uid, useDelay));
}
}
-using Content.Shared.DoAfter;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Popups;
+using Content.Shared.Timing;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Melee;
-using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Melee.Components;
+using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
-using Content.Shared.Timing;
namespace Content.Shared.Wieldable;
public sealed class WieldableSystem : EntitySystem
{
- [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedHandVirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
if (args.Handled)
return;
- if(!component.Wielded)
+ if (!component.Wielded)
args.Handled = TryWield(uid, component, args.User);
else
args.Handled = TryUnwield(uid, component, args.User);
}
- public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet=false)
+ public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet = false)
{
// Do they have enough hands free?
if (!EntityManager.TryGetComponent<HandsComponent>(user, out var hands))
{
- if(!quiet)
+ if (!quiet)
_popupSystem.PopupClient(Loc.GetString("wieldable-component-no-hands"), user, user);
return false;
}
_virtualItemSystem.TrySpawnVirtualItemInHand(used, user);
}
- _delay.BeginDelay(used);
+ if (TryComp(used, out UseDelayComponent? useDelay)
+ && !_delay.TryResetDelay((used, useDelay), true))
+ return false;
_popupSystem.PopupClient(Loc.GetString("wieldable-component-successful-wield", ("item", used)), user, user);
- _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other", ("user", user),("item", used)), user, Filter.PvsExcept(user), true);
+ _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other", ("user", user), ("item", used)), user, Filter.PvsExcept(user), true);
var targEv = new ItemWieldedEvent();
RaiseLocalEvent(used, ref targEv);
{
if (args.User == null)
return;
+
if (!component.Wielded)
return;
{
if (!component.Wielded || uid != args.Unequipped)
return;
+
RaiseLocalEvent(uid, new ItemUnwieldedEvent(args.User, force: true), true);
}
{
if (!TryComp<WieldableComponent>(uid, out var wield))
return;
+
if (!wield.Wielded)
return;
- Pressed: Toggle
1065:
- Pressed: Toggle
- - type: ItemCooldown
- uid: 7826
components:
- type: Transform
- Pressed: Toggle
2822:
- Pressed: Toggle
- - type: ItemCooldown
- uid: 8749
components:
- type: Transform
linkedPorts:
4205:
- Pressed: Toggle
- - type: ItemCooldown
- uid: 17649
components:
- type: Transform
linkedPorts:
3066:
- Pressed: Toggle
- - type: ItemCooldown
- proto: SignalButtonBridge
entities:
- uid: 4305
3218:\r
- Pressed: Toggle\r
type: DeviceLinkSource\r
- - type: ItemCooldown\r
+ - type: UseDelay\r
- uid: 5389\r
components:\r
- rot: 1.5707963267948966 rad\r
3218:\r
- Pressed: Toggle\r
type: DeviceLinkSource\r
- - type: ItemCooldown\r
+ - type: UseDelay\r
- uid: 5447\r
components:\r
- rot: 3.141592653589793 rad\r
1663:
- Pressed: Toggle
type: DeviceLinkSource
- - type: ItemCooldown
+ - type: UseDelay
- uid: 9640
components:
- pos: -20.5,-1.5
- Pressed: Toggle\r
20499:\r
- Pressed: Toggle\r
- - type: ItemCooldown\r
- uid: 20513\r
components:\r
- type: Transform\r
- Pressed: Toggle\r
20423:\r
- Pressed: Toggle\r
- - type: ItemCooldown\r
- uid: 20528\r
components:\r
- type: Transform\r
- Pressed: Toggle\r
9404:\r
- Pressed: Toggle\r
- - type: ItemCooldown\r
- uid: 8610\r
components:\r
- type: Transform\r
- Pressed: Toggle\r
19434:\r
- Pressed: Toggle\r
- - type: ItemCooldown\r
- uid: 19438\r
components:\r
- type: MetaData\r
- pos: -46.5,62.5
parent: 2
type: Transform
- - type: ItemCooldown
- proto: ClothingBackpackMedical
entities:
- uid: 12350
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24195
components:
- rot: -1.5707963267948966 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24196
components:
- name: shutters switch
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24197
components:
- name: blast door switch
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24198
components:
- rot: -1.5707963267948966 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24199
components:
- rot: 1.5707963267948966 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24200
components:
- rot: 3.141592653589793 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24204
components:
- rot: 3.141592653589793 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24207
components:
- pos: -52.5,-11.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24210
components:
- pos: -49.5,24.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24214
components:
- pos: 58.5,48.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24228
components:
- pos: 54.5,51.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24229
components:
- pos: 5.5,49.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24238
components:
- rot: 3.141592653589793 rad
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24239
components:
- name: shutters switch
- Off: Close
- Status: Toggle
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24240
components:
- pos: -51.5,36.5
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24241
components:
- name: shutters switch
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24242
components:
- name: visitation switch
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24243
components:
- name: shutters switch
- On: Open
- Off: Close
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24245
components:
- name: shutters switch
- Off: Close
- Status: Toggle
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 24248
components:
- pos: -74.5,-43.5
- Right: Reverse
- Middle: Off
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 25925
components:
- pos: -41.5,17.5
- Right: Reverse
- Middle: Off
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 25933
components:
- pos: 47.5,39.5
- Right: Forward
- Middle: Off
type: DeviceLinkSource
- - type: ItemCooldown
- proto: UnfinishedMachineFrame
entities:
- uid: 25938
260:
- Pressed: Toggle
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 8766
components:
- pos: -13.5,12.5
7588:
- Pressed: Toggle
type: DeviceLinkSource
- - type: ItemCooldown
- uid: 10449
components:
- pos: 51.5,18.5
qualities:
- Rolling
speed: 0.25 # its small so takes longer to roll the entire dough flat
- - type: ItemCooldown
- type: SpaceGarbage
- type: TrashOnSolutionEmpty
solution: drink
damage:
types:
Blunt: 0
- - type: ItemCooldown
- type: entity
parent: DrinkBaseCup
prototype: MobHumanSyndicateAgent
- type: EmitSoundOnUse
sound: /Audio/Effects/Emotes/parp1.ogg
- - type: ItemCooldown
- type: UseDelay
delay: 300
description: A handy-dandy holographic projector that displays a janitorial sign.
components:
- type: HolosignProjector
- - type: ItemCooldown
- type: UseDelay
- delay: 1.0
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot {}
sprite: Objects/Fun/bikehorn.rsi
slots: [Belt]
quickEquip: false
- - type: ItemCooldown
- type: EmitSoundOnUse
sound:
collection: BikeHorn
name: broken bike horn
description: A broken horn off of a bicycle.
components:
- - type: ItemCooldown
+ - type: UseDelay
- type: Sprite
sprite: Objects/Fun/cluwnehorn.rsi
state: icon
- type: Item
size: Tiny
sprite: Objects/Fun/Darts/dart_red.rsi
- - type: ItemCooldown
- type: SolutionContainerManager
solutions:
melee:
items:
- id: HypoDart
sound:
- path: /Audio/Effects/unwrap.ogg
\ No newline at end of file
+ path: /Audio/Effects/unwrap.ogg
id: BaseDice
components:
- type: Dice
- - type: ItemCooldown
- type: UseDelay
- type: Sprite
sprite: Objects/Fun/dice.rsi
state: icon
- type: Item
sprite: Objects/Misc/skub.rsi
- - type: ItemCooldown
- type: EmitSoundOnUse
sound:
collection: Skub
- type: EmitSoundOnLand
sound:
collection: ToyFall
- - type: ItemCooldown
- type: UseDelay
delay: 1.0
- type: MeleeWeapon
size: Small
sprite: Objects/Fun/toys.rsi
heldPrefix: foamblade
- - type: ItemCooldown
+ - type: UseDelay
# MISC
sprite: Objects/Fun/whoopie.rsi
state: icon
quickEquip: false
- - type: ItemCooldown
- type: EmitSoundOnUse
sound:
collection: Parp
- enum.DamageStateVisualLayers.Base:
shard3: ""
- type: SpaceGarbage
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
shard2: ""
- enum.DamageStateVisualLayers.Base:
shard3: ""
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
description: In Space Glasgow this is called a conversation starter.
components:
- type: Sharp
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
materialComposition:
Glass: 50
- type: SpaceGarbage
-
params:
variation: 0.03
volume: 3
- - type: ItemCooldown
- type: UseDelay
delay: 0.5
- type: MeleeWeapon
- type: DrainableSolution
solution: spray
- type: SolutionTransfer
- - type: ItemCooldown
+ - type: UseDelay
- type: Spray
transferAmount: 10
pushbackAmount: 60
- type: Sprite
sprite: Objects/Misc/bureaucracy.rsi
state: overpriced_pen
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -45
damage:
mixMessage: "bible-mixing-success"
reactionTypes:
- Holy
- - type: ItemCooldown
- type: Sprite
sprite: Objects/Specific/Chapel/bible.rsi
state: icon
solution: spray
- type: Spillable
solution: spray
- - type: ItemCooldown
- type: Tag
tags:
- Spray
- type: Sprite
sprite: Objects/Tools/Hydroponics/hoe.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 135
swingLeft: true
- type: Sprite
sprite: Objects/Tools/Hydroponics/clippers.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 90
damage:
- type: Sprite
sprite: Objects/Tools/Hydroponics/scythe.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 135
damage:
- type: Sprite
sprite: Objects/Tools/Hydroponics/hatchet.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 135
swingLeft: true
- type: Sprite
sprite: Objects/Tools/Hydroponics/spade.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 45
damage:
- type: Item
sprite: Objects/Specific/Janitorial/plunger.rsi
heldPrefix: plunger
- - type: ItemCooldown
- type: GuideHelp
guides:
- Janitorial
solution: spray
- type: SolutionTransfer
canChangeTransferAmount: true
- - type: ItemCooldown
+ - type: UseDelay
- type: Spray
transferAmount: 10
sprayVelocity: 2
False: { visible: false }
- type: Item
size: Large
- - type: ItemCooldown
- type: Speech
speechVerb: Robotic
- type: Defibrillator
state: cautery
- type: Item
sprite: Objects/Specific/Medical/Surgery/cautery.rsi
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
state: drill
- type: Item
sprite: Objects/Specific/Medical/Surgery/drill.rsi
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
state: scalpel
- type: Item
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
state: retractor
- type: Item
sprite: Objects/Specific/Medical/Surgery/scissors.rsi
- - type: ItemCooldown
- type: entity
name: hemostat
state: saw
- type: Item
sprite: Objects/Specific/Medical/Surgery/saw.rsi
- - type: ItemCooldown
- type: Tool
qualities:
- Sawing
- state: base
- state: green_bit
shader: unshaded
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
- type: Sprite
sprite: Objects/Tools/Cowtools/haycutters.rsi
state: haycutters
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
state: moodriver
- type: Item
sprite: Objects/Tools/Cowtools/moodriver.rsi
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
state: wronch
- type: Item
sprite: Objects/Tools/Cowtools/wronch.rsi
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
state: cowbar
- type: Item
sprite: Objects/Tools/Cowtools/cowbar.rsi
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
- id: Mooltitool
- id: Cowelder
- id: Milkalyzer
-
- type: Item
size: Tiny
sprite: Objects/Tools/lighters.rsi
- - type: ItemCooldown
+ - type: UseDelay
- type: RefillableSolution
solution: Welder
- type: SolutionContainerManager
- 0,0,6,3
- type: Item
size: Ginormous
- - type: ItemCooldown
- type: MeleeWeapon
damage:
types:
- state: cutters
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: cutters-cutty-thingy
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -90
damage:
- type: Item
sprite: Objects/Tools/screwdriver.rsi
storedRotation: -90
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -90
attackRate: 1
storedSprite:
sprite: Objects/Tools/wrench.rsi
state: storage
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: 135
attackRate: 1.5
storedSprite:
sprite: Objects/Tools/crowbar.rsi
state: storage
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -135
damage:
maxCharges: 5
charges: 5
- type: UseDelay
- delay: 1.0
- type: Sprite
sprite: Objects/Tools/rcd.rsi
state: icon
- type: Sprite
sprite: Objects/Tools/shovel.rsi
state: icon
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -135
damage:
quickEquip: false
slots:
- Belt
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -135
damage:
right:
- state: inhand-right-flame
shader: unshaded
- - type: ItemCooldown
+ - type: UseDelay
- type: MeleeWeapon
wideAnimationRotation: -90
damage:
- type: Utensil
types:
- Knife
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -135
damage:
- type: Sprite
sprite: Objects/Weapons/Melee/pickaxe.rsi
state: pickaxe
- - type: ItemCooldown
- type: MeleeWeapon
wideAnimationRotation: -135
damage:
sprite: Objects/Weapons/Melee/pickaxe.rsi
storedRotation: -45
- type: UseDelay
- delay: 1
- type: entity
name: mining drill
- type: Sprite
sprite: Objects/Tools/handdrill.rsi
state: handdrill
- - type: ItemCooldown
- type: MeleeWeapon
attackRate: 1.5
damage:
- type: Construction
graph: Spear
node: spear
- - type: ItemCooldown
- type: SolutionContainerManager
solutions:
melee:
types:
Blunt: 5
- type: UseDelay
- delay: 1
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 1
- type: Battery
maxCharge: 360
startingCharge: 360
- - type: ItemCooldown
+ - type: UseDelay
- type: Item
heldPrefix: off
size: Normal
- type: Battery
maxCharge: 1000
startingCharge: 1000
- - type: ItemCooldown
+ - type: UseDelay
- type: Item
heldPrefix: off
size: Normal
- type: Item
size: Small
sprite: Objects/Weapons/Melee/flash.rsi
- - type: ItemCooldown
+ - type: UseDelay
- type: StaticPrice
price: 40
- type: Appearance
guides: [ Singularity, Power ]
- type: ContainerContainer
containers:
- GasTank: !type:ContainerSlot {}
+ gas_tank: !type:ContainerSlot {}
- type: ItemSlots
slots:
- GasTank:
+ gas_tank:
startingItem: PlasmaTank
whitelist:
components:
- GasTank
+ - type: UseDelay
+ delay: 1
- type: entity
id: RadiationCollectorNoTank
components:
- type: ItemSlots
slots:
- GasTank:
+ gas_tank:
whitelist:
components:
- GasTank
-
+
- type: entity
id: RadiationCollectorFullTank
suffix: Filled tank
components:
- type: ItemSlots
slots:
- GasTank:
+ gas_tank:
startingItem: PlasmaTankFilled
whitelist:
components:
- - GasTank
\ No newline at end of file
+ - GasTank
False: { visible: false }
- type: Item
size: Normal
- - type: ItemCooldown
- type: EmitSoundOnUse #placeholder for future unical mechanic
sound:
collection: RadiationPulse
components:
- Item
permanentComponents:
- - type: ItemCooldown
+ - type: UseDelay
- type: MeleeWeapon
damage:
types: