Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syndicate Reinforcement Specializations: Medic, Spy, Thief #29853

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Content.Client/Ghost/GhostRoleRadioBoundUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Shared.Ghost.Roles;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;

namespace Content.Client.Ghost;

public sealed class GhostRoleRadioBoundUserInterface : BoundUserInterface
{
private GhostRoleRadioMenu? _ghostRoleRadioMenu;

public GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}

protected override void Open()
{
base.Open();

_ghostRoleRadioMenu = this.CreateWindow<GhostRoleRadioMenu>();
_ghostRoleRadioMenu.SetEntity(Owner);
_ghostRoleRadioMenu.SendGhostRoleRadioMessageAction += SendGhostRoleRadioMessage;
}

public void SendGhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)
{
SendMessage(new GhostRoleRadioMessage(protoId));
}
}
8 changes: 8 additions & 0 deletions Content.Client/Ghost/GhostRoleRadioMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>
107 changes: 107 additions & 0 deletions Content.Client/Ghost/GhostRoleRadioMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;

namespace Content.Client.Ghost;

public sealed partial class GhostRoleRadioMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

public event Action<ProtoId<GhostRolePrototype>>? SendGhostRoleRadioMessageAction;

public EntityUid Entity { get; set; }

public GhostRoleRadioMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}

public void SetEntity(EntityUid uid)
{
Entity = uid;
RefreshUI();
}

private void RefreshUI()
{
// The main control that will contain all of the clickable options
var main = FindControl<RadialContainer>("Main");

// The purpose of this radial UI is for ghost role radios that allow you to select
// more than one potential option, such as with kobolds/lizards.
// This means that it won't show anything if SelectablePrototypes is empty.
if (!_entityManager.TryGetComponent<GhostRoleMobSpawnerComponent>(Entity, out var comp))
return;

foreach (var ghostRoleProtoString in comp.SelectablePrototypes)
{
// For each prototype we find we want to create a button that uses the name of the ghost role
// as the hover tooltip, and the icon is taken from either the ghost role entityprototype
// or the indicated icon entityprototype.
if (!_prototypeManager.TryIndex<GhostRolePrototype>(ghostRoleProtoString, out var ghostRoleProto))
continue;

var button = new GhostRoleRadioMenuButton()
{
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64, 64),
ToolTip = Loc.GetString(ghostRoleProto.Name),
ProtoId = ghostRoleProto.ID,
};

var entProtoView = new EntityPrototypeView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill
};

// pick the icon if it exists, otherwise fallback to the ghost role's entity
if (_prototypeManager.TryIndex(ghostRoleProto.IconPrototype, out var iconProto))
entProtoView.SetPrototype(iconProto);
else
entProtoView.SetPrototype(comp.Prototype);

button.AddChild(entProtoView);
main.AddChild(button);
AddGhostRoleRadioMenuButtonOnClickActions(main);
}
}

private void AddGhostRoleRadioMenuButtonOnClickActions(Control control)
{
var mainControl = control as RadialContainer;

if (mainControl == null)
return;

foreach (var child in mainControl.Children)
{
var castChild = child as GhostRoleRadioMenuButton;

if (castChild == null)
continue;

castChild.OnButtonUp += _ =>
{
SendGhostRoleRadioMessageAction?.Invoke(castChild.ProtoId);
Close();
};
}
}
}

public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButton
{
public ProtoId<GhostRolePrototype> ProtoId { get; set; }
}
2 changes: 1 addition & 1 deletion Content.Server/Bible/BibleSystem.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using Content.Server.Bible.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Ghost.Roles.Events;
using Content.Server.Popups;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
using Content.Shared.Bible;
using Content.Shared.Damage;
using Content.Shared.Ghost.Roles.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
Expand Down
18 changes: 17 additions & 1 deletion Content.Server/Ghost/Roles/GhostRoleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Content.Server.EUI;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Ghost.Roles.Events;
using Content.Server.Ghost.Roles.Raffles;
using Content.Shared.Ghost.Roles.Raffles;
using Content.Server.Ghost.Roles.UI;
using Content.Server.Mind.Commands;
Expand Down Expand Up @@ -31,6 +30,7 @@
using Content.Server.Popups;
using Content.Shared.Verbs;
using Robust.Shared.Collections;
using Content.Shared.Ghost.Roles.Components;

