Skip to content

Commit

Permalink
Merge pull request #311 from FoxxoTrystan/hypnosis
Browse files Browse the repository at this point in the history
Psionic Hypnosis / MindSwap Nerf
  • Loading branch information
FoxxoTrystan authored Nov 3, 2024
2 parents 32d4d88 + 87dc1da commit 103092f
Show file tree
Hide file tree
Showing 19 changed files with 534 additions and 6 deletions.
35 changes: 35 additions & 0 deletions Content.Client/Floofstation/HypnotizedSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Content.Shared.Floofstation.Hypno;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
using Robust.Client.Player;
using Content.Client.Overlays;

namespace Content.Client.Floofstation;

public sealed class HypnotizedSystem : EquipmentHudSystem<HypnotizedComponent>
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

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

SubscribeLocalEvent<PsionicHypnoComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}

private void OnGetStatusIconsEvent(EntityUid uid, PsionicHypnoComponent component, ref GetStatusIconsEvent args)
{
if (!IsActive || args.InContainer)
return;

if (_playerManager.LocalEntity is not { Valid: true } player
|| !TryComp<HypnotizedComponent>(player, out var hypnoComp)
|| hypnoComp.Master != uid)
return;

if (_prototype.TryIndex<StatusIconPrototype>(component.MasterIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
}
}
35 changes: 35 additions & 0 deletions Content.Client/Floofstation/PsionicHypnoSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Content.Shared.Floofstation.Hypno;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
using Robust.Client.Player;
using Content.Client.Overlays;

namespace Content.Client.Floofstation;

public sealed class PsionicHypnoSystem : EquipmentHudSystem<PsionicHypnoComponent>
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

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

SubscribeLocalEvent<HypnotizedComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}

private void OnGetStatusIconsEvent(EntityUid uid, HypnotizedComponent component, ref GetStatusIconsEvent args)
{
if (!IsActive || args.InContainer)
return;

if (_playerManager.LocalEntity is not { Valid: true } player
|| !TryComp<PsionicHypnoComponent>(player, out var hypnoComp)
|| component.Master != player)
return;

if (_prototype.TryIndex<StatusIconPrototype>(hypnoComp.SubjectIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
}
}
32 changes: 29 additions & 3 deletions Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Content.Server.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Actions.Events;
using Content.Server.DoAfter;
using Content.Shared.DoAfter;

namespace Content.Server.Abilities.Psionics
{
Expand All @@ -23,11 +25,13 @@ public sealed class MindSwapPowerSystem : EntitySystem
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MindSwapPowerActionEvent>(OnPowerUsed);
SubscribeLocalEvent<MindSwapPowerComponent, MindSwapPowerActionEvent>(OnPowerUsed);
SubscribeLocalEvent<PsionicComponent, MindSwapPowerDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<MindSwappedComponent, MindSwapPowerReturnActionEvent>(OnPowerReturned);
SubscribeLocalEvent<MindSwappedComponent, DispelledEvent>(OnDispelled);
SubscribeLocalEvent<MindSwappedComponent, MobStateChangedEvent>(OnMobStateChanged);
Expand All @@ -36,18 +40,40 @@ public override void Initialize()
SubscribeLocalEvent<MindSwappedComponent, ComponentInit>(OnSwapInit);
}

private void OnPowerUsed(MindSwapPowerActionEvent args)
private void OnPowerUsed(EntityUid uid, MindSwapPowerComponent component, MindSwapPowerActionEvent args)
{
if (!_psionics.OnAttemptPowerUse(args.Performer, "mind swap")
|| !(TryComp<DamageableComponent>(args.Target, out var damageable) && damageable.DamageContainerID == "Biological"))
return;

Swap(args.Performer, args.Target);
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, component.UseDelay, new MindSwapPowerDoAfterEvent(), args.Performer, target: args.Target)
{
Hidden = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
BreakOnUserMove = true
}, out var doAfterId);

if (TryComp<PsionicComponent>(uid, out var magic))
magic.DoAfter = doAfterId;

_psionics.LogPowerUsed(args.Performer, "mind swap");
args.Handled = true;
}

private void OnDoAfter(EntityUid uid, PsionicComponent component, MindSwapPowerDoAfterEvent args)
{
if (component is null)
return;
component.DoAfter = null;

if (args.Target is null
|| args.Cancelled)
return;

Swap(uid, args.Target.Value);
}

