Files

337 lines
12 KiB
C#
Raw Permalink Normal View History

using System.Linq;
using Content.Server.Administration;
using Content.Server.Chat.Managers;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Content.Shared.Chat;
using Content.Shared.Emag.Systems;
2024-11-19 21:16:49 -08:00
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Radio.Components;
using Content.Shared.Roles;
using Content.Shared.Roles.Components;
using Content.Shared.Silicons.Laws;
using Content.Shared.Silicons.Laws.Components;
using Robust.Server.GameObjects;
2024-11-19 21:16:49 -08:00
using Robust.Shared.Audio;
using Robust.Shared.Containers;
2023-10-29 04:21:02 +11:00
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
namespace Content.Server.Silicons.Laws;
/// <inheritdoc/>
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!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SiliconLawBoundComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SiliconLawBoundComponent, MindAddedMessage>(OnMindAdded);
SubscribeLocalEvent<SiliconLawBoundComponent, ToggleLawsScreenEvent>(OnToggleLawsScreen);
SubscribeLocalEvent<SiliconLawBoundComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
SubscribeLocalEvent<SiliconLawBoundComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawnComplete);
SubscribeLocalEvent<SiliconLawProviderComponent, GetSiliconLawsEvent>(OnDirectedGetLaws);
SubscribeLocalEvent<SiliconLawProviderComponent, IonStormLawsEvent>(OnIonStormLaws);
2024-11-14 17:21:03 +01:00
SubscribeLocalEvent<SiliconLawProviderComponent, MindAddedMessage>(OnLawProviderMindAdded);
SubscribeLocalEvent<SiliconLawProviderComponent, MindRemovedMessage>(OnLawProviderMindRemoved);
SubscribeLocalEvent<SiliconLawProviderComponent, SiliconEmaggedEvent>(OnEmagLawsAdded);
}
private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args)
{
GetLaws(uid, component);
}
private void OnMindAdded(EntityUid uid, SiliconLawBoundComponent component, MindAddedMessage args)
{
if (!TryComp<ActorComponent>(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"));
2024-11-16 02:06:52 +01:00
if (!TryComp<SiliconLawProviderComponent>(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));
2024-11-17 20:23:45 +01:00
_chatManager.ChatMessageToOne(ChatChannel.Server, modifedLawMsg, modifiedLawWrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
}
2024-11-16 02:06:52 +01:00
private void OnLawProviderMindAdded(Entity<SiliconLawProviderComponent> ent, ref MindAddedMessage args)
2024-11-14 17:21:03 +01:00
{
2024-11-16 02:06:52 +01:00
if (!ent.Comp.Subverted)
2024-11-14 17:21:03 +01:00
return;
2024-11-14 17:53:15 +01:00
EnsureSubvertedSiliconRole(args.Mind);
2024-11-14 17:21:03 +01:00
}
2024-11-16 02:06:52 +01:00
private void OnLawProviderMindRemoved(Entity<SiliconLawProviderComponent> ent, ref MindRemovedMessage args)
2024-11-14 17:21:03 +01:00
{
2024-11-16 02:06:52 +01:00
if (!ent.Comp.Subverted)
2024-11-14 17:21:03 +01:00
return;
2024-11-14 17:53:15 +01:00
RemoveSubvertedSiliconRole(args.Mind);
2024-11-14 17:21:03 +01:00
}
private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent component, ToggleLawsScreenEvent args)
{
if (args.Handled || !TryComp<ActorComponent>(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);
2024-11-14 17:21:03 +01:00
// Show the silicon has been subverted.
component.Subverted = true;
// new laws may allow antagonist behaviour so make it clear for admins
2024-11-16 02:06:52 +01:00
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);
2024-11-14 17:21:03 +01:00
// 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<SubvertedSiliconRoleComponent>(mindId))
Role Types (#33420) * mindcomponent namespace * wip MindRole stuff * admin player tab * mindroletype comment * mindRolePrototype redesign * broken param * wip RoleType implementation * basic role type switching for antags * traitor fix * fix AdminPanel update * the renameningTM * cleanup * feature uncreeping * roletypes on mind roles * update MindComponent.RoleType when MindRoles change * ghostrole configuration * ghostrole config improvements * live update of roleType on the character window * logging stuff and notes * remove thing no one asked for * weh * Mind Role Entities wip * headrev count fix * silicon stuff, cleanup * exclusive antag config, cleanup * jobroleadd overwerite * logging stuff * MindHasRole cleanup, admin log stuff * last second cleanup * ocd * move roletypeprototype to its own file, minor note stuff * remove Roletype.Created * log stuff * roletype setup for ghostroles and autotraitor reinforcements * ghostrole type configs * adjustable admin overlay * cleanup * fix this in its own PR * silicon antagonist * borg stuff * mmi roletype handling * spawnable borg roletype handling * weh * ghost role cleanup * weh * RoleEvent update * polish * log stuff * admin overlay config * ghostrolecomponent cleanup * weh * admin overlay code cleanup * minor cleanup * Obsolete MindRoleAddedEvent * comment * minor code cleanup * MindOnDoGreeting fix * Role update message * fix duplicate job greeting for cyborgs * fix emag job message dupe * nicer-looking role type update * crew aligned * syndicate assault borg role fix * fix test fail * fix a merge mistake * fix LoneOp role type * Update Content.Client/Administration/AdminNameOverlay.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Content.Shared/Roles/SharedRoleSystem.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * comment formatting Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * change logging category Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fix a space Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * use MindAddRoles Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * get MindComponent from TryGetMind Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * move var declaration outside loop * remove TryComp * take RoleEnum behind the barn * don't use ensurecomp unnecessarily * cvar comments * toggleableghostrolecomponent documentation * skrek * use EntProtoId * mindrole config * merge baserolecomponent into basemindrolecomponent * ai and borg silicon role tweaks * formatting Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * I will end you (the color) Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * use LocId type for a locale id * update RoleEvent documentation * update RoleEvent documentation * remove obsolete MindRoleAddedEvent * refine MindRolesUpdate() * use dependency Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * inject dependency Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * roleType.Name no longer required * reformatted draw code logic * GhostRoleMarkerRoleComponent comment * minor SharedRoleSystem cleanup * StartingMindRoleComponent, unhardcode roundstart silicon * Update Content.Shared/Roles/SharedRoleSystem.cs * remove a whitespace --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2025-01-11 22:17:26 +01:00
_roles.MindAddRole(mindId, "MindRoleSubvertedSilicon", silent: true);
}
protected override void RemoveSubvertedSiliconRole(EntityUid mindId)
{
base.RemoveSubvertedSiliconRole(mindId);
if (_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
_roles.MindRemoveRole<SubvertedSiliconRoleComponent>(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<ActorComponent>(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);
}
/// <summary>
/// Extract all the laws from a lawset's prototype ids.
/// </summary>
public SiliconLawset GetLawset(ProtoId<SiliconLawsetPrototype> lawset)
{
var proto = _prototype.Index(lawset);
var laws = new SiliconLawset()
{
Laws = new List<SiliconLaw>(proto.Laws.Count)
};
foreach (var law in proto.Laws)
{
laws.Laws.Add(_prototype.Index<SiliconLawPrototype>(law).ShallowClone());
}
laws.ObeysTo = proto.ObeysTo;
return laws;
}
/// <summary>
/// Set the laws of a silicon entity while notifying the player.
/// </summary>
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null)
{
if (!TryComp<SiliconLawProviderComponent>(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<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
{
// TODO: Prediction dump this
if (!TryComp<SiliconLawProviderComponent>(args.Entity, out var provider))
return;
var lawset = provider.Lawset ?? GetLawset(provider.Laws);
var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components);
while (query.MoveNext(out var update))
{
SetLaws(lawset.Laws, update, provider.LawUploadSound);
}
}
}
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class LawsCommand : ToolshedCommand
{
private SiliconLawSystem? _law;
[CommandImplementation("list")]
public IEnumerable<EntityUid> List()
{
var query = EntityManager.EntityQueryEnumerator<SiliconLawBoundComponent>();
while (query.MoveNext(out var uid, out _))
{
yield return uid;
}
}
[CommandImplementation("get")]
public IEnumerable<string> Get([PipedArgument] EntityUid lawbound)
{
_law ??= GetSys<SiliconLawSystem>();
foreach (var law in _law.GetLaws(lawbound).Laws)
{
yield return $"law {law.LawIdentifierOverride ?? law.Order.ToString()}: {Loc.GetString(law.LawString)}";
}
}
}