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

[Species] Shadowkin Improvements #209

Draft
wants to merge 24 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0bef833
goggles that can peer into The Dark
DEATHB4DEFEAT Sep 17, 2023
fe59256
a softsuit to darkswap the wearer
DEATHB4DEFEAT Sep 17, 2023
e47e2bd
locale
DEATHB4DEFEAT Sep 17, 2023
6f88662
lower base power gain
DEATHB4DEFEAT Sep 18, 2023
1d13340
anti-shadowkin handcuffs
DEATHB4DEFEAT Sep 18, 2023
d6421be
some sprites
DEATHB4DEFEAT Sep 17, 2023
3a7f5b1
helmet
DEATHB4DEFEAT Sep 21, 2023
121bcc4
helmet
DEATHB4DEFEAT Sep 21, 2023
73e721c
organs
DEATHB4DEFEAT Sep 21, 2023
5775fbf
move body part sprites
DEATHB4DEFEAT Sep 21, 2023
195350c
organ sprites
DEATHB4DEFEAT Sep 21, 2023
79895d8
rest of tha sprites
DEATHB4DEFEAT Oct 4, 2023
0b9b468
fix DarkSwapAttemptEvent Performer being private
DEATHB4DEFEAT Oct 14, 2023
642e0a6
clean up DarkSwap and add variables to the power component
DEATHB4DEFEAT Oct 14, 2023
702fa9a
fix darkenrange being set to darkenrate
DEATHB4DEFEAT Oct 14, 2023
d3f5a6f
Merge branch 'master' into shadowkin
DEATHB4DEFEAT Oct 14, 2023
03a9053
comment the DarkSwap system
DEATHB4DEFEAT Oct 14, 2023
cee48bf
fix RSI validator error
DEATHB4DEFEAT Oct 14, 2023
59ea236
don't grant powers back to blackeyed Shadowkin that are cloned and al…
DEATHB4DEFEAT Oct 14, 2023
1c2d093
Merge branch 'master' into shadowkin
DEATHB4DEFEAT Oct 14, 2023
383c38e
fix
DEATHB4DEFEAT Oct 14, 2023
3fa32e8
disallow teleporting out of entity storage
DEATHB4DEFEAT Oct 14, 2023
9000a6f
Merge branch 'master' into shadowkin
DEATHB4DEFEAT Oct 20, 2023
8073a56
fix Shadowkin visible in The Dark
DEATHB4DEFEAT Oct 20, 2023
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
2 changes: 1 addition & 1 deletion Content.Server/Cloning/CloningSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Mind.Mind mind, Clo
AddComp<ActiveCloningPodComponent>(uid);

// For other systems adding components to the mob
var ev = new BeenClonedEvent(pref, mind, mob, clonePod.Owner);
var ev = new BeenClonedEvent(pref, mind, mob, bodyToClone, clonePod.Owner);
RaiseLocalEvent(ev);

return true;
Expand Down
37 changes: 18 additions & 19 deletions Content.Server/SimpleStation14/Eye/EyeStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,28 @@
using Content.Server.Visible;
using Robust.Server.GameObjects;

namespace Content.Server.SimpleStation14.Eye
namespace Content.Server.SimpleStation14.Eye;

/// <summary>
/// Place to handle eye component startup for whatever systems.
/// </summary>
public sealed class EyeStartup : EntitySystem
{
/// <summary>
/// Place to handle eye component startup for whatever systems.
/// </summary>
public sealed class EyeStartup : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly ShadowkinDarkSwapSystem _shadowkinPowerSystem = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly ShadowkinDarkSwapSystem _shadowkinPowerSystem = default!;

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

SubscribeLocalEvent<EyeComponent, ComponentStartup>(OnEyeStartup);
}
SubscribeLocalEvent<EyeComponent, ComponentStartup>(OnEyeStartup);
}

