Delta-v/Content.Shared/Nyanotrasen/ReverseEngineering/SharedReverseEngineeringSys...

245 lines
8.4 KiB
C#

using Content.Shared.Audio;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Examine;
using Content.Shared.Nutrition.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Shared.ReverseEngineering;
public abstract class SharedReverseEngineeringSystem : EntitySystem
{
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] private readonly ItemSlotsSystem _slots = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReverseEngineeringComponent, ExaminedEvent>(OnItemExamined);
SubscribeLocalEvent<ReverseEngineeringMachineComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
SubscribeLocalEvent<ReverseEngineeringMachineComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
Subs.BuiEvents<ReverseEngineeringMachineComponent>(ReverseEngineeringMachineUiKey.Key, subs =>
{
subs.Event<BoundUIOpenedEvent>(OnOpened);
subs.Event<ReverseEngineeringScanMessage>(OnScanPressed);
subs.Event<ReverseEngineeringSafetyMessage>(OnSafetyToggled);
subs.Event<ReverseEngineeringAutoScanMessage>(OnAutoScanToggled);
subs.Event<ReverseEngineeringStopMessage>(OnStopPressed);
subs.Event<ReverseEngineeringEjectMessage>(OnEjectPressed);
});
SubscribeLocalEvent<ActiveReverseEngineeringMachineComponent, ComponentStartup>(OnActiveStartup);
SubscribeLocalEvent<ActiveReverseEngineeringMachineComponent, ComponentShutdown>(OnActiveShutdown);
}
/// <summary>
/// Returns true if the machine is actively reverse engineering something.
/// </summary>
public bool IsActive(EntityUid uid)
{
return HasComp<ActiveReverseEngineeringMachineComponent>(uid);
}
/// <summary>
/// Gets the item currently in the machine's target slot.
/// </summary>
public EntityUid? GetItem(Entity<ReverseEngineeringMachineComponent> ent)
{
if (!_slots.TryGetSlot(ent, ent.Comp.Slot, out var slot))
return null;
return slot.Item;
}
/// <summary>
/// Gets the difficulty of the current item, or 0 if there is none.
/// </summary>
public int GetDifficulty(Entity<ReverseEngineeringMachineComponent> ent)
{
if (GetItem(ent) is not {} item || !TryComp<ReverseEngineeringComponent>(item, out var rev))
return 0;
return rev.Difficulty;
}
private void OnItemExamined(Entity<ReverseEngineeringComponent> ent, ref ExaminedEvent args)
{
// TODO: Eventually this should probably get shoved into a contextual examine somewhere like health or machine upgrading.
args.PushMarkup(Loc.GetString("reverse-engineering-examine", ("diff", ent.Comp.Difficulty)));
}
private void OnEntInserted(Entity<ReverseEngineeringMachineComponent> ent, ref EntInsertedIntoContainerMessage args)
{
var (uid, comp) = ent;
if (args.Container.ID != comp.Slot)
return;
_slots.SetLock(uid, comp.Slot, true);
UpdateUI(ent);
_appearance.SetData(uid, OpenableVisuals.Opened, false);
}
private void OnEntRemoved(Entity<ReverseEngineeringMachineComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (args.Container.ID != ent.Comp.Slot)
return;
CancelProbe(ent);
_appearance.SetData(ent, OpenableVisuals.Opened, true);
}
private void OnActiveStartup(Entity<ActiveReverseEngineeringMachineComponent> ent, ref ComponentStartup args)
{
_ambientSound.SetAmbience(ent, true);
}
private void OnActiveShutdown(Entity<ActiveReverseEngineeringMachineComponent> ent, ref ComponentShutdown args)
{
_ambientSound.SetAmbience(ent, false);
}
#region UI
protected void UpdateUI(Entity<ReverseEngineeringMachineComponent> ent)
{
var scanMessage = GetScanMessage(ent);
var state = new ReverseEngineeringMachineState(scanMessage);
_ui.SetUiState(ent.Owner, ReverseEngineeringMachineUiKey.Key, state);
}
private void OnOpened(Entity<ReverseEngineeringMachineComponent> ent, ref BoundUIOpenedEvent args)
{
UpdateUI(ent);
}
private void OnScanPressed(Entity<ReverseEngineeringMachineComponent> ent, ref ReverseEngineeringScanMessage args)
{
if (!Timing.IsFirstTimePredicted)
return;
if (IsActive(ent) || GetItem(ent) == null)
return;
var (uid, comp) = ent;
Audio.PlayPredicted(comp.ClickSound, uid, args.Actor);
var active = EnsureComp<ActiveReverseEngineeringMachineComponent>(uid);
StartProbing((uid, comp, active));
}
private void OnSafetyToggled(Entity<ReverseEngineeringMachineComponent> ent, ref ReverseEngineeringSafetyMessage args)
{
if (!Timing.IsFirstTimePredicted)
return;
var (uid, comp) = ent;
Audio.PlayPredicted(comp.ClickSound, uid, args.Actor);
comp.SafetyOn = !comp.SafetyOn;
Dirty(uid, comp);
UpdateUI(ent);
}
private void OnAutoScanToggled(Entity<ReverseEngineeringMachineComponent> ent, ref ReverseEngineeringAutoScanMessage args)
{
if (!Timing.IsFirstTimePredicted)
return;
var (uid, comp) = ent;
Audio.PlayPredicted(comp.ClickSound, uid, args.Actor);
comp.AutoScan = !comp.AutoScan;
Dirty(uid, comp);
UpdateUI(ent);
}
private void OnStopPressed(Entity<ReverseEngineeringMachineComponent> ent, ref ReverseEngineeringStopMessage args)
{
if (!Timing.IsFirstTimePredicted)
return;
Audio.PlayPredicted(ent.Comp.ClickSound, ent, args.Actor);
CancelProbe(ent);
}
private void OnEjectPressed(Entity<ReverseEngineeringMachineComponent> ent, ref ReverseEngineeringEjectMessage args)
{
if (!Timing.IsFirstTimePredicted)
return;
Audio.PlayPredicted(ent.Comp.ClickSound, ent, args.Actor);
Eject(ent);
}
#endregion
protected void StartProbing(Entity<ReverseEngineeringMachineComponent, ActiveReverseEngineeringMachineComponent> ent)
{
ent.Comp2.NextProbe = Timing.CurTime + ent.Comp1.AnalysisDuration;
Dirty(ent, ent.Comp2);
}
protected void CancelProbe(Entity<ReverseEngineeringMachineComponent> ent)
{
ent.Comp.LastResult = null;
Dirty(ent, ent.Comp);
RemComp<ActiveReverseEngineeringMachineComponent>(ent);
UpdateUI(ent);
}
protected void Eject(Entity<ReverseEngineeringMachineComponent> ent)
{
if (!TryComp<ItemSlotsComponent>(ent, out var slots) || !_slots.TryGetSlot(ent, ent.Comp.Slot, out var slot, slots))
return;
_slots.SetLock(ent, slot, false, slots);
_slots.TryEject(ent, slot, user: null, out _);
}
private FormattedMessage GetScanMessage(Entity<ReverseEngineeringMachineComponent> ent)
{
var msg = new FormattedMessage();
if (GetItem(ent) is not {} item || !TryComp<ReverseEngineeringComponent>(item, out var rev))
{
msg.AddMarkup(Loc.GetString("reverse-engineering-status-ready"));
return msg;
}
var comp = ent.Comp;
msg.PushMarkup(Loc.GetString("reverse-engineering-current-item", ("item", item)));
msg.PushNewline();
var analysisScore = comp.ScanBonus;
if (!comp.SafetyOn)
analysisScore += comp.DangerBonus;
msg.PushMarkup(Loc.GetString("reverse-engineering-analysis-score", ("score", analysisScore)));
msg.PushMarkup(Loc.GetString("reverse-engineering-item-difficulty", ("difficulty", rev.Difficulty)));
msg.PushMarkup(Loc.GetString("reverse-engineering-progress", ("progress", rev.Progress)));
if (comp.LastResult is {} result)
{
var lastProbe = Loc.GetString($"reverse-engineering-result-{result}");
msg.AddMarkup(Loc.GetString("reverse-engineering-last-attempt-result", ("result", lastProbe)));
}
return msg;
}
}