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

[Port] MindSlave Implant / Имплант Подчинения #85

Merged
merged 4 commits into from
Oct 16, 2024
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
60 changes: 60 additions & 0 deletions Content.Client/_White/Overlays/MindSlave/MindSlaveIconsSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Content.Client.Overlays;
using Content.Shared._White.Implants.MindSlave;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Client.Player;
using Robust.Shared.Prototypes;

namespace Content.Client._White.Overlays.MindSlave;

public sealed class MindSlaveIconsSystem : EquipmentHudSystem<MindSlaveComponent>
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _player = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<MindSlaveComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}

private void OnGetStatusIconsEvent(
EntityUid uid,
MindSlaveComponent mindSlaveComponent,
ref GetStatusIconsEvent args)
{
if (!IsActive
|| args.InContainer
|| !TryComp(_player.LocalEntity, out MindSlaveComponent? ownerMindSlave))
return;
Spatison marked this conversation as resolved.
Show resolved Hide resolved

var mindSlaveIcon = MindSlaveIcon(uid, ownerMindSlave);
args.StatusIcons.AddRange(mindSlaveIcon);
}

private IEnumerable<StatusIconPrototype> MindSlaveIcon(EntityUid uid, MindSlaveComponent mindSlave)
{
var result = new List<StatusIconPrototype>();
string iconType;
var netUid = GetNetEntity(uid);

if (mindSlave.Master == netUid)
{
iconType = mindSlave.MasterStatusIcon;
}
else if (mindSlave.Slaves.Contains(netUid))
{
iconType = mindSlave.SlaveStatusIcon;
}
else
{
return result;
}

if (_prototype.TryIndex<StatusIconPrototype>(iconType, out var mindslaveIcon))
result.Add(mindslaveIcon);

return result;
}
}
12 changes: 8 additions & 4 deletions Content.Server/Implants/ImplanterSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,20 @@ private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent componen
}
else
{
if (!CanImplant(args.User, target, uid, component, out var implant, out _))
if (!CanImplant(args.User, target, uid, component, out var implant, out _, out var popup)) // WD EDIT
{
// no popup if implant doesn't exist
if (implant == null)
return;

// show popup to the user saying implant failed
var name = Identity.Name(target, EntityManager, args.User);
var msg = Loc.GetString("implanter-component-implant-failed", ("implant", implant), ("target", name));
_popup.PopupEntity(msg, target, args.User);
if (popup) // WD EDIT
{
var name = Identity.Name(target, EntityManager, args.User);
var msg = Loc.GetString("implanter-component-implant-failed", ("implant", implant),
("target", name));
_popup.PopupEntity(msg, target, args.User);
}
// prevent further interaction since popup was shown
args.Handled = true;
return;
Expand Down
152 changes: 141 additions & 11 deletions Content.Server/_White/Implants/ImplantsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.Mind;
using Content.Server.Popups;
using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Shared._White.Implants.MindSlave;
using Content.Shared._White.Implants.NeuroStabilization;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Implants;
using Content.Shared.Implants.Components;
Expand All @@ -14,33 +18,42 @@ namespace Content.Server._White.Implants;

public sealed class ImplantsSystem : EntitySystem
{
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
[Dependency] private readonly RoleSystem _roleSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLog = default!;
[Dependency] private readonly RoleSystem _role = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly JobSystem _job = default!;

[ValidatePrototypeId<TagPrototype>]
private const string MindShieldTag = "MindShield";

[ValidatePrototypeId<TagPrototype>]
private const string NeuroStabilizationTag = "NeuroStabilization";

[ValidatePrototypeId<TagPrototype>]
private const string MindSlaveTag = "MindSlave";

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SubdermalImplantComponent, SubdermalImplantInserted>(OnImplantInserted);
SubscribeLocalEvent<SubdermalImplantComponent, SubdermalImplantRemoved>(OnImplantRemoved);
SubscribeLocalEvent<SubdermalImplantComponent, AttemptSubdermalImplantInserted>(AttemptImplantInserted);
}

private void OnImplantInserted(EntityUid uid, SubdermalImplantComponent component, SubdermalImplantInserted args)
{
if (_tag.HasTag(uid, MindShieldTag)
&& RevolutionCheck(uid, args.Target))
&& MindShieldCheck(uid, args.Target))
EnsureComp<MindShieldComponent>(args.Target);

if (_tag.HasTag(uid, NeuroStabilizationTag))
EnsureComp<NeuroStabilizationComponent>(args.Target);

if (_tag.HasTag(uid, MindSlaveTag))
MindSlaveInserted(args.User, args.Target);
}

private void OnImplantRemoved(EntityUid uid, SubdermalImplantComponent component, SubdermalImplantRemoved args)
Expand All @@ -50,27 +63,144 @@ private void OnImplantRemoved(EntityUid uid, SubdermalImplantComponent component

if (_tag.HasTag(uid, NeuroStabilizationTag))
RemComp<NeuroStabilizationComponent>(args.Target);

if (_tag.HasTag(uid, MindSlaveTag))
MindSlaveRemoved(args.User, args.Target);
}

private void AttemptImplantInserted(EntityUid uid, SubdermalImplantComponent component, ref AttemptSubdermalImplantInserted args)
{
if (_tag.HasTag(uid, MindSlaveTag)
&& !MindSlaveCheck(args.User, args.Target))
{
args.Popup = false;
args.Cancel();
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved
}

#region MindShield

/// <summary>
/// Checks if the implanted person was a Rev or Head Rev and remove role or destroy mindshield respectively.
/// </summary>
private bool RevolutionCheck(EntityUid uid, EntityUid target)
private bool MindShieldCheck(EntityUid uid, EntityUid target)
{
if (HasComp<HeadRevolutionaryComponent>(target))
if (HasComp<HeadRevolutionaryComponent>(target)
|| (TryComp<MindSlaveComponent>(target, out var mindSlave)
&& mindSlave.Master.HasValue))
{
_popupSystem.PopupEntity(Loc.GetString("head-rev-break-mindshield"), target);
_popup.PopupEntity(Loc.GetString("head-rev-break-mindshield"), target);
QueueDel(uid);
return false;
}

if (_mindSystem.TryGetMind(target, out var mindId, out _)
&& _roleSystem.MindTryRemoveRole<RevolutionaryRoleComponent>(mindId))
if (_mind.TryGetMind(target, out var mindId, out _)
&& _role.MindTryRemoveRole<RevolutionaryRoleComponent>(mindId))
{
_adminLogManager.Add(LogType.Mind, LogImpact.Medium,
_adminLog.Add(LogType.Mind, LogImpact.Medium,
$"{ToPrettyString(target)} was deconverted due to being implanted with a Mindshield.");
}

return true;
}

#endregion

#region MindSlave

private void MindSlaveInserted(EntityUid user, EntityUid target)
{
var slaveComponent = EnsureComp<MindSlaveComponent>(target);
slaveComponent.Master = GetNetEntity(user);

var masterComponent = EnsureComp<MindSlaveComponent>(user);
masterComponent.Slaves.Add(GetNetEntity(target));

Dirty(user, masterComponent);
Dirty(target, slaveComponent);

if (!_mind.TryGetMind(target, out var targetMindId, out var targetMind)
|| targetMind.Session is null)
return;

Spatison marked this conversation as resolved.
Show resolved Hide resolved
var jobName = _job.MindTryGetJobName(user);

// send message to chat
var message = Loc.GetString("mindslave-chat-message", ("player", user), ("role", jobName));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chat.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false,
targetMind.Session.Channel, Color.FromHex("#5e9cff"));

// add briefing in character menu
if (TryComp<RoleBriefingComponent>(targetMindId, out var roleBriefing))
{
roleBriefing.Briefing += Loc.GetString("mindslave-briefing", ("player", user), ("role", jobName));
Dirty(targetMindId, roleBriefing);
}
else
{
_role.MindAddRole(targetMindId, new RoleBriefingComponent
{
Briefing = Loc.GetString("mindslave-briefing", ("player", user), ("role", jobName))
}, targetMind);
}

_adminLog.Add(LogType.Mind, LogImpact.High,
$"{ToPrettyString(user)} MindSlaved {ToPrettyString(target)}");
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved

private void MindSlaveRemoved(EntityUid user, EntityUid target)
{
if (!TryComp(target, out MindSlaveComponent? mindslave)
|| !mindslave.Master.HasValue)
return;

var master = GetEntity(mindslave.Master.Value);
if (_mind.TryGetMind(target, out var mindId, out _))
{
_role.MindTryRemoveRole<RoleBriefingComponent>(mindId);
_popup.PopupEntity(Loc.GetString("mindslave-freed", ("player", master)), target, target);
}

if (TryComp(master, out MindSlaveComponent? masterMindslave))
{
masterMindslave.Slaves.Remove(GetNetEntity(target));
if (masterMindslave.Slaves.Count == 0)
RemComp<MindSlaveComponent>(master);
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved

RemComp<MindSlaveComponent>(target);

_adminLog.Add(LogType.Mind, LogImpact.High,
$"{ToPrettyString(user)} UnMindSlaved {ToPrettyString(target)}");
}

/// <summary>
/// Checks if the target can be an MindSlaved
/// </summary>
private bool MindSlaveCheck(EntityUid user, EntityUid target)
{
string message;
if (target == user)
{
message = Loc.GetString("mindslave-target-self");
}
else if (HasComp<MindShieldComponent>(target)
|| HasComp<RevolutionaryComponent>(target)
|| !_mind.TryGetMind(target, out _, out _)
|| (TryComp<MindSlaveComponent>(target, out var mindSlave)
&& mindSlave.Master.HasValue))
{
message = Loc.GetString("mindslave-cant-insert");
}
else
{
return true;
}

_popup.PopupEntity(message, target, user);
return false;
}

#endregion
}
42 changes: 25 additions & 17 deletions Content.Shared/Implants/SharedImplanterSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private void OnExamine(EntityUid uid, ImplanterComponent component, ExaminedEven
//Set to draw mode if not implant only
public void Implant(EntityUid user, EntityUid target, EntityUid implanter, ImplanterComponent component)
{
if (!CanImplant(user, target, implanter, component, out var implant, out var implantComp))
if (!CanImplant(user, target, implanter, component, out var implant, out var implantComp, out _))
return;

//If the target doesn't have the implanted component, add it.
Expand Down Expand Up @@ -88,9 +88,11 @@ public bool CanImplant(
EntityUid implanter,
ImplanterComponent component,
[NotNullWhen(true)] out EntityUid? implant,
[NotNullWhen(true)] out SubdermalImplantComponent? implantComp)
[NotNullWhen(true)] out SubdermalImplantComponent? implantComp,
out bool popup) // WD EDIT
{
implant = component.ImplanterSlot.ContainerSlot?.ContainedEntities.FirstOrNull();
popup = true; // WD EDIF
if (!TryComp(implant, out implantComp))
return false;

Expand All @@ -100,8 +102,12 @@ public bool CanImplant(
return false;
}

var ev = new AddImplantAttemptEvent(user, target, implant.Value, implanter);
RaiseLocalEvent(target, ev);
// WD EDIT START
var ev = new AttemptSubdermalImplantInserted(user, target);
RaiseLocalEvent(implant.Value, ev);

popup = ev.Popup;
// WD EDIT END
return !ev.Cancelled;
}

Expand Down Expand Up @@ -214,23 +220,25 @@ public sealed partial class DrawEvent : SimpleDoAfterEvent
{
}

public sealed class AddImplantAttemptEvent : CancellableEntityEventArgs
// WD EDIT START
public sealed class AttemptSubdermalImplantInserted(EntityUid user, EntityUid target, bool popup = true) : CancellableEntityEventArgs
{
public readonly EntityUid User;
public readonly EntityUid Target;
public readonly EntityUid Implant;
public readonly EntityUid Implanter;
/// <summary>
/// Entity who implants
/// </summary>
public EntityUid User = user;

public AddImplantAttemptEvent(EntityUid user, EntityUid target, EntityUid implant, EntityUid implanter)
{
User = user;
Target = target;
Implant = implant;
Implanter = implanter;
}
/// <summary>
/// Entity being implanted
/// </summary>
public EntityUid Target = target;

/// <summary>
/// Popup if implanted failed
/// </summary>
public bool Popup = popup;
Spatison marked this conversation as resolved.
Show resolved Hide resolved
}

// WD EDIT START
public sealed class SubdermalImplantInserted(EntityUid user, EntityUid target)
{
/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions Content.Shared/_White/Implants/MindSlave/MindSlaveComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Shared._White.Implants.MindSlave;

[RegisterComponent, AutoGenerateComponentState, NetworkedComponent]
public sealed partial class MindSlaveComponent : Component
{
[ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public List<NetEntity> Slaves = [];
Spatison marked this conversation as resolved.
Show resolved Hide resolved

[ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public NetEntity? Master;

[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))]
public string SlaveStatusIcon = "SlaveMindSlaveIcon";

[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<StatusIconPrototype>))]
public string MasterStatusIcon = "MasterMindSlaveIcon";
}
Loading
Loading