namespace Content.Server.Ghost.Roles
{
Expand Down Expand Up @@ -80,6 +80,7 @@ public override void Initialize()
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GetVerbsEvent<Verb>>(OnVerb);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GhostRoleRadioMessage>(OnGhostRoleRadioMessage);
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
}

Expand Down Expand Up @@ -786,6 +787,21 @@ public void SetMode(EntityUid uid, GhostRolePrototype prototype, string verbText
_popupSystem.PopupEntity(msg, uid, userUid.Value);
}
}

public void OnGhostRoleRadioMessage(Entity<GhostRoleMobSpawnerComponent> entity, ref GhostRoleRadioMessage args)
{
if (!_prototype.TryIndex(args.ProtoId, out var ghostRoleProto))
return;

// if the prototype chosen isn't actually part of the selectable options, ignore it
foreach (var selectableProto in entity.Comp.SelectablePrototypes)
{
if (selectableProto == ghostRoleProto.EntityPrototype.Id)
return;
}

SetMode(entity.Owner, ghostRoleProto, ghostRoleProto.Name, entity.Comp);
}
}

[AnyCommand]
Expand Down
4 changes: 1 addition & 3 deletions Content.Server/Zombies/ZombieSystem.Transform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Content.Server.Mind;
using Content.Server.Mind.Commands;
using Content.Server.NPC;
using Content.Server.NPC.Components;
using Content.Server.NPC.HTN;
using Content.Server.NPC.Systems;
using Content.Server.Roles;
Expand All @@ -24,10 +23,8 @@
using Content.Shared.Interaction.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Nutrition.AnimalHusbandry;
using Content.Shared.Nutrition.Components;
Expand All @@ -38,6 +35,7 @@
using Content.Shared.Prying.Components;
using Content.Shared.Traits.Assorted;
using Robust.Shared.Audio.Systems;
using Content.Shared.Ghost.Roles.Components;

namespace Content.Server.Zombies
{
Expand Down
21 changes: 21 additions & 0 deletions Content.Shared/Ghost/GhostRoleRadioEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

namespace Content.Shared.Ghost.Roles;

[Serializable, NetSerializable]
public sealed class GhostRoleRadioMessage : BoundUserInterfaceMessage
{
public ProtoId<GhostRolePrototype> ProtoId;

public GhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)
{
ProtoId = protoId;
}
}