private void OnPowerReturned(EntityUid uid, MindSwappedComponent component, MindSwapPowerReturnActionEvent args)
{
if (HasComp<PsionicInsulationComponent>(component.OriginalEntity) || HasComp<PsionicInsulationComponent>(uid))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;
using Content.Shared.Floofstation.Hypno;
using Content.Shared.Popups;
using Content.Server.DoAfter;
using Content.Shared.DoAfter;
using Robust.Shared.Utility;
using Content.Shared.Verbs;
using Content.Shared.Examine;
using Content.Shared.Mood;
using Content.Server.Chat.Managers;
using Content.Shared.Chat;
using Robust.Server.Player;
using Content.Shared.Database;
using Content.Shared.Administration.Logs;
using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.Mindshield.Components;
using Content.Shared.Psionics;
using Content.Shared.Tag;
using Content.Shared.Implants;


namespace Content.Server.Abilities.Psionics
{
public sealed class PsionicHypnoSystem : EntitySystem
{
[Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly TagSystem _tag = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PsionicHypnoComponent, HypnoPowerActionEvent>(OnPowerUsed);
SubscribeLocalEvent<PsionicHypnoComponent, GetVerbsEvent<InnateVerb>>(ReleaseSubjectVerb);
SubscribeLocalEvent<HypnotizedComponent, DispelledEvent>(OnDispelledHypnotized);
SubscribeLocalEvent<PsionicHypnoComponent, DispelledEvent>(OnDispelled);
SubscribeLocalEvent<PsionicHypnoComponent, PsionicHypnosisDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<HypnotizedComponent, OnMindbreakEvent>(OnMindbreak);
SubscribeLocalEvent<HypnotizedComponent, ImplantImplantedEvent>(OnMindShield);
SubscribeLocalEvent<HypnotizedComponent, ExaminedEvent>((uid, _, args) => OnExamine(uid, args));
}

private void OnPowerUsed(EntityUid uid, PsionicHypnoComponent component, HypnoPowerActionEvent args)
{
if (!_psionics.OnAttemptPowerUse(args.Performer, "hypno")
|| !TryComp<MobStateComponent>(args.Target, out var mob)
|| _mobState.IsDead(args.Target, mob)
|| _mobState.IsCritical(args.Target, mob))
return;

if (component.Subjects >= component.MaxSubjects)
{
_popups.PopupEntity(Loc.GetString("hypno-max-subject"), uid, uid, PopupType.Large);
return;
}

if (!component.ByPassMindShield && HasComp<MindShieldComponent>(args.Target))
{
_popups.PopupEntity(Loc.GetString("has-mindshield"), uid, uid, PopupType.Large);
return;
}

if (HasComp<HypnotizedComponent>(args.Target))
{
_popups.PopupEntity(Loc.GetString("hypno-already-under", ("target", uid)), uid, uid, PopupType.Large);
return;
}

_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, component.UseDelay, new PsionicHypnosisDoAfterEvent(1), uid, target: args.Target)
{
Hidden = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
BreakOnUserMove = true
}, out var doAfterId);

component.DoAfter = doAfterId;

_popups.PopupEntity(Loc.GetString("hypno-start", ("target", args.Target)), uid, uid, PopupType.LargeCaution);
_popups.PopupEntity(Loc.GetString("hypno-phase-1", ("target", uid)), args.Target, args.Target, PopupType.Small);

args.Handled = true;
_psionics.LogPowerUsed(args.Performer, "hypno");
}

private void OnDispelled(EntityUid uid, PsionicHypnoComponent component, DispelledEvent args)
{
if (component.DoAfter is null)
return;

_doAfterSystem.Cancel(component.DoAfter);
component.DoAfter = null;
args.Handled = true;
}

private void OnDispelledHypnotized(EntityUid uid, HypnotizedComponent component, DispelledEvent args)
{
StopHypno(uid, component);
}

private void OnMindbreak(EntityUid uid, HypnotizedComponent component, ref OnMindbreakEvent args)
{
StopHypno(uid, component);
}

private void OnMindShield(EntityUid uid, HypnotizedComponent component, ref ImplantImplantedEvent ev)
{
if (_tag.HasTag(ev.Implant, "MindShield") && ev.Implanted != null)
{
if (component.Master is not null
&& TryComp<PsionicHypnoComponent>(component.Master, out var hypnotist)
&& hypnotist.ByPassMindShield)
return;

StopHypno(uid, component);
}
}

private void ReleaseSubjectVerb(EntityUid uid, PsionicHypnoComponent component, GetVerbsEvent<InnateVerb> args)
{
if (args.User == args.Target
|| !TryComp<HypnotizedComponent>(args.Target, out var hypno)
|| args.User != uid)
return;

InnateVerb verbReleaseHypno = new()
{
Act = () => StopHypno(args.Target),
Text = Loc.GetString("hypno-release"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Floof/Interface/Actions/hypno.png")),
Priority = 1
};
args.Verbs.Add(verbReleaseHypno);
}

private void OnDoAfter(EntityUid uid, PsionicHypnoComponent component, PsionicHypnosisDoAfterEvent args)
{
if (component is null)
return;

component.DoAfter = null;

if (args.Target is null
|| args.Cancelled)
return;

if (args.Phase == 1)
{
_popups.PopupEntity(Loc.GetString("hypno-phase-2", ("target", uid)), args.Target.Value, args.Target.Value, PopupType.Medium);

_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, component.UseDelay, new PsionicHypnosisDoAfterEvent(2), uid, target: args.Target)
{
Hidden = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
BreakOnUserMove = true
}, out var doAfterId);
component.DoAfter = doAfterId;
}
else if (args.Phase == 2)
{
_popups.PopupEntity(Loc.GetString("hypno-phase-3"), args.Target.Value, args.Target.Value, PopupType.Medium);

_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, component.UseDelay, new PsionicHypnosisDoAfterEvent(3), uid, target: args.Target)
{
Hidden = true,
BreakOnTargetMove = true,
BreakOnDamage = true,
BreakOnUserMove = true
}, out var doAfterId);
component.DoAfter = doAfterId;
}
else
{
_popups.PopupEntity(Loc.GetString("hypno-success", ("target", uid)), uid, uid, PopupType.LargeCaution);

Hypnotize(uid, args.Target.Value, component);
}
}

