Magical vision skill (#1467)
* fix firewave error spamming * basic magic traces entities * Add magical vision system and mana trace entities Introduces the magical vision mechanic, including the CP14MagicVisionComponent, marker entities, and related systems for tracking and displaying magical traces. Adds new actions, skill integration, localization strings, and icons for magical vision and trace markers. Magic traces are now spawned on spell use and mob state changes, with directional pointers and localized descriptions. * Show time passed for magic vision markers Adds a display of the time elapsed since a magic vision marker was spawned, using a localized string. Updates English and Russian locale files with the new 'cp14-magic-vision-timed-past' entry. * aura imprints * Update critical and death messages for inclusivity Revised the 'critical' and 'dead' messages in both English and Russian locale files to use more inclusive language, replacing references to 'magical creature' with 'someone'. * Move magic vision spawn on mob state change to server Transferred the logic for spawning magic vision markers on mob state changes (Critical/Dead) from CP14SharedMagicVisionSystem to CP14AuraImprintSystem. This centralizes the event handling on the server side. Also increased the duration of magic vision markers for spell usage from 20 to 50 seconds. * Integrate magic vision with visibility mask system Added a CP14MagicVision flag to VisibilityFlags and updated the magic vision and religion systems to use the visibility mask system. Magic vision markers now use the Visibility component, and visibility is refreshed when relevant components are added or removed. Removed client-side toggling logic in favor of server-driven visibility. * drowsiness overlay * noir shader * sfx design
This commit is contained in:
159
Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs
Normal file
159
Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared._CP14.MagicVision;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._CP14.MagicVision;
|
||||
|
||||
public sealed class CP14ClientMagicVisionSystem : CP14SharedMagicVisionSystem
|
||||
{
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
private CP14MagicVisionOverlay? _overlay;
|
||||
private CP14MagicVisionNoirOverlay? _overlay2;
|
||||
|
||||
private TimeSpan _nextUpdate = TimeSpan.Zero;
|
||||
|
||||
private SoundSpecifier _startSound = new SoundPathSpecifier(new ResPath("/Audio/Effects/eye_open.ogg"));
|
||||
private SoundSpecifier _endSound = new SoundPathSpecifier(new ResPath("/Audio/Effects/eye_close.ogg"));
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14MagicVisionMarkerComponent, AfterAutoHandleStateEvent>(OnHandleStateMarker);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicVisionComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<CP14MagicVisionComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicVisionComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<CP14MagicVisionComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
}
|
||||
|
||||
private void OnComponentShutdown(Entity<CP14MagicVisionComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_player.LocalEntity != ent)
|
||||
return;
|
||||
if (_overlay != null)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
_overlay = null;
|
||||
}
|
||||
if (_overlay2 != null)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay2);
|
||||
_overlay2 = null;
|
||||
}
|
||||
|
||||
_audio.PlayGlobal(_endSound, ent);
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<CP14MagicVisionComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (_player.LocalEntity != ent)
|
||||
return;
|
||||
|
||||
_overlay = new CP14MagicVisionOverlay();
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
_overlay.StartOverlay = _timing.CurTime;
|
||||
|
||||
_overlay2 = new CP14MagicVisionNoirOverlay();
|
||||
_overlayMan.AddOverlay(_overlay2);
|
||||
|
||||
_audio.PlayGlobal(_startSound, ent);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<CP14MagicVisionComponent> ent, ref LocalPlayerAttachedEvent args)
|
||||
{
|
||||
_overlay = new CP14MagicVisionOverlay();
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
_overlay.StartOverlay = _timing.CurTime;
|
||||
|
||||
_overlay2 = new CP14MagicVisionNoirOverlay();
|
||||
_overlayMan.AddOverlay(_overlay2);
|
||||
|
||||
_audio.PlayGlobal(_startSound, ent);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<CP14MagicVisionComponent> ent, ref LocalPlayerDetachedEvent args)
|
||||
{
|
||||
if (_overlay != null)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
_overlay = null;
|
||||
}
|
||||
if (_overlay2 != null)
|
||||
{
|
||||
_overlayMan.RemoveOverlay(_overlay2);
|
||||
_overlay2 = null;
|
||||
}
|
||||
_audio.PlayGlobal(_endSound, ent);
|
||||
}
|
||||
|
||||
protected override void OnExamined(Entity<CP14MagicVisionMarkerComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
base.OnExamined(ent, ref args);
|
||||
|
||||
if (ent.Comp.TargetCoordinates is null)
|
||||
return;
|
||||
|
||||
var originPosition = _transform.GetWorldPosition(ent);
|
||||
var targetPosition = _transform.ToWorldPosition(ent.Comp.TargetCoordinates.Value);
|
||||
|
||||
if ((targetPosition - originPosition).Length() < 0.5f)
|
||||
return;
|
||||
|
||||
Angle angle = new(targetPosition - originPosition);
|
||||
|
||||
var pointer = Spawn(ent.Comp.PointerProto, new MapCoordinates(originPosition, _transform.GetMapId(Transform(ent).Coordinates)));
|
||||
|
||||
_transform.SetWorldRotation(pointer, angle + Angle.FromDegrees(90));
|
||||
}
|
||||
|
||||
private void OnHandleStateMarker(Entity<CP14MagicVisionMarkerComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
if (ent.Comp.Icon is null)
|
||||
return;
|
||||
|
||||
var layer = _sprite.AddLayer(ent.Owner, ent.Comp.Icon);
|
||||
sprite.LayerSetShader(layer, "unshaded");
|
||||
_sprite.LayerSetScale(ent.Owner, layer, new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
return;
|
||||
|
||||
_nextUpdate = _timing.CurTime + TimeSpan.FromSeconds(0.5f);
|
||||
var queryFade = EntityQueryEnumerator<CP14MagicVisionMarkerComponent, SpriteComponent>();
|
||||
while (queryFade.MoveNext(out var uid, out var fade, out var sprite))
|
||||
{
|
||||
UpdateOpaque((uid, fade), sprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOpaque(Entity<CP14MagicVisionMarkerComponent> ent, SpriteComponent sprite)
|
||||
{
|
||||
var progress = Math.Clamp((_timing.CurTime.TotalSeconds - ent.Comp.SpawnTime.TotalSeconds) / (ent.Comp.EndTime.TotalSeconds - ent.Comp.SpawnTime.TotalSeconds), 0, 1);
|
||||
var alpha = 1 - progress;
|
||||
_sprite.SetColor(ent.Owner, Color.White.WithAlpha((float)alpha));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._CP14.MagicVision;
|
||||
|
||||
public sealed class CP14MagicVisionNoirOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
public override bool RequestScreenTexture => true;
|
||||
private readonly ShaderInstance _noirShader;
|
||||
|
||||
public CP14MagicVisionNoirOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_noirShader = _prototypeManager.Index<ShaderPrototype>("CP14BlueNoir").InstanceUnique();
|
||||
ZIndex = 9; // draw this over the DamageOverlay, RainbowOverlay etc, but before the black and white shader
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (ScreenTexture == null)
|
||||
return;
|
||||
|
||||
var handle = args.WorldHandle;
|
||||
_noirShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
|
||||
handle.UseShader(_noirShader);
|
||||
handle.DrawRect(args.WorldBounds, Color.White);
|
||||
handle.UseShader(null);
|
||||
}
|
||||
}
|
||||
75
Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs
Normal file
75
Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Content.Shared._CP14.MagicVision;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._CP14.MagicVision;
|
||||
|
||||
public sealed class CP14MagicVisionOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
public override bool RequestScreenTexture => true;
|
||||
|
||||
private readonly ShaderInstance _drowsinessShader;
|
||||
|
||||
public float CurrentPower = 10.0f;
|
||||
public TimeSpan StartOverlay = TimeSpan.Zero; // when the overlay started
|
||||
|
||||
private const float PowerDivisor = 250.0f;
|
||||
private const float Intensity = 0.2f; // for adjusting the visual scale
|
||||
private float _visualScale = 0; // between 0 and 1
|
||||
|
||||
public CP14MagicVisionOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
var playerEntity = _playerManager.LocalEntity;
|
||||
|
||||
if (playerEntity == null)
|
||||
return;
|
||||
|
||||
if (!_entityManager.HasComponent<CP14MagicVisionComponent>(playerEntity))
|
||||
return;
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
var timeLeft = (float)(curTime - StartOverlay).TotalSeconds;
|
||||
|
||||
CurrentPower = Math.Max(50f, 200f - (150f * Math.Min((float)(timeLeft / 3.0), 1.0f)));
|
||||
}
|
||||
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
|
||||
return false;
|
||||
|
||||
if (args.Viewport.Eye != eyeComp.Eye)
|
||||
return false;
|
||||
|
||||
_visualScale = Math.Clamp(CurrentPower / PowerDivisor, 0.0f, 1.0f);
|
||||
return _visualScale > 0;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (ScreenTexture == null)
|
||||
return;
|
||||
|
||||
var handle = args.WorldHandle;
|
||||
_drowsinessShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
|
||||
_drowsinessShader.SetParameter("Strength", _visualScale * Intensity);
|
||||
handle.UseShader(_drowsinessShader);
|
||||
handle.DrawRect(args.WorldBounds, Color.White);
|
||||
handle.UseShader(null);
|
||||
}
|
||||
}
|
||||
69
Content.Server/_CP14/AuraImprint/CP14AuraImprintSystem.cs
Normal file
69
Content.Server/_CP14/AuraImprint/CP14AuraImprintSystem.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Content.Shared._CP14.AuraDNA;
|
||||
using Content.Shared._CP14.MagicVision;
|
||||
using Content.Shared.Mobs;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._CP14.AuraImprint;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles the basic mechanics of spell use, such as doAfter, event invocation, and energy spending.
|
||||
/// </summary>
|
||||
public sealed partial class CP14AuraImprintSystem : CP14SharedAuraImprintSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly CP14SharedMagicVisionSystem _vision = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14AuraImprintComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<CP14AuraImprintComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<CP14AuraImprintComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.Imprint = GenerateAuraImprint(ent);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
public string GenerateAuraImprint(Entity<CP14AuraImprintComponent> ent)
|
||||
{
|
||||
var letters = new[] { "ä", "ã", "ç", "ø", "ђ", "œ", "Ї", "Ћ", "ў", "ž", "Ћ", "ö", "є", "þ"};
|
||||
var imprint = string.Empty;
|
||||
|
||||
for (var i = 0; i < ent.Comp.ImprintLength; i++)
|
||||
{
|
||||
imprint += letters[_random.Next(letters.Length)];
|
||||
}
|
||||
|
||||
return $"[color={ent.Comp.ImprintColor.ToHex()}]{imprint}[/color]";
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(Entity<CP14AuraImprintComponent> ent, ref MobStateChangedEvent args)
|
||||
{
|
||||
switch (args.NewMobState)
|
||||
{
|
||||
case MobState.Critical:
|
||||
{
|
||||
_vision.SpawnMagicVision(
|
||||
Transform(ent).Coordinates,
|
||||
new SpriteSpecifier.Rsi(new ResPath("_CP14/Actions/Spells/misc.rsi"), "skull"),
|
||||
Loc.GetString("cp14-magic-vision-crit"),
|
||||
TimeSpan.FromMinutes(10),
|
||||
ent);
|
||||
break;
|
||||
}
|
||||
case MobState.Dead:
|
||||
{
|
||||
_vision.SpawnMagicVision(
|
||||
Transform(ent).Coordinates,
|
||||
new SpriteSpecifier.Rsi(new ResPath("_CP14/Actions/Spells/misc.rsi"), "skull_red"),
|
||||
Loc.GetString("cp14-magic-vision-dead"),
|
||||
TimeSpan.FromMinutes(10),
|
||||
ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs
Normal file
54
Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Content.Shared._CP14.MagicVision;
|
||||
using Content.Shared.Eye;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._CP14.MagicVision;
|
||||
|
||||
public sealed class CP14MagicVisionSystem : CP14SharedMagicVisionSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedEyeSystem _eye = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MetaDataComponent, CP14MagicVisionToggleActionEvent>(OnMagicVisionToggle);
|
||||
SubscribeLocalEvent<CP14MagicVisionComponent, GetVisMaskEvent>(OnGetVisMask);
|
||||
}
|
||||
|
||||
private void OnGetVisMask(Entity<CP14MagicVisionComponent> ent, ref GetVisMaskEvent args)
|
||||
{
|
||||
args.VisibilityMask |= (int)VisibilityFlags.CP14MagicVision;
|
||||
}
|
||||
|
||||
private void OnMagicVisionToggle(Entity<MetaDataComponent> ent, ref CP14MagicVisionToggleActionEvent args)
|
||||
{
|
||||
if (!HasComp<CP14MagicVisionComponent>(ent))
|
||||
{
|
||||
AddComp<CP14MagicVisionComponent>(ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<CP14MagicVisionComponent>(ent);
|
||||
}
|
||||
_eye.RefreshVisibilityMask(ent.Owner);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<CP14MagicVisionMarkerComponent>();
|
||||
while (query.MoveNext(out var uid, out var marker))
|
||||
{
|
||||
if (marker.EndTime == TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < marker.EndTime)
|
||||
continue;
|
||||
|
||||
QueueDel(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
[Dependency] private readonly CP14MagicEnergySystem _magicEnergy = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedEyeSystem _eye = default!;
|
||||
|
||||
private EntityQuery<CP14ReligionEntityComponent> _godQuery;
|
||||
|
||||
@@ -158,11 +159,13 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
private void OnGodInit(Entity<CP14ReligionEntityComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
AddPvsOverrides(ent);
|
||||
_eye.RefreshVisibilityMask(ent.Owner);
|
||||
}
|
||||
|
||||
private void OnGodShutdown(Entity<CP14ReligionEntityComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
RemovePvsOverrides(ent);
|
||||
_eye.RefreshVisibilityMask(ent.Owner);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<CP14ReligionEntityComponent> ent, ref PlayerAttachedEvent args)
|
||||
|
||||
@@ -10,5 +10,8 @@ namespace Content.Shared.Eye
|
||||
Normal = 1 << 0,
|
||||
Ghost = 1 << 1,
|
||||
Subfloor = 1 << 2,
|
||||
//CP14 zone
|
||||
CP14MagicVision = 1 << 3,
|
||||
//CP14 zone end
|
||||
}
|
||||
}
|
||||
|
||||
19
Content.Shared/_CP14/AuraDNA/CP14AuraImprintComponent.cs
Normal file
19
Content.Shared/_CP14/AuraDNA/CP14AuraImprintComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._CP14.AuraDNA;
|
||||
|
||||
/// <summary>
|
||||
/// A component that stores a “blueprint” of the aura, unique to each mind.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedAuraImprintSystem))]
|
||||
public sealed partial class CP14AuraImprintComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public string Imprint = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public int ImprintLength = 8;
|
||||
|
||||
[DataField]
|
||||
public Color ImprintColor = Color.White;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._CP14.AuraDNA;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles the basic mechanics of spell use, such as doAfter, event invocation, and energy spending.
|
||||
/// </summary>
|
||||
public abstract partial class CP14SharedAuraImprintSystem : EntitySystem
|
||||
{
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Shared._CP14.MagicEnergy;
|
||||
using Content.Shared._CP14.MagicEnergy.Components;
|
||||
using Content.Shared._CP14.MagicSpell.Components;
|
||||
using Content.Shared._CP14.MagicSpell.Events;
|
||||
using Content.Shared._CP14.MagicSpell.Spells;
|
||||
using Content.Shared._CP14.MagicVision;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
@@ -32,6 +33,7 @@ public abstract partial class CP14SharedMagicSystem : EntitySystem
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly CP14SharedMagicVisionSystem _magicVision = default!;
|
||||
|
||||
private EntityQuery<CP14MagicEnergyContainerComponent> _magicContainerQuery;
|
||||
private EntityQuery<CP14MagicEffectComponent> _magicEffectQuery;
|
||||
@@ -162,6 +164,9 @@ public abstract partial class CP14SharedMagicSystem : EntitySystem
|
||||
|
||||
private void CastTelegraphy(Entity<CP14MagicEffectComponent> ent, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
foreach (var effect in ent.Comp.TelegraphyEffects)
|
||||
{
|
||||
effect.Effect(EntityManager, args);
|
||||
@@ -170,6 +175,9 @@ public abstract partial class CP14SharedMagicSystem : EntitySystem
|
||||
|
||||
private void CastSpell(Entity<CP14MagicEffectComponent> ent, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var ev = new CP14MagicEffectConsumeResourceEvent(args.User);
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
|
||||
@@ -177,6 +185,19 @@ public abstract partial class CP14SharedMagicSystem : EntitySystem
|
||||
{
|
||||
effect.Effect(EntityManager, args);
|
||||
}
|
||||
|
||||
if (args.User is not null
|
||||
&& TryComp<ActionComponent>(ent, out var actionComp)
|
||||
&& TryComp<CP14MagicEffectManaCostComponent>(ent, out var manaCost))
|
||||
{
|
||||
_magicVision.SpawnMagicVision(
|
||||
Transform(args.User.Value).Coordinates,
|
||||
actionComp.Icon,
|
||||
Loc.GetString("cp14-magic-vision-used-spell", ("name", MetaData(ent).EntityName)),
|
||||
TimeSpan.FromSeconds((float)manaCost.ManaCost * 50),
|
||||
args.User,
|
||||
args.Position);
|
||||
}
|
||||
}
|
||||
|
||||
protected FixedPoint2 CalculateManacost(Entity<CP14MagicEffectManaCostComponent> ent, EntityUid? caster)
|
||||
|
||||
11
Content.Shared/_CP14/MagicVision/CP14MagicVisionComponent.cs
Normal file
11
Content.Shared/_CP14/MagicVision/CP14MagicVisionComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._CP14.MagicVision;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to see magic vision trace entities
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CP14MagicVisionComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._CP14.MagicVision;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the visibility of this entity to the client, based on the length of time it has existed and the client's ability to see the magic
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
|
||||
public sealed partial class CP14MagicVisionMarkerComponent : Component
|
||||
{
|
||||
[DataField, AutoPausedField, AutoNetworkedField]
|
||||
public TimeSpan SpawnTime = TimeSpan.Zero;
|
||||
|
||||
[DataField, AutoPausedField, AutoNetworkedField]
|
||||
public TimeSpan EndTime = TimeSpan.Zero;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityCoordinates? TargetCoordinates;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public SpriteSpecifier? Icon;
|
||||
|
||||
[DataField]
|
||||
public string? AuraImprint;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntProtoId PointerProto = "CP14ManaVisionPointer";
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Text;
|
||||
using Content.Shared._CP14.AuraDNA;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Mobs;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._CP14.MagicVision;
|
||||
|
||||
public abstract class CP14SharedMagicVisionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly MetaDataSystem _meta = default!;
|
||||
|
||||
public readonly EntProtoId MagicTraceProto = "CP14MagicVisionMarker";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14MagicVisionMarkerComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<CP14AuraImprintComponent, ExaminedEvent>(OnAuraHolderExamine);
|
||||
}
|
||||
|
||||
private void OnAuraHolderExamine(Entity<CP14AuraImprintComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
if (!HasComp<CP14MagicVisionComponent>(args.Examiner))
|
||||
return;
|
||||
|
||||
args.PushMarkup($"{Loc.GetString("cp14-magic-vision-aura")} {ent.Comp.Imprint}");
|
||||
}
|
||||
|
||||
protected virtual void OnExamined(Entity<CP14MagicVisionMarkerComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var timePassed = _timing.CurTime - ent.Comp.SpawnTime;
|
||||
sb.Append($"{Loc.GetString("cp14-magic-vision-timed-past")} {timePassed.Minutes}:{(timePassed.Seconds < 10 ? "0" : "")}{timePassed.Seconds}\n");
|
||||
|
||||
if (ent.Comp.AuraImprint is not null)
|
||||
{
|
||||
sb.Append($"{Loc.GetString("cp14-magic-vision-aura")} {ent.Comp.AuraImprint}");
|
||||
}
|
||||
|
||||
args.AddMarkup(sb.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an invisible “magical trace” entity that can be seen with magical vision.
|
||||
/// </summary>
|
||||
/// <param name="position">Coordinates where the magic trail will be created</param>
|
||||
/// <param name="icon">Magic trace icon</param>
|
||||
/// <param name="description">Description that can be obtained when examining the magical trace</param>
|
||||
/// <param name="duration">Duration of the magical trace</param>
|
||||
/// <param name="target">Optional: The direction in which this trace “faces.” When studying the trace,
|
||||
/// this direction can be seen in order to understand, for example, in which direction the spell was used.</param>
|
||||
public void SpawnMagicVision(EntityCoordinates position, SpriteSpecifier? icon, string description, TimeSpan duration, EntityUid? aura = null, EntityCoordinates? target = null)
|
||||
{
|
||||
var ent = PredictedSpawnAtPosition(MagicTraceProto, position);
|
||||
var markerComp = EnsureComp<CP14MagicVisionMarkerComponent>(ent);
|
||||
|
||||
markerComp.SpawnTime = _timing.CurTime;
|
||||
markerComp.EndTime = _timing.CurTime + duration;
|
||||
markerComp.TargetCoordinates = target;
|
||||
markerComp.Icon = icon;
|
||||
|
||||
if (aura is not null && TryComp<CP14AuraImprintComponent>(aura, out var auraImprint))
|
||||
{
|
||||
markerComp.AuraImprint = auraImprint.Imprint;
|
||||
}
|
||||
|
||||
_meta.SetEntityDescription(ent, description);
|
||||
|
||||
Dirty(ent, markerComp);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class CP14MagicVisionToggleActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
6
Resources/Locale/en-US/_CP14/magicVision/vision.ftl
Normal file
6
Resources/Locale/en-US/_CP14/magicVision/vision.ftl
Normal file
@@ -0,0 +1,6 @@
|
||||
cp14-magic-vision-used-spell = The spell "{$name}" was used here.
|
||||
cp14-magic-vision-crit = Someone here is in critical condition.
|
||||
cp14-magic-vision-dead = Someone here has died.
|
||||
|
||||
cp14-magic-vision-timed-past = Time has passed:
|
||||
cp14-magic-vision-aura = Aura imprint:
|
||||
6
Resources/Locale/ru-RU/_CP14/magicVision/vision.ftl
Normal file
6
Resources/Locale/ru-RU/_CP14/magicVision/vision.ftl
Normal file
@@ -0,0 +1,6 @@
|
||||
cp14-magic-vision-used-spell = Здесь было использовано заклинание "{$name}".
|
||||
cp14-magic-vision-crit = Здесь кто-то упал в критическое состояние.
|
||||
cp14-magic-vision-dead = Здесь кто-то умер.
|
||||
|
||||
cp14-magic-vision-timed-past = Времени прошло:
|
||||
cp14-magic-vision-aura = Слепок ауры:
|
||||
@@ -56,6 +56,12 @@
|
||||
categories: [ HideSpawnMenu ]
|
||||
save: false
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: _CP14/Effects/fire.rsi
|
||||
layers:
|
||||
- state: small
|
||||
visible: false
|
||||
map: ["enum.EffectLayers.Unshaded"]
|
||||
- type: PointLight
|
||||
color: "#E25822"
|
||||
radius: 2.0
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
- type: entity
|
||||
id: CP14ActionToggleMagicVision
|
||||
parent: BaseMentalAction
|
||||
name: Magical vision
|
||||
description: You focus on magical flows to track recent events and scan the aura imprints of other living beings.
|
||||
components:
|
||||
- type: Action
|
||||
useDelay: 5
|
||||
itemIconStyle: BigAction
|
||||
checkCanInteract: false
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Magic/rumble.ogg
|
||||
icon:
|
||||
sprite: _CP14/Actions/Spells/meta.rsi
|
||||
state: magic_vision
|
||||
- type: InstantAction
|
||||
event: !type:CP14MagicVisionToggleActionEvent
|
||||
|
||||
- type: entity
|
||||
id: CP14ManaVisionPointer
|
||||
name: pointer
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Sprite
|
||||
color: "#42a4f5"
|
||||
sprite: /Textures/_CP14/Effects/Magic/pointer.rsi
|
||||
offset: 0, -1
|
||||
layers:
|
||||
- state: pointer
|
||||
shader: unshaded
|
||||
drawdepth: LowFloors
|
||||
- type: TimedDespawn
|
||||
lifetime: 2
|
||||
- type: Tag
|
||||
tags:
|
||||
- HideContextMenu
|
||||
|
||||
- type: entity
|
||||
id: CP14MagicVisionMarker
|
||||
categories: [ HideSpawnMenu ]
|
||||
name: mana trace
|
||||
components:
|
||||
- type: Sprite
|
||||
noRot: true
|
||||
drawdepth: Effects
|
||||
layers:
|
||||
- state: sphere
|
||||
sprite: /Textures/_CP14/Effects/Magic/pointer_sphere.rsi
|
||||
shader: unshaded
|
||||
color: "#42a4f5"
|
||||
- type: Clickable
|
||||
- type: Visibility
|
||||
layer: 8 #magic vision only
|
||||
@@ -9,5 +9,4 @@
|
||||
- state: green
|
||||
- sprite: /Textures/_CP14/Structures/Dungeon/demiplan_rift.rsi
|
||||
state: pulse
|
||||
- type: CP14DemiplaneRift
|
||||
|
||||
- type: CP14DemiplaneRift
|
||||
@@ -232,6 +232,7 @@
|
||||
Cold: 0.25
|
||||
Bloodloss: 0.25
|
||||
- type: CP14TradingReputation
|
||||
- type: CP14AuraImprint
|
||||
|
||||
|
||||
- type: entity
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
- type: CP14NightVision #Night vision
|
||||
- type: FootstepModifier
|
||||
footstepSoundCollection: null # Silent footstep
|
||||
- type: CP14AuraImprint
|
||||
imprintColor: "#9e7e5d"
|
||||
- type: Inventory
|
||||
templateId: CP14Carcat # Cant wear shoes
|
||||
speciesId: carcat
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
spawned:
|
||||
- id: CP14FoodMeatHuman
|
||||
amount: 5
|
||||
- type: CP14AuraImprint
|
||||
imprintColor: "#91898d"
|
||||
- type: Body
|
||||
prototype: CP14Dwarf
|
||||
requiredLegs: 2
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
freeLearnedSkills:
|
||||
- MetamagicT1
|
||||
- MetamagicT2
|
||||
- type: CP14AuraImprint
|
||||
imprintLength: 10 #Long imprint hehe
|
||||
imprintColor: "#42aaf5"
|
||||
- type: Inventory
|
||||
templateId: CP14Human
|
||||
displacements:
|
||||
|
||||
@@ -128,6 +128,9 @@
|
||||
- type: CP14MagicEnergyDraw #Cool x3 mana regen!
|
||||
energy: 1.5
|
||||
delay: 3
|
||||
- type: CP14AuraImprint
|
||||
imprintLength: 6 #Short imprint hehe
|
||||
imprintColor: "#919e19"
|
||||
- type: Hands
|
||||
handDisplacement:
|
||||
sizeMaps:
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
- type: Body
|
||||
prototype: CP14Silva
|
||||
requiredLegs: 2
|
||||
- type: CP14AuraImprint
|
||||
imprintColor: "#31cc3b"
|
||||
- type: Bloodstream
|
||||
bloodReagent: CP14BloodFlowerSap #Lol
|
||||
- type: Inventory
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
- MobMask
|
||||
layer:
|
||||
- MobLayer
|
||||
- type: CP14AuraImprint
|
||||
imprintColor: "#a81b5d"
|
||||
- type: Damageable
|
||||
damageContainer: CP14Biological
|
||||
damageModifierSet: CP14Skeleton
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
damage:
|
||||
Heat: 1 #Magic regen from fire
|
||||
Cold: -1
|
||||
- type: CP14AuraImprint
|
||||
imprintColor: "#e0762b"
|
||||
- type: CP14SkillStorage #Innate pyrokinetic
|
||||
freeLearnedSkills:
|
||||
- PyrokineticT1
|
||||
|
||||
@@ -23,4 +23,10 @@
|
||||
- type: shader
|
||||
id: CP14ReligionVision
|
||||
kind: source
|
||||
path: "/Textures/_CP14/Shaders/religion.swsl"
|
||||
path: "/Textures/_CP14/Shaders/religion.swsl"
|
||||
|
||||
|
||||
- type: shader
|
||||
id: CP14BlueNoir
|
||||
kind: source
|
||||
path: "/Textures/_CP14/Shaders/blue_noir.swsl"
|
||||
@@ -138,6 +138,20 @@
|
||||
- !type:NeedPrerequisite
|
||||
prerequisite: MetamagicT2
|
||||
|
||||
- type: cp14Skill
|
||||
id: CP14ActionToggleMagicVision
|
||||
skillUiPosition: 6, 4
|
||||
tree: Metamagic
|
||||
icon:
|
||||
sprite: _CP14/Actions/Spells/meta.rsi
|
||||
state: magic_vision
|
||||
effects:
|
||||
- !type:AddAction
|
||||
action: CP14ActionToggleMagicVision
|
||||
restrictions:
|
||||
- !type:NeedPrerequisite
|
||||
prerequisite: MetamagicT2
|
||||
|
||||
# T3
|
||||
|
||||
- type: cp14Skill
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 355 B |
@@ -24,6 +24,9 @@
|
||||
},
|
||||
{
|
||||
"name": "mana_trance"
|
||||
},
|
||||
{
|
||||
"name": "magic_vision"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -12,6 +12,12 @@
|
||||
},
|
||||
{
|
||||
"name": "polymorph"
|
||||
},
|
||||
{
|
||||
"name": "skull"
|
||||
},
|
||||
{
|
||||
"name": "skull_red"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/_CP14/Actions/Spells/misc.rsi/skull.png
Normal file
BIN
Resources/Textures/_CP14/Actions/Spells/misc.rsi/skull.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 269 B |
BIN
Resources/Textures/_CP14/Actions/Spells/misc.rsi/skull_red.png
Normal file
BIN
Resources/Textures/_CP14/Actions/Spells/misc.rsi/skull_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 B |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-4.0",
|
||||
"copyright": "Created by TheShuEd",
|
||||
"states": [
|
||||
{
|
||||
"name": "sphere"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 232 B |
43
Resources/Textures/_CP14/Shaders/blue_noir.swsl
Normal file
43
Resources/Textures/_CP14/Shaders/blue_noir.swsl
Normal file
@@ -0,0 +1,43 @@
|
||||
uniform sampler2D SCREEN_TEXTURE;
|
||||
|
||||
const highp float BlueHue = 210.0;
|
||||
const highp float HueRange = 80.0;
|
||||
const highp float MinSaturation = 0.1;
|
||||
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
||||
highp vec3 rgb2hsv(highp vec3 c) {
|
||||
highp float xMax = max(c.r, max(c.g, c.b));
|
||||
highp float xMin = min(c.r, min(c.g, c.b));
|
||||
highp float delta = xMax - xMin;
|
||||
|
||||
highp float hue = 0.0;
|
||||
if (delta > 0.0) {
|
||||
if (xMax == c.r) {
|
||||
hue = mod((c.g - c.b) / delta, 6.0);
|
||||
} else if (xMax == c.g) {
|
||||
hue = (c.b - c.r) / delta + 2.0;
|
||||
} else {
|
||||
hue = (c.r - c.g) / delta + 4.0;
|
||||
}
|
||||
hue *= 60.0;
|
||||
if (hue < 0.0) hue += 360.0;
|
||||
}
|
||||
|
||||
highp float sat = (xMax == 0.0) ? 0.0 : delta / xMax;
|
||||
return vec3(hue, sat, xMax);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
highp vec4 color = zTextureSpec(SCREEN_TEXTURE, UV);
|
||||
highp vec3 gray = vec3(zGrayscale(color.rgb));
|
||||
highp vec3 hsv = rgb2hsv(color.rgb);
|
||||
|
||||
bool is_blue = hsv.x > (BlueHue - HueRange) && hsv.x < (BlueHue + HueRange);
|
||||
bool saturated_enough = hsv.y > MinSaturation;
|
||||
|
||||
if (is_blue && saturated_enough) {
|
||||
COLOR = color;
|
||||
} else {
|
||||
COLOR = vec4(gray, color.a);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user