From 79d34a02bb04e48847461acc108e5d1ba2f69722 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:39:19 +0000 Subject: [PATCH] add ai detector from ss13 (#3192) * add CollidingVisuals system * add AiDetectable to ai eye * add AI detector item * add AI detector to uplink, clean up observation kit too * switch to component... --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../_DV/Physics/CollidingVisualsComponent.cs | 42 ++++++++++++ .../_DV/Physics/CollidingVisualsSystem.cs | 64 +++++++++++++++++++ .../_DV/Whitelist/AiDetectableComponent.cs | 9 +++ .../en-US/_DV/store/uplink/deception.ftl | 4 ++ .../Locale/en-US/store/uplink-catalog.ftl | 2 +- .../Entities/Mobs/Player/silicon.yml | 1 + .../_DV/Catalog/Uplink/deception.yml | 29 +++++++++ .../Prototypes/_DV/Catalog/uplink_catalog.yml | 14 ---- .../Entities/Objects/Tools/ai_detector.yml | 57 +++++++++++++++++ 9 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 Content.Shared/_DV/Physics/CollidingVisualsComponent.cs create mode 100644 Content.Shared/_DV/Physics/CollidingVisualsSystem.cs create mode 100644 Content.Shared/_DV/Whitelist/AiDetectableComponent.cs create mode 100644 Resources/Locale/en-US/_DV/store/uplink/deception.ftl create mode 100644 Resources/Prototypes/_DV/Catalog/Uplink/deception.yml create mode 100644 Resources/Prototypes/_DV/Entities/Objects/Tools/ai_detector.yml diff --git a/Content.Shared/_DV/Physics/CollidingVisualsComponent.cs b/Content.Shared/_DV/Physics/CollidingVisualsComponent.cs new file mode 100644 index 0000000000..dfa1800652 --- /dev/null +++ b/Content.Shared/_DV/Physics/CollidingVisualsComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Physics; + +/// +/// Changes an appearance data string depending on active collisions with fixtures. +/// +[RegisterComponent, Access(typeof(CollidingVisualsSystem))] +public sealed partial class CollidingVisualsComponent : Component +{ + /// + /// A whitelist entities must match to be counted for collisions. + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// The string to use for appearance data when no fixtures are being collided with. + /// + [DataField] + public string Default = "none"; + + /// + /// The list of fixtures to check for collisions, first one colliding is used so is most important. + /// + [DataField(required: true)] + public List Fixtures = new(); + + /// + /// Actively colliding fixtures. + /// + [DataField] + public HashSet Active = new(); +} + +[Serializable, NetSerializable] +public enum CollidingVisuals : byte +{ + Layer, + Fixture +} diff --git a/Content.Shared/_DV/Physics/CollidingVisualsSystem.cs b/Content.Shared/_DV/Physics/CollidingVisualsSystem.cs new file mode 100644 index 0000000000..a6b41c8476 --- /dev/null +++ b/Content.Shared/_DV/Physics/CollidingVisualsSystem.cs @@ -0,0 +1,64 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Physics.Events; + +namespace Content.Shared._DV.Physics; + +public sealed class CollidingVisualsSystem : EntitySystem +{ + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartCollide); + SubscribeLocalEvent(OnEndCollide); + } + + private void OnStartCollide(Entity ent, ref StartCollideEvent args) + { + if (_whitelist.IsWhitelistFail(ent.Comp.Whitelist, args.OtherEntity)) + return; + + // update active fixtures and state + var state = ent.Comp.Default; + foreach (var id in ent.Comp.Fixtures) + { + if (args.OurFixtureId == id) + { + ent.Comp.Active.Add(id); + state = id; + break; + } + } + + SetState(ent, state); + } + + private void OnEndCollide(Entity ent, ref EndCollideEvent args) + { + if (_whitelist.IsWhitelistFail(ent.Comp.Whitelist, args.OtherEntity)) + return; + + ent.Comp.Active.Remove(args.OurFixtureId); + + // find the first state that is still active + var state = ent.Comp.Default; + foreach (var id in ent.Comp.Fixtures) + { + if (ent.Comp.Active.Contains(id)) + { + state = id; + break; + } + } + + SetState(ent, state); + } + + public void SetState(EntityUid uid, string state) + { + _appearance.SetData(uid, CollidingVisuals.Fixture, state); + } +} diff --git a/Content.Shared/_DV/Whitelist/AiDetectableComponent.cs b/Content.Shared/_DV/Whitelist/AiDetectableComponent.cs new file mode 100644 index 0000000000..2a07a98947 --- /dev/null +++ b/Content.Shared/_DV/Whitelist/AiDetectableComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Whitelist; + +/// +/// Component added to AI eye entity that lets it get detected by the syndicate ai detector. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class AiDetectableComponent : Component; diff --git a/Resources/Locale/en-US/_DV/store/uplink/deception.ftl b/Resources/Locale/en-US/_DV/store/uplink/deception.ftl new file mode 100644 index 0000000000..2bc05a77a6 --- /dev/null +++ b/Resources/Locale/en-US/_DV/store/uplink/deception.ftl @@ -0,0 +1,4 @@ +uplink-observation-kit-desc-deltav = Includes a syndicate crew monitor, high-capacity power cell and security hud disguised as sunglasses. + +uplink-ai-detector-name = AI Detector +uplink-ai-detector-desc = This multitool has a custom display that changes color depending on how close the AI's camera is to you. Use it to stay clear of snooping eyes! diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 5d0bd7e33b..6c6a6e1108 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -210,7 +210,7 @@ uplink-micro-bomb-implanter-desc = Explode on death or manual activation with th # Bundles uplink-observation-kit-name = Observation Kit -uplink-observation-kit-desc = Includes syndicate crew monitor, high power cell and security hud disguised as sunglasses. +uplink-observation-kit-desc = Includes surveillance camera monitor board and security hud disguised as sunglasses. uplink-emp-kit-name = Electrical Disruptor Kit uplink-emp-kit-desc = The ultimate reversal on energy-based weaponry: Disables disablers, stuns stunbatons, discharges laser guns! Contains 3 EMP grenades and an EMP implanter. Note: Does not disrupt actual firearms. diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index baa02a248c..920acbdb4c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -478,6 +478,7 @@ - state: ai_camera shader: unshaded map: ["base"] + - type: AiDetectable # DeltaV - AI detector # The holographic representation of the AI that is projected from a holopad. - type: entity diff --git a/Resources/Prototypes/_DV/Catalog/Uplink/deception.yml b/Resources/Prototypes/_DV/Catalog/Uplink/deception.yml new file mode 100644 index 0000000000..a80fcbcaf8 --- /dev/null +++ b/Resources/Prototypes/_DV/Catalog/Uplink/deception.yml @@ -0,0 +1,29 @@ +- type: listing + id: Observationkit + name: uplink-observation-kit-name + description: uplink-observation-kit-desc-deltav + icon: + sprite: Objects/Storage/boxicons.rsi + state: tracks + productEntity: Observationskit + discountCategory: usualDiscounts + discountDownTo: + Telecrystal: 3 + cost: + Telecrystal: 6 + categories: + - UplinkDeception + +- type: listing + id: MultitoolAiDetector + name: uplink-ai-detector-name + description: uplink-ai-detector-desc + icon: + sprite: Objects/Tools/multitool.rsi + state: icon + productEntity: MultitoolAiDetector + cost: + Telecrystal: 1 + categories: + - UplinkDeception + diff --git a/Resources/Prototypes/_DV/Catalog/uplink_catalog.yml b/Resources/Prototypes/_DV/Catalog/uplink_catalog.yml index 49151ee02a..f6a9107a36 100644 --- a/Resources/Prototypes/_DV/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/_DV/Catalog/uplink_catalog.yml @@ -77,20 +77,6 @@ components: - SurplusBundle -- type: listing - id: Observationkit - name: uplink-observation-kit-name - description: uplink-observation-kit-desc - icon: { sprite: /Textures/Objects/Storage/boxicons.rsi, state: tracks } - productEntity: Observationskit - discountCategory: usualDiscounts - discountDownTo: - Telecrystal: 3 - cost: - Telecrystal: 6 # DeltaV - Price Goes up, but the goods are better! - categories: - - UplinkDeception - - type: listing id: UplinkSyndicateRadioImplanter name: uplink-syndicate-radio-implanter-name diff --git a/Resources/Prototypes/_DV/Entities/Objects/Tools/ai_detector.yml b/Resources/Prototypes/_DV/Entities/Objects/Tools/ai_detector.yml new file mode 100644 index 0000000000..6c3f2a04b3 --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Objects/Tools/ai_detector.yml @@ -0,0 +1,57 @@ +- type: entity + parent: Multitool + id: MultitoolAiDetector + suffix: AI Detector + components: + - type: Sprite + layers: + - state: icon + - state: green-unlit + shader: unshaded + map: [ enum.CollidingVisuals.Layer ] + - type: Physics + canCollide: true + - type: Fixtures + fixtures: + fix1: + shape: !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 20 + mask: + - ItemMask + restitution: 0.3 # fite me + friction: 0.2 + yellow: + shape: !type:PhysShapeCircle + radius: 12 + density: 0 + hard: false + mask: + - GhostImpassable + red: + shape: !type:PhysShapeCircle + radius: 5 + density: 0 + hard: false + mask: + - GhostImpassable + - type: CollisionWake # don't stop checking for AI just because this isn't moving + enabled: false + - type: Appearance + - type: GenericVisualizer + visuals: + enum.CollidingVisuals.Fixture: + enum.CollidingVisuals.Layer: + none: { state: "green-unlit" } + yellow: { state: "yellow-unlit" } + red: { state: "red-unlit" } + - type: CollidingVisuals + whitelist: + components: + - AiDetectable + fixtures: + - red + - yellow + - type: MappingCategories # don't map valid multitool by mistake + categories: + - Syndicate