Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intellicards now have a doAfter. #33198

Merged
merged 11 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions Content.Server/Silicons/StationAi/StationAiSystem.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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();

Expand Down Expand Up @@ -43,6 +46,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 @@ -54,7 +54,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 @@ -71,6 +71,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, 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.Map.Components;
Expand All @@ -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!;
Expand Down Expand Up @@ -87,6 +91,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 @@ -197,15 +202,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 @@ -215,7 +227,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 @@ -226,6 +238,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 @@ -378,6 +439,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 @@ -419,6 +482,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 @@ -303,6 +303,7 @@
unshaded:
Empty: { state: empty }
Occupied: { state: full }
- type: Intellicard

- type: entity
id: PlayerStationAiEmpty
Expand Down
Loading