Skip to content

Commit

Permalink
monkey reinforcement teleporters can now select between kobold or mon…
Browse files Browse the repository at this point in the history
…key with a verb (space-wizards#25982)

* inital

* Update animals.yml

* Update animals.yml

* Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs

Co-authored-by: Tayrtahn <[email protected]>

* Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs

Co-authored-by: Tayrtahn <[email protected]>

* Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs

Co-authored-by: Tayrtahn <[email protected]>

* Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs

Co-authored-by: Tayrtahn <[email protected]>

* Update Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs

Co-authored-by: Tayrtahn <[email protected]>

* selecting different role will change the description and name

* fix name

* gargh

* the review

Hello

* e

---------

Co-authored-by: Tayrtahn <[email protected]>
  • Loading branch information
Dutch-VanDerLinde and Tayrtahn authored Apr 26, 2024
1 parent 36084d9 commit 50fb91b
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server.Ghost.Roles.Components
{
Expand All @@ -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<EntityPrototype>))]
public string? Prototype { get; private set; }
[DataField]
public EntProtoId? Prototype;

/// <summary>
/// If this ghostrole spawner has multiple selectable ghostrole prototypes.
/// </summary>
[DataField]
public List<string> SelectablePrototypes = [];
}
}
82 changes: 73 additions & 9 deletions Content.Server/Ghost/Roles/GhostRoleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
Expand All @@ -63,6 +69,7 @@ public override void Initialize()
SubscribeLocalEvent<GhostRoleComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GetVerbsEvent<Verb>>(OnVerb);
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
}

Expand All @@ -74,11 +81,11 @@ private void OnMobStateChanged(Entity<GhostTakeoverAvailableComponent> 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));
Expand All @@ -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<GhostComponent>(attached))
return;

if(_openUis.ContainsKey(session))
if (_openUis.ContainsKey(session))
CloseEui(session);

var eui = _openUis[session] = new GhostRolesEui();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -407,6 +414,63 @@ private void OnTakeoverTakeRole(EntityUid uid, GhostTakeoverAvailableComponent c

args.TookRole = true;
}

private void OnVerb(EntityUid uid, GhostRoleMobSpawnerComponent component, GetVerbsEvent<Verb> args)
{
var prototypes = component.SelectablePrototypes;
if (prototypes.Count < 1)
return;

if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return;

var verbs = new ValueList<Verb>();

foreach (var prototypeID in prototypes)
{
if (_prototype.TryIndex<GhostRolePrototype>(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<GhostRoleComponent>(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]
Expand All @@ -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<GhostRoleSystem>().OpenEui(shell.Player);
else
shell.WriteLine("You can only open the ghost roles UI on a client.");
Expand Down
38 changes: 38 additions & 0 deletions Content.Shared/Ghost/Roles/GhostRolePrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Robust.Shared.Prototypes;

namespace Content.Shared.Ghost.Roles;

/// <summary>
/// For selectable ghostrole prototypes in ghostrole spawners.
/// </summary>
[Prototype]
public sealed partial class GhostRolePrototype : IPrototype
{
[ViewVariables]
[IdDataField]
public string ID { get; private set; } = default!;

/// <summary>
/// The name of the ghostrole.
/// </summary>
[DataField]
public string Name { get; set; } = default!;

/// <summary>
/// The description of the ghostrole.
/// </summary>
[DataField]
public string Description { get; set; } = default!;

/// <summary>
/// The entity prototype of the ghostrole
/// </summary>
[DataField]
public string EntityPrototype = default!;

/// <summary>
/// Rules of the ghostrole
/// </summary>
[DataField]
public string Rules = default!;
}
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ghostrole-spawner-select = Selected: {$mode}
4 changes: 2 additions & 2 deletions Resources/Locale/en-US/store/uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 8 additions & 8 deletions Resources/Prototypes/Catalog/uplink_catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
61 changes: 51 additions & 10 deletions Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,26 @@

- 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
description: ghost-role-information-syndicate-monkey-reinforcement-description
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
Expand Down
Loading

0 comments on commit 50fb91b

Please sign in to comment.