SiliconLawSystem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using System.Linq;
  2. using Content.Server.Administration;
  3. using Content.Server.Chat.Managers;
  4. using Content.Server.Radio.Components;
  5. using Content.Server.Roles;
  6. using Content.Server.Station.Systems;
  7. using Content.Shared.Administration;
  8. using Content.Shared.Chat;
  9. using Content.Shared.Emag.Systems;
  10. using Content.Shared.GameTicking;
  11. using Content.Shared.Mind;
  12. using Content.Shared.Mind.Components;
  13. using Content.Shared.Roles;
  14. using Content.Shared.Silicons.Laws;
  15. using Content.Shared.Silicons.Laws.Components;
  16. using Content.Shared.Wires;
  17. using Robust.Server.GameObjects;
  18. using Robust.Shared.Audio;
  19. using Robust.Shared.Containers;
  20. using Robust.Shared.Player;
  21. using Robust.Shared.Prototypes;
  22. using Robust.Shared.Toolshed;
  23. namespace Content.Server.Silicons.Laws;
  24. /// <inheritdoc/>
  25. public sealed class SiliconLawSystem : SharedSiliconLawSystem
  26. {
  27. [Dependency] private readonly IChatManager _chatManager = default!;
  28. [Dependency] private readonly SharedMindSystem _mind = default!;
  29. [Dependency] private readonly IPrototypeManager _prototype = default!;
  30. [Dependency] private readonly SharedRoleSystem _roles = default!;
  31. [Dependency] private readonly StationSystem _station = default!;
  32. [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
  33. [Dependency] private readonly EmagSystem _emag = default!;
  34. /// <inheritdoc/>
  35. public override void Initialize()
  36. {
  37. base.Initialize();
  38. SubscribeLocalEvent<SiliconLawBoundComponent, MapInitEvent>(OnMapInit);
  39. SubscribeLocalEvent<SiliconLawBoundComponent, MindAddedMessage>(OnMindAdded);
  40. SubscribeLocalEvent<SiliconLawBoundComponent, ToggleLawsScreenEvent>(OnToggleLawsScreen);
  41. SubscribeLocalEvent<SiliconLawBoundComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
  42. SubscribeLocalEvent<SiliconLawBoundComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawnComplete);
  43. SubscribeLocalEvent<SiliconLawProviderComponent, GetSiliconLawsEvent>(OnDirectedGetLaws);
  44. SubscribeLocalEvent<SiliconLawProviderComponent, IonStormLawsEvent>(OnIonStormLaws);
  45. SubscribeLocalEvent<SiliconLawProviderComponent, MindAddedMessage>(OnLawProviderMindAdded);
  46. SubscribeLocalEvent<SiliconLawProviderComponent, MindRemovedMessage>(OnLawProviderMindRemoved);
  47. SubscribeLocalEvent<SiliconLawProviderComponent, SiliconEmaggedEvent>(OnEmagLawsAdded);
  48. }
  49. private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args)
  50. {
  51. GetLaws(uid, component);
  52. }
  53. private void OnMindAdded(EntityUid uid, SiliconLawBoundComponent component, MindAddedMessage args)
  54. {
  55. if (!TryComp<ActorComponent>(uid, out var actor))
  56. return;
  57. var msg = Loc.GetString("laws-notify");
  58. var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
  59. _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.FromHex("#5ed7aa"));
  60. if (!TryComp<SiliconLawProviderComponent>(uid, out var lawcomp))
  61. return;
  62. if (!lawcomp.Subverted)
  63. return;
  64. var modifedLawMsg = Loc.GetString("laws-notify-subverted");
  65. var modifiedLawWrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", modifedLawMsg));
  66. _chatManager.ChatMessageToOne(ChatChannel.Server, modifedLawMsg, modifiedLawWrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
  67. }
  68. private void OnLawProviderMindAdded(Entity<SiliconLawProviderComponent> ent, ref MindAddedMessage args)
  69. {
  70. if (!ent.Comp.Subverted)
  71. return;
  72. EnsureSubvertedSiliconRole(args.Mind);
  73. }
  74. private void OnLawProviderMindRemoved(Entity<SiliconLawProviderComponent> ent, ref MindRemovedMessage args)
  75. {
  76. if (!ent.Comp.Subverted)
  77. return;
  78. RemoveSubvertedSiliconRole(args.Mind);
  79. }
  80. private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent component, ToggleLawsScreenEvent args)
  81. {
  82. if (args.Handled || !TryComp<ActorComponent>(uid, out var actor))
  83. return;
  84. args.Handled = true;
  85. _userInterface.TryToggleUi(uid, SiliconLawsUiKey.Key, actor.PlayerSession);
  86. }
  87. private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args)
  88. {
  89. TryComp(uid, out IntrinsicRadioTransmitterComponent? intrinsicRadio);
  90. var radioChannels = intrinsicRadio?.Channels;
  91. var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels);
  92. _userInterface.SetUiState(args.Entity, SiliconLawsUiKey.Key, state);
  93. }
  94. private void OnPlayerSpawnComplete(EntityUid uid, SiliconLawBoundComponent component, PlayerSpawnCompleteEvent args)
  95. {
  96. component.LastLawProvider = args.Station;
  97. }
  98. private void OnDirectedGetLaws(EntityUid uid, SiliconLawProviderComponent component, ref GetSiliconLawsEvent args)
  99. {
  100. if (args.Handled)
  101. return;
  102. if (component.Lawset == null)
  103. component.Lawset = GetLawset(component.Laws);
  104. args.Laws = component.Lawset;
  105. args.Handled = true;
  106. }
  107. private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component, ref IonStormLawsEvent args)
  108. {
  109. // Emagged borgs are immune to ion storm
  110. if (!_emag.CheckFlag(uid, EmagType.Interaction))
  111. {
  112. component.Lawset = args.Lawset;
  113. // gotta tell player to check their laws
  114. NotifyLawsChanged(uid, component.LawUploadSound);
  115. // Show the silicon has been subverted.
  116. component.Subverted = true;
  117. // new laws may allow antagonist behaviour so make it clear for admins
  118. if(_mind.TryGetMind(uid, out var mindId, out _))
  119. EnsureSubvertedSiliconRole(mindId);
  120. }
  121. }
  122. private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref SiliconEmaggedEvent args)
  123. {
  124. if (component.Lawset == null)
  125. component.Lawset = GetLawset(component.Laws);
  126. // Show the silicon has been subverted.
  127. component.Subverted = true;
  128. // Add the first emag law before the others
  129. component.Lawset?.Laws.Insert(0, new SiliconLaw
  130. {
  131. LawString = Loc.GetString("law-emag-custom", ("name", Name(args.user)), ("title", Loc.GetString(component.Lawset.ObeysTo))),
  132. Order = 0
  133. });
  134. //Add the secrecy law after the others
  135. component.Lawset?.Laws.Add(new SiliconLaw
  136. {
  137. LawString = Loc.GetString("law-emag-secrecy", ("faction", Loc.GetString(component.Lawset.ObeysTo))),
  138. Order = component.Lawset.Laws.Max(law => law.Order) + 1
  139. });
  140. }
  141. protected override void EnsureSubvertedSiliconRole(EntityUid mindId)
  142. {
  143. base.EnsureSubvertedSiliconRole(mindId);
  144. if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
  145. _roles.MindAddRole(mindId, "MindRoleSubvertedSilicon", silent: true);
  146. }
  147. protected override void RemoveSubvertedSiliconRole(EntityUid mindId)
  148. {
  149. base.RemoveSubvertedSiliconRole(mindId);
  150. if (_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
  151. _roles.MindTryRemoveRole<SubvertedSiliconRoleComponent>(mindId);
  152. }
  153. public SiliconLawset GetLaws(EntityUid uid, SiliconLawBoundComponent? component = null)
  154. {
  155. if (!Resolve(uid, ref component))
  156. return new SiliconLawset();
  157. var ev = new GetSiliconLawsEvent(uid);
  158. RaiseLocalEvent(uid, ref ev);
  159. if (ev.Handled)
  160. {
  161. component.LastLawProvider = uid;
  162. return ev.Laws;
  163. }
  164. var xform = Transform(uid);
  165. if (_station.GetOwningStation(uid, xform) is { } station)
  166. {
  167. RaiseLocalEvent(station, ref ev);
  168. if (ev.Handled)
  169. {
  170. component.LastLawProvider = station;
  171. return ev.Laws;
  172. }
  173. }
  174. if (xform.GridUid is { } grid)
  175. {
  176. RaiseLocalEvent(grid, ref ev);
  177. if (ev.Handled)
  178. {
  179. component.LastLawProvider = grid;
  180. return ev.Laws;
  181. }
  182. }
  183. if (component.LastLawProvider == null ||
  184. Deleted(component.LastLawProvider) ||
  185. Terminating(component.LastLawProvider.Value))
  186. {
  187. component.LastLawProvider = null;
  188. }
  189. else
  190. {
  191. RaiseLocalEvent(component.LastLawProvider.Value, ref ev);
  192. if (ev.Handled)
  193. {
  194. return ev.Laws;
  195. }
  196. }
  197. RaiseLocalEvent(ref ev);
  198. return ev.Laws;
  199. }
  200. public override void NotifyLawsChanged(EntityUid uid, SoundSpecifier? cue = null)
  201. {
  202. base.NotifyLawsChanged(uid, cue);
  203. if (!TryComp<ActorComponent>(uid, out var actor))
  204. return;
  205. var msg = Loc.GetString("laws-update-notify");
  206. var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
  207. _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
  208. if (cue != null && _mind.TryGetMind(uid, out var mindId, out _))
  209. _roles.MindPlaySound(mindId, cue);
  210. }
  211. /// <summary>
  212. /// Extract all the laws from a lawset's prototype ids.
  213. /// </summary>
  214. public SiliconLawset GetLawset(ProtoId<SiliconLawsetPrototype> lawset)
  215. {
  216. var proto = _prototype.Index(lawset);
  217. var laws = new SiliconLawset()
  218. {
  219. Laws = new List<SiliconLaw>(proto.Laws.Count)
  220. };
  221. foreach (var law in proto.Laws)
  222. {
  223. laws.Laws.Add(_prototype.Index<SiliconLawPrototype>(law));
  224. }
  225. laws.ObeysTo = proto.ObeysTo;
  226. return laws;
  227. }
  228. /// <summary>
  229. /// Set the laws of a silicon entity while notifying the player.
  230. /// </summary>
  231. public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null)
  232. {
  233. if (!TryComp<SiliconLawProviderComponent>(target, out var component))
  234. return;
  235. if (component.Lawset == null)
  236. component.Lawset = new SiliconLawset();
  237. component.Lawset.Laws = newLaws;
  238. NotifyLawsChanged(target, cue);
  239. }
  240. protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
  241. {
  242. // TODO: Prediction dump this
  243. if (!TryComp(args.Entity, out SiliconLawProviderComponent? provider))
  244. return;
  245. var lawset = GetLawset(provider.Laws).Laws;
  246. var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components);
  247. while (query.MoveNext(out var update))
  248. {
  249. SetLaws(lawset, update, provider.LawUploadSound);
  250. }
  251. }
  252. }
  253. [ToolshedCommand, AdminCommand(AdminFlags.Admin)]
  254. public sealed class LawsCommand : ToolshedCommand
  255. {
  256. private SiliconLawSystem? _law;
  257. [CommandImplementation("list")]
  258. public IEnumerable<EntityUid> List()
  259. {
  260. var query = EntityManager.EntityQueryEnumerator<SiliconLawBoundComponent>();
  261. while (query.MoveNext(out var uid, out _))
  262. {
  263. yield return uid;
  264. }
  265. }
  266. [CommandImplementation("get")]
  267. public IEnumerable<string> Get([PipedArgument] EntityUid lawbound)
  268. {
  269. _law ??= GetSys<SiliconLawSystem>();
  270. foreach (var law in _law.GetLaws(lawbound).Laws)
  271. {
  272. yield return $"law {law.LawIdentifierOverride ?? law.Order.ToString()}: {Loc.GetString(law.LawString)}";
  273. }
  274. }
  275. }