[Serializable, NetSerializable]
public enum GhostRoleRadioUiKey : byte
{
Key
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Prototypes;

namespace Content.Server.Ghost.Roles.Components
namespace Content.Shared.Ghost.Roles.Components
{
/// <summary>
/// Allows a ghost to take this role, spawning a new entity.
/// </summary>
[RegisterComponent, EntityCategory("Spawner")]
[Access(typeof(GhostRoleSystem))]
public sealed partial class GhostRoleMobSpawnerComponent : Component
{
[DataField]
Expand Down
7 changes: 7 additions & 0 deletions Content.Shared/Ghost/Roles/GhostRolePrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ public sealed partial class GhostRolePrototype : IPrototype
[DataField(required: true)]
public EntProtoId EntityPrototype;

/// <summary>
/// The entity prototype's sprite to use to represent the ghost role
/// Use this if you don't want to use the entity itself
/// </summary>
[DataField]
public EntProtoId? IconPrototype = null;

/// <summary>
/// Rules of the ghostrole
/// </summary>
Expand Down
10 changes: 10 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 @@ -197,6 +197,16 @@ ghost-role-information-syndicate-reinforcement-name = Syndicate Agent
ghost-role-information-syndicate-reinforcement-description = Someone needs reinforcements. You, the first person the syndicate could find, will help them.
ghost-role-information-syndicate-reinforcement-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with the agent who summoned you.

ghost-role-information-syndicate-reinforcement-medic-name = Syndicate Medic
ghost-role-information-syndicate-reinforcement-medic-description = Someone needs reinforcements. Your task is to keep the agent who called you alive.

ghost-role-information-syndicate-reinforcement-spy-name = Syndicate Spy
ghost-role-information-syndicate-reinforcement-spy-description = Someone needs reinforcements. Your speciality lies in espionage, do not be discovered.

ghost-role-information-syndicate-reinforcement-thief-name = Syndicate Thief
ghost-role-information-syndicate-reinforcement-thief-description = Someone needs reinforcements. Your job is to break in and retrieve something valuable for your agent.


ghost-role-information-syndicate-monkey-reinforcement-name = Syndicate Monkey Agent
ghost-role-information-syndicate-monkey-reinforcement-description = Someone needs reinforcements. You, a trained monkey, will help them.
ghost-role-information-syndicate-monkey-reinforcement-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with the agent who summoned you.
Expand Down
4 changes: 3 additions & 1 deletion Resources/Locale/en-US/store/uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ uplink-black-jetpack-desc = A black jetpack. It allows you to fly around in spac
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.
uplink-reinforcement-radio-traitor-desc = Radio in a reinforcement agent of extremely questionable quality. No off button, buy this if you're ready to party. Call in a medic or spy or thief to help you out. Good luck.
uplink-reinforcement-radio-nukeops-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.

uplink-reinforcement-radio-cyborg-assault-name = Syndicate Assault Cyborg Teleporter
uplink-reinforcement-radio-cyborg-assault-desc = A lean, mean killing machine with access to an Energy Sword, LMG, Cryptographic Sequencer, and a Pinpointer.
Expand Down
4 changes: 2 additions & 2 deletions Resources/Prototypes/Catalog/uplink_catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@
- type: listing
id: UplinkReinforcementRadioSyndicate
name: uplink-reinforcement-radio-name
description: uplink-reinforcement-radio-desc
description: uplink-reinforcement-radio-traitor-desc
productEntity: ReinforcementRadioSyndicate
icon: { sprite: Objects/Devices/communication.rsi, state: old-radio-urist }
cost:
Expand All @@ -918,7 +918,7 @@
- type: listing
id: UplinkReinforcementRadioSyndicateNukeops # Version for Nukeops that spawns an agent with the NukeOperative component.
name: uplink-reinforcement-radio-name
description: uplink-reinforcement-radio-desc
description: uplink-reinforcement-radio-nukeops-desc
productEntity: ReinforcementRadioSyndicateNukeops
icon: { sprite: Objects/Devices/communication.rsi, state: old-radio-urist }
cost:
Expand Down
24 changes: 24 additions & 0 deletions Resources/Prototypes/Entities/Mobs/Player/human.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,30 @@
giveUplink: false
giveObjectives: false

- type: entity
parent: MobHumanSyndicateAgent
id: MobHumanSyndicateAgentMedic
name: syndicate medic
components:
- type: Loadout
prototypes: [SyndicateReinforcementMedic]

- type: entity
parent: MobHumanSyndicateAgent
id: MobHumanSyndicateAgentSpy
name: syndicate spy
components:
- type: Loadout
prototypes: [SyndicateReinforcementSpy]

- type: entity
parent: MobHumanSyndicateAgent
id: MobHumanSyndicateAgentThief
name: syndicate thief
components:
- type: Loadout
prototypes: [SyndicateReinforcementThief]

- type: entity
parent: MobHumanSyndicateAgentBase
id: MobHumanSyndicateAgentNukeops # Reinforcement exclusive to nukeops uplink
Expand Down
Loading
Loading