From 50fb91bd1837ea8a78677dbb211559ded5bba018 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Fri, 26 Apr 2024 09:06:43 -0400 Subject: [PATCH] monkey reinforcement teleporters can now select between kobold or monkey with a verb (#25982) * inital * Update animals.yml * Update animals.yml * Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs Co-authored-by: Tayrtahn * Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs Co-authored-by: Tayrtahn * Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs Co-authored-by: Tayrtahn * Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs Co-authored-by: Tayrtahn * Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs Co-authored-by: Tayrtahn * selecting different role will change the description and name * fix name * gargh * the review Hello * e --------- Co-authored-by: Tayrtahn --- .../GhostRoleMobSpawnerComponent.cs | 16 ++-- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 82 +++++++++++++++++-- .../Ghost/Roles/GhostRolePrototype.cs | 38 +++++++++ .../ghost/roles/ghost-role-component.ftl | 4 + .../ghostrole-spawner-verb-selectable.ftl | 1 + .../Locale/en-US/store/uplink-catalog.ftl | 4 +- .../Prototypes/Catalog/uplink_catalog.yml | 16 ++-- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 61 +++++++++++--- .../reinforcement_teleporter.yml | 12 +-- .../Prototypes/Roles/Ghostroles/syndicate.yml | 27 ++++++ Resources/migration.yml | 2 + 11 files changed, 223 insertions(+), 40 deletions(-) create mode 100644 Content.Shared/Ghost/Roles/GhostRolePrototype.cs create mode 100644 Resources/Locale/en-US/ghost/roles/ghostrole-spawner-verb-selectable.ftl create mode 100644 Resources/Prototypes/Roles/Ghostroles/syndicate.yml diff --git a/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs b/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs index 4cdab6ce0702..6c2a6986fc96 100644 --- a/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs +++ b/Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs @@ -1,5 +1,4 @@ using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Ghost.Roles.Components { @@ -10,17 +9,22 @@ namespace Content.Server.Ghost.Roles.Components [Access(typeof(GhostRoleSystem))] public sealed partial class GhostRoleMobSpawnerComponent : Component { - [ViewVariables(VVAccess.ReadWrite)] [DataField("deleteOnSpawn")] + [DataField] public bool DeleteOnSpawn = true; - [ViewVariables(VVAccess.ReadWrite)] [DataField("availableTakeovers")] + [DataField] public int AvailableTakeovers = 1; [ViewVariables] public int CurrentTakeovers = 0; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? Prototype { get; private set; } + [DataField] + public EntProtoId? Prototype; + + /// + /// If this ghostrole spawner has multiple selectable ghostrole prototypes. + /// + [DataField] + public List SelectablePrototypes = []; } } diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 0649e68a317c..e7495020c801 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -23,6 +23,10 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Utility; +using Content.Server.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Prototypes; +using Robust.Shared.Collections; namespace Content.Server.Ghost.Roles { @@ -37,6 +41,8 @@ public sealed class GhostRoleSystem : EntitySystem [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly SharedRoleSystem _roleSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; private uint _nextRoleIdentifier; private bool _needsUpdateGhostRoleCount = true; @@ -63,6 +69,7 @@ public override void Initialize() SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnSpawnerTakeRole); SubscribeLocalEvent(OnTakeoverTakeRole); + SubscribeLocalEvent>(OnVerb); _playerManager.PlayerStatusChanged += PlayerStatusChanged; } @@ -74,11 +81,11 @@ private void OnMobStateChanged(Entity component switch (args.NewMobState) { case MobState.Alive: - { - if (!ghostRole.Taken) - RegisterGhostRole((component, ghostRole)); - break; - } + { + if (!ghostRole.Taken) + RegisterGhostRole((component, ghostRole)); + break; + } case MobState.Critical: case MobState.Dead: UnregisterGhostRole((component, ghostRole)); @@ -100,11 +107,11 @@ private uint GetNextRoleIdentifier() public void OpenEui(ICommonSession session) { - if (session.AttachedEntity is not {Valid: true} attached || + if (session.AttachedEntity is not { Valid: true } attached || !EntityManager.HasComponent(attached)) return; - if(_openUis.ContainsKey(session)) + if (_openUis.ContainsKey(session)) CloseEui(session); var eui = _openUis[session] = new GhostRolesEui(); @@ -250,7 +257,7 @@ public GhostRoleInfo[] GetGhostRolesInfo() if (metaQuery.GetComponent(uid).EntityPaused) continue; - roles.Add(new GhostRoleInfo {Identifier = id, Name = role.RoleName, Description = role.RoleDescription, Rules = role.RoleRules, Requirements = role.Requirements}); + roles.Add(new GhostRoleInfo { Identifier = id, Name = role.RoleName, Description = role.RoleDescription, Rules = role.RoleRules, Requirements = role.Requirements }); } return roles.ToArray(); @@ -407,6 +414,63 @@ private void OnTakeoverTakeRole(EntityUid uid, GhostTakeoverAvailableComponent c args.TookRole = true; } + + private void OnVerb(EntityUid uid, GhostRoleMobSpawnerComponent component, GetVerbsEvent args) + { + var prototypes = component.SelectablePrototypes; + if (prototypes.Count < 1) + return; + + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; + + var verbs = new ValueList(); + + foreach (var prototypeID in prototypes) + { + if (_prototype.TryIndex(prototypeID, out var prototype)) + { + var verb = CreateVerb(uid, component, args.User, prototype); + verbs.Add(verb); + } + } + + args.Verbs.UnionWith(verbs); + } + + private Verb CreateVerb(EntityUid uid, GhostRoleMobSpawnerComponent component, EntityUid userUid, GhostRolePrototype prototype) + { + var verbText = Loc.GetString(prototype.Name); + + return new Verb() + { + Text = verbText, + Disabled = component.Prototype == prototype.EntityPrototype, + Category = VerbCategory.SelectType, + Act = () => SetMode(uid, prototype, verbText, component, userUid) + }; + } + + public void SetMode(EntityUid uid, GhostRolePrototype prototype, string verbText, GhostRoleMobSpawnerComponent? component, EntityUid? userUid = null) + { + if (!Resolve(uid, ref component)) + return; + + var ghostrolecomp = EnsureComp(uid); + + component.Prototype = prototype.EntityPrototype; + ghostrolecomp.RoleName = verbText; + ghostrolecomp.RoleDescription = prototype.Description; + ghostrolecomp.RoleRules = prototype.Rules; + + // Dirty(ghostrolecomp); + + if (userUid != null) + { + var msg = Loc.GetString("ghostrole-spawner-select", ("mode", verbText)); + _popupSystem.PopupEntity(msg, uid, userUid.Value); + } + } } [AnyCommand] @@ -417,7 +481,7 @@ public sealed class GhostRoles : IConsoleCommand public string Help => $"{Command}"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - if(shell.Player != null) + if (shell.Player != null) EntitySystem.Get().OpenEui(shell.Player); else shell.WriteLine("You can only open the ghost roles UI on a client."); diff --git a/Content.Shared/Ghost/Roles/GhostRolePrototype.cs b/Content.Shared/Ghost/Roles/GhostRolePrototype.cs new file mode 100644 index 000000000000..43d64322504d --- /dev/null +++ b/Content.Shared/Ghost/Roles/GhostRolePrototype.cs @@ -0,0 +1,38 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Ghost.Roles; + +/// +/// For selectable ghostrole prototypes in ghostrole spawners. +/// +[Prototype] +public sealed partial class GhostRolePrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The name of the ghostrole. + /// + [DataField] + public string Name { get; set; } = default!; + + /// + /// The description of the ghostrole. + /// + [DataField] + public string Description { get; set; } = default!; + + /// + /// The entity prototype of the ghostrole + /// + [DataField] + public string EntityPrototype = default!; + + /// + /// Rules of the ghostrole + /// + [DataField] + public string Rules = default!; +} \ No newline at end of file diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index 23a178d282a1..d307813c26ed 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -228,6 +228,10 @@ ghost-role-information-syndicate-monkey-reinforcement-name = Syndicate Monkey Ag ghost-role-information-syndicate-monkey-reinforcement-description = Someone needs reinforcements. You, a trained monkey, will help them. ghost-role-information-syndicate-monkey-reinforcement-rules = Normal syndicate antagonist rules apply. Work with whoever called you in, and don't harm them. +ghost-role-information-syndicate-kobold-reinforcement-name = Syndicate Kobold Agent +ghost-role-information-syndicate-kobold-reinforcement-description = Someone needs reinforcements. You, a trained kobold, will help them. +ghost-role-information-syndicate-kobold-reinforcement-rules = Normal syndicate antagonist rules apply. Work with whoever called you in, and don't harm them. + ghost-role-information-artifact-name = Sentient Artifact ghost-role-information-artifact-description = Enact your eldritch whims. diff --git a/Resources/Locale/en-US/ghost/roles/ghostrole-spawner-verb-selectable.ftl b/Resources/Locale/en-US/ghost/roles/ghostrole-spawner-verb-selectable.ftl new file mode 100644 index 000000000000..9d649a5e0701 --- /dev/null +++ b/Resources/Locale/en-US/ghost/roles/ghostrole-spawner-verb-selectable.ftl @@ -0,0 +1 @@ +ghostrole-spawner-select = Selected: {$mode} \ No newline at end of file diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 57a309ac69a9..49d4ca0028dd 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -121,8 +121,8 @@ uplink-agent-id-card-desc = A modified ID card that can copy accesses from other uplink-black-jetpack-name = Black Jetpack uplink-black-jetpack-desc = A black jetpack. It allows you to fly around in space. Refills not included, use your fuel wisely. -uplink-reinforcement-radio-monkey-name = Monkey Reinforcement Teleporter -uplink-reinforcement-radio-monkey-desc = Call in a trained monkey to assist you. Comes with a single syndicate cigarette. +uplink-reinforcement-radio-ancestor-name = Genetic Ancestor Reinforcement Teleporter +uplink-reinforcement-radio-ancestor-desc = Call in a trained ancestor of your choosing to assist you. Comes with a single syndicate cigarette. uplink-reinforcement-radio-name = Reinforcement Teleporter uplink-reinforcement-radio-desc = Radio in a reinforcement agent of extremely questionable quality. No off button, buy this if you're ready to party. They have a pistol with no reserve ammo, and a knife. That's it. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index a7c2dbc7a4f5..ca1a2e4f2dac 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -937,10 +937,10 @@ - NukeOpsUplink - type: listing - id: UplinkReinforcementRadioSyndicateMonkey - name: uplink-reinforcement-radio-monkey-name - description: uplink-reinforcement-radio-monkey-desc - productEntity: ReinforcementRadioSyndicateMonkey + id: UplinkReinforcementRadioSyndicateAncestor + name: uplink-reinforcement-radio-ancestor-name + description: uplink-reinforcement-radio-ancestor-desc + productEntity: ReinforcementRadioSyndicateAncestor icon: { sprite: Objects/Devices/communication.rsi, state: old-radio } cost: Telecrystal: 6 @@ -953,10 +953,10 @@ - NukeOpsUplink - type: listing - id: UplinkReinforcementRadioSyndicateMonkeyNukeops # Version for Nukeops that spawns a syndicate monkey with the NukeOperative component. - name: uplink-reinforcement-radio-monkey-name - description: uplink-reinforcement-radio-monkey-desc - productEntity: ReinforcementRadioSyndicateMonkeyNukeops + id: UplinkReinforcementRadioSyndicateAncestorNukeops # Version for Nukeops that spawns a syndicate monkey with the NukeOperative component. + name: uplink-reinforcement-radio-ancestor-name + description: uplink-reinforcement-radio-ancestor-desc + productEntity: ReinforcementRadioSyndicateAncestorNukeops icon: { sprite: Objects/Devices/communication.rsi, state: old-radio } cost: Telecrystal: 6 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index c0a99ec8a0c6..ab6afb540320 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1324,9 +1324,10 @@ - type: entity name: kobold - id: MobKobold + id: MobBaseKobold parent: MobBaseAncestor description: Cousins to the sentient race of lizard people, kobolds blend in with their natural habitat and are as nasty as monkeys; ready to pull out your hair and stab you to death. + abstract: true components: - type: NameIdentifier group: Kobold @@ -1420,15 +1421,6 @@ spawned: - id: FoodMeat amount: 2 - - type: Clumsy - clumsyDamage: - types: - Blunt: 2 - Piercing: 7 - groups: - Burn: 3 - clumsySound: - path: /Audio/Voice/Reptilian/reptilian_scream.ogg - type: AlwaysRevolutionaryConvertible - type: GhostTakeoverAvailable - type: SentienceTarget @@ -1439,6 +1431,55 @@ name: ghost-role-information-kobold-name description: ghost-role-information-kobold-description +- type: entity + name: kobold + id: MobKobold + parent: MobBaseKobold + description: Cousins to the sentient race of lizard people, kobolds blend in with their natural habitat and are as nasty as monkeys; ready to pull out your hair and stab you to death. + components: + - type: Clumsy + clumsyDamage: + types: + Blunt: 2 + Piercing: 7 + groups: + Burn: 3 + clumsySound: + path: /Audio/Voice/Reptilian/reptilian_scream.ogg + +- type: entity + id: MobBaseSyndicateKobold + parent: MobBaseKobold + suffix: syndicate base + components: + - type: MobThresholds + thresholds: + 0: Alive + 75: Critical + 200: Dead + - type: NpcFactionMember + factions: + - Syndicate + - type: Loadout + prototypes: [SyndicateOperativeGearMonkey] + +- type: entity + id: MobKoboldSyndicateAgent + parent: MobBaseSyndicateKobold + suffix: syndicate agent + components: + # make the player a traitor once its taken + - type: AutoTraitor + giveUplink: false + giveObjectives: false + +- type: entity + id: MobKoboldSyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink + parent: MobBaseSyndicateKobold + suffix: NukeOps + components: + - type: NukeOperative + - type: entity name: guidebook monkey parent: MobMonkey diff --git a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml index f6d57f53a50d..3ce39afcebbc 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml @@ -29,9 +29,9 @@ - type: entity parent: ReinforcementRadioSyndicate - id: ReinforcementRadioSyndicateMonkey - name: syndicate monkey reinforcement radio - description: Calls in a specially trained monkey to assist you. + id: ReinforcementRadioSyndicateAncestor + name: syndicate genetic ancestor reinforcement radio + description: Calls in a specially trained ancestor of your choosing to assist you. components: - type: GhostRole name: ghost-role-information-syndicate-monkey-reinforcement-name @@ -39,14 +39,16 @@ rules: ghost-role-information-syndicate-monkey-reinforcement-rules - type: GhostRoleMobSpawner prototype: MobMonkeySyndicateAgent + selectablePrototypes: ["SyndicateMonkey", "SyndicateKobold"] - type: entity - parent: ReinforcementRadioSyndicateMonkey - id: ReinforcementRadioSyndicateMonkeyNukeops # Reinforcement radio exclusive to nukeops uplink + parent: ReinforcementRadioSyndicateAncestor + id: ReinforcementRadioSyndicateAncestorNukeops # Reinforcement radio exclusive to nukeops uplink suffix: NukeOps components: - type: GhostRoleMobSpawner prototype: MobMonkeySyndicateAgentNukeops + selectablePrototypes: ["SyndicateMonkeyNukeops", "SyndicateKoboldNukeops"] - type: entity parent: ReinforcementRadioSyndicate diff --git a/Resources/Prototypes/Roles/Ghostroles/syndicate.yml b/Resources/Prototypes/Roles/Ghostroles/syndicate.yml new file mode 100644 index 000000000000..24c0d8b3e3be --- /dev/null +++ b/Resources/Prototypes/Roles/Ghostroles/syndicate.yml @@ -0,0 +1,27 @@ +- type: ghostRole + id: SyndicateKobold + name: ghost-role-information-syndicate-kobold-reinforcement-name + description: ghost-role-information-syndicate-kobold-reinforcement-description + rules: ghost-role-information-syndicate-kobold-reinforcement-rules + entityPrototype: MobKoboldSyndicateAgent + +- type: ghostRole + id: SyndicateKoboldNukeops + name: ghost-role-information-syndicate-kobold-reinforcement-name + description: ghost-role-information-syndicate-kobold-reinforcement-description + rules: ghost-role-information-syndicate-kobold-reinforcement-rules + entityPrototype: MobKoboldSyndicateAgentNukeops + +- type: ghostRole + id: SyndicateMonkey + name: ghost-role-information-syndicate-monkey-reinforcement-name + description: ghost-role-information-syndicate-monkey-reinforcement-description + rules: ghost-role-information-syndicate-monkey-reinforcement-name + entityPrototype: MobMonkeySyndicateAgent + +- type: ghostRole + id: SyndicateMonkeyNukeops + name: ghost-role-information-syndicate-monkey-reinforcement-name + description: ghost-role-information-syndicate-monkey-reinforcement-description + rules: ghost-role-information-syndicate-monkey-reinforcement-name + entityPrototype: MobMonkeySyndicateAgentNukeops \ No newline at end of file diff --git a/Resources/migration.yml b/Resources/migration.yml index 04e4f4b0801f..bf18cfe555b5 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -327,3 +327,5 @@ WeaponPistolN1984Nonlethal: WeaponPistolN1984 WeaponSubMachineGunVectorRubber: WeaponSubMachineGunVector WeaponSubMachineGunDrozdRubber: WeaponSubMachineGunDrozd WeaponRifleLecterRubber: WeaponRifleLecter +ReinforcementRadioSyndicateMonkey: ReinforcementRadioSyndicateAncestor +ReinforcementRadioSyndicateMonkeyNukeops: ReinforcementRadioSyndicateAncestorNukeops