Intellicards now have a doAfter. (#33198)
* 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>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
3
Resources/Locale/en-US/intellicard/intellicard.ftl
Normal file
3
Resources/Locale/en-US/intellicard/intellicard.ftl
Normal file
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
unshaded:
|
||||
Empty: { state: empty }
|
||||
Occupied: { state: full }
|
||||
- type: Intellicard
|
||||
|
||||
- type: entity
|
||||
id: PlayerStationAiEmpty
|
||||
|
||||
Reference in New Issue
Block a user