private void OnEyeStartup(EntityUid uid, EyeComponent component, ComponentStartup args)
{
if (_entityManager.HasComponent<GhostComponent>(uid))
component.VisibilityMask |= (uint) VisibilityFlags.AIEye;
private void OnEyeStartup(EntityUid uid, EyeComponent component, ComponentStartup args)
{
if (_entityManager.HasComponent<GhostComponent>(uid))
component.VisibilityMask |= (uint) VisibilityFlags.AIEye;

_shadowkinPowerSystem.SetVisibility(uid, _entityManager.HasComponent<GhostComponent>(uid));
}
_shadowkinPowerSystem.SetVisibility(uid, _entityManager.HasComponent<GhostComponent>(uid), false, !_entityManager.HasComponent<GhostComponent>(uid));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,36 @@ public sealed class ShadowkinDarkSwapPowerComponent : Component
/// </summary>
[DataField("factions", customTypeSerializer: typeof(PrototypeIdListSerializer<NpcFactionPrototype>))]
public List<string> AddedFactions = new() { "ShadowkinDarkFriendly" };


/// <summary>
/// If the entity should be sent to the dark
/// </summary>
[DataField("invisible")]
public bool Invisible = true;

/// <summary>
/// If it should be pacified
/// </summary>
[DataField("pacify")]
public bool Pacify = true;

/// <summary>
/// If the entity should dim nearby lights when swapped
/// </summary>
[DataField("darken"), ViewVariables(VVAccess.ReadWrite)]
public bool Darken = true;

/// <summary>
/// How far to dim nearby lights
/// </summary>
[DataField("range"), ViewVariables(VVAccess.ReadWrite)]
public float DarkenRange = 5f;

/// <summary>
/// How fast to refresh nearby light dimming in seconds
/// Without this performance would be significantly worse
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float DarkenRate = 0.084f; // 1/12th of a second
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Content.Server.SimpleStation14.Species.Shadowkin.Components;

[RegisterComponent]
public sealed class ShadowkinSightComponent : Component
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public sealed class ShadowkinDarkSwapEvent : InstantActionEvent, ISpeakSpell

public sealed class ShadowkinDarkSwapAttemptEvent : CancellableEntityEventArgs
{
EntityUid Performer;
public EntityUid Performer;

public ShadowkinDarkSwapAttemptEvent(EntityUid performer)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,82 +66,106 @@ private void DarkSwap(EntityUid uid, ShadowkinDarkSwapPowerComponent component,
if (!_entity.HasComponent<ShadowkinComponent>(args.Performer))
return;

// Don't activate abilities if handcuffed
// TODO: Something like the Psionic Headcage to disable powers for Shadowkin
if (_entity.HasComponent<HandcuffComponent>(args.Performer))
// Don't activate abilities if specially handcuffed
if (_entity.TryGetComponent<HandcuffComponent>(args.Performer, out var cuffs) && cuffs.AntiShadowkin)
return;


var hasComp = _entity.HasComponent<ShadowkinDarkSwappedComponent>(args.Performer);

SetDarkened(
args.Performer,
!hasComp,
!hasComp,
!hasComp,
true,
args.StaminaCostOn,
args.PowerCostOn,
!_entity.HasComponent<ShadowkinDarkSwappedComponent>(args.Performer),
args.SoundOn,
args.VolumeOn,
args.StaminaCostOff,
args.PowerCostOff,
args.SoundOff,
args.VolumeOff,
args
args,
args.StaminaCostOn,
args.PowerCostOn,
args.StaminaCostOff,
args.PowerCostOff
);

_magic.Speak(args, false);
}


/// <summary>
/// Handles the effects of darkswapping
/// </summary>
/// <param name="performer">The entity being modified</param>
/// <param name="addComp">Is the entity swapping in to or out of The Dark?</param>
/// <param name="soundOn">Sound for the darkswapping</param>
/// <param name="volumeOn">Volume for the on sound</param>
/// <param name="soundOff">Sound for the un swapping</param>
/// <param name="volumeOff">Volume for the off sound</param>
/// <param name="staminaCostOn">Stamina cost for darkswapping</param>
/// <param name="powerCostOn">Power cost for darkswapping</param>
/// <param name="staminaCostOff">Stamina cost for un swapping</param>
/// <param name="powerCostOff">Power cost for un swapping</param>
/// <param name="args">If from an event, handle it</param>
public void SetDarkened(
EntityUid performer,
bool addComp,
bool invisible,
bool pacify,
bool darken,
float staminaCostOn,
float powerCostOn,
SoundSpecifier soundOn,
float volumeOn,
float staminaCostOff,
float powerCostOff,
SoundSpecifier soundOff,
float volumeOff,
ShadowkinDarkSwapEvent? args
SoundSpecifier? soundOn,
float? volumeOn,
SoundSpecifier? soundOff,
float? volumeOff,
ShadowkinDarkSwapEvent? args,
float staminaCostOn = 0,
float powerCostOn = 0,
float staminaCostOff = 0,
float powerCostOff = 0
)
{
// Ask other systems if we can DarkSwap
var ev = new ShadowkinDarkSwapAttemptEvent(performer);
RaiseLocalEvent(ev);
if (ev.Cancelled)
return;

if (addComp)
// We require the power component to DarkSwap
if (!_entity.TryGetComponent<ShadowkinDarkSwapPowerComponent>(performer, out var power))
return;

if (addComp) // Into The Dark
{
// Add the DarkSwapped component and set variables to match the power component
var comp = _entity.EnsureComponent<ShadowkinDarkSwappedComponent>(performer);
comp.Invisible = invisible;
comp.Pacify = pacify;
comp.Darken = darken;
comp.Invisible = power.Invisible;
comp.Pacify = power.Pacify;
comp.Darken = power.Darken;
comp.DarkenRange = power.DarkenRange;
comp.DarkenRate = power.DarkenRate;

// Tell other systems we've DarkSwapped
RaiseNetworkEvent(new ShadowkinDarkSwappedEvent(performer, true));

_audio.PlayPvs(soundOn, performer, AudioParams.Default.WithVolume(volumeOn));
// Play a sound if we have one
if (soundOn != null)
_audio.PlayPvs(soundOn, performer, AudioParams.Default.WithVolume(volumeOn ?? 5f));

// Drain power and stamina if we have a cost
_power.TryAddPowerLevel(performer, -powerCostOn);
_stamina.TakeStaminaDamage(performer, staminaCostOn);
}
else
else // Out of The Dark
{
// Remove the DarkSwapped component, the rest is handled in the shutdown event
_entity.RemoveComponent<ShadowkinDarkSwappedComponent>(performer);

// Tell other systems we've un DarkSwapped
RaiseNetworkEvent(new ShadowkinDarkSwappedEvent(performer, false));

_audio.PlayPvs(soundOff, performer, AudioParams.Default.WithVolume(volumeOff));
// Play a sound if we have one
if (soundOff != null)
_audio.PlayPvs(soundOff, performer, AudioParams.Default.WithVolume(volumeOff ?? 5f));

// Drain power and stamina if we have a cost
_power.TryAddPowerLevel(performer, -powerCostOff);
_stamina.TakeStaminaDamage(performer, staminaCostOff);
}

// If we have an event, mark it as handled
if (args != null)
args.Handled = true;
}
Expand All @@ -154,7 +178,7 @@ private void OnInvisStartup(EntityUid uid, ShadowkinDarkSwappedComponent compone

if (component.Invisible)
{
SetVisibility(uid, true);
SetVisibility(uid, true, true, true);
SuppressFactions(uid, true);
}
}
Expand All @@ -165,12 +189,14 @@ private void OnInvisShutdown(EntityUid uid, ShadowkinDarkSwappedComponent compon

if (component.Invisible)
{
SetVisibility(uid, false);
SetVisibility(uid, false, true, true);
SuppressFactions(uid, false);
}

// Prevent more updates while we're cleaning up
component.Darken = false;

// In case more updates occur for some reason, create a copy of the list to prevent error
foreach (var light in component.DarkenedLights.ToArray())
{
if (!_entity.TryGetComponent<PointLightComponent>(light, out var pointLight) ||
Expand All @@ -180,11 +206,19 @@ private void OnInvisShutdown(EntityUid uid, ShadowkinDarkSwappedComponent compon
_darken.ResetLight(pointLight, shadowkinLight);
}

// Clear the original array
component.DarkenedLights.Clear();
}


public void SetVisibility(EntityUid uid, bool set)
/// <summary>
/// Makes the specified entity able to see Shadowkin invisibility.
/// </summary>
/// <param name="uid">Entity to modify</param>
/// <param name="set">Whether the entity can see invisibility</param>
/// <param name="invisibility">Should the entity be moved to another visibility layer?</param>
/// <param name="stealth">(Only gets considered if set is true) Adds stealth to the entity</param>
public void SetVisibility(EntityUid uid, bool set, bool invisibility, bool stealth)
{
// We require the visibility component for this to work
var visibility = EnsureComp<VisibilityComponent>(uid);
Expand All @@ -196,12 +230,15 @@ public void SetVisibility(EntityUid uid, bool set)
eye.VisibilityMask |= (uint) VisibilityFlags.DarkSwapInvisibility;

// Make other entities unable to see the entity unless also DarkSwapped
_visibility.AddLayer(uid, visibility, (int) VisibilityFlags.DarkSwapInvisibility, false);
_visibility.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
if (invisibility)
{
_visibility.AddLayer(uid, visibility, (int) VisibilityFlags.DarkSwapInvisibility, false);
_visibility.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
}
_visibility.RefreshVisibility(uid);

// If not a ghost, add a stealth shader to the entity
if (!_entity.TryGetComponent<GhostComponent>(uid, out _))
if (!_entity.TryGetComponent<GhostComponent>(uid, out _) && stealth)
_stealth.SetVisibility(uid, 0.8f, _entity.EnsureComponent<StealthComponent>(uid));
}
else // Visible
Expand All @@ -211,13 +248,16 @@ public void SetVisibility(EntityUid uid, bool set)
eye.VisibilityMask &= ~(uint) VisibilityFlags.DarkSwapInvisibility;

// Make other entities able to see the entity again
_visibility.RemoveLayer(uid, visibility, (int) VisibilityFlags.DarkSwapInvisibility, false);
_visibility.AddLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
if (invisibility)
{
_visibility.RemoveLayer(uid, visibility, (int) VisibilityFlags.DarkSwapInvisibility, false);
_visibility.AddLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
}
_visibility.RefreshVisibility(uid);

// Remove the stealth shader from the entity
if (!_entity.TryGetComponent<GhostComponent>(uid, out _))
_stealth.SetVisibility(uid, 1f, _entity.EnsureComponent<StealthComponent>(uid));
_stealth.SetEnabled(uid, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ private void Rest(EntityUid uid, ShadowkinRestPowerComponent component, Shadowki
return;

// Rest is a funny ability, keep it :)
// // Don't activate abilities if handcuffed
// if (_entity.HasComponent<HandcuffComponent>(args.Performer))
// // Don't activate abilities if specially handcuffed
// if (_entity.TryGetComponent<HandcuffComponent>(args.Performer, out var cuffs) && cuffs.AntiShadowkin)
// return;


Expand All @@ -59,7 +59,7 @@ private void Rest(EntityUid uid, ShadowkinRestPowerComponent component, Shadowki
_entity.EnsureComponent<ForcedSleepingComponent>(args.Performer);
// No waking up normally (it would do nothing)
_actions.RemoveAction(args.Performer, new InstantAction(_prototype.Index<InstantActionPrototype>("Wake")));
_power.TryAddMultiplier(args.Performer, 1.5f);
_power.TryAddMultiplier(args.Performer, 2f);
// No action cooldown
args.Handled = false;
}
Expand All @@ -69,7 +69,7 @@ private void Rest(EntityUid uid, ShadowkinRestPowerComponent component, Shadowki
// Wake up
_entity.RemoveComponent<ForcedSleepingComponent>(args.Performer);
_entity.RemoveComponent<SleepingComponent>(args.Performer);
_power.TryAddMultiplier(args.Performer, -1.5f);
_power.TryAddMultiplier(args.Performer, -2f);
// Action cooldown
args.Handled = true;
}
Expand Down
Loading
Loading