]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Intellicards now have a doAfter. (#33198)
authorScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Sat, 9 Nov 2024 19:29:29 +0000 (20:29 +0100)
committerGitHub <noreply@github.com>
Sat, 9 Nov 2024 19:29:29 +0000 (13:29 -0600)
* init

* cleanup

* Oops! Forgot something

* addressing changes

* guh

* guh 2.0

* some cleanup

* all bless the intellicard

* Yippee

* small locale thing

* changes + small bugfix

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Server/Silicons/StationAi/StationAiSystem.cs
Content.Shared/Intellicard/Components/IntellicardComponent.cs [new file with mode: 0644]
Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs
Resources/Locale/en-US/intellicard/intellicard.ftl [new file with mode: 0644]
Resources/Locale/en-US/silicons/station-ai.ftl
Resources/Prototypes/Entities/Mobs/Player/silicon.yml

index 846497387d24aebb50a210b18f24f4028b00a0be..a10833dc63b0bae43123965cb05ed73dfe9bb9d4 100644 (file)
@@ -1,10 +1,11 @@
 using System.Linq;
 using Content.Server.Chat.Managers;
-using Content.Server.Chat.Systems;
 using Content.Shared.Chat;
+using Content.Shared.Mind;
+using Content.Shared.Roles;
 using Content.Shared.Silicons.StationAi;
 using Content.Shared.StationAi;
-using Robust.Shared.Audio.Systems;
+using Robust.Shared.Audio;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Player;
 
@@ -14,6 +15,8 @@ public sealed class StationAiSystem : SharedStationAiSystem
 {
     [Dependency] private readonly IChatManager _chats = default!;
     [Dependency] private readonly EntityLookupSystem _lookup = default!;
+    [Dependency] private readonly SharedMindSystem _mind = default!;
+    [Dependency] private readonly SharedRoleSystem _roles = default!;
 
     private readonly HashSet<Entity<StationAiCoreComponent>> _ais = new();
 
@@ -43,6 +46,19 @@ public sealed class StationAiSystem : SharedStationAiSystem
         return true;
     }
 
+    public override void AnnounceIntellicardUsage(EntityUid uid, SoundSpecifier? cue = null)
+    {
+        if (!TryComp<ActorComponent>(uid, out var actor))
+            return;
+
+        var msg = Loc.GetString("ai-consciousness-download-warning");
+        var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
+        _chats.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
+
+        if (cue != null && _mind.TryGetMind(uid, out var mindId, out _))
+            _roles.MindPlaySound(mindId, cue);
+    }
+
     private void AnnounceSnip(EntityUid entity)
     {
         var xform = Transform(entity);
diff --git a/Content.Shared/Intellicard/Components/IntellicardComponent.cs b/Content.Shared/Intellicard/Components/IntellicardComponent.cs
new file mode 100644 (file)
index 0000000..e271749
--- /dev/null
@@ -0,0 +1,39 @@
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Intellicard;
+
+/// <summary>
+/// Allows this entity to download the station AI onto an AiHolderComponent.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class IntellicardComponent : Component
+{
+    /// <summary>
+    /// The duration it takes to download the AI from an AiHolder.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public int DownloadTime = 15;
+
+    /// <summary>
+    /// The duration it takes to upload the AI to an AiHolder.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public int UploadTime = 3;
+
+    /// <summary>
+    /// The sound that plays for the AI
+    /// when they are being downloaded
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public SoundSpecifier? WarningSound = new SoundPathSpecifier("/Audio/Misc/notice2.ogg");
+
+    /// <summary>
+    /// The delay before allowing the warning to play again in seconds.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan WarningDelay = TimeSpan.FromSeconds(8);
+
+    [ViewVariables]
+    public TimeSpan NextWarningAllowed = TimeSpan.Zero;
+}
index e067cf3efadb60cd9216d9eb8c53e63f2db72aea..c9279b02151381cafe4abf15d112d42e00cba211 100644 (file)
@@ -54,7 +54,7 @@ public abstract partial class SharedStationAiSystem
     }
 
     /// <summary>
-    /// Tries to get the entity held in the AI core.
+    /// Tries to get the entity held in the AI core using StationAiCore.
     /// </summary>
     private bool TryGetHeld(Entity<StationAiCoreComponent?> entity, out EntityUid held)
     {
@@ -71,6 +71,24 @@ public abstract partial class SharedStationAiSystem
         return true;
     }
 
+    /// <summary>
+    /// Tries to get the entity held in the AI using StationAiHolder.
+    /// </summary>
+    private bool TryGetHeldFromHolder(Entity<StationAiHolderComponent?> entity, out EntityUid held)
+    {
+        held = EntityUid.Invalid;
+
+        if (!Resolve(entity.Owner, ref entity.Comp))
+            return false;
+
+        if (!_containers.TryGetContainer(entity.Owner, StationAiHolderComponent.Container, out var container) ||
+            container.ContainedEntities.Count == 0)
+            return false;
+
+        held = container.ContainedEntities[0];
+        return true;
+    }
+
     private bool TryGetCore(EntityUid ent, out Entity<StationAiCoreComponent?> core)
     {
         if (!_containers.TryGetContainingContainer(ent, out var container) ||
index f88df9eea6b7ffcc3b79be63c583026bb713aaa1..189515635a82ba9127bd084a2dd52796318d96ce 100644 (file)
@@ -4,7 +4,9 @@ using Content.Shared.Administration.Managers;
 using Content.Shared.Containers.ItemSlots;
 using Content.Shared.Database;
 using Content.Shared.Doors.Systems;
+using Content.Shared.DoAfter;
 using Content.Shared.Electrocution;
+using Content.Shared.Intellicard;
 using Content.Shared.Interaction;
 using Content.Shared.Item.ItemToggle;
 using Content.Shared.Mind;
@@ -15,6 +17,7 @@ using Content.Shared.Power;
 using Content.Shared.Power.EntitySystems;
 using Content.Shared.StationAi;
 using Content.Shared.Verbs;
+using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Map.Components;
@@ -40,6 +43,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
     [Dependency] private readonly   SharedAudioSystem _audio = default!;
     [Dependency] private readonly   SharedContainerSystem _containers = default!;
     [Dependency] private readonly   SharedDoorSystem _doors = default!;
+    [Dependency] private readonly   SharedDoAfterSystem _doAfter = default!;
     [Dependency] private readonly   SharedElectrocutionSystem _electrify = default!;
     [Dependency] private readonly   SharedEyeSystem _eye = default!;
     [Dependency] protected readonly SharedMapSystem Maps = default!;
@@ -87,6 +91,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
         SubscribeLocalEvent<StationAiHolderComponent, MapInitEvent>(OnHolderMapInit);
         SubscribeLocalEvent<StationAiHolderComponent, EntInsertedIntoContainerMessage>(OnHolderConInsert);
         SubscribeLocalEvent<StationAiHolderComponent, EntRemovedFromContainerMessage>(OnHolderConRemove);
+        SubscribeLocalEvent<StationAiHolderComponent, IntellicardDoAfterEvent>(OnIntellicardDoAfter);
 
         SubscribeLocalEvent<StationAiCoreComponent, EntInsertedIntoContainerMessage>(OnAiInsert);
         SubscribeLocalEvent<StationAiCoreComponent, EntRemovedFromContainerMessage>(OnAiRemove);
@@ -197,15 +202,22 @@ public abstract partial class SharedStationAiSystem : EntitySystem
         args.InRange = _vision.IsAccessible((targetXform.GridUid.Value, broadphase, grid), targetTile);
     }
 
-    private void OnHolderInteract(Entity<StationAiHolderComponent> ent, ref AfterInteractEvent args)
+
+    private void OnIntellicardDoAfter(Entity<StationAiHolderComponent> ent, ref IntellicardDoAfterEvent args)
     {
-        if (!TryComp(args.Target, out StationAiHolderComponent? targetHolder))
+        if (args.Cancelled)
+            return;
+
+        if (args.Handled)
+            return;
+
+        if (!TryComp(args.Args.Target, out StationAiHolderComponent? targetHolder))
             return;
 
         // Try to insert our thing into them
         if (_slots.CanEject(ent.Owner, args.User, ent.Comp.Slot))
         {
-            if (!_slots.TryInsert(args.Target.Value, targetHolder.Slot, ent.Comp.Slot.Item!.Value, args.User, excludeUserAudio: true))
+            if (!_slots.TryInsert(args.Args.Target.Value, targetHolder.Slot, ent.Comp.Slot.Item!.Value, args.User, excludeUserAudio: true))
             {
                 return;
             }
@@ -215,7 +227,7 @@ public abstract partial class SharedStationAiSystem : EntitySystem
         }
 
         // Otherwise try to take from them
-        if (_slots.CanEject(args.Target.Value, args.User, targetHolder.Slot))
+        if (_slots.CanEject(args.Args.Target.Value, args.User, targetHolder.Slot))
         {
             if (!_slots.TryInsert(ent.Owner, ent.Comp.Slot, targetHolder.Slot.Item!.Value, args.User, excludeUserAudio: true))
             {
@@ -226,6 +238,55 @@ public abstract partial class SharedStationAiSystem : EntitySystem
         }
     }
 
+    private void OnHolderInteract(Entity<StationAiHolderComponent> ent, ref AfterInteractEvent args)
+    {
+        if (args.Handled || !args.CanReach || args.Target == null)
+            return;
+
+        if (!TryComp(args.Target, out StationAiHolderComponent? targetHolder))
+            return;
+
+        //Don't want to download/upload between several intellicards. You can just pick it up at that point.
+        if (HasComp<IntellicardComponent>(args.Target))
+            return;
+
+        if (!TryComp(args.Used, out IntellicardComponent? intelliComp))
+            return;
+
+        var cardHasAi = _slots.CanEject(ent.Owner, args.User, ent.Comp.Slot);
+        var coreHasAi = _slots.CanEject(args.Target.Value, args.User, targetHolder.Slot);
+
+        if (cardHasAi && coreHasAi)
+        {
+            _popup.PopupClient(Loc.GetString("intellicard-core-occupied"), args.User, args.User, PopupType.Medium);
+            args.Handled = true;
+            return;
+        }
+        if (!cardHasAi && !coreHasAi)
+        {
+            _popup.PopupClient(Loc.GetString("intellicard-core-empty"), args.User, args.User, PopupType.Medium);
+            args.Handled = true;
+            return;
+        }
+
+        if (TryGetHeldFromHolder((args.Target.Value, targetHolder), out var held) && _timing.CurTime > intelliComp.NextWarningAllowed)
+        {
+            intelliComp.NextWarningAllowed = _timing.CurTime + intelliComp.WarningDelay;
+            AnnounceIntellicardUsage(held, intelliComp.WarningSound);
+        }
+
+        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cardHasAi ? intelliComp.UploadTime : intelliComp.DownloadTime, new IntellicardDoAfterEvent(), args.Target, ent.Owner)
+        {
+            BreakOnDamage = true,
+            BreakOnMove = true,
+            NeedHand = true,
+            BreakOnDropItem = true
+        };
+
+        _doAfter.TryStartDoAfter(doAfterArgs);
+        args.Handled = true;
+    }
+
     private void OnHolderInit(Entity<StationAiHolderComponent> ent, ref ComponentInit args)
     {
         _slots.AddItemSlot(ent.Owner, StationAiHolderComponent.Container, ent.Comp.Slot);
@@ -378,6 +439,8 @@ public abstract partial class SharedStationAiSystem : EntitySystem
         _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Occupied);
     }
 
+    public virtual void AnnounceIntellicardUsage(EntityUid uid, SoundSpecifier? cue = null) { }
+
     public virtual bool SetVisionEnabled(Entity<StationAiVisionComponent> entity, bool enabled, bool announce = false)
     {
         if (entity.Comp.Enabled == enabled)
@@ -419,6 +482,10 @@ public sealed partial class JumpToCoreEvent : InstantActionEvent
 
 }
 
+[Serializable, NetSerializable]
+public sealed partial class IntellicardDoAfterEvent : SimpleDoAfterEvent;
+
+
 [Serializable, NetSerializable]
 public enum StationAiVisualState : byte
 {
diff --git a/Resources/Locale/en-US/intellicard/intellicard.ftl b/Resources/Locale/en-US/intellicard/intellicard.ftl
new file mode 100644 (file)
index 0000000..aed155a
--- /dev/null
@@ -0,0 +1,3 @@
+# General
+intellicard-core-occupied = The AI core is already occupied by another digital consciousness.
+intellicard-core-empty = The AI core has no digital consciousness to download.
\ No newline at end of file
index 7d9db3f6dc5a8b39b183c78335942bf87759250f..76c30eb10187282ae0764b19ab79755dac8b8062 100644 (file)
@@ -20,3 +20,5 @@ electrify-door-off = Disable overcharge
 toggle-light = Toggle light
 
 ai-device-not-responding = Device is not responding
+
+ai-consciousness-download-warning = Your consciousness is being downloaded.
index 39750b470f5994fc762cf887ab431bc3ce5f5a7d..e787ef59f00a8f28f2897ac7d4e4ee4e52e5db4d 100644 (file)
         unshaded:
           Empty: { state: empty }
           Occupied: { state: full }
+  - type: Intellicard
 
 - type: entity
   id: PlayerStationAiEmpty