public void Hypnotize(EntityUid uid, EntityUid target, PsionicHypnoComponent? component = null)
{
if (!Resolve(uid, ref component))
return;

EnsureComp<HypnotizedComponent>(target, out var hypnotized);
hypnotized.Master = uid;

Dirty(target, hypnotized);

component.Subjects += 1;

_adminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(uid)} hypnotized {ToPrettyString(target)}");

RaiseLocalEvent(target, new MoodEffectEvent("BeingHypnotized"));

if (_playerManager.TryGetSessionByEntity(target, out var session)
|| session is not null)
{
var message = Loc.GetString("hypnotized", ("entity", uid));
_chatManager.ChatMessageToOne(
ChatChannel.Emotes,
message,
message,
EntityUid.Invalid,
false,
session.Channel);
}
}

public void StopHypno(EntityUid uid, HypnotizedComponent? component = null)
{
if (!Resolve(uid, ref component))
return;

_adminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(uid)} is not longer hypnotized.");

_popups.PopupEntity(Loc.GetString("hypno-free"), uid, uid, PopupType.LargeCaution);

RaiseLocalEvent(uid, new MoodEffectEvent("LostHypnosis"));

if (_playerManager.TryGetSessionByEntity(uid, out var session)
|| session is not null)
{
var message = Loc.GetString("stophypno", ("entity", uid));
_chatManager.ChatMessageToOne(
ChatChannel.Emotes,
message,
message,
EntityUid.Invalid,
false,
session.Channel);
}

if (component.Master is not null
&& TryComp<PsionicHypnoComponent>(component.Master, out var hypnotist))
{
hypnotist.Subjects -= 1;
_popups.PopupEntity(Loc.GetString("lost-subject"), hypnotist.Owner, hypnotist.Owner, PopupType.LargeCaution);
}

RemComp(uid, component);
}

private void OnExamine(EntityUid uid, ExaminedEvent args)
{
if (args.IsInDetailsRange)
args.PushMarkup(Loc.GetString("examined-hypno"), -1);
}
}
}


Loading

0 comments on commit 103092f

Please sign in to comment.