using System.Linq; using Content.Server.Administration; using Content.Server.Chat.Managers; using Content.Server.Radio.Components; using Content.Server.Roles; using Content.Server.Station.Systems; using Content.Shared.Administration; using Content.Shared.Chat; using Content.Shared.Emag.Systems; using Content.Shared.GameTicking; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Roles; using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; namespace Content.Server.Silicons.Laws; /// public sealed class SiliconLawSystem : SharedSiliconLawSystem { [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly EmagSystem _emag = default!; /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnToggleLawsScreen); SubscribeLocalEvent(OnBoundUIOpened); SubscribeLocalEvent(OnPlayerSpawnComplete); SubscribeLocalEvent(OnDirectedGetLaws); SubscribeLocalEvent(OnIonStormLaws); SubscribeLocalEvent(OnLawProviderMindAdded); SubscribeLocalEvent(OnLawProviderMindRemoved); SubscribeLocalEvent(OnEmagLawsAdded); } private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args) { GetLaws(uid, component); } private void OnMindAdded(EntityUid uid, SiliconLawBoundComponent component, MindAddedMessage args) { if (!TryComp(uid, out var actor)) return; var msg = Loc.GetString("laws-notify"); var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.FromHex("#5ed7aa")); if (!TryComp(uid, out var lawcomp)) return; if (!lawcomp.Subverted) return; var modifedLawMsg = Loc.GetString("laws-notify-subverted"); var modifiedLawWrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", modifedLawMsg)); _chatManager.ChatMessageToOne(ChatChannel.Server, modifedLawMsg, modifiedLawWrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red); } private void OnLawProviderMindAdded(Entity ent, ref MindAddedMessage args) { if (!ent.Comp.Subverted) return; EnsureSubvertedSiliconRole(args.Mind); } private void OnLawProviderMindRemoved(Entity ent, ref MindRemovedMessage args) { if (!ent.Comp.Subverted) return; RemoveSubvertedSiliconRole(args.Mind); } private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent component, ToggleLawsScreenEvent args) { if (args.Handled || !TryComp(uid, out var actor)) return; args.Handled = true; _userInterface.TryToggleUi(uid, SiliconLawsUiKey.Key, actor.PlayerSession); } private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args) { TryComp(uid, out IntrinsicRadioTransmitterComponent? intrinsicRadio); var radioChannels = intrinsicRadio?.Channels; var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels); _userInterface.SetUiState(args.Entity, SiliconLawsUiKey.Key, state); } private void OnPlayerSpawnComplete(EntityUid uid, SiliconLawBoundComponent component, PlayerSpawnCompleteEvent args) { component.LastLawProvider = args.Station; } private void OnDirectedGetLaws(EntityUid uid, SiliconLawProviderComponent component, ref GetSiliconLawsEvent args) { if (args.Handled) return; if (component.Lawset == null) component.Lawset = GetLawset(component.Laws); args.Laws = component.Lawset; args.Handled = true; } private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component, ref IonStormLawsEvent args) { // Emagged borgs are immune to ion storm if (!_emag.CheckFlag(uid, EmagType.Interaction)) { component.Lawset = args.Lawset; // gotta tell player to check their laws NotifyLawsChanged(uid, component.LawUploadSound); // Show the silicon has been subverted. component.Subverted = true; // new laws may allow antagonist behaviour so make it clear for admins if(_mind.TryGetMind(uid, out var mindId, out _)) EnsureSubvertedSiliconRole(mindId); } } private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref SiliconEmaggedEvent args) { if (component.Lawset == null) component.Lawset = GetLawset(component.Laws); // Show the silicon has been subverted. component.Subverted = true; // Add the first emag law before the others component.Lawset?.Laws.Insert(0, new SiliconLaw { LawString = Loc.GetString("law-emag-custom", ("name", Name(args.user)), ("title", Loc.GetString(component.Lawset.ObeysTo))), Order = 0 }); //Add the secrecy law after the others component.Lawset?.Laws.Add(new SiliconLaw { LawString = Loc.GetString("law-emag-secrecy", ("faction", Loc.GetString(component.Lawset.ObeysTo))), Order = component.Lawset.Laws.Max(law => law.Order) + 1 }); } protected override void EnsureSubvertedSiliconRole(EntityUid mindId) { base.EnsureSubvertedSiliconRole(mindId); if (!_roles.MindHasRole(mindId)) _roles.MindAddRole(mindId, "MindRoleSubvertedSilicon", silent: true); } protected override void RemoveSubvertedSiliconRole(EntityUid mindId) { base.RemoveSubvertedSiliconRole(mindId); if (_roles.MindHasRole(mindId)) _roles.MindTryRemoveRole(mindId); } public SiliconLawset GetLaws(EntityUid uid, SiliconLawBoundComponent? component = null) { if (!Resolve(uid, ref component)) return new SiliconLawset(); var ev = new GetSiliconLawsEvent(uid); RaiseLocalEvent(uid, ref ev); if (ev.Handled) { component.LastLawProvider = uid; return ev.Laws; } var xform = Transform(uid); if (_station.GetOwningStation(uid, xform) is { } station) { RaiseLocalEvent(station, ref ev); if (ev.Handled) { component.LastLawProvider = station; return ev.Laws; } } if (xform.GridUid is { } grid) { RaiseLocalEvent(grid, ref ev); if (ev.Handled) { component.LastLawProvider = grid; return ev.Laws; } } if (component.LastLawProvider == null || Deleted(component.LastLawProvider) || Terminating(component.LastLawProvider.Value)) { component.LastLawProvider = null; } else { RaiseLocalEvent(component.LastLawProvider.Value, ref ev); if (ev.Handled) { return ev.Laws; } } RaiseLocalEvent(ref ev); return ev.Laws; } public override void NotifyLawsChanged(EntityUid uid, SoundSpecifier? cue = null) { base.NotifyLawsChanged(uid, cue); if (!TryComp(uid, out var actor)) return; var msg = Loc.GetString("laws-update-notify"); var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); _chatManager.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); } /// /// Extract all the laws from a lawset's prototype ids. /// public SiliconLawset GetLawset(ProtoId lawset) { var proto = _prototype.Index(lawset); var laws = new SiliconLawset() { Laws = new List(proto.Laws.Count) }; foreach (var law in proto.Laws) { laws.Laws.Add(_prototype.Index(law)); } laws.ObeysTo = proto.ObeysTo; return laws; } /// /// Set the laws of a silicon entity while notifying the player. /// public void SetLaws(List newLaws, EntityUid target, SoundSpecifier? cue = null) { if (!TryComp(target, out var component)) return; if (component.Lawset == null) component.Lawset = new SiliconLawset(); component.Lawset.Laws = newLaws; NotifyLawsChanged(target, cue); } protected override void OnUpdaterInsert(Entity ent, ref EntInsertedIntoContainerMessage args) { // TODO: Prediction dump this if (!TryComp(args.Entity, out SiliconLawProviderComponent? provider)) return; var lawset = GetLawset(provider.Laws).Laws; var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components); while (query.MoveNext(out var update)) { SetLaws(lawset, update, provider.LawUploadSound); } } } [ToolshedCommand, AdminCommand(AdminFlags.Admin)] public sealed class LawsCommand : ToolshedCommand { private SiliconLawSystem? _law; [CommandImplementation("list")] public IEnumerable List() { var query = EntityManager.EntityQueryEnumerator(); while (query.MoveNext(out var uid, out _)) { yield return uid; } } [CommandImplementation("get")] public IEnumerable Get([PipedArgument] EntityUid lawbound) { _law ??= GetSys(); foreach (var law in _law.GetLaws(lawbound).Laws) { yield return $"law {law.LawIdentifierOverride ?? law.Order.ToString()}: {Loc.GetString(law.LawString)}"; } } }