diff --git a/Content.Server/GameTicking/Rules/Configurations/NukeopsRuleConfiguration.cs b/Content.Server/GameTicking/Rules/Configurations/NukeopsRuleConfiguration.cs index 49c3b5ede5..9cb21c202b 100644 --- a/Content.Server/GameTicking/Rules/Configurations/NukeopsRuleConfiguration.cs +++ b/Content.Server/GameTicking/Rules/Configurations/NukeopsRuleConfiguration.cs @@ -1,5 +1,6 @@ using Content.Server.GameTicking.Rules.Configurations; using Content.Shared.Dataset; +using Content.Shared.Humanoid.Prototypes; using Content.Shared.Roles; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -25,8 +26,8 @@ public sealed class NukeopsRuleConfiguration : GameRuleConfiguration [DataField("maxOps")] public int MaxOperatives = 5; - [DataField("spawnEntityProto", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string SpawnEntityPrototype = "MobHumanNukeOp"; + [DataField("randomHumanoidSettings", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string RandomHumanoidSettingsPrototype = "NukeOp"; [DataField("spawnPointProto", customTypeSerializer: typeof(PrototypeIdSerializer))] public string SpawnPointPrototype = "SpawnPointNukies"; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 9e1e10a972..5f9150cfd2 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -35,6 +35,7 @@ using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.Player; using Content.Server.Administration.Commands; +using Content.Server.Humanoid.Systems; using Content.Shared.Preferences; using Content.Server.Preferences.Managers; @@ -57,6 +58,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly RandomHumanoidSystem _randomHumanoid = default!; private enum WinType @@ -732,7 +734,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem if (sessions.TryGetValue(i, out var session)) { - var mob = EntityManager.SpawnEntity(_nukeopsRuleConfig.SpawnEntityPrototype, _random.Pick(spawns)); + var mob = _randomHumanoid.SpawnRandomHumanoid(_nukeopsRuleConfig.RandomHumanoidSettingsPrototype, _random.Pick(spawns), string.Empty); var profile = _prefs.GetPreferences(session.UserId).SelectedCharacter as HumanoidCharacterProfile; SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile); diff --git a/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs b/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs new file mode 100644 index 0000000000..7abfe096e8 --- /dev/null +++ b/Content.Server/Humanoid/Components/RandomHumanoidSpawnerComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Humanoid.Prototypes; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Humanoid.Components; + +/// +/// This is added to a marker entity in order to spawn a randomized +/// humanoid ingame. +/// +[RegisterComponent] +public sealed class RandomHumanoidSpawnerComponent : Component +{ + [DataField("settings", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string SettingsPrototypeId = default!; +} diff --git a/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs b/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs new file mode 100644 index 0000000000..8c148bf430 --- /dev/null +++ b/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs @@ -0,0 +1,66 @@ +using Content.Server.Humanoid.Components; +using Content.Server.RandomMetadata; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Preferences; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; + +namespace Content.Server.Humanoid.Systems; + +/// +/// This deals with spawning and setting up random humanoids. +/// +public sealed class RandomHumanoidSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IComponentFactory _compFactory = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; + + [Dependency] private readonly HumanoidSystem _humanoid = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit, + after: new []{ typeof(RandomMetadataSystem) }); + } + + private void OnMapInit(EntityUid uid, RandomHumanoidSpawnerComponent component, MapInitEvent args) + { + QueueDel(uid); + SpawnRandomHumanoid(component.SettingsPrototypeId, Transform(uid).Coordinates, MetaData(uid).EntityName); + } + + public EntityUid SpawnRandomHumanoid(string prototypeId, EntityCoordinates coordinates, string name) + { + if (!_prototypeManager.TryIndex(prototypeId, out var prototype)) + { + throw new ArgumentException("Could not get random humanoid settings"); + } + + var profile = HumanoidCharacterProfile.Random(prototype.SpeciesBlacklist); + var speciesProto = _prototypeManager.Index(profile.Species); + var humanoid = Spawn(speciesProto.Prototype, coordinates); + + MetaData(humanoid).EntityName = prototype.RandomizeName + ? profile.Name + : name; + + _humanoid.LoadProfile(humanoid, profile); + + if (prototype.Components == null) + { + return humanoid; + } + + foreach (var entry in prototype.Components.Values) + { + var comp = (Component) _serialization.Copy(entry.Component); + comp.Owner = humanoid; + EntityManager.AddComponent(humanoid, comp, true); + } + + return humanoid; + } +} diff --git a/Content.Shared/Humanoid/Prototypes/RandomHumanoidSettingsPrototype.cs b/Content.Shared/Humanoid/Prototypes/RandomHumanoidSettingsPrototype.cs new file mode 100644 index 0000000000..2c2da188b3 --- /dev/null +++ b/Content.Shared/Humanoid/Prototypes/RandomHumanoidSettingsPrototype.cs @@ -0,0 +1,37 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; + +namespace Content.Shared.Humanoid.Prototypes; + +/// +/// This is what is used to change a humanoid spawned by RandomHumanoidSystem in Content.Server. +/// +[Prototype("randomHumanoidSettings")] +public sealed class RandomHumanoidSettingsPrototype : IPrototype, IInheritingPrototype +{ + [IdDataField] public string ID { get; } = default!; + + [ParentDataField(typeof(PrototypeIdArraySerializer))] + public string[]? Parents { get; } + + [AbstractDataField] + public bool Abstract { get; } + + /// + /// Whether the humanoid's name should take from the randomized profile or not. + /// + [DataField("randomizeName")] + public bool RandomizeName { get; } = true; + + /// + /// Species that will be ignored by the randomizer. + /// + [DataField("speciesBlacklist")] + public HashSet SpeciesBlacklist { get; } = new(); + + /// + /// Extra components to add to this entity. + /// + [DataField("components")] + public EntityPrototype.ComponentRegistry? Components { get; } +} diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml index 6cf9a01e2d..86ebb59e1a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml @@ -26,20 +26,6 @@ - type: HTN rootTask: RangedCombatCompound -- type: entity - parent: MobHuman - id: MobCBURNUnit - name: CBURN Agent - description: A miserable pile of secrets - components: - - type: RandomHumanoidAppearance - - type: Loadout - prototypes: [CBURNGear] - - type: GhostTakeoverAvailable - makeSentient: false - name: CBURN Agent - description: A highly trained CentCom agent, capable of dealing with various threats. - - type: entity parent: BaseMobHuman suffix: Dead diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index 8cc3a1200f..b407f9e073 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -34,170 +34,17 @@ factions: - NanoTrasen -- type: entity - name: CentCom official - parent: MobHuman - id: MobHumanCentcomOfficial - components: - - type: Icon - sprite: Markers/jobs.rsi - state: centcom - - type: GhostTakeoverAvailable - name: CentCom official - description: Inspect the station, jot down performance reviews for heads of staff, bug the Captain. - - type: Loadout - prototypes: [CentcomGear] - - type: RandomHumanoidAppearance - -# ERT Leader -- type: entity - name: ERT leader - parent: MobHuman - id: MobHumanERTLeader - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertleader - - type: GhostTakeoverAvailable - name: ERT Leader - description: Lead a team of specialists to resolve the stations issues. - - type: Loadout - prototypes: [ERTLeaderGear] - - type: RandomMetadata - nameSegments: [NamesFirstMilitaryLeader] - - type: RandomHumanoidAppearance - randomizeName: false - -- type: entity - name: ERT leader - suffix: EVA - parent: MobHumanERTLeader - id: MobHumanERTLeaderEVA - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertleadereva - - type: Loadout - prototypes: [ERTLeaderGearEVA] - -# ERT Engineer -- type: entity - name: ERT engineer - parent: MobHumanERTLeader - id: MobHumanERTEngineer - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertengineer - - type: GhostTakeoverAvailable - name: ERT Engineer - description: Assist with engineering efforts to resolve the stations issues. - - type: Loadout - prototypes: [ERTEngineerGear] - -- type: entity - name: ERT engineer - suffix: EVA - parent: MobHumanERTEngineer - id: MobHumanERTEngineerEVA - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertengineereva - - type: Loadout - prototypes: [ERTEngineerGearEVA] - -# ERT Security -- type: entity - name: ERT security - parent: MobHumanERTLeader - id: MobHumanERTSecurity - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertsecurity - - type: GhostTakeoverAvailable - name: ERT Security - description: Assist with security efforts to resolve the stations issues. - - type: Loadout - prototypes: [ERTSecurityGear] - -- type: entity - name: ERT security - suffix: EVA - parent: MobHumanERTEngineer - id: MobHumanERTSecurityEVA - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertsecurityeva - - type: Loadout - prototypes: [ERTSecurityGearEVA] - -# ERT Medical -- type: entity - name: ERT medic - parent: MobHumanERTLeader - id: MobHumanERTMedical - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertmedical - - type: GhostTakeoverAvailable - name: ERT Medic - description: Assist with medical efforts to resolve the stations issues. - - type: Loadout - prototypes: [ERTMedicalGear] - -- type: entity - name: ERT medic - suffix: EVA - parent: MobHumanERTMedical - id: MobHumanERTMedicalEVA - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertmedicaleva - - type: Loadout - prototypes: [ERTMedicalGearEVA] - -# ERT Janitor -- type: entity - name: ERT janitor - parent: MobHumanERTLeader - id: MobHumanERTJanitor - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertjanitor - - type: GhostTakeoverAvailable - name: ERT Janitor - description: Assist with custodial efforts to resolve the stations issues. - - type: Loadout - prototypes: [ERTJanitorGear] - -- type: entity - name: ERT janitor - suffix: EVA - parent: MobHumanERTJanitor - id: MobHumanERTJanitorEVA - components: - - type: Icon - sprite: Markers/jobs.rsi - state: ertjanitoreva - - type: Loadout - prototypes: [ERTJanitorGearEVA] - #Syndie - type: entity parent: MobHuman id: MobHumanSyndicateAgent name: Syndicate Agent components: - - type: Loadout - prototypes: [SyndicateOperativeGearExtremelyBasic] - - type: RandomMetadata - nameSegments: [names_death_commando] + - type: Loadout + prototype: SyndicateOperativeGearExtremelyBasic + prototypes: [SyndicateOperativeGearExtremelyBasic] + - type: RandomMetadata + nameSegments: [names_death_commando] # Nuclear Operative - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml new file mode 100644 index 0000000000..26702d34ab --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -0,0 +1,288 @@ +# Random humanoids + +## ERT Leader + +- type: entity + id: RandomHumanoidSpawnerERTLeader + name: ERT leader + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertleader + - type: RandomMetadata + nameSegments: [ NamesFirstMilitaryLeader ] + - type: RandomHumanoidSpawner + settings: ERTLeader + +- type: randomHumanoidSettings + id: ERTLeader + randomizeName: false + components: + - type: GhostTakeoverAvailable + name: ERT Leader + description: Lead a team of specialists to resolve the stations issues. + - type: Loadout + prototypes: [ ERTLeaderGear ] + - type: RandomMetadata + nameSegments: [ NamesFirstMilitaryLeader ] + +- type: entity + id: RandomHumanoidSpawnerERTLeaderEVA + parent: RandomHumanoidSpawnerERTLeader + name: ERT leader + suffix: EVA + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertleadereva + - type: RandomHumanoidSpawner + settings: ERTLeaderEVA + +- type: randomHumanoidSettings + id: ERTLeaderEVA + parent: ERTLeader + components: + - type: Loadout + prototypes: [ ERTLeaderGearEVA ] + +## ERT Janitor + +- type: entity + id: RandomHumanoidSpawnerERTJanitor + parent: RandomHumanoidSpawnerERTLeader + name: ERT janitor + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertjanitor + - type: RandomHumanoidSpawner + settings: ERTJanitor + +- type: randomHumanoidSettings + id: ERTJanitor + parent: ERTLeader + components: + - type: GhostTakeoverAvailable + name: ERT Janitor + description: Assist with custodial efforts to resolve the stations issues. + - type: Loadout + prototypes: [ ERTJanitorGear ] + +- type: entity + id: RandomHumanoidSpawnerERTJanitorEVA + parent: RandomHumanoidSpawnerERTJanitor + name: ERT janitor + suffix: EVA + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertjanitoreva + - type: RandomHumanoidSpawner + settings: ERTJanitorEVA + +- type: randomHumanoidSettings + id: ERTJanitorEVA + parent: ERTJanitor + components: + - type: Loadout + prototypes: [ ERTJanitorGearEVA ] + +## ERT Engineer + +- type: entity + id: RandomHumanoidSpawnerERTEngineer + parent: RandomHumanoidSpawnerERTLeader + name: ERT engineer + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertengineer + - type: RandomHumanoidSpawner + settings: ERTEngineer + +- type: randomHumanoidSettings + id: ERTEngineer + parent: ERTLeader + components: + - type: GhostTakeoverAvailable + name: ERT Engineer + description: Assist with engineering efforts to resolve the stations issues. + - type: Loadout + prototypes: [ ERTEngineerGear ] + +- type: entity + id: RandomHumanoidSpawnerERTEngineerEVA + parent: RandomHumanoidSpawnerERTEngineer + name: ERT engineer + suffix: EVA + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertengineereva + - type: RandomHumanoidSpawner + settings: ERTEngineerEVA + +- type: randomHumanoidSettings + id: ERTEngineerEVA + parent: ERTEngineer + components: + - type: Loadout + prototypes: [ ERTEngineerGearEVA ] + +## ERT Security + +- type: entity + id: RandomHumanoidSpawnerERTSecurity + parent: RandomHumanoidSpawnerERTLeader + name: ERT security + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertsecurity + - type: RandomHumanoidSpawner + settings: ERTSecurity + +- type: randomHumanoidSettings + id: ERTSecurity + parent: ERTLeader + components: + - type: GhostTakeoverAvailable + name: ERT Security + description: Assist with security efforts to resolve the stations issues. + - type: Loadout + prototypes: [ ERTSecurityGear ] + +- type: entity + id: RandomHumanoidSpawnerERTSecurityEVA + parent: RandomHumanoidSpawnerERTSecurity + name: ERT security + suffix: EVA + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertsecurityeva + - type: RandomHumanoidSpawner + settings: ERTSecurityEVA + +- type: randomHumanoidSettings + id: ERTSecurityEVA + parent: ERTSecurity + components: + - type: Loadout + prototypes: [ ERTSecurityGearEVA ] + +## ERT Medic + +- type: entity + id: RandomHumanoidSpawnerERTMedical + parent: RandomHumanoidSpawnerERTLeader + name: ERT medic + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertmedical + - type: RandomHumanoidSpawner + settings: ERTMedical + +- type: randomHumanoidSettings + id: ERTMedical + parent: ERTLeader + components: + - type: GhostTakeoverAvailable + name: ERT Medical + description: Assist with medicaling efforts to resolve the stations issues. + - type: Loadout + prototypes: [ ERTMedicalGear ] + +- type: entity + id: RandomHumanoidSpawnerERTMedicalEVA + parent: RandomHumanoidSpawnerERTMedical + name: ERT medic + suffix: EVA + components: + - type: Icon + sprite: Markers/jobs.rsi + state: ertmedicaleva + - type: RandomHumanoidSpawner + settings: ERTMedicalEVA + +- type: randomHumanoidSettings + id: ERTMedicalEVA + parent: ERTMedical + components: + - type: Loadout + prototypes: [ ERTMedicalGearEVA ] + +## CBURN + +- type: entity + id: RandomHumanoidSpawnerCBURNUnit + name: CBURN Agent + components: + - type: RandomHumanoidSpawner + settings: CBURNAgent + +- type: randomHumanoidSettings + id: CBURNAgent + components: + - type: Loadout + prototypes: [CBURNGear] + - type: GhostTakeoverAvailable + name: CBURN Agent + description: A highly trained CentCom agent, capable of dealing with various threats. + +## Central Command + +- type: entity + name: CentCom official + id: RandomHumanoidSpawnerCentcomOfficial + components: + - type: Icon + sprite: Markers/jobs.rsi + state: centcom + - type: RandomHumanoidSpawner + settings: CentcomOfficial + +- type: randomHumanoidSettings + id: CentcomOfficial + components: + - type: GhostTakeoverAvailable + name: CentCom official + description: Inspect the station, jot down performance reviews for heads of staff, bug the Captain. + - type: Loadout + prototypes: [ CentcomGear ] + +## Syndicate + +- type: entity + id: RandomHumanoidSpawnerSyndicateAgent + name: Syndicate Agent + components: + - type: Icon + sprite: Mobs/Species/Human/parts.rsi + state: full + - type: RandomMetadata + nameSegments: [ names_death_commando ] + - type: RandomHumanoidSpawner + settings: SyndicateAgent + +- type: randomHumanoidSettings + id: SyndicateAgent + components: + - type: Loadout + prototypes: [SyndicateOperativeGearExtremelyBasic] + +- type: entity + id: RandomHumanoidSpawnerNukeOp + name: Nuclear Operative + components: + - type: Icon + sprite: Mobs/Species/Human/parts.rsi + state: full + - type: RandomHumanoidSpawner + settings: NukeOp + +- type: randomHumanoidSettings + id: NukeOp + components: + - type: NukeOperative