From fadee354db38d9aadbf6e350a506bcb67b28639c Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 23 Apr 2023 01:39:33 -0400 Subject: [PATCH] Anomaly Locator (#15677) --- .../Pinpointer/ProximityBeeperComponent.cs | 54 ++++++ .../Pinpointer/ProximityBeeperSystem.cs | 167 ++++++++++++++++++ .../Pinpointer/SharedProximityBeeper.cs | 9 + Resources/Audio/Items/attributions.yml | 5 + Resources/Audio/Items/locator_beep.ogg | Bin 0 -> 5858 bytes .../Catalog/Research/technologies.yml | 1 + .../Objects/Specific/Research/anomaly.yml | 43 +++++ .../Entities/Structures/Machines/lathe.yml | 1 + .../Prototypes/Recipes/Lathes/devices.yml | 8 + .../Research/anomalylocator.rsi/icon.png | Bin 0 -> 442 bytes .../anomalylocator.rsi/inhand-left.png | Bin 0 -> 307 bytes .../anomalylocator.rsi/inhand-right.png | Bin 0 -> 295 bytes .../Research/anomalylocator.rsi/meta.json | 33 ++++ .../Research/anomalylocator.rsi/screen.png | Bin 0 -> 223 bytes 14 files changed, 321 insertions(+) create mode 100644 Content.Server/Pinpointer/ProximityBeeperComponent.cs create mode 100644 Content.Server/Pinpointer/ProximityBeeperSystem.cs create mode 100644 Content.Shared/Pinpointer/SharedProximityBeeper.cs create mode 100644 Resources/Audio/Items/locator_beep.ogg create mode 100644 Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/icon.png create mode 100644 Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/screen.png diff --git a/Content.Server/Pinpointer/ProximityBeeperComponent.cs b/Content.Server/Pinpointer/ProximityBeeperComponent.cs new file mode 100644 index 0000000000..8abc7b6df7 --- /dev/null +++ b/Content.Server/Pinpointer/ProximityBeeperComponent.cs @@ -0,0 +1,54 @@ +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.Pinpointer; + +/// +/// This is used for an item that beeps based on +/// proximity to a specified component. +/// +[RegisterComponent, Access(typeof(ProximityBeeperSystem))] +public sealed class ProximityBeeperComponent : Component +{ + /// + /// Whether or not it's on. + /// + [DataField("enabled")] + public bool Enabled; + + /// + /// The target component that is being searched for + /// + [DataField("component", required: true), ViewVariables(VVAccess.ReadWrite)] + public string Component = default!; + + /// + /// The farthest distance a target can be for the beep to occur + /// + [DataField("maximumDistance"), ViewVariables(VVAccess.ReadWrite)] + public float MaximumDistance = 10f; + + /// + /// The maximum interval between beeps. + /// + [DataField("maxBeepInterval"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan MaxBeepInterval = TimeSpan.FromSeconds(1.5f); + + /// + /// The minimum interval between beeps. + /// + [DataField("minBeepInterval"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan MinBeepInterval = TimeSpan.FromSeconds(0.25f); + + /// + /// When the next beep will occur + /// + [DataField("nextBeepTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextBeepTime; + + /// + /// The sound played when the locator beeps. + /// + [DataField("beepSound")] + public SoundSpecifier? BeepSound; +} diff --git a/Content.Server/Pinpointer/ProximityBeeperSystem.cs b/Content.Server/Pinpointer/ProximityBeeperSystem.cs new file mode 100644 index 0000000000..472a50fb23 --- /dev/null +++ b/Content.Server/Pinpointer/ProximityBeeperSystem.cs @@ -0,0 +1,167 @@ +using Content.Server.PowerCell; +using Content.Shared.Interaction.Events; +using Content.Shared.Pinpointer; +using Robust.Server.GameObjects; +using Robust.Shared.Timing; + +namespace Content.Server.Pinpointer; + +/// +/// This handles logic and interaction relating to +/// +public sealed class ProximityBeeperSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnUnpaused); + SubscribeLocalEvent(OnPowerCellSlotEmpty); + } + private void OnUseInHand(EntityUid uid, ProximityBeeperComponent component, UseInHandEvent args) + { + if (args.Handled) + return; + + args.Handled = TryToggle(uid, component, args.User); + } + + private void OnInit(EntityUid uid, ProximityBeeperComponent component, ComponentInit args) + { + if (component.NextBeepTime < _timing.CurTime) + component.NextBeepTime = _timing.CurTime; + } + + private void OnUnpaused(EntityUid uid, ProximityBeeperComponent component, ref EntityUnpausedEvent args) + { + component.NextBeepTime += args.PausedTime; + } + + private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent component, ref PowerCellSlotEmptyEvent args) + { + if (component.Enabled) + TryDisable(uid, component); + } + + /// + /// Beeps the proximitybeeper as well as sets the time for the next beep + /// based on proximity to entities with the target component. + /// + public void UpdateBeep(EntityUid uid, ProximityBeeperComponent? component = null, bool playBeep = true) + { + if (!Resolve(uid, ref component)) + return; + + if (!component.Enabled) + { + component.NextBeepTime += component.MinBeepInterval; + return; + } + + var xformQuery = GetEntityQuery(); + var xform = xformQuery.GetComponent(uid); + var comp = EntityManager.ComponentFactory.GetRegistration(component.Component).Type; + float? closestDistance = null; + foreach (var targetXform in _entityLookup.GetComponentsInRange(xform.MapPosition, component.MaximumDistance)) + { + // forgive me father, for i have sinned. + var ent = targetXform.Owner; + + if (!HasComp(ent, comp)) + continue; + + var dist = (_transform.GetWorldPosition(xform, xformQuery) - _transform.GetWorldPosition(targetXform, xformQuery)).Length; + if (dist >= (closestDistance ?? float.MaxValue)) + continue; + closestDistance = dist; + } + + if (closestDistance is not { } distance) + return; + + if (playBeep) + _audio.PlayPvs(component.BeepSound, uid); + + var scalingFactor = distance / component.MaximumDistance; + var interval = (component.MaxBeepInterval - component.MinBeepInterval) * scalingFactor + component.MinBeepInterval; + component.NextBeepTime += interval; + } + + /// + /// Enables the proximity beeper + /// + public bool TryEnable(EntityUid uid, ProximityBeeperComponent? component = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component)) + return false; + + TryComp(uid, out var draw); + + if (!_powerCell.HasActivatableCharge(uid, battery: draw, user: user)) + return false; + + component.Enabled = true; + _appearance.SetData(uid, ProximityBeeperVisuals.Enabled, true); + component.NextBeepTime = _timing.CurTime; + UpdateBeep(uid, component, false); + if (draw != null) + draw.Enabled = true; + return true; + } + + /// + /// Disables the proximity beeper + /// + public bool TryDisable(EntityUid uid, ProximityBeeperComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + if (!component.Enabled) + return false; + + component.Enabled = false; + _appearance.SetData(uid, ProximityBeeperVisuals.Enabled, false); + if (TryComp(uid, out var draw)) + draw.Enabled = true; + UpdateBeep(uid, component); + return true; + } + + /// + /// toggles the proximity beeper + /// + public bool TryToggle(EntityUid uid, ProximityBeeperComponent? component = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component)) + return false; + + return component.Enabled + ? TryDisable(uid, component) + : TryEnable(uid, component, user); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var beeper)) + { + if (!beeper.Enabled) + continue; + + if (_timing.CurTime < beeper.NextBeepTime) + continue; + UpdateBeep(uid, beeper); + } + } +} diff --git a/Content.Shared/Pinpointer/SharedProximityBeeper.cs b/Content.Shared/Pinpointer/SharedProximityBeeper.cs new file mode 100644 index 0000000000..5163112683 --- /dev/null +++ b/Content.Shared/Pinpointer/SharedProximityBeeper.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Pinpointer; + +[Serializable, NetSerializable] +public enum ProximityBeeperVisuals : byte +{ + Enabled +} diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml index 19f084fed6..1ebbf8311f 100644 --- a/Resources/Audio/Items/attributions.yml +++ b/Resources/Audio/Items/attributions.yml @@ -3,6 +3,11 @@ copyright: "Created by Pól, converted to OGG and Mono by EmoGarbage" source: "https://freesound.org/people/P%C3%B3l/sounds/385927/" +- files: ["locator_beep.ogg"] + license: "CC0-1.0" + copyright: "Created by MATRIXXX_, converted to OGG, shortened, sped up, and pitched up by EmoGarbage404 (github)" + source: "https://freesound.org/people/MATRIXXX_/sounds/657947/" + - files: ["trayhit1.ogg"] license: "CC-BY-SA-3.0" copyright: "Time immemorial" diff --git a/Resources/Audio/Items/locator_beep.ogg b/Resources/Audio/Items/locator_beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ae37c1e0ca2cca3afee769e57de345133a293a60 GIT binary patch literal 5858 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 C8i05J literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/Catalog/Research/technologies.yml b/Resources/Prototypes/Catalog/Research/technologies.yml index 1ad09c82ba..9db86f498a 100644 --- a/Resources/Prototypes/Catalog/Research/technologies.yml +++ b/Resources/Prototypes/Catalog/Research/technologies.yml @@ -521,6 +521,7 @@ - ScanningModuleStockPart - NodeScanner - AnomalyScanner + - AnomalyLocator - type: technology name: technologies-anomaly-technology diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index ff3c7606ad..a91be8c588 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -20,3 +20,46 @@ - type: GuideHelp guides: - ScannersAndVessels + +- type: entity + id: AnomalyLocator + parent: [ BaseItem, PowerCellSlotSmallItem ] + name: anomaly locator + description: A device designed to aid in the locating of anomalies. Did you check the gas miners? + components: + - type: Sprite + sprite: Objects/Specific/Research/anomalylocator.rsi + netsync: false + layers: + - state: icon + - state: screen + shader: unshaded + visible: false + map: ["enum.PowerDeviceVisualLayers.Powered"] + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ProximityBeeperVisuals.Enabled: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: PowerCellDraw + drawRate: 10 + useRate: 0 + - type: ProximityBeeper + component: Anomaly + beepSound: + path: "/Audio/Items/locator_beep.ogg" + params: + maxdistance: 1 + volume: -8 + +- type: entity + id: AnomalyLocatorNoBattery + parent: AnomalyLocator + suffix: No Battery + components: + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 18bae43997..05ed6926d3 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -170,6 +170,7 @@ - ConveyorBeltAssembly - AppraisalTool - AnomalyScanner + - AnomalyLocator - RCD - RCDAmmo - HydroponicsToolScythe diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 135d1005ae..36b66a7cc4 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -49,6 +49,14 @@ Plastic: 200 Glass: 100 +- type: latheRecipe + id: AnomalyLocator + result: AnomalyLocatorNoBattery + completetime: 3 + materials: + Steel: 400 + Glass: 100 + - type: latheRecipe id: AnomalyScanner result: AnomalyScanner diff --git a/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/icon.png b/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1ded1594d4c7aa5dec8cc0eed408ab9763bb77cb GIT binary patch literal 442 zcmV;r0Y(0aP)Px$bV)=(R9J=WmA`AkKorNnkq(`bMY1{yBG}QbP-#0j1Lq>~c0Lhk(0ZE#H%% zp1=%3zM-UYu6NA>lYlG-kyaJhwzCl1n?SjIwKi9+-tJBy&>O@0G9gJ42q7zZNs?e0 zCIX*h|21g@b^(>Z&t$UI`vA&UFpY^2k3%3g1c?)MCEt3jL%a-OdiViY{%TVH36)gN zO{>b<{6oEg#^asux=i1Icl-H?Z&jd}&j_7%7XZ+#)Lta5#JB7jyiE+=EJVivR!s07*qoM6N<$g32z!)&Kwi literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-left.png b/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..84f9ca5cc6e1440cf8f16a3d007c26ff1f1785a1 GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|-g>$?hE&XX zd&`>Zkb_9;!^KN{XNz`DGddDtmL$BPTvV)SZg0eml{eUq^4`qU?F-d9=su^L?O#5l z`a?gpv$jG&BY?&$=$-wWVDWALxeC=y9Hp7>+zXcl|JL%X+d0EzRmvBm%cU3s;4s>sB1&%K}aEa|xN>KNnwSAic^PE+3=`Ci3y_uD<6jALe1d%jBbYyTS< z7Vl^PG`VD((az16Z=U*7FW^#{ReF*YY$=HN7qjAB!_BHYK|34Y-M9Kx#<5kVL1dlv vhx@<3sb^i6-4IkJlyy;dgHt(3@%-J)1#3gwPfxE+193fF{an^LB{Ts5S4Mz= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-right.png b/Resources/Textures/Objects/Specific/Research/anomalylocator.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4743df715b2e9340e99c8a8e4d823b59fa7bab GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|9(uYshE&XX zd&`io$w8p?;qrw`zd9|Nc1&X`@4}BPbDCSu&Ew-;o4)DrtsP=*N8DUel~@12p0}xT zwq!<{3Q#`-LxXkToEb-plK0JhEStNz+E?6XS#mGmye(5Szu$J;F8H?d(UCR!#7^GBG-v!aGl~*F^EdkAyo@zjcb!%jEQ}32)vC9`x;%W!q${z{Oiow- zx9@m#WJzS6dh&hw_+nLcux%h>&f`yfjaN3vUpS^+{9|9$4*3gVw=HYp_r@A#7gva1 nDXy5l|8M`_ua6lS7(OtsJIWf#`}@1b0+3EmS3j3^P6*Upi@`lKfN>izSXi!>%n+|uhZnmIWwbTiw9E6GdRZDv)) zteW5d=MWQ6GYITUy8NH<>Qf2l+pbk79@o{@{kKoZ^`7(G+!iF;pn30vt`#>=-lU5l NuBWS?%Q~loCIG_FR7L;* literal 0 HcmV?d00001