using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Mind; using Content.Shared.Roles.Jobs; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Roles; public abstract class SharedRoleSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _minds = default!; // TODO please lord make role entities private readonly HashSet _antagTypes = new(); public override void Initialize() { // TODO make roles entities SubscribeLocalEvent(OnJobGetAllRoles); } private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args) { var name = "game-ticker-unknown-role"; var prototype = ""; string? playTimeTracker = null; if (component.Prototype != null && _prototypes.TryIndex(component.Prototype, out JobPrototype? job)) { name = job.Name; prototype = job.ID; playTimeTracker = job.PlayTimeTracker; } name = Loc.GetString(name); args.Roles.Add(new RoleInfo(component, name, false, playTimeTracker, prototype)); } protected void SubscribeAntagEvents() where T : AntagonistRoleComponent { SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) => { var name = "game-ticker-unknown-role"; var prototype = ""; if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag)) { name = antag.Name; prototype = antag.ID; } name = Loc.GetString(name); args.Roles.Add(new RoleInfo(component, name, true, null, prototype)); }); SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => { args.IsAntagonist = true; args.IsExclusiveAntagonist |= typeof(T).TryGetCustomAttribute(out _); }); _antagTypes.Add(typeof(T)); } public void MindAddRole(EntityUid mindId, Component component, MindComponent? mind = null, bool silent = false) { if (!Resolve(mindId, ref mind)) return; if (HasComp(mindId, component.GetType())) { throw new ArgumentException($"We already have this role: {component}"); } EntityManager.AddComponent(mindId, component); var antagonist = IsAntagonistRole(component.GetType()); var mindEv = new MindRoleAddedEvent(silent); RaiseLocalEvent(mindId, ref mindEv); var message = new RoleAddedEvent(mindId, mind, antagonist, silent); if (mind.OwnedEntity != null) { RaiseLocalEvent(mind.OwnedEntity.Value, message, true); } _adminLogger.Add(LogType.Mind, LogImpact.Low, $"'Role {component}' added to mind of {_minds.MindOwnerLoggingString(mind)}"); } /// /// Gives this mind a new role. /// /// The mind to add the role to. /// The role instance to add. /// The role type to add. /// Whether or not the role should be added silently /// The instance of the role. /// /// Thrown if we already have a role with this type. /// public void MindAddRole(EntityUid mindId, T component, MindComponent? mind = null, bool silent = false) where T : IComponent, new() { if (!Resolve(mindId, ref mind)) return; if (HasComp(mindId)) { throw new ArgumentException($"We already have this role: {typeof(T)}"); } AddComp(mindId, component); var antagonist = IsAntagonistRole(); var mindEv = new MindRoleAddedEvent(silent); RaiseLocalEvent(mindId, ref mindEv); var message = new RoleAddedEvent(mindId, mind, antagonist, silent); if (mind.OwnedEntity != null) { RaiseLocalEvent(mind.OwnedEntity.Value, message, true); } _adminLogger.Add(LogType.Mind, LogImpact.Low, $"'Role {typeof(T).Name}' added to mind of {_minds.MindOwnerLoggingString(mind)}"); } /// /// Removes a role from this mind. /// /// The mind to remove the role from. /// The type of the role to remove. /// /// Thrown if we do not have this role. /// public void MindRemoveRole(EntityUid mindId) where T : IComponent { if (!RemComp(mindId)) { throw new ArgumentException($"We do not have this role: {typeof(T)}"); } var mind = Comp(mindId); var antagonist = IsAntagonistRole(); var message = new RoleRemovedEvent(mindId, mind, antagonist); if (mind.OwnedEntity != null) { RaiseLocalEvent(mind.OwnedEntity.Value, message, true); } _adminLogger.Add(LogType.Mind, LogImpact.Low, $"'Role {typeof(T).Name}' removed from mind of {_minds.MindOwnerLoggingString(mind)}"); } public bool MindTryRemoveRole(EntityUid mindId) where T : IComponent { if (!MindHasRole(mindId)) return false; MindRemoveRole(mindId); return true; } public bool MindHasRole(EntityUid mindId) where T : IComponent { DebugTools.Assert(HasComp(mindId)); return HasComp(mindId); } public List MindGetAllRoles(EntityUid mindId) { DebugTools.Assert(HasComp(mindId)); var ev = new MindGetAllRolesEvent(new List()); RaiseLocalEvent(mindId, ref ev); return ev.Roles; } public bool MindIsAntagonist(EntityUid? mindId) { if (mindId == null) return false; DebugTools.Assert(HasComp(mindId)); var ev = new MindIsAntagonistEvent(); RaiseLocalEvent(mindId.Value, ref ev); return ev.IsAntagonist; } /// /// Does this mind possess an exclusive antagonist role /// /// The mind entity /// True if the mind possesses an exclusive antag role public bool MindIsExclusiveAntagonist(EntityUid? mindId) { if (mindId == null) return false; var ev = new MindIsAntagonistEvent(); RaiseLocalEvent(mindId.Value, ref ev); return ev.IsExclusiveAntagonist; } public bool IsAntagonistRole() { return _antagTypes.Contains(typeof(T)); } public bool IsAntagonistRole(Type component) { return _antagTypes.Contains(component); } /// /// Play a sound for the mind, if it has a session attached. /// Use this for role greeting sounds. /// public void MindPlaySound(EntityUid mindId, SoundSpecifier? sound, MindComponent? mind = null) { if (Resolve(mindId, ref mind) && mind.Session != null) _audio.PlayGlobal(sound, mind.Session); } }