using System.Diagnostics.CodeAnalysis; using Content.Shared._Horizon.FlavorText; using Content.Shared._Horizon.OutpostCapture.Components; using Content.Shared.Access.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Storage; using Robust.Shared.Containers; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; // ReSharper disable BadListLineBreaks namespace Content.Shared._Horizon.OutpostCapture; public class SharedOutpostCaptureSystem : EntitySystem { [Dependency] protected readonly INetManager NetMan = null!; [Dependency] protected readonly IRobustRandom Random = null!; [Dependency] protected readonly ItemSlotsSystem ItemSlotsSystem = null!; [Dependency] protected readonly IPrototypeManager PrototypeManager = null!; [Dependency] protected readonly SharedUserInterfaceSystem UiSystem = null!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = null!; [Dependency] protected readonly SharedTransformSystem TransformSystem = null!; protected ISawmill Sawmill = null!; public override void Initialize() { SawmillInit(); SubscribeLocalEvent(OnConsoleInit); SubscribeLocalEvent(OnConsoleRemove); SubscribeLocalEvent(OnGetInsertedAttempt); SubscribeLocalEvent(OnGetRemovedAttempt); SubscribeLocalEvent(OnOutpostInit); SubscribeLocalEvent(OnOutpostRemove); Subs.BuiEvents(CaptureUIKey.Key, subs => { subs.Event((uid, comp, _) => UpdateConsole((uid, comp))); subs.Event((uid, comp, _) => OnButtonPressed((uid, comp))); }); } #region Init private void SawmillInit() { Sawmill = LogManager.GetSawmill("OutpostCapture"); Sawmill.Info("_outpostCaptureSystem start his work!"); } private void OnConsoleInit(Entity console, ref ComponentInit args) { if (TryGetOutpost(console, out var outpost, out var outpostUid)) return; if (console.Comp.CanUseAsSpawnPoint && TransformSystem.TryGetMapOrGridCoordinates(console, out var coords)) outpost.SpawnLocation = coords; console.Comp.LinkedOutpost = GetNetEntity(outpostUid); outpost.LinkedConsoles.Add(GetNetEntity(console)); ItemSlotsSystem.AddItemSlot(console, console.Comp.ContainerSlot, console.Comp.IdCardSlot); } private void OnConsoleRemove(Entity console, ref ComponentRemove args) { if (TryGetOutpost(console, out var outpost, out _) || !TryGetNetEntity(console, out var netConsole)) return; console.Comp.LinkedOutpost = null; outpost.LinkedConsoles.Remove(netConsole.Value); outpost.CapturingConsoles.Remove(netConsole.Value); outpost.CapturedConsoles.Remove(netConsole.Value); ItemSlotsSystem.RemoveItemSlot(console, console.Comp.IdCardSlot); } private void OnGetInsertedAttempt(Entity console, ref EntInsertedIntoContainerMessage args) { UpdateConsole(console); } private void OnGetRemovedAttempt(Entity console, ref EntRemovedFromContainerMessage args) { UpdateConsole(console); } private void OnOutpostInit(Entity outpost, ref ComponentInit args) { if (outpost.Comp.NeedCaptured <= 0) { RemComp(outpost); return; } if (outpost.Comp.OutpostName == "empty") outpost.Comp.OutpostName = MetaData(outpost).EntityName; outpost.Comp.NextSpawn = outpost.Comp.SpawnCooldown; } private void OnOutpostRemove(Entity outpost, ref ComponentRemove args) { if (outpost.Comp.LinkedConsoles.Count <= 0 || outpost.Comp.CapturedConsoles.Count <= 0) return; UnlinkAllConsoles(outpost.Comp); } public void UnlinkAllConsoles(OutpostCaptureComponent outpost) { foreach (var console in outpost.LinkedConsoles) { if (TryGetEntity(console, out var actualConsole) || !TryComp(actualConsole, out var consoleComp)) continue; consoleComp.LinkedOutpost = null; consoleComp.CapturedFaction = null; consoleComp.CapturedFactionName = null; consoleComp.CapturingTime = null; Dirty(actualConsole.Value, consoleComp); } } #endregion #region Private methods private bool TryGetOutpost(EntityUid uid, [NotNullWhen(false)] out OutpostCaptureComponent? outpost, [NotNullWhen(false)] out EntityUid? grid) { outpost = null; grid = TransformSystem.GetGrid(Transform(uid).Coordinates); return grid == null || !TryComp(grid, out outpost); } #region Messages private void UpdateConsole(Entity console) { var progress = UpdateProgressBar(console); var buttonStateDisabled = !CanChangeCaptureState(console, out _); var labelState = FormattedLabel(console); var buttonState = FormattedButton(console); var state = new OutpostUIState(progress, buttonStateDisabled, labelState, buttonState); UiSystem.SetUiState(console.Owner, CaptureUIKey.Key, state); } private void OnButtonPressed(Entity console) { ChangeCaptureState(console); UpdateConsole(console); // Update console } #endregion #region Formats private string FormattedLabel(Entity console) { switch (console.Comp.State) { case OutpostConsoleState.Uncaptured: return $"{Loc.GetString("faction-uncaptured-state")}"; case OutpostConsoleState.Capturing: return $"{Loc.GetString("faction-capturing-state-by")} {Loc.GetString(console.Comp.CapturedFactionName ?? "faction-none")}!"; case OutpostConsoleState.Captured: return $"{Loc.GetString("faction-captured-state-by")} {Loc.GetString(console.Comp.CapturedFactionName ?? "faction-none")}!"; default: Sawmill.Error($"Unknown {console.Comp.State} state!"); break; } return Loc.GetString("faction-default-string"); } private string FormattedButton(Entity console) { var faction = TryGetFactionInSlot(console, out var slot); if (slot.ContainedEntity == null) return $"{Loc.GetString("faction-insert-faction-id")}"; if (faction == null) return $"{Loc.GetString("faction-id-card-not-belong-to-any-faction")}"; return faction.ID == console.Comp.CapturedFaction ? $"{Loc.GetString("faction-cant-capture-already-captured-outpost")}" : $"{Loc.GetString("faction-start-capture-outpost")}"; } #endregion private void ChangeCaptureState(Entity console) { if (!CanChangeCaptureState(console, out var faction)) return; var oldFaction = console.Comp.CapturedFaction; console.Comp.CapturedFaction = faction.ID; switch (console.Comp.State) { case OutpostConsoleState.Uncaptured: Sawmill.Info($"Faction {faction.ID}, start capturing console id - {console.Owner}"); break; case OutpostConsoleState.Capturing: Sawmill.Info($"Faction {faction.ID}, intercepts capturing console faction, {oldFaction} on id - {console.Owner}"); break; case OutpostConsoleState.Captured: Sawmill.Info($"Faction {faction.ID}, start recapturing console of faction, {oldFaction} on id - {console.Owner}"); break; default: Sawmill.Error("Don't know how to change default capture state!"); return; } string? oldFactionChannel = null; string? oldFactionName = null; if (oldFaction != null && PrototypeManager.TryIndex(oldFaction, out var oldFactionProto)) { oldFactionChannel = oldFactionProto.RadioChannel; oldFactionName = oldFactionProto.Name; } StartCapture(console, faction.RadioChannel, oldFactionChannel, faction.ID, faction.Name, oldFactionName, console.Comp.State ); console.Comp.State = OutpostConsoleState.Capturing; console.Comp.CapturedFactionName = faction.Name; Dirty(console); } private void StartCapture(Entity console, string? newChannel, string? oldChannel, string? faction, string? factionName, string? oldFactionName, OutpostConsoleState state) { if (!TryGetEntity(console.Comp.LinkedOutpost, out var outpost) || !TryComp(outpost, out var outpostCapture) || !TryGetNetEntity(console, out var netConsole)) return; console.Comp.CapturingTime = console.Comp.CaptureTime; if (outpostCapture.CapturedConsoles.Contains(netConsole.Value)) outpostCapture.CapturedConsoles.Remove(netConsole.Value); if (NetMan.IsServer) { var ev = new CaptureStartedEvent(newChannel, oldChannel, faction, factionName, oldFactionName, state); RaiseLocalEvent(console, ev); } if (outpostCapture.CapturingConsoles.Contains(netConsole.Value)) return; outpostCapture.CapturingConsoles.Add(netConsole.Value); } private CharacterFactionPrototype? TryGetFactionInSlot(Entity console, out ContainerSlot slot) { slot = ContainerSystem.EnsureContainer(console, console.Comp.ContainerSlot, out var existed); if (!existed || slot.ContainedEntity == null) return null; var id = slot.ContainedEntity.Value; if (!TryComp(id, out var card) || card.JobPrototype == null) return null; var prototype = card.JobPrototype.Value; if (!PrototypeManager.TryIndex(prototype, out var index)) return null; var faction = index.ForceFaction; return !PrototypeManager.TryIndex(faction, out var factionIndex) ? null : factionIndex; } private bool CanChangeCaptureState(Entity console, [NotNullWhen(true)] out CharacterFactionPrototype? faction) { faction = TryGetFactionInSlot(console, out _); if (faction == null) return false; if (console.Comp.LinkedOutpost == null) return false; return console.Comp.CapturedFaction != faction.ID; } #endregion public float? UpdateProgressBar(Entity console) { var progress = (float?) (1f - console.Comp.CapturingTime / console.Comp.CaptureTime) * 100f; progress = progress != null ? float.Clamp(progress.Value, 0f, 100f) : null; return progress; } } [Prototype("outpostSpawn")] // ReSharper disable once PartialTypeWithSinglePart Ридер глупи. public sealed partial class OutpostSpawnPrototype : IPrototype { [IdDataField] public string ID { get; } = null!; [DataField("spawn")] public List SpawnList = []; } #region Serializable [Serializable, NetSerializable] public sealed class OutpostUIState(float? progress, bool disabled, string labelState, string buttonState) : BoundUserInterfaceState { public float? Progress => progress; public bool Disabled => disabled; public string LabelState => labelState; public string ButtonState => buttonState; } [Serializable, NetSerializable] public sealed class OutpostCaptureButtonPressed : BoundUserInterfaceMessage; [Serializable, NetSerializable] public sealed class ProgressBarUpdate(float? value) : BoundUserInterfaceMessage { public float? Value = value; } [Serializable, NetSerializable] public sealed class CaptureStartedEvent( string? radioChannel, string? oldRadioChannel, string? faction, string? factionName, string? oldFactionName, OutpostConsoleState state) : EntityEventArgs { public string? RadioChannel => radioChannel; public string? OldRadioChannel => oldRadioChannel; public string? Faction => faction; public string? FactionName => factionName; public string? OldFactionName => oldFactionName; public OutpostConsoleState State => state; } [Serializable, NetSerializable] public enum CaptureUIKey : byte { Key = 0, } #endregion