Skip to content

Commit

Permalink
Intellicards now have a doAfter. (#33198)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
2 people authored and sleepyyapril committed Jan 16, 2025
1 parent 0383dc1 commit c359d95
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 7 deletions.
16 changes: 14 additions & 2 deletions Content.Server/Silicons/StationAi/StationAiSystem.cs
Original file line number Diff line number Diff line change
@@ -1,12 +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;
using static Content.Server.Chat.Systems.ChatSystem;
Expand Down Expand Up @@ -87,6 +86,19 @@ public override bool SetWhitelistEnabled(Entity<StationAiWhitelistComponent> ent
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);
Expand Down
39 changes: 39 additions & 0 deletions Content.Shared/Intellicard/Components/IntellicardComponent.cs
Original file line number Diff line number Diff line change
@@ -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;
}
20 changes: 19 additions & 1 deletion Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private void OnCoreJump(Entity<StationAiHeldComponent> ent, ref JumpToCoreEvent
}

/// <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)
{
Expand All @@ -70,6 +70,24 @@ private bool TryGetHeld(Entity<StationAiCoreComponent?> entity, out EntityUid he
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, null, null), out var container) ||
Expand Down
75 changes: 71 additions & 4 deletions Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
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;
Expand All @@ -15,6 +17,7 @@
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.GameObjects;
Expand Down Expand Up @@ -43,6 +46,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!;
Expand Down Expand Up @@ -92,6 +96,7 @@ public override void Initialize()
SubscribeLocalEvent<StationAiHolderComponent, MapInitEvent>(OnHolderMapInit);
SubscribeLocalEvent<StationAiHolderComponent, EntInsertedIntoContainerMessage>(OnHolderConInsert);
SubscribeLocalEvent<StationAiHolderComponent, EntRemovedFromContainerMessage>(OnHolderConRemove);
SubscribeLocalEvent<StationAiHolderComponent, IntellicardDoAfterEvent>(OnIntellicardDoAfter);

SubscribeLocalEvent<StationAiCoreComponent, EntInsertedIntoContainerMessage>(OnAiInsert);
SubscribeLocalEvent<StationAiCoreComponent, EntRemovedFromContainerMessage>(OnAiRemove);
Expand Down Expand Up @@ -205,15 +210,22 @@ private void OnAiInRange(Entity<StationAiOverlayComponent> ent, ref InRangeOverr
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;
}
Expand All @@ -223,7 +235,7 @@ private void OnHolderInteract(Entity<StationAiHolderComponent> ent, ref AfterInt
}

// 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))
{
Expand All @@ -234,6 +246,55 @@ private void OnHolderInteract(Entity<StationAiHolderComponent> ent, ref AfterInt
}
}

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);
Expand Down Expand Up @@ -434,6 +495,8 @@ private void UpdateAppearance(Entity<StationAiHolderComponent?> entity)
_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)
Expand Down Expand Up @@ -505,6 +568,10 @@ public sealed partial class JumpToCoreEvent : InstantActionEvent

}

[Serializable, NetSerializable]
public sealed partial class IntellicardDoAfterEvent : SimpleDoAfterEvent;


[Serializable, NetSerializable]
public enum StationAiVisualState : byte
{
Expand Down
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/intellicard/intellicard.ftl
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/silicons/station-ai.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Player/silicon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
unshaded:
Empty: { state: empty }
Occupied: { state: full }
- type: Intellicard

- type: entity
id: PlayerStationAiEmpty
Expand Down

0 comments on commit c359d95

Please sign in to comment.