diff --git a/Content.Client/Overlays/ColorTintOverlay.cs b/Content.Client/Overlays/ColorTintOverlay.cs new file mode 100644 index 00000000000..f40a8d7342e --- /dev/null +++ b/Content.Client/Overlays/ColorTintOverlay.cs @@ -0,0 +1,63 @@ +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; + +namespace Content.Client.Overlays; + +/// +/// A simple overlay that applies a colored tint to the screen. +/// +public sealed class ColorTintOverlay : Overlay +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] IEntityManager _entityManager = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + private readonly ShaderInstance _shader; + + /// + /// The color to tint the screen to as RGB on a scale of 0-1. + /// + public Vector3? TintColor = null; + /// + /// The percent to tint the screen by on a scale of 0-1. + /// + public float? TintAmount = null; + + public ColorTintOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index("ColorTint").InstanceUnique(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (_player.LocalEntity is not { Valid: true } player + || !_entityManager.HasComponent(player)) + return false; + + return base.BeforeDraw(in args); + } + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + if (TintColor != null) + _shader.SetParameter("tint_color", (Vector3) TintColor); + if (TintAmount != null) + _shader.SetParameter("tint_amount", (float) TintAmount); + + var worldHandle = args.WorldHandle; + var viewport = args.WorldBounds; + worldHandle.SetTransform(Matrix3.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(viewport, Color.White); + worldHandle.UseShader(null); + } +} \ No newline at end of file diff --git a/Content.Client/Overlays/EtherealOverlay.cs b/Content.Client/Overlays/EtherealOverlay.cs new file mode 100644 index 00000000000..3d771de8ceb --- /dev/null +++ b/Content.Client/Overlays/EtherealOverlay.cs @@ -0,0 +1,48 @@ +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; + +namespace Content.Client.Overlays; + +public sealed class EtherealOverlay : Overlay +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] IEntityManager _entityManager = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; + private readonly ShaderInstance _shader; + + public EtherealOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index("Ethereal").InstanceUnique(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (_player.LocalEntity is not { Valid: true } player + || !_entityManager.HasComponent(player)) + return false; + + return base.BeforeDraw(in args); + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + + var worldHandle = args.WorldHandle; + var viewport = args.WorldBounds; + worldHandle.SetTransform(Matrix3.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(viewport, Color.White); + worldHandle.UseShader(null); + } +} \ No newline at end of file diff --git a/Content.Client/Shadowkin/EtherealSystem.cs b/Content.Client/Shadowkin/EtherealSystem.cs new file mode 100644 index 00000000000..cb289a87f12 --- /dev/null +++ b/Content.Client/Shadowkin/EtherealSystem.cs @@ -0,0 +1,52 @@ +using Content.Shared.Shadowkin; +using Robust.Client.Graphics; +using Robust.Shared.Player; +using Content.Client.Overlays; + +namespace Content.Client.Shadowkin; + +public sealed partial class EtherealSystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ISharedPlayerManager _playerMan = default!; + + private EtherealOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(Onhutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + _overlay = new(); + } + + private void OnInit(EntityUid uid, EtherealComponent component, ComponentInit args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void Onhutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnPlayerAttached(EntityUid uid, EtherealComponent component, LocalPlayerAttachedEvent args) + { + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(EntityUid uid, EtherealComponent component, LocalPlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } +} diff --git a/Content.Client/Shadowkin/ShadowkinSystem.cs b/Content.Client/Shadowkin/ShadowkinSystem.cs new file mode 100644 index 00000000000..d8e1b69fc71 --- /dev/null +++ b/Content.Client/Shadowkin/ShadowkinSystem.cs @@ -0,0 +1,114 @@ +using Content.Shared.Shadowkin; +using Content.Shared.CCVar; +using Robust.Client.Graphics; +using Robust.Shared.Configuration; +using Robust.Shared.Player; +using Content.Shared.Humanoid; +using Content.Shared.Abilities.Psionics; +using Content.Client.Overlays; + +namespace Content.Client.Shadowkin; + +public sealed partial class ShadowkinSystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ISharedPlayerManager _playerMan = default!; + + private ColorTintOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(Onhutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + Subs.CVar(_cfg, CCVars.NoVisionFilters, OnNoVisionFiltersChanged); + + _overlay = new(); + } + + private void OnInit(EntityUid uid, ShadowkinComponent component, ComponentInit args) + { + if (uid != _playerMan.LocalEntity + || _cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void Onhutdown(EntityUid uid, ShadowkinComponent component, ComponentShutdown args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnPlayerAttached(EntityUid uid, ShadowkinComponent component, LocalPlayerAttachedEvent args) + { + if (_cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(EntityUid uid, ShadowkinComponent component, LocalPlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnNoVisionFiltersChanged(bool enabled) + { + if (enabled) + _overlayMan.RemoveOverlay(_overlay); + else + _overlayMan.AddOverlay(_overlay); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + var uid = _playerMan.LocalEntity; + if (uid == null + || !TryComp(uid, out var comp) + || !TryComp(uid, out var humanoid)) + return; + + // 1/3 = 0.333... + // intensity = min + (power / max) + // intensity = intensity / 0.333 + // intensity = clamp intensity min, max + + var tintIntensity = 0.65f; + if (TryComp(uid, out var magic)) + { + var min = 0.45f; + var max = 0.75f; + tintIntensity = Math.Clamp(min + (magic.Mana / magic.MaxMana) * 0.333f, min, max); + } + + UpdateShader(new Vector3(humanoid.EyeColor.R, humanoid.EyeColor.G, humanoid.EyeColor.B), tintIntensity); + } + + private void UpdateShader(Vector3? color, float? intensity) + { + while (_overlayMan.HasOverlay()) + _overlayMan.RemoveOverlay(_overlay); + + if (color != null) + _overlay.TintColor = color; + if (intensity != null) + _overlay.TintAmount = intensity; + + if (!_cfg.GetCVar(CCVars.NoVisionFilters)) + _overlayMan.AddOverlay(_overlay); + } +} diff --git a/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs b/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs new file mode 100644 index 00000000000..fd394e0a228 --- /dev/null +++ b/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs @@ -0,0 +1,58 @@ +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions.Events; +using Content.Shared.Shadowkin; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Maps; +using Robust.Server.GameObjects; + +namespace Content.Server.Abilities.Psionics +{ + public sealed class DarkSwapSystem : EntitySystem + { + [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPowerUsed); + } + + private void OnPowerUsed(DarkSwapActionEvent args) + { + if (TryComp(args.Performer, out var ethereal)) + { + var tileref = Transform(args.Performer).Coordinates.GetTileRef(); + if (tileref != null + && _physics.GetEntitiesIntersectingBody(args.Performer, (int) CollisionGroup.Impassable).Count > 0) + { + _popup.PopupEntity(Loc.GetString("revenant-in-solid"), args.Performer, args.Performer); + return; + } + + if (_psionics.OnAttemptPowerUse(args.Performer, "DarkSwap", args.ManaCost / 2, args.CheckInsulation)) + { + RemComp(args.Performer, ethereal); + args.Handled = true; + } + } + else if (_psionics.OnAttemptPowerUse(args.Performer, "DarkSwap", args.ManaCost, args.CheckInsulation)) + { + var newethereal = EnsureComp(args.Performer); + newethereal.Darken = true; + + SpawnAtPosition("ShadowkinShadow", Transform(args.Performer).Coordinates); + SpawnAtPosition("EffectFlashShadowkinDarkSwapOn", Transform(args.Performer).Coordinates); + + args.Handled = true; + } + + if (args.Handled) + _psionics.LogPowerUsed(args.Performer, "DarkSwap", 0, 0); + } + } +} + + diff --git a/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs index cdfda7c8013..ecffc86c76d 100644 --- a/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs @@ -38,6 +38,9 @@ public override void Initialize() private void OnPowerUsed(DispelPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "dispel")) + return; + var ev = new DispelledEvent(); RaiseLocalEvent(args.Target, ev, false); diff --git a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs index 138a341ce85..6a2e90dd88d 100644 --- a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs @@ -44,7 +44,7 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicHealOtherPowerActionEvent args) { - if (component.DoAfter is not null) + if (!_psionics.OnAttemptPowerUse(args.Performer, args.PowerName)) return; args.ModifiedAmplification = _psionics.ModifiedAmplification(uid, component); diff --git a/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs index 58d7d804da6..24ef344f638 100644 --- a/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs @@ -19,6 +19,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, MetapsionicPowerComponent component, MetapsionicPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "metapsionic pulse")) + return; + foreach (var entity in _lookup.GetEntitiesInRange(uid, component.Range)) { if (HasComp(entity) && entity != uid && !HasComp(entity) && diff --git a/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs index 2d106706c67..869bf269ab6 100644 --- a/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs @@ -38,7 +38,8 @@ public override void Initialize() private void OnPowerUsed(MindSwapPowerActionEvent args) { - if (!(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) + if (!_psionics.OnAttemptPowerUse(args.Performer, "mind swap") + || !(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) return; Swap(args.Performer, args.Target); @@ -116,8 +117,8 @@ private void OnGhostAttempt(GhostAttemptHandleEvent args) private void OnSwapInit(EntityUid uid, MindSwappedComponent component, ComponentInit args) { - _actions.AddAction(uid, ref component.MindSwapReturnActionEntity, component.MindSwapReturnActionId ); - _actions.TryGetActionData( component.MindSwapReturnActionEntity, out var actionData ); + _actions.AddAction(uid, ref component.MindSwapReturnActionEntity, component.MindSwapReturnActionId); + _actions.TryGetActionData(component.MindSwapReturnActionEntity, out var actionData); if (actionData is { UseDelay: not null }) _actions.StartUseDelay(component.MindSwapReturnActionEntity); } @@ -132,11 +133,13 @@ public void Swap(EntityUid performer, EntityUid target, bool end = false) MindComponent? targetMind = null; // This is here to prevent missing MindContainerComponent Resolve errors. - if(!_mindSystem.TryGetMind(performer, out var performerMindId, out performerMind)){ + if (!_mindSystem.TryGetMind(performer, out var performerMindId, out performerMind)) + { performerMind = null; }; - if(!_mindSystem.TryGetMind(target, out var targetMindId, out targetMind)){ + if (!_mindSystem.TryGetMind(target, out var targetMindId, out targetMind)) + { targetMind = null; }; //This is a terrible way to 'unattach' minds. I wanted to use UnVisit but in TransferTo's code they say diff --git a/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs index c2f59206392..22c4f2e5005 100644 --- a/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs @@ -22,6 +22,9 @@ public override void Initialize() private void OnPowerUsed(NoosphericZapPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "noospheric zap")) + return; + _beam.TryCreateBeam(args.Performer, args.Target, "LightningNoospheric"); _stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(5), false); diff --git a/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs index 19658629545..c6a01912a08 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs @@ -31,13 +31,14 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityPowerActionEvent args) { - if (HasComp(uid)) + if (!_psionics.OnAttemptPowerUse(args.Performer, "psionic invisibility") + || HasComp(uid)) return; ToggleInvisibility(args.Performer); var action = Spawn(PsionicInvisibilityUsedComponent.PsionicInvisibilityUsedActionPrototype); _actions.AddAction(uid, action, action); - _actions.TryGetActionData( action, out var actionData ); + _actions.TryGetActionData(action, out var actionData); if (actionData is { UseDelay: not null }) _actions.StartUseDelay(action); @@ -93,7 +94,8 @@ public void ToggleInvisibility(EntityUid uid) if (!HasComp(uid)) { EnsureComp(uid); - } else + } + else { RemComp(uid); } diff --git a/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs index 17e9249e655..d7ad2d49aba 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs @@ -40,6 +40,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicRegenerationPowerComponent component, PsionicRegenerationPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "psionic regeneration")) + return; + var ev = new PsionicRegenerationDoAfterEvent(_gameTiming.CurTime); var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid); diff --git a/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs index 3740822667b..4a750836020 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs @@ -19,6 +19,9 @@ public override void Initialize() } private void OnPowerUsed(PyrokinesisPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "pyrokinesis")) + return; + if (!TryComp(args.Target, out var flammableComponent)) return; diff --git a/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs index 7a3f663a43f..abbbdfacc56 100644 --- a/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs @@ -18,6 +18,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, TelegnosisPowerComponent component, TelegnosisPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "telegnosis")) + return; + var projection = Spawn(component.Prototype, Transform(uid).Coordinates); Transform(projection).AttachToGridOrMap(); _mindSwap.Swap(uid, projection); diff --git a/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs b/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs index 73d5aae2102..ff9910c400d 100644 --- a/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs @@ -49,8 +49,7 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args) { - if (HasComp(uid) - || HasComp(uid)) + if (!_psionics.OnAttemptPowerUse(args.Performer, args.Settings.PowerName, args.Settings.ManaCost, args.Settings.CheckInsulation)) return; var overcharged = args.Settings.DoSupercritical ? _glimmerSystem.Glimmer * component.CurrentAmplification diff --git a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs index 536235b6d63..841d923a24c 100644 --- a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs +++ b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs @@ -217,6 +217,10 @@ public void RemoveAllPsionicPowers(EntityUid uid, bool mindbreak = false) RemComp(uid); RemComp(uid); + + var ev = new OnMindbreakEvent(); + RaiseLocalEvent(uid, ref ev); + return; } RefreshPsionicModifiers(uid, psionic); diff --git a/Content.Server/Body/Components/RespiratorImmuneComponent.cs b/Content.Server/Body/Components/RespiratorImmuneComponent.cs new file mode 100644 index 00000000000..afc261eff24 --- /dev/null +++ b/Content.Server/Body/Components/RespiratorImmuneComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server.Body.Components; + +[RegisterComponent] +public sealed partial class RespiratorImmuneComponent : Component { } \ No newline at end of file diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 389e5fbab72..f6a17d32b65 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -66,6 +66,9 @@ public override void Update(float frameTime) if (_mobState.IsDead(uid)) continue; + if (HasComp(uid)) + continue; + UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator); if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit. diff --git a/Content.Server/Chat/EmpathyChatSystem.cs b/Content.Server/Chat/EmpathyChatSystem.cs new file mode 100644 index 00000000000..b46bbdc34e9 --- /dev/null +++ b/Content.Server/Chat/EmpathyChatSystem.cs @@ -0,0 +1,83 @@ +using System.Linq; +using Robust.Shared.Utility; +using Content.Server.Chat.Managers; +using Content.Server.Language; +using Content.Server.Chat.Systems; +using Content.Server.Administration.Managers; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Content.Shared.Chat; +using Content.Shared.Language; +using Robust.Shared.Prototypes; +using Content.Shared.Language.Components; + +namespace Content.Server.Chat; + +public sealed partial class EmpathyChatSystem : EntitySystem +{ + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly LanguageSystem _language = default!; + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnSpeak); + } + + private void OnSpeak(EntityUid uid, LanguageSpeakerComponent component, EntitySpokeEvent args) + { + if (args.Source != uid + || !args.Language.SpeechOverride.EmpathySpeech + || args.IsWhisper) + return; + + SendEmpathyChat(args.Source, args.Message, false); + } + + /// + /// Send a Message in the Shadowkin Empathy Chat. + /// + /// The entity making the message + /// The contents of the message + /// Set the ChatTransmitRange + public void SendEmpathyChat(EntityUid source, string message, bool hideChat) + { + var clients = GetEmpathChatClients(); + string wrappedMessage; + + wrappedMessage = Loc.GetString("chat-manager-send-empathy-chat-wrap-message", + ("source", source), + ("message", FormattedMessage.EscapeText(message))); + + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, wrappedMessage, source, hideChat, true, clients.ToList(), Color.FromHex("#be3cc5")); + } + + private IEnumerable GetEmpathChatClients() + { + return Filter.Empty() + .AddWhereAttachedEntity(entity => + CanHearEmpathy(entity)) + .Recipients + .Union(_adminManager.ActiveAdmins) + .Select(p => p.Channel); + } + + /// + /// Check if an entity can hear Empathy. + /// (Admins will always be able to hear Empathy) + /// + /// The entity to check + public bool CanHearEmpathy(EntityUid entity) + { + var understood = _language.GetUnderstoodLanguages(entity); + for (int i = 0; i < understood.Count; i++) + { + var language = _prototype.Index(understood[i]); + if (language.SpeechOverride.EmpathySpeech) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/EtherealLightComponent.cs b/Content.Server/Shadowkin/EtherealLightComponent.cs new file mode 100644 index 00000000000..8f47d862002 --- /dev/null +++ b/Content.Server/Shadowkin/EtherealLightComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Shadowkin; + +[RegisterComponent] +public sealed partial class EtherealLightComponent : Component +{ + public EntityUid AttachedEntity = EntityUid.Invalid; + + public float OldRadius = 0f; + + public bool OldRadiusEdited = false; + + public float OldEnergy = 0f; + + public bool OldEnergyEdited = false; +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/EtherealStunItemSystem.cs b/Content.Server/Shadowkin/EtherealStunItemSystem.cs new file mode 100644 index 00000000000..b48b4d4fecf --- /dev/null +++ b/Content.Server/Shadowkin/EtherealStunItemSystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Interaction.Events; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Shadowkin; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Stacks; + +namespace Content.Server.Shadowkin; + +public sealed class EtherealStunItemSystem : EntitySystem +{ + [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedStackSystem _sharedStackSystem = default!; + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + } + + private void OnUseInHand(EntityUid uid, EtherealStunItemComponent component, UseInHandEvent args) + { + foreach (var ent in _lookup.GetEntitiesInRange(uid, component.Radius)) + { + if (!TryComp(ent, out var ethereal)) + continue; + + RemComp(ent, ethereal); + + if (TryComp(ent, out var stamina)) + _stamina.TakeStaminaDamage(ent, stamina.CritThreshold, stamina, ent); + + if (TryComp(ent, out var magic)) + magic.Mana = 0; + } + + if (!component.DeleteOnUse) + return; + + if (TryComp(uid, out var stack)) + _sharedStackSystem.Use(uid, 1, stack); + else + QueueDel(uid); + } +} diff --git a/Content.Server/Shadowkin/EtherealSystem.cs b/Content.Server/Shadowkin/EtherealSystem.cs new file mode 100644 index 00000000000..2622547a3f6 --- /dev/null +++ b/Content.Server/Shadowkin/EtherealSystem.cs @@ -0,0 +1,216 @@ +using Content.Shared.Eye; +using Content.Shared.Shadowkin; +using Robust.Server.GameObjects; +using Content.Server.Atmos.Components; +using Content.Server.Temperature.Components; +using Content.Shared.Movement.Components; +using Content.Shared.Stealth; +using Content.Shared.Stealth.Components; +using Content.Server.Body.Components; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using System.Linq; +using Content.Shared.Abilities.Psionics; +using Robust.Shared.Random; +using Content.Server.Light.Components; + +namespace Content.Server.Shadowkin; + +public sealed class EtherealSystem : SharedEtherealSystem +{ + [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; + [Dependency] private readonly SharedStealthSystem _stealth = default!; + [Dependency] private readonly EyeSystem _eye = default!; + [Dependency] private readonly NpcFactionSystem _factions = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedPointLightSystem _light = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void OnStartup(EntityUid uid, EtherealComponent component, MapInitEvent args) + { + base.OnStartup(uid, component, args); + + var visibility = EnsureComp(uid); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ethereal, false); + _visibilitySystem.RefreshVisibility(uid, visibility); + + if (TryComp(uid, out var eye)) + _eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) (VisibilityFlags.Ethereal), eye); + + if (TryComp(uid, out var temp)) + temp.AtmosTemperatureTransferEfficiency = 0; + + var stealth = EnsureComp(uid); + _stealth.SetVisibility(uid, 0.8f, stealth); + + SuppressFactions(uid, component, true); + + EnsureComp(uid); + EnsureComp(uid); + EnsureComp(uid); + + if (HasComp(uid)) + RemComp(uid, component); + } + + public override void OnShutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + base.OnShutdown(uid, component, args); + + if (TryComp(uid, out var visibility)) + { + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ethereal, false); + _visibilitySystem.RefreshVisibility(uid, visibility); + } + + if (TryComp(uid, out var eye)) + _eye.SetVisibilityMask(uid, (int) VisibilityFlags.Normal, eye); + + if (TryComp(uid, out var temp)) + temp.AtmosTemperatureTransferEfficiency = 0.1f; + + SuppressFactions(uid, component, false); + + RemComp(uid); + RemComp(uid); + RemComp(uid); + RemComp(uid); + + SpawnAtPosition("ShadowkinShadow", Transform(uid).Coordinates); + SpawnAtPosition("EffectFlashShadowkinDarkSwapOff", Transform(uid).Coordinates); + + foreach (var light in component.DarkenedLights.ToArray()) + { + if (!TryComp(light, out var pointLight) + || !TryComp(light, out var etherealLight)) + continue; + + ResetLight(light, pointLight, etherealLight); + } + } + + public void SuppressFactions(EntityUid uid, EtherealComponent component, bool set) + { + if (set) + { + if (!TryComp(uid, out var factions)) + return; + + component.SuppressedFactions = factions.Factions.ToList(); + + foreach (var faction in factions.Factions) + _factions.RemoveFaction(uid, faction); + } + else + { + foreach (var faction in component.SuppressedFactions) + _factions.AddFaction(uid, faction); + + component.SuppressedFactions.Clear(); + } + } + + public void ResetLight(EntityUid uid, PointLightComponent light, EtherealLightComponent etherealLight) + { + etherealLight.AttachedEntity = EntityUid.Invalid; + + if (etherealLight.OldRadiusEdited) + _light.SetRadius(uid, etherealLight.OldRadius); + etherealLight.OldRadiusEdited = false; + + if (etherealLight.OldEnergyEdited) + _light.SetEnergy(uid, etherealLight.OldEnergy); + etherealLight.OldEnergyEdited = false; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (!component.Darken) + continue; + + component.DarkenAccumulator += frameTime; + + if (component.DarkenAccumulator <= 1) + continue; + + component.DarkenAccumulator -= component.DarkenRate; + + var darkened = new List(); + var lightQuery = _lookup.GetEntitiesInRange(uid, component.DarkenRange, flags: LookupFlags.StaticSundries) + .Where(x => HasComp(x) && HasComp(x)); + + foreach (var entity in lightQuery) + if (!darkened.Contains(entity)) + darkened.Add(entity); + + _random.Shuffle(darkened); + component.DarkenedLights = darkened; + + var playerPos = _transform.GetWorldPosition(uid); + + foreach (var light in component.DarkenedLights.ToArray()) + { + var lightPos = _transform.GetWorldPosition(light); + if (!TryComp(light, out var pointLight) + || !TryComp(light, out var etherealLight)) + continue; + + if (TryComp(light, out var powered) && !powered.On) + { + ResetLight(light, pointLight, etherealLight); + continue; + } + + if (etherealLight.AttachedEntity == EntityUid.Invalid) + etherealLight.AttachedEntity = uid; + + if (etherealLight.AttachedEntity != EntityUid.Invalid + && etherealLight.AttachedEntity != uid) + { + component.DarkenedLights.Remove(light); + continue; + } + + if (etherealLight.AttachedEntity == uid + && _random.Prob(0.03f)) + etherealLight.AttachedEntity = EntityUid.Invalid; + + if (!etherealLight.OldRadiusEdited) + { + etherealLight.OldRadius = pointLight.Radius; + etherealLight.OldRadiusEdited = true; + } + if (!etherealLight.OldEnergyEdited) + { + etherealLight.OldEnergy = pointLight.Energy; + etherealLight.OldEnergyEdited = true; + } + + var distance = (lightPos - playerPos).Length(); + var radius = distance * 2f; + var energy = distance * 0.8f; + + if (etherealLight.OldRadiusEdited && radius > etherealLight.OldRadius) + radius = etherealLight.OldRadius; + if (etherealLight.OldRadiusEdited && radius < etherealLight.OldRadius * 0.20f) + radius = etherealLight.OldRadius * 0.20f; + + if (etherealLight.OldEnergyEdited && energy > etherealLight.OldEnergy) + energy = etherealLight.OldEnergy; + if (etherealLight.OldEnergyEdited && energy < etherealLight.OldEnergy * 0.20f) + energy = etherealLight.OldEnergy * 0.20f; + + _light.SetRadius(light, radius); + _light.SetEnergy(light, energy); + } + } + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/ShadowkinCuffSystem.cs b/Content.Server/Shadowkin/ShadowkinCuffSystem.cs new file mode 100644 index 00000000000..ce2b2588174 --- /dev/null +++ b/Content.Server/Shadowkin/ShadowkinCuffSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Clothing.Components; +using Content.Shared.Shadowkin; + +namespace Content.Server.Shadowkin; + +public sealed class ShadowkinCuffSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnEquipped(EntityUid uid, ShadowkinCuffComponent component, GotEquippedEvent args) + { + if (!TryComp(uid, out var clothing) + || !clothing.Slots.HasFlag(args.SlotFlags)) + return; + + EnsureComp(args.Equipee); + } + + private void OnUnequipped(EntityUid uid, ShadowkinCuffComponent component, GotUnequippedEvent args) + { + RemComp(args.Equipee); + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/ShadowkinSystem.cs b/Content.Server/Shadowkin/ShadowkinSystem.cs new file mode 100644 index 00000000000..83461e7a7fe --- /dev/null +++ b/Content.Server/Shadowkin/ShadowkinSystem.cs @@ -0,0 +1,181 @@ +using Content.Shared.Examine; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Humanoid; +using Content.Shared.Psionics; +using Content.Shared.Bed.Sleep; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Shadowkin; +using Content.Shared.Rejuvenate; +using Content.Shared.Alert; +using Content.Shared.Rounding; +using Content.Shared.Actions; +using Robust.Shared.Prototypes; +using Content.Server.Abilities.Psionics; + +namespace Content.Server.Shadowkin; + +public sealed class ShadowkinSystem : EntitySystem +{ + [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public const string ShadowkinSleepActionId = "ShadowkinActionSleep"; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnMindbreak); + SubscribeLocalEvent(OnAttemptPowerUse); + SubscribeLocalEvent(OnManaUpdate); + SubscribeLocalEvent(OnRejuvenate); + SubscribeLocalEvent(OnEyeColorChange); + } + + private void OnInit(EntityUid uid, ShadowkinComponent component, ComponentStartup args) + { + if (component.BlackeyeSpawn) + ApplyBlackEye(uid); + + _actionsSystem.AddAction(uid, ref component.ShadowkinSleepAction, ShadowkinSleepActionId, uid); + + UpdateShadowkinAlert(uid, component); + } + + private void OnEyeColorChange(EntityUid uid, ShadowkinComponent component, EyeColorInitEvent args) + { + if (!TryComp(uid, out var humanoid) + || !component.BlackeyeSpawn + || humanoid.EyeColor == component.OldEyeColor) + return; + + component.OldEyeColor = humanoid.EyeColor; + humanoid.EyeColor = component.BlackEyeColor; + Dirty(humanoid); + } + + private void OnExamined(EntityUid uid, ShadowkinComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange + || !TryComp(uid, out var magic) + || HasComp(uid)) + return; + + var severity = "shadowkin-power-" + ContentHelpers.RoundToLevels(magic.Mana, magic.MaxMana, 6); + var powerType = Loc.GetString(severity); + + if (args.Examined == args.Examiner) + args.PushMarkup(Loc.GetString("shadowkin-power-examined-self", + ("power", Math.Floor(magic.Mana)), + ("powerMax", Math.Floor(magic.MaxMana)), + ("powerType", powerType) + )); + else + args.PushMarkup(Loc.GetString("shadowkin-power-examined-other", + ("target", uid), + ("powerType", powerType) + )); + } + + /// + /// Update the Shadowkin Alert, if Blackeye will remove the Alert, if not will update to its current power status. + /// + public void UpdateShadowkinAlert(EntityUid uid, ShadowkinComponent component) + { + if (TryComp(uid, out var magic)) + { + var severity = (short) ContentHelpers.RoundToLevels(magic.Mana, magic.MaxMana, 8); + _alerts.ShowAlert(uid, AlertType.ShadowkinPower, severity); + } + else + _alerts.ClearAlert(uid, AlertType.ShadowkinPower); + } + + private void OnAttemptPowerUse(EntityUid uid, ShadowkinComponent component, OnAttemptPowerUseEvent args) + { + if (HasComp(uid)) + args.Cancel(); + } + + private void OnManaUpdate(EntityUid uid, ShadowkinComponent component, ref OnManaUpdateEvent args) + { + if (!TryComp(uid, out var magic)) + return; + + if (component.SleepManaRegen + && TryComp(uid, out var sleep)) + magic.ManaGainMultiplier = component.SleepManaRegenMultiplier; + else + magic.ManaGainMultiplier = 1; + + if (magic.Mana <= component.BlackEyeMana) + ApplyBlackEye(uid); + + Dirty(magic); // Update Shadowkin Overlay. + UpdateShadowkinAlert(uid, component); + } + + /// + /// Blackeye the Shadowkin, its just a function to mindbreak the shadowkin but making sure "Removable" is checked true during it. + /// + /// + public void ApplyBlackEye(EntityUid uid) + { + if (!TryComp(uid, out var magic)) + return; + + magic.Removable = true; + _psionicAbilitiesSystem.MindBreak(uid); + } + + private void OnMindbreak(EntityUid uid, ShadowkinComponent component, ref OnMindbreakEvent args) + { + if (TryComp(uid, out var mindbreak)) + mindbreak.MindbrokenExaminationText = "examine-mindbroken-shadowkin-message"; + + if (TryComp(uid, out var humanoid)) + { + component.OldEyeColor = humanoid.EyeColor; + humanoid.EyeColor = component.BlackEyeColor; + Dirty(humanoid); + } + + if (component.BlackeyeSpawn) + return; + + if (TryComp(uid, out var stamina)) + _stamina.TakeStaminaDamage(uid, stamina.CritThreshold, stamina, uid); + } + + private void OnRejuvenate(EntityUid uid, ShadowkinComponent component, RejuvenateEvent args) + { + if (component.BlackeyeSpawn + || !HasComp(uid)) + return; + + RemComp(uid); + + if (TryComp(uid, out var humanoid)) + { + humanoid.EyeColor = component.OldEyeColor; + Dirty(humanoid); + } + + EnsureComp(uid, out var magic); + magic.Mana = 250; + magic.MaxMana = 250; + magic.ManaGain = 0.25f; + magic.BypassManaCheck = true; + magic.Removable = false; + magic.MindbreakingFeedback = "shadowkin-blackeye"; + + if (_prototypeManager.TryIndex("ShadowkinPowers", out var shadowkinPowers)) + _psionicAbilitiesSystem.InitializePsionicPower(uid, shadowkinPowers); + + UpdateShadowkinAlert(uid, component); + } +} diff --git a/Content.Server/Shadowkin/ShowEtherealSystem.cs b/Content.Server/Shadowkin/ShowEtherealSystem.cs new file mode 100644 index 00000000000..151c379afbb --- /dev/null +++ b/Content.Server/Shadowkin/ShowEtherealSystem.cs @@ -0,0 +1,88 @@ +using Content.Shared.Shadowkin; +using Content.Shared.Eye; +using Robust.Server.GameObjects; +using Content.Shared.Inventory.Events; +using Content.Shared.Interaction.Events; +using Robust.Shared.Timing; +using Content.Shared.Popups; +using Content.Shared.Clothing.Components; + +namespace Content.Server.Shadowkin; +public sealed class ShowEtherealSystem : EntitySystem +{ + [Dependency] private readonly EyeSystem _eye = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnAttackAttempt); + } + + private void OnInit(EntityUid uid, ShowEtherealComponent component, MapInitEvent args) + { + Toggle(uid, true); + } + + public void OnShutdown(EntityUid uid, ShowEtherealComponent component, ComponentShutdown args) + { + Toggle(uid, false); + } + + private void OnEquipped(EntityUid uid, ShowEtherealComponent component, GotEquippedEvent args) + { + if (!TryComp(uid, out var clothing) + || !clothing.Slots.HasFlag(args.SlotFlags)) + return; + + EnsureComp(args.Equipee); + } + + private void OnUnequipped(EntityUid uid, ShowEtherealComponent component, GotUnequippedEvent args) + { + RemComp(args.Equipee); + } + + private void Toggle(EntityUid uid, bool toggle) + { + if (!TryComp(uid, out var eye)) + return; + + if (toggle) + { + _eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) (VisibilityFlags.Ethereal), eye); + return; + } + else if (HasComp(uid)) + return; + + _eye.SetVisibilityMask(uid, (int) VisibilityFlags.Normal, eye); + } + + private void OnInteractionAttempt(EntityUid uid, ShowEtherealComponent component, InteractionAttemptEvent args) + { + if (HasComp(uid) + || !HasComp(args.Target)) + return; + + args.Cancel(); + if (_gameTiming.InPrediction) + return; + + _popup.PopupEntity(Loc.GetString("ethereal-pickup-fail"), args.Target.Value, uid); + } + + private void OnAttackAttempt(EntityUid uid, ShowEtherealComponent component, AttackAttemptEvent args) + { + if (HasComp(uid) + || !HasComp(args.Target)) + return; + + args.Cancel(); + } +} \ No newline at end of file diff --git a/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs b/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs index 7ef98a152c2..9184460153b 100644 --- a/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs +++ b/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs @@ -76,6 +76,10 @@ public partial record struct AnomalyPowerSettings() { public string PowerName; + public float ManaCost; + + public bool CheckInsulation; + /// /// When casting above the Supercritical Threshold, if not 0, this will cause all powers to enter cooldown for the given duration. /// diff --git a/Content.Shared/Actions/Events/DarkSwapActionEvent.cs b/Content.Shared/Actions/Events/DarkSwapActionEvent.cs new file mode 100644 index 00000000000..ad60d0ede45 --- /dev/null +++ b/Content.Shared/Actions/Events/DarkSwapActionEvent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Actions.Events; +public sealed partial class DarkSwapActionEvent : InstantActionEvent +{ + [DataField] + public float ManaCost; + + [DataField] + public bool CheckInsulation; +} \ No newline at end of file diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 0808114b9d5..bd8c1dbe257 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -71,6 +71,7 @@ public enum AlertType : byte BorgCrit, BorgDead, Offer, + ShadowkinPower, Deflecting, } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 679b99524ed..9f972b656d7 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1994,6 +1994,12 @@ public static readonly CVarDef public static readonly CVarDef ICShowSSDIndicator = CVarDef.Create("ic.show_ssd_indicator", true, CVar.CLIENTONLY); + /// + /// Allow Ethereal Ent to PassThrough Walls/Objects while in Ethereal. + /// + public static readonly CVarDef EtherealPassThrough = + CVarDef.Create("ic.EtherealPassThrough", false, CVar.SERVER); + /* * Salvage */ diff --git a/Content.Shared/Eye/VisibilityFlags.cs b/Content.Shared/Eye/VisibilityFlags.cs index 7e2dd33d7d4..2e20b1de4f3 100644 --- a/Content.Shared/Eye/VisibilityFlags.cs +++ b/Content.Shared/Eye/VisibilityFlags.cs @@ -6,10 +6,11 @@ namespace Content.Shared.Eye [FlagsFor(typeof(VisibilityMaskLayer))] public enum VisibilityFlags : int { - None = 0, + None = 0, Normal = 1 << 0, - Ghost = 1 << 1, + Ghost = 1 << 1, PsionicInvisibility = 1 << 2, //Nyano - Summary: adds Psionic Invisibility as a visibility layer. Currently does nothing. - TelegnosticProjection = 5, + TelegnosticProjection = 5, + Ethereal = 1 << 3, } } diff --git a/Content.Shared/Humanoid/EyeColor.cs b/Content.Shared/Humanoid/EyeColor.cs new file mode 100644 index 00000000000..a39e090a86c --- /dev/null +++ b/Content.Shared/Humanoid/EyeColor.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Humanoid; + +[ByRefEvent] +public record struct EyeColorInitEvent(); \ No newline at end of file diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index dc1e6b736c2..a1e8bec2cd8 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown; using Robust.Shared.Utility; @@ -104,6 +105,12 @@ private void OnExamined(EntityUid uid, HumanoidAppearanceComponent component, Ex var identity = Identity.Entity(uid, EntityManager); var species = GetSpeciesRepresentation(component.Species, component.CustomSpecieName).ToLower(); var age = GetAgeRepresentation(component.Species, component.Age); + if (HasComp(uid)) + { + var color = component.EyeColor.Name(); + if (color != null) + age = Loc.GetString("identity-eye-shadowkin", ("color", color)); + } args.PushText(Loc.GetString("humanoid-appearance-component-examine", ("user", identity), ("age", age), ("species", species))); } @@ -362,6 +369,8 @@ public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, SetSpecies(uid, profile.Species, false, humanoid); SetSex(uid, profile.Sex, false, humanoid); humanoid.EyeColor = profile.Appearance.EyeColor; + var ev = new EyeColorInitEvent(); + RaiseLocalEvent(uid, ref ev); SetSkinColor(uid, profile.Appearance.SkinColor, false); diff --git a/Content.Shared/Language/LanguagePrototype.cs b/Content.Shared/Language/LanguagePrototype.cs index 2137e4e838f..3b2b24c4b25 100644 --- a/Content.Shared/Language/LanguagePrototype.cs +++ b/Content.Shared/Language/LanguagePrototype.cs @@ -53,6 +53,14 @@ public sealed partial class SpeechOverrideInfo [DataField] public bool AllowRadio = true; + /// + /// If true, the message will be relayed to the Empathy Chat and + /// anyone with that language will also hear Empathy Chat. (Unless user has ShadowkinBlackeyeComponent) + /// This is mostly only use for "Marish" but... fuckit modularity :p + /// + [DataField] + public bool EmpathySpeech = false; + /// /// If false, the entity can use this language even when it's unable to speak (i.e. muffled or muted), /// and accents are not applied to messages in this language. diff --git a/Content.Shared/Psionics/PsionicComponent.cs b/Content.Shared/Psionics/PsionicComponent.cs index 37d0a9a7ef4..5a3cce19ad3 100644 --- a/Content.Shared/Psionics/PsionicComponent.cs +++ b/Content.Shared/Psionics/PsionicComponent.cs @@ -5,9 +5,38 @@ namespace Content.Shared.Abilities.Psionics { - [RegisterComponent, NetworkedComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class PsionicComponent : Component { + /// + /// Current Mana. + /// + [DataField, AutoNetworkedField] + public float Mana; + + /// + /// Max Mana Possible. + /// + [DataField, AutoNetworkedField] + public float MaxMana = 100; + + /// + /// How much energy is gained per second. + /// + [DataField] + public float ManaGain = 1; + + /// + /// ManaGain Multiplier + /// + [DataField] + public float ManaGainMultiplier = 1; + + public float ManaAccumulator; + + [DataField] + public bool BypassManaCheck; + /// /// How close a Psion is to generating a new power. When Potentia reaches the NextPowerCost, it is "Spent" in order to "Buy" a random new power. /// TODO: Psi-Potentiometry should be able to read how much Potentia a person has. @@ -45,6 +74,10 @@ public sealed partial class PsionicComponent : Component public string MindbreakingStutterAccent = "StutteringAccent"; + /// + /// The message feedback given on mindbreak. + /// + [DataField] public string MindbreakingFeedback = "mindbreaking-feedback"; /// @@ -163,6 +196,7 @@ private set [ViewVariables(VVAccess.ReadWrite)] public int PowerSlotsTaken; + /// /// List of descriptors this entity will bring up for psychognomy. Used to remove /// unneccesary subs for unique psionic entities like e.g. Oracle. /// @@ -176,5 +210,8 @@ private set /// Popup to play if a Psion attempts to start casting a power while already casting one [DataField] public string AlreadyCasting = "already-casting"; + + /// Popup to play if there no Mana left for a power to execute. + public string NoMana = "no-mana"; } } diff --git a/Content.Shared/Psionics/PsionicEvents.cs b/Content.Shared/Psionics/PsionicEvents.cs index be3bf03af62..4d134417988 100644 --- a/Content.Shared/Psionics/PsionicEvents.cs +++ b/Content.Shared/Psionics/PsionicEvents.cs @@ -8,4 +8,10 @@ namespace Content.Shared.Psionics; /// /// [ByRefEvent] -public record struct OnSetPsionicStatsEvent(float AmplificationChangedAmount, float DampeningChangedAmount); \ No newline at end of file +public record struct OnSetPsionicStatsEvent(float AmplificationChangedAmount, float DampeningChangedAmount); + +[ByRefEvent] +public record struct OnMindbreakEvent(); + +[ByRefEvent] +public record struct OnManaUpdateEvent(); \ No newline at end of file diff --git a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs index 27928645684..b79dabbc416 100644 --- a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs +++ b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs @@ -1,9 +1,13 @@ using Content.Shared.Administration.Logs; using Content.Shared.Contests; using Content.Shared.Popups; +using Content.Shared.Psionics; using Content.Shared.Psionics.Glimmer; using Robust.Shared.Random; using Robust.Shared.Serialization; +using Content.Shared.Mobs.Systems; +using Content.Shared.FixedPoint; +using Content.Shared.Rejuvenate; namespace Content.Shared.Abilities.Psionics { @@ -15,11 +19,54 @@ public sealed class SharedPsionicAbilitiesSystem : EntitySystem [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ContestsSystem _contests = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnRejuvenate); + } + + public bool OnAttemptPowerUse(EntityUid uid, string power, float? manacost = null, bool checkInsulation = true) + { + if (!TryComp(uid, out var component) + || HasComp(uid) + || checkInsulation + && HasComp(uid)) + return false; + + var tev = new OnAttemptPowerUseEvent(uid, power); + RaiseLocalEvent(uid, tev); + + if (tev.Cancelled) + return false; + + if (component.DoAfter is not null) + { + _popups.PopupEntity(Loc.GetString(component.AlreadyCasting), uid, uid, PopupType.LargeCaution); + return false; + } + + if (manacost is null) + return true; + + if (component.Mana >= manacost + || component.BypassManaCheck) + { + var newmana = component.Mana - manacost; + component.Mana = newmana ?? component.Mana; + + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + else + { + _popups.PopupEntity(Loc.GetString(component.NoMana), uid, uid, PopupType.LargeCaution); + return false; + } + + return true; } private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicPowerUsedEvent args) @@ -85,6 +132,45 @@ public float ModifiedDampening(EntityUid uid, PsionicComponent component) { return component.CurrentDampening / _contests.MoodContest(uid, true); } + + public void OnRejuvenate(EntityUid uid, PsionicComponent component, RejuvenateEvent args) + { + component.Mana = component.MaxMana; + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (_mobState.IsDead(uid)) + continue; + + component.ManaAccumulator += frameTime; + + if (component.ManaAccumulator <= 1) + continue; + + component.ManaAccumulator -= 1; + + if (component.Mana > component.MaxMana) + component.Mana = component.MaxMana; + + if (component.Mana < component.MaxMana) + { + var gainedmana = component.ManaGain * component.ManaGainMultiplier; + component.Mana += gainedmana; + FixedPoint2.Min(component.Mana, component.MaxMana); + + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + } + } } public sealed class PsionicPowerUsedEvent : HandledEntityEventArgs @@ -99,6 +185,18 @@ public PsionicPowerUsedEvent(EntityUid user, string power) } } + public sealed class OnAttemptPowerUseEvent : CancellableEntityEventArgs + { + public EntityUid User { get; } + public string Power = string.Empty; + + public OnAttemptPowerUseEvent(EntityUid user, string power) + { + User = user; + Power = power; + } + } + [Serializable] [NetSerializable] public sealed class PsionicsChangedEvent : EntityEventArgs diff --git a/Content.Shared/Shadowkin/EtherealComponent.cs b/Content.Shared/Shadowkin/EtherealComponent.cs new file mode 100644 index 00000000000..0fc50c0f12e --- /dev/null +++ b/Content.Shared/Shadowkin/EtherealComponent.cs @@ -0,0 +1,36 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent, NetworkedComponent] +public sealed partial class EtherealComponent : Component +{ + /// + /// Does the Ent, Dark lights around it? + /// + [DataField] + public bool Darken = false; + + /// + /// Range of the Darken Effect. + /// + [DataField] + public float DarkenRange = 5; + + /// + /// Darken Effect Rate. + /// + [DataField] + public float DarkenRate = 0.084f; + + public List DarkenedLights = new(); + + public float DarkenAccumulator; + + public int OldMobMask; + + public int OldMobLayer; + + public List SuppressedFactions = new(); + public bool HasDoorBumpTag; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/EtherealStunItemComponent.cs b/Content.Shared/Shadowkin/EtherealStunItemComponent.cs new file mode 100644 index 00000000000..053b5c11f67 --- /dev/null +++ b/Content.Shared/Shadowkin/EtherealStunItemComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class EtherealStunItemComponent : Component +{ + [DataField] + public float Radius = 10; + + [DataField] + public bool DeleteOnUse = true; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/ShadowkinComponent.cs b/Content.Shared/Shadowkin/ShadowkinComponent.cs new file mode 100644 index 00000000000..b382f3112b7 --- /dev/null +++ b/Content.Shared/Shadowkin/ShadowkinComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ShadowkinComponent : Component +{ + /// + /// Apply the SleepManaRegenMultiplier on SleepComponent if true. + /// + [DataField] + public bool SleepManaRegen = true; + + /// + /// What do edit the ManaRegenMultiplier when on Sleep. + /// + [DataField] + public float SleepManaRegenMultiplier = 4; + + /// + /// On MapInitEvent, will Blackeye the Shadowkin. + /// + [DataField] + public bool BlackeyeSpawn; + + /// + /// If mana is equal or lower then this value, blackeye the shadowkin. + /// + [DataField] + public float BlackEyeMana; + + /// + /// Set the Black-Eye Color. + /// + [DataField] + public Color BlackEyeColor = Color.Black; + + public Color OldEyeColor = Color.LimeGreen; + + [DataField] + public EntityUid? ShadowkinSleepAction; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs b/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs new file mode 100644 index 00000000000..b4c62d66647 --- /dev/null +++ b/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class ShadowkinCuffComponent : Component { } \ No newline at end of file diff --git a/Content.Shared/Shadowkin/SharedEtherealSystem.cs b/Content.Shared/Shadowkin/SharedEtherealSystem.cs new file mode 100644 index 00000000000..66196faf0a3 --- /dev/null +++ b/Content.Shared/Shadowkin/SharedEtherealSystem.cs @@ -0,0 +1,141 @@ +using Content.Shared.Physics; +using Robust.Shared.Physics; +using System.Linq; +using Robust.Shared.Physics.Systems; +using Content.Shared.Interaction.Events; +using Robust.Shared.Timing; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.CombatMode.Pacification; +using Content.Shared.Psionics; +using Content.Shared.Mobs; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Tag; + +namespace Content.Shared.Shadowkin; + +public abstract class SharedEtherealSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly TagSystem _tag = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnAttemptPowerUse); + SubscribeLocalEvent(OnAttackAttempt); + SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnMindbreak); + SubscribeLocalEvent(OnMobStateChanged); + } + + public virtual void OnStartup(EntityUid uid, EtherealComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var fixtures)) + return; + + var fixture = fixtures.Fixtures.First(); + + component.OldMobMask = fixture.Value.CollisionMask; + component.OldMobLayer = fixture.Value.CollisionLayer; + + if (_cfg.GetCVar(CCVars.EtherealPassThrough)) + { + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) CollisionGroup.GhostImpassable, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, 0, fixtures); + + if (_tag.RemoveTag(uid, "DoorBumpOpener")) + component.HasDoorBumpTag = true; + + return; + } + + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobMask, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures); + } + + public virtual void OnShutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + if (!TryComp(uid, out var fixtures)) + return; + + var fixture = fixtures.Fixtures.First(); + + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, component.OldMobMask, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, component.OldMobLayer, fixtures); + + if (component.HasDoorBumpTag) + _tag.AddTag(uid, "DoorBumpOpener"); + } + + private void OnMindbreak(EntityUid uid, EtherealComponent component, ref OnMindbreakEvent args) + { + RemComp(uid, component); + } + + private void OnMobStateChanged(EntityUid uid, EtherealComponent component, MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Critical + || args.NewMobState == MobState.Dead) + RemComp(uid, component); + } + + private void OnShootAttempt(Entity ent, ref ShotAttemptedEvent args) + { + args.Cancel(); + } + + private void OnAttackAttempt(EntityUid uid, EtherealComponent component, AttackAttemptEvent args) + { + if (HasComp(args.Target)) + return; + + args.Cancel(); + } + + private void OnBeforeThrow(Entity ent, ref BeforeThrowEvent args) + { + var thrownItem = args.ItemUid; + + // Raise an AttemptPacifiedThrow event and rely on other systems to check + // whether the candidate item is OK to throw: + var ev = new AttemptPacifiedThrowEvent(thrownItem, ent); + RaiseLocalEvent(thrownItem, ref ev); + if (!ev.Cancelled) + return; + + args.Cancelled = true; + } + + private void OnInteractionAttempt(EntityUid uid, EtherealComponent component, InteractionAttemptEvent args) + { + if (!HasComp(args.Target) + || HasComp(args.Target)) + return; + + args.Cancel(); + if (_gameTiming.InPrediction) + return; + + _popup.PopupEntity(Loc.GetString("ethereal-pickup-fail"), args.Target.Value, uid); + } + + private void OnAttemptPowerUse(EntityUid uid, EtherealComponent component, OnAttemptPowerUseEvent args) + { + if (args.Power == "DarkSwap") + return; + + args.Cancel(); + } +} diff --git a/Content.Shared/Shadowkin/ShowEtherealComponent.cs b/Content.Shared/Shadowkin/ShowEtherealComponent.cs new file mode 100644 index 00000000000..45fa78fa0c4 --- /dev/null +++ b/Content.Shared/Shadowkin/ShowEtherealComponent.cs @@ -0,0 +1,6 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class ShowEtherealComponent : Component { } \ No newline at end of file diff --git a/Resources/Audio/Effects/Shadowkin/Powers/darkswapoff.ogg b/Resources/Audio/Effects/Shadowkin/darkswapoff.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/darkswapoff.ogg rename to Resources/Audio/Effects/Shadowkin/darkswapoff.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/darkswapon.ogg b/Resources/Audio/Effects/Shadowkin/darkswapon.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/darkswapon.ogg rename to Resources/Audio/Effects/Shadowkin/darkswapon.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/futuristic-teleport.ogg b/Resources/Audio/Effects/Shadowkin/futuristic-teleport.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/futuristic-teleport.ogg rename to Resources/Audio/Effects/Shadowkin/futuristic-teleport.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/license.txt b/Resources/Audio/Effects/Shadowkin/license.txt similarity index 75% rename from Resources/Audio/Effects/Shadowkin/Powers/license.txt rename to Resources/Audio/Effects/Shadowkin/license.txt index c77ea8eb098..d87bd10983a 100644 --- a/Resources/Audio/Effects/Shadowkin/Powers/license.txt +++ b/Resources/Audio/Effects/Shadowkin/license.txt @@ -1,4 +1,4 @@ darkswapon.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ darkswapoff.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ futuristic-teleport.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ -teleport.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ +shadeskip.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ diff --git a/Resources/Audio/Effects/Shadowkin/Powers/teleport.ogg b/Resources/Audio/Effects/Shadowkin/shadeskip.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/teleport.ogg rename to Resources/Audio/Effects/Shadowkin/shadeskip.ogg diff --git a/Resources/Audio/Voice/Shadowkin/attributions.yml b/Resources/Audio/Voice/Shadowkin/attributions.yml new file mode 100644 index 00000000000..4ab746f4659 --- /dev/null +++ b/Resources/Audio/Voice/Shadowkin/attributions.yml @@ -0,0 +1,4 @@ +- files: ["wurble.ogg", "mar.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from CHOMPStation" + source: "https://github.com/CHOMPStation2/CHOMPStation2" \ No newline at end of file diff --git a/Resources/Audio/Voice/Shadowkin/mar.ogg b/Resources/Audio/Voice/Shadowkin/mar.ogg new file mode 100644 index 00000000000..b13d2df8377 Binary files /dev/null and b/Resources/Audio/Voice/Shadowkin/mar.ogg differ diff --git a/Resources/Audio/Voice/Shadowkin/wurble.ogg b/Resources/Audio/Voice/Shadowkin/wurble.ogg new file mode 100644 index 00000000000..859c9df3530 Binary files /dev/null and b/Resources/Audio/Voice/Shadowkin/wurble.ogg differ diff --git a/Resources/Fonts/Lymphatic.ttf b/Resources/Fonts/Lymphatic.ttf new file mode 100644 index 00000000000..36beef04b1e Binary files /dev/null and b/Resources/Fonts/Lymphatic.ttf differ diff --git a/Resources/Locale/en-US/actions/actions/shadowkin.ftl b/Resources/Locale/en-US/actions/actions/shadowkin.ftl new file mode 100644 index 00000000000..063e1eafb74 --- /dev/null +++ b/Resources/Locale/en-US/actions/actions/shadowkin.ftl @@ -0,0 +1,2 @@ +action-name-shadowkin-rest = Rest +action-description-shadowkin-rest = Rama diff --git a/Resources/Locale/en-US/alerts/shadowkin.ftl b/Resources/Locale/en-US/alerts/shadowkin.ftl new file mode 100644 index 00000000000..10f8438b76c --- /dev/null +++ b/Resources/Locale/en-US/alerts/shadowkin.ftl @@ -0,0 +1,2 @@ +alerts-shadowkin-power-name = Power Level +alerts-shadowkin-power-desc = How much energy is available to spend on Shadowkin powers. \ No newline at end of file diff --git a/Resources/Locale/en-US/chat/emotes.ftl b/Resources/Locale/en-US/chat/emotes.ftl index e95cb2795db..4e26752c4b4 100644 --- a/Resources/Locale/en-US/chat/emotes.ftl +++ b/Resources/Locale/en-US/chat/emotes.ftl @@ -28,6 +28,8 @@ chat-emote-name-monkeyscreeches = Monkey Screeches chat-emote-name-robotbeep = Robot chat-emote-name-yawn = Yawn chat-emote-name-snore = Snore +chat-emote-name-mars = Mars +chat-emote-name-wurble = Wurble # Message chat-emote-msg-scream = screams! diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 034f38f854c..e25522ac1df 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -50,6 +50,8 @@ chat-manager-admin-channel-name = ADMIN chat-manager-rate-limited = You are sending messages too quickly! chat-manager-rate-limit-admin-announcement = Player { $player } breached chat rate limits. Watch them if this is a regular occurence. +chat-manager-send-empathy-chat-wrap-message = {$source}: {$message} + ## Speech verbs for chat chat-speech-verb-suffix-exclamation = ! @@ -156,3 +158,5 @@ chat-speech-verb-name-electricity = Electricity chat-speech-verb-electricity-1 = crackles chat-speech-verb-electricity-2 = buzzes chat-speech-verb-electricity-3 = screeches + +chat-speech-verb-marish = Mars \ No newline at end of file diff --git a/Resources/Locale/en-US/chat/ui/chat-box.ftl b/Resources/Locale/en-US/chat/ui/chat-box.ftl index 720f0d15ab4..0dbfc0a27b1 100644 --- a/Resources/Locale/en-US/chat/ui/chat-box.ftl +++ b/Resources/Locale/en-US/chat/ui/chat-box.ftl @@ -30,4 +30,4 @@ hud-chatbox-channel-Notifications = Notifications hud-chatbox-channel-Server = Server hud-chatbox-channel-Visual = Actions hud-chatbox-channel-Damage = Damage -hud-chatbox-channel-Unspecified = Unspecified +hud-chatbox-channel-Unspecified = Unspecified \ No newline at end of file diff --git a/Resources/Locale/en-US/language/languages.ftl b/Resources/Locale/en-US/language/languages.ftl index 02527498dbd..76d172658b1 100644 --- a/Resources/Locale/en-US/language/languages.ftl +++ b/Resources/Locale/en-US/language/languages.ftl @@ -61,6 +61,9 @@ language-RobotTalk-description = A language consisting of harsh binary chirps, w language-Sign-name = Tau-Ceti Basic Sign Language language-Sign-description = TCB-SL for short, this sign language is prevalent among mute and deaf people. +language-Marish-name = Marish +language-Marish-description = An Language that can be used to speak in Empathy, Sharing eachother emotions with only one word, Shadowkins speaks this language with ease, tho its is nearly impossible to replicate it or learn it. + language-ValyrianStandard-name = Valyrian Standard language-ValyrianStandard-description = A language descended from eastern european languages of old earth - Valyrian Standard is the commonly spoken tongue of Harpies brought up on their homeworld of Valyrian 4b diff --git a/Resources/Locale/en-US/markings/shadowkin.ftl b/Resources/Locale/en-US/markings/shadowkin.ftl new file mode 100644 index 00000000000..5ad1f09963c --- /dev/null +++ b/Resources/Locale/en-US/markings/shadowkin.ftl @@ -0,0 +1,7 @@ +marking-EyesShadowkin = Shadowkin + +marking-TailShadowkin = Shadowkin +marking-TailShadowkinBig = Shadowkin (Big) +marking-TailShadowkinShorter = Shadowkin (Short) +marking-TailShadowkinMedium = Shadowkin (Medium) +marking-TailShadowkinBigFluff = Shadowkin (Big and Fluffy) \ No newline at end of file diff --git a/Resources/Locale/en-US/metabolism/metabolizer-types.ftl b/Resources/Locale/en-US/metabolism/metabolizer-types.ftl index 30ab6c050e2..d0f57e2bc0b 100644 --- a/Resources/Locale/en-US/metabolism/metabolizer-types.ftl +++ b/Resources/Locale/en-US/metabolism/metabolizer-types.ftl @@ -11,3 +11,4 @@ metabolizer-type-moth = Moth metabolizer-type-arachnid = Arachnid metabolizer-type-vampiric = Vampiric metabolizer-type-liquorlifeline = Liquor Lifeline +metabolizer-type-shadowkin = Shadowkin diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index 37482a41b65..f9954d7b001 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -1,5 +1,6 @@ generic-power-initialization-feedback = I Awaken. already-casting = I cannot channel more than one power at a time. +no-mana = I cannot channel enough power. # Dispel dispel-power-description = Dispel summoned entities such as familiars or forcewalls. @@ -139,3 +140,12 @@ examine-mindbroken-message = psionic-roll-failed = For a moment, my consciousness expands, yet I feel that it is not enough. entity-anomaly-no-grid = There is nowhere for me to conjure beings. power-overwhelming-power-feedback = {CAPITALIZE($entity)} wields a vast connection to the noösphere + +# Shadowkin ShadeSkip +action-description-shadowkin-shadeskip = Aaramrra! + +# DarkSwap +action-name-darkswap = DarkSwap +action-description-darkswap = Mmra Mamm! + +ethereal-pickup-fail = My hand sizzles as it passes through... \ No newline at end of file diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 684a08dd9ae..fe7293d8481 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -43,6 +43,7 @@ research-technology-basic-xenoarcheology = Basic XenoArcheology research-technology-alternative-research = Alternative Research research-technology-magnets-tech = Localized Magnetism research-technology-advanced-parts = Advanced Parts +research-technology-advanced-bluespace = Advanced Bluespace Research research-technology-anomaly-harnessing = Anomaly Core Harnessing research-technology-grappling = Grappling research-technology-abnormal-artifact-manipulation = Artifact Recycling diff --git a/Resources/Locale/en-US/species/shadowkin.ftl b/Resources/Locale/en-US/species/shadowkin.ftl new file mode 100644 index 00000000000..ebc56487b7a --- /dev/null +++ b/Resources/Locale/en-US/species/shadowkin.ftl @@ -0,0 +1,15 @@ +shadowkin-power-examined-other = {CAPITALIZE(SUBJECT($target))} seems to be {$powerType}. +shadowkin-power-examined-self = I have {$power}/{$powerMax} energy, I am {$powerType}. + +shadowkin-power-5 = energetic +shadowkin-power-4 = great +shadowkin-power-3 = good +shadowkin-power-2 = okay +shadowkin-power-1 = exhausted +shadowkin-power-0 = drained + +examine-mindbroken-shadowkin-message = {CAPITALIZE($entity)} seems to be a blackeye. + +identity-eye-shadowkin = {$color}-eye + +shadowkin-blackeye = I feel my power draining away... \ No newline at end of file diff --git a/Resources/Locale/en-US/species/species.ftl b/Resources/Locale/en-US/species/species.ftl index 6c40c454040..9278267cc44 100644 --- a/Resources/Locale/en-US/species/species.ftl +++ b/Resources/Locale/en-US/species/species.ftl @@ -11,3 +11,4 @@ species-name-moth = Moth Person species-name-skeleton = Skeleton species-name-vox = Vox species-name-ipc = IPC +species-name-shadowkin = Shadowkin \ No newline at end of file diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index cfd53bbde33..d5c993e2994 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -400,3 +400,6 @@ trait-description-CyberEyesOmni = This expensive implant provides the combined benefits of a SecHud, MedHud, and a DiagHud. Note that this augmentation is considered Contraband for anyone not under the employ of station Security personel, and may be disabled by your employer before dispatch to the station. + +trait-name-ShadowkinBlackeye = Blackeye +trait-description-ShadowkinBlackeye = You lose your special Shadowkin powers, in return for some points. diff --git a/Resources/Prototypes/Actions/psionics.yml b/Resources/Prototypes/Actions/psionics.yml index 138823313aa..70f78460885 100644 --- a/Resources/Prototypes/Actions/psionics.yml +++ b/Resources/Prototypes/Actions/psionics.yml @@ -4,18 +4,18 @@ description: action-description-dispel noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/dispel.png - useDelay: 45 - checkCanAccess: false - range: 6 - itemIconStyle: BigAction - canTargetSelf: false - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:DispelPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/dispel.png + useDelay: 45 + checkCanAccess: false + range: 6 + itemIconStyle: BigAction + canTargetSelf: false + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:DispelPowerActionEvent - type: entity id: ActionMassSleep @@ -23,13 +23,13 @@ description: action-description-mass-sleep noSpawn: true components: - - type: WorldTargetAction - icon: Interface/VerbIcons/mass_sleep.png - useDelay: 60 - checkCanAccess: false - range: 8 - itemIconStyle: BigAction - event: !type:MassSleepPowerActionEvent + - type: WorldTargetAction + icon: Interface/VerbIcons/mass_sleep.png + useDelay: 60 + checkCanAccess: false + range: 8 + itemIconStyle: BigAction + event: !type:MassSleepPowerActionEvent - type: entity id: ActionMindSwap @@ -37,17 +37,17 @@ description: action-description-mind-swap noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/mind_swap.png - useDelay: 240 - checkCanAccess: false - range: 8 - itemIconStyle: BigAction - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:MindSwapPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/mind_swap.png + useDelay: 240 + checkCanAccess: false + range: 8 + itemIconStyle: BigAction + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:MindSwapPowerActionEvent - type: entity id: ActionMindSwapReturn @@ -55,11 +55,11 @@ description: action-description-mind-swap-return noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/mind_swap_return.png - useDelay: 20 - checkCanInteract: false - event: !type:MindSwapPowerReturnActionEvent + - type: InstantAction + icon: Interface/VerbIcons/mind_swap_return.png + useDelay: 20 + checkCanInteract: false + event: !type:MindSwapPowerReturnActionEvent - type: entity id: ActionNoosphericZap @@ -67,16 +67,16 @@ description: action-description-noospheric-zap noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/noospheric_zap.png - useDelay: 100 - range: 5 - itemIconStyle: BigAction - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:NoosphericZapPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/noospheric_zap.png + useDelay: 100 + range: 5 + itemIconStyle: BigAction + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:NoosphericZapPowerActionEvent - type: entity id: ActionPyrokinesis @@ -84,13 +84,13 @@ description: action-description-pyrokinesis noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/pyrokinesis.png - useDelay: 50 - range: 6 - checkCanAccess: false - itemIconStyle: BigAction - event: !type:PyrokinesisPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/pyrokinesis.png + useDelay: 50 + range: 6 + checkCanAccess: false + itemIconStyle: BigAction + event: !type:PyrokinesisPowerActionEvent - type: entity id: ActionMetapsionic @@ -98,10 +98,10 @@ description: action-description-metapsionic noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/metapsionic.png - useDelay: 45 - event: !type:MetapsionicPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/metapsionic.png + useDelay: 45 + event: !type:MetapsionicPowerActionEvent - type: entity id: ActionPsionicRegeneration @@ -109,10 +109,10 @@ description: action-description-psionic-regeneration noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_regeneration.png - useDelay: 120 - event: !type:PsionicRegenerationPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_regeneration.png + useDelay: 120 + event: !type:PsionicRegenerationPowerActionEvent - type: entity id: ActionTelegnosis @@ -120,10 +120,10 @@ description: action-description-telegnosis noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/telegnosis.png - useDelay: 150 - event: !type:TelegnosisPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/telegnosis.png + useDelay: 150 + event: !type:TelegnosisPowerActionEvent - type: entity id: ActionPsionicInvisibility @@ -131,10 +131,10 @@ description: action-description-psionic-invisibility noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_invisibility.png - useDelay: 120 - event: !type:PsionicInvisibilityPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_invisibility.png + useDelay: 120 + event: !type:PsionicInvisibilityPowerActionEvent - type: entity id: ActionPsionicInvisibilityUsed @@ -142,9 +142,9 @@ description: action-description-psionic-invisibility-off noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_invisibility_off.png - event: !type:RemovePsionicInvisibilityOffPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_invisibility_off.png + event: !type:RemovePsionicInvisibilityOffPowerActionEvent - type: entity id: ActionHealingWord @@ -152,36 +152,36 @@ description: action-description-healing-word noSpawn: true components: - - type: EntityTargetAction - icon: { sprite : Interface/Actions/psionics.rsi, state: healing_word } - useDelay: 10 - checkCanAccess: false - range: 6 - itemIconStyle: BigAction - canTargetSelf: true - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:PsionicHealOtherPowerActionEvent - healingAmount: - groups: # These all get divided by the number of damage types in the group. So they're all -2.5. - Genetic: -2.5 - Toxin: -5 - Airloss: -5 - Brute: -7.5 - Burn: -10 - rotReduction: 10 - useDelay: 1 - doRevive: true - powerName: Healing Word - popupText: healing-word-begin - playSound: true - minGlimmer: 2 - maxGlimmer: 4 - glimmerSoundThreshold: 100 - glimmerPopupThreshold: 200 - glimmerDoAfterVisibilityThreshold: 70 + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: healing_word } + useDelay: 10 + checkCanAccess: false + range: 6 + itemIconStyle: BigAction + canTargetSelf: true + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + groups: # These all get divided by the number of damage types in the group. So they're all -2.5. + Genetic: -2.5 + Toxin: -5 + Airloss: -5 + Brute: -7.5 + Burn: -10 + rotReduction: 10 + useDelay: 1 + doRevive: true + powerName: Healing Word + popupText: healing-word-begin + playSound: true + minGlimmer: 2 + maxGlimmer: 4 + glimmerSoundThreshold: 100 + glimmerPopupThreshold: 200 + glimmerDoAfterVisibilityThreshold: 70 - type: entity id: ActionRevivify @@ -189,38 +189,38 @@ description: action-description-revivify noSpawn: true components: - - type: EntityTargetAction - icon: { sprite : Interface/Actions/psionics.rsi, state: revivify } - useDelay: 120 - checkCanAccess: false - range: 2 - itemIconStyle: BigAction - canTargetSelf: false - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:PsionicHealOtherPowerActionEvent - healingAmount: - # These all get divided by the number of damage types in the group. So they're all -15 - # Additionally, they're multiplied by the caster's Amplification, which, - # assuming this is the only power they have, the multiplier is between 2.9-3.9 - groups: - Genetic: -15 - Toxin: -30 - Airloss: -60 # Except airloss, which heals 30 per type - Brute: -45 - Burn: -60 - rotReduction: 60 - doRevive: true - powerName: Revivify - popupText: revivify-begin - playSound: true - minGlimmer: 10 # These also get multiplied by caster stats. So, - maxGlimmer: 15 # keeping in mind the ~3.5x multiplier, this spikes glimmer by as much as 60 points. - glimmerSoundThreshold: 50 - glimmerPopupThreshold: 100 - glimmerDoAfterVisibilityThreshold: 35 + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: revivify } + useDelay: 120 + checkCanAccess: false + range: 2 + itemIconStyle: BigAction + canTargetSelf: false + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + # These all get divided by the number of damage types in the group. So they're all -15 + # Additionally, they're multiplied by the caster's Amplification, which, + # assuming this is the only power they have, the multiplier is between 2.9-3.9 + groups: + Genetic: -15 + Toxin: -30 + Airloss: -60 # Except airloss, which heals 30 per type + Brute: -45 + Burn: -60 + rotReduction: 60 + doRevive: true + powerName: Revivify + popupText: revivify-begin + playSound: true + minGlimmer: 10 # These also get multiplied by caster stats. So, + maxGlimmer: 15 # keeping in mind the ~3.5x multiplier, this spikes glimmer by as much as 60 points. + glimmerSoundThreshold: 50 + glimmerPopupThreshold: 100 + glimmerDoAfterVisibilityThreshold: 35 - type: entity id: ActionShadeskip @@ -263,30 +263,78 @@ description: action-description-telekinetic-pulse noSpawn: true components: - - type: InstantAction - icon: { sprite : Interface/Actions/psionics.rsi, state: telekinetic_pulse } - useDelay: 45 - checkCanInteract: false - event: !type:AnomalyPowerActionEvent - settings: - powerName: "Telekinetic Pulse" - overchargeFeedback: "shadeskip-overcharge-feedback" # The text behind this is fine. - overchargeCooldown: 120 - overchargeRecoil: - groups: - Burn: -100 #This will be divided by the caster's Dampening. - minGlimmer: 6 - maxGlimmer: 8 - doSupercritical: false - entitySpawnEntries: - - settings: - spawnOnPulse: true - minAmount: 1 - maxAmount: 1 - maxRange: 0.5 - spawns: - - EffectFlashTelekineticPulse - gravity: - maxThrowRange: 3 - maxThrowStrength: 5 - spaceRange: 3 + - type: InstantAction + icon: { sprite: Interface/Actions/psionics.rsi, state: telekinetic_pulse } + useDelay: 45 + checkCanInteract: false + event: !type:AnomalyPowerActionEvent + settings: + powerName: "Telekinetic Pulse" + overchargeFeedback: "shadeskip-overcharge-feedback" # The text behind this is fine. + overchargeCooldown: 120 + overchargeRecoil: + groups: + Burn: -100 #This will be divided by the caster's Dampening. + minGlimmer: 6 + maxGlimmer: 8 + doSupercritical: false + entitySpawnEntries: + - settings: + spawnOnPulse: true + minAmount: 1 + maxAmount: 1 + maxRange: 0.5 + spawns: + - EffectFlashTelekineticPulse + gravity: + maxThrowRange: 3 + maxThrowStrength: 5 + spaceRange: 3 + +- type: entity + id: ActionShadowkinShadeskip + name: action-name-shadeskip + description: action-description-shadowkin-shadeskip + noSpawn: true + components: + - type: InstantAction + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: shadeskip } + useDelay: 10 + checkCanInteract: false + event: !type:AnomalyPowerActionEvent + settings: + powerName: "Shadowkin-Shadeskip" + manaCost: 25 + checkInsulation: false + minGlimmer: 0 + maxGlimmer: 0 + doSupercritical: false + entitySpawnEntries: + - settings: + spawnOnPulse: true + minAmount: 5 + maxAmount: 10 + maxRange: 2.5 + spawns: + - ShadowkinShadow + - settings: + spawnOnPulse: true + minAmount: 1 + maxAmount: 1 + maxRange: 0.5 + spawns: + - EffectFlashShadowkinShadeskip + +- type: entity + id: ActionDarkSwap + name: action-name-darkswap + description: action-description-darkswap + noSpawn: true + components: + - type: InstantAction + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: darkswap } + useDelay: 10 + checkCanInteract: false + event: !type:DarkSwapActionEvent + manaCost: 100 + checkInsulation: false diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index f81def7f7c3..a7144cdda57 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -304,6 +304,18 @@ icon: { sprite: Clothing/Head/Hats/pyjamasyndicatered.rsi, state: icon } event: !type:SleepActionEvent +- type: entity + id: ShadowkinActionSleep + name: action-name-shadowkin-rest + description: action-description-shadowkin-rest + noSpawn: true + components: + - type: InstantAction + checkCanInteract: false + checkConsciousness: false + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: rest } + event: !type:SleepActionEvent + - type: entity id: ActionWake name: Wake up diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 1e0a8a8c626..bb0c47f48fb 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -7,6 +7,7 @@ - category: Health - category: Mood - category: Stamina + - alertType: ShadowkinPower - alertType: SuitPower - category: Internals - alertType: Fire diff --git a/Resources/Prototypes/Alerts/shadowkin.yml b/Resources/Prototypes/Alerts/shadowkin.yml new file mode 100644 index 00000000000..66d41351bab --- /dev/null +++ b/Resources/Prototypes/Alerts/shadowkin.yml @@ -0,0 +1,23 @@ +- type: alert + id: ShadowkinPower + icons: + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power0 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power1 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power2 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power3 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power4 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power5 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power6 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power7 + name: alerts-shadowkin-power-name + description: alerts-shadowkin-power-desc + minSeverity: 0 + maxSeverity: 7 \ No newline at end of file diff --git a/Resources/Prototypes/Body/Organs/shadowkin.yml b/Resources/Prototypes/Body/Organs/shadowkin.yml new file mode 100644 index 00000000000..695ddec1ab2 --- /dev/null +++ b/Resources/Prototypes/Body/Organs/shadowkin.yml @@ -0,0 +1,113 @@ +- type: entity + id: OrganShadowkinBrain + parent: OrganHumanBrain + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: brain + +- type: entity + id: OrganShadowkinEyes + parent: OrganHumanEyes + description: I see beyond anything you ever will! + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: eyes + +- type: entity + id: OrganShadowkinEars + parent: OrganHumanEars + description: Hey, listen! + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: ears + +- type: entity + id: OrganShadowkinTongue + parent: OrganHumanTongue + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: tongue + + +- type: entity + id: OrganShadowkinAppendix + parent: OrganHumanAppendix + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: appendix + + +- type: entity + id: OrganShadowkinHeart + parent: OrganHumanHeart + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: heart + - type: Metabolizer + maxReagents: 2 + metabolizerTypes: [Shadowkin] + groups: + - id: Medicine + - id: Poison + - id: Narcotic + +- type: entity + id: OrganShadowkinStomach + parent: OrganHumanStomach + description: '"Yummy!", says the stomach, although you are unable to hear it.' + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: stomach + - type: SolutionContainerManager + solutions: + stomach: + maxVol: 40 + food: + maxVol: 5 + reagents: + - ReagentId: UncookedAnimalProteins + Quantity: 5 + - type: Metabolizer + maxReagents: 3 + metabolizerTypes: [Shadowkin] + groups: + - id: Food + - id: Drink + +- type: entity + id: OrganShadowkinLiver + parent: OrganHumanLiver + description: "Live 'er? I hardly know 'er!" + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: liver + - type: Metabolizer + maxReagents: 1 + metabolizerTypes: [Shadowkin] + groups: + - id: Alcohol + rateModifier: 0.1 + +- type: entity + id: OrganShadowkinKidneys + parent: OrganHumanKidneys + description: Give the kid their knees back, please, this is the third time this week. + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: kidneys + - type: Metabolizer + maxReagents: 5 + metabolizerTypes: [Shadowkin] + removeEmpty: true \ No newline at end of file diff --git a/Resources/Prototypes/Body/Parts/shadowkin.yml b/Resources/Prototypes/Body/Parts/shadowkin.yml new file mode 100644 index 00000000000..0ddff93443c --- /dev/null +++ b/Resources/Prototypes/Body/Parts/shadowkin.yml @@ -0,0 +1,155 @@ +- type: entity + id: PartShadowkin + parent: BaseItem + name: "Shadowkin body part" + abstract: true + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Shadowkin/parts.rsi + - type: Icon + sprite: Mobs/Species/Shadowkin/parts.rsi + - type: Damageable + damageContainer: Biological + - type: BodyPart + - type: ContainerContainer + containers: + bodypart: !type:Container + ents: [] + +- type: entity + id: TorsoShadowkin + name: "Shadowkin torso" + parent: PartShadowkin + components: + - type: Sprite + state: "torso_m" + - type: Icon + state: "torso_m" + - type: BodyPart + partType: Torso + +- type: entity + id: HeadShadowkin + name: "Shadowkin head" + parent: PartShadowkin + components: + - type: Sprite + state: "head_m" + - type: Icon + state: "head_m" + - type: BodyPart + partType: Head + - type: Input + context: "ghost" + - type: MovementSpeedModifier + baseWalkSpeed: 0 + baseSprintSpeed: 0 + - type: InputMover + - type: GhostOnMove + +- type: entity + id: LeftArmShadowkin + name: "left Shadowkin arm" + parent: PartShadowkin + components: + - type: Sprite + state: "l_arm" + - type: Icon + state: "l_arm" + - type: BodyPart + partType: Arm + symmetry: Left + +- type: entity + id: RightArmShadowkin + name: "right Shadowkin arm" + parent: PartShadowkin + components: + - type: Sprite + state: "r_arm" + - type: Icon + state: "r_arm" + - type: BodyPart + partType: Arm + symmetry: Right + +- type: entity + id: LeftHandShadowkin + name: "left Shadowkin hand" + parent: PartShadowkin + components: + - type: Sprite + state: "l_hand" + - type: Icon + state: "l_hand" + - type: BodyPart + partType: Hand + symmetry: Left + +- type: entity + id: RightHandShadowkin + name: "right Shadowkin hand" + parent: PartShadowkin + components: + - type: Sprite + state: "r_hand" + - type: Icon + state: "r_hand" + - type: BodyPart + partType: Hand + symmetry: Right + +- type: entity + id: LeftLegShadowkin + name: "left Shadowkin leg" + parent: PartShadowkin + components: + - type: Sprite + state: "l_leg" + - type: Icon + state: "l_leg" + - type: BodyPart + partType: Leg + symmetry: Left + - type: MovementBodyPart + +- type: entity + id: RightLegShadowkin + name: "right Shadowkin leg" + parent: PartShadowkin + components: + - type: Sprite + state: "r_leg" + - type: Icon + state: "r_leg" + - type: BodyPart + partType: Leg + symmetry: Right + - type: MovementBodyPart + +- type: entity + id: LeftFootShadowkin + name: "left Shadowkin foot" + parent: PartShadowkin + components: + - type: Sprite + state: "l_foot" + - type: Icon + state: "l_foot" + - type: BodyPart + partType: Foot + symmetry: Left + +- type: entity + id: RightFootShadowkin + name: "right Shadowkin foot" + parent: PartShadowkin + components: + - type: Sprite + state: "r_foot" + - type: Icon + state: "r_foot" + - type: BodyPart + partType: Foot + symmetry: Right diff --git a/Resources/Prototypes/Body/Prototypes/shadowkin.yml b/Resources/Prototypes/Body/Prototypes/shadowkin.yml new file mode 100644 index 00000000000..dddad7bdb5d --- /dev/null +++ b/Resources/Prototypes/Body/Prototypes/shadowkin.yml @@ -0,0 +1,48 @@ +- type: body + id: Shadowkin + name: "Shadowkin" + root: torso + slots: + head: + part: HeadShadowkin + connections: + - torso + organs: + brain: OrganShadowkinBrain + eyes: OrganShadowkinEyes + torso: + part: TorsoShadowkin + connections: + - left arm + - right arm + - left leg + - right leg + organs: + heart: OrganShadowkinHeart + stomach: OrganShadowkinStomach + liver: OrganShadowkinLiver + kidneys: OrganShadowkinKidneys + right arm: + part: RightArmShadowkin + connections: + - right hand + left arm: + part: LeftArmShadowkin + connections: + - left hand + right hand: + part: RightHandShadowkin + left hand: + part: LeftHandShadowkin + right leg: + part: RightLegShadowkin + connections: + - right foot + left leg: + part: LeftLegShadowkin + connections: + - left foot + right foot: + part: RightFootShadowkin + left foot: + part: LeftFootShadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Chemistry/metabolizer_types.yml b/Resources/Prototypes/Chemistry/metabolizer_types.yml index b49fad6c277..80f69893c6e 100644 --- a/Resources/Prototypes/Chemistry/metabolizer_types.yml +++ b/Resources/Prototypes/Chemistry/metabolizer_types.yml @@ -52,3 +52,7 @@ - type: metabolizerType id: LiquorLifeline name: metabolizer-type-liquorlifeline + +- type: metabolizerType + id: Shadowkin + name: metabolizer-type-shadowkin diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index 39c956df624..8cca467f69b 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -358,6 +358,20 @@ Piercing: 0.6 Holy: 1.5 +- type: damageModifierSet + id: Shadowkin + coefficients: + Blunt: 0.95 + Slash: 1.2 + Piercing: 1.1 + Asphyxiation: 0 + Cold: 0.75 + Heat: 1.2 + Cellular: 0.25 + Bloodloss: 1.35 + Shock: 1.25 + Radiation: 1.3 + - type: damageModifierSet id: DermalArmor coefficients: diff --git a/Resources/Prototypes/Datasets/Names/shadowkin.yml b/Resources/Prototypes/Datasets/Names/shadowkin.yml new file mode 100644 index 00000000000..4dbf4c5dc54 --- /dev/null +++ b/Resources/Prototypes/Datasets/Names/shadowkin.yml @@ -0,0 +1,70 @@ +# Names for the shadowkin, +# Shadowkin names are descriptive of +# Their Primary Emotion, +# A State of Being, +# Or past Memories. + +- type: dataset + id: names_shadowkin + values: + # Mar + # - Mar + + # Sad + - Fragile + - Heartbreak + - Inferior + - Lone + - Lonesome + - Loss + - Solitary + - Solitude + - Sorrow + - Shade + + # Angry + - Fear + - Fearful + - Fury + - Pain + - Rage + - Rush + - Wrath + + # Happy + - Calm + - Content + - Contented + - Happy + - Hope + - Joyous + - Lovely + - Peace + - Peaceful + - Quiet + - Serene + - Serenity + - Tranquil + - Tranquility + + # Memory + - Dillusioned + - Forgotten + - Focusless + - Lost + - Memory + - Recollection + - Remembrance + - Reminisce + - Reminiscence + + # Other + - Apathy + - Collected + - Curiosity + - Free + - Interest + - Jax # White eye (jack of all trades) :) + - Still + - Unbound + - Shadows \ No newline at end of file diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml index 2c8b01553a7..4db83e9ab13 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml @@ -10,6 +10,8 @@ - type: randomHumanoidSettings id: SyndicateListener + speciesBlacklist: + - Shadowkin components: - type: Loadout prototypes: [SyndicateListenerGear] @@ -34,6 +36,8 @@ - type: randomHumanoidSettings id: Mobster randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: GhostRole name: Mobster @@ -65,6 +69,8 @@ - type: randomHumanoidSettings id: MobsterAlt randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: GhostRole name: Mobster diff --git a/Resources/Prototypes/DeltaV/Traits/altvision.yml b/Resources/Prototypes/DeltaV/Traits/altvision.yml index 390e14d4ad1..54fec7df8b9 100644 --- a/Resources/Prototypes/DeltaV/Traits/altvision.yml +++ b/Resources/Prototypes/DeltaV/Traits/altvision.yml @@ -7,6 +7,7 @@ species: - Vulpkanin - Harpy + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -23,6 +24,7 @@ species: - Vulpkanin - Harpy + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: diff --git a/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml b/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml index 119134dcf18..ad91d9c2d2e 100644 --- a/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml @@ -60,6 +60,10 @@ collection: VulpkaninHowls Weh: collection: Weh + Mars: + collection: Mars + Wurble: + collection: Wurble - type: emoteSounds id: MaleVulpkanin diff --git a/Resources/Prototypes/Entities/Clothing/Back/specific.yml b/Resources/Prototypes/Entities/Clothing/Back/specific.yml index fcdecffc8f1..e2932537e90 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/specific.yml @@ -61,3 +61,25 @@ solution: tank - type: ExaminableSolution solution: tank + +- type: entity + parent: Clothing + id: ClothingBackpackEtherealTeleporter + name: ethereal teleporter + description: Originally created while several research facilities were experimenting on Shadowkin, this backpack allows the wearer to jump the gap between the "normal" dimension and The Dark. + components: + - type: Tag + tags: + - WhitelistChameleon + - type: Sprite + sprite: Clothing/Back/etherealteleporter.rsi + state: icon + - type: Item + size: Ginormous + - type: Clothing + slots: BACK + sprite: Clothing/Back/etherealteleporter.rsi + # TODO: Uncomment this when ClothingGrantPsionicPower is fixed and back working. + # - type: ClothingGrantPsionicPower + # power: DarkSwapPower + # - type: Psionic diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index 9e47a685f0b..f50b0dbca54 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -265,3 +265,15 @@ sprite: Clothing/Eyes/Glasses/meson.rsi - type: Clothing sprite: Clothing/Eyes/Glasses/meson.rsi + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesGlassesEthereal + name: ethereal goggles + description: An unusual pair of goggles developed during a time of inhumane experimentation involving Shadowkin. They are designed to allow the wearer to peer into The Dark. + components: + - type: Sprite + sprite: Clothing/Eyes/Glasses/etherealgoogles.rsi + - type: Clothing + sprite: Clothing/Eyes/Glasses/etherealgoogles.rsi + - type: ShowEthereal \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml index f908465f7a5..b5169a9cf6b 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml @@ -428,3 +428,20 @@ sprite: Clothing/OuterClothing/Misc/unathirobe.rsi - type: Clothing sprite: Clothing/OuterClothing/Misc/unathirobe.rsi + +- type: entity + parent: ClothingOuterBase + id: ClothingOuterShadowkinRestraints + name: shadowkin restraints + description: One of the first creations after finding Shadowkin, these were used to contain the Shadowkin during research so they didn't teleport away. + components: + - type: Sprite + sprite: Clothing/OuterClothing/Misc/shadowkinrestraints.rsi + - type: Clothing + sprite: Clothing/OuterClothing/Misc/shadowkinrestraints.rsi + equipDelay: 0.5 + unequipDelay: 10 + - type: ShadowkinCuff + - type: GuideHelp + guides: + - Shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Effects/bluespace_flash.yml b/Resources/Prototypes/Entities/Effects/bluespace_flash.yml index a0b3f81abce..e9ad08f0156 100644 --- a/Resources/Prototypes/Entities/Effects/bluespace_flash.yml +++ b/Resources/Prototypes/Entities/Effects/bluespace_flash.yml @@ -38,4 +38,46 @@ lifetime: 1 - type: EmitSoundOnSpawn sound: - path: /Audio/Effects/Lightning/lightningbolt.ogg \ No newline at end of file + path: /Audio/Effects/Lightning/lightningbolt.ogg + +- type: entity + id: EffectFlashShadowkinShadeskip + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/shadeskip.ogg + +- type: entity + id: EffectFlashShadowkinDarkSwapOn + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/darkswapon.ogg + +- type: entity + id: EffectFlashShadowkinDarkSwapOff + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/darkswapoff.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index cad3e779621..6197f820308 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -208,7 +208,7 @@ id: LizardChestTiger bodyPart: Chest markingCategory: Chest - speciesRestriction: [Reptilian] + speciesRestriction: [Reptilian, Shadowkin] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: body_tiger diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml new file mode 100644 index 00000000000..f86852a9879 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml @@ -0,0 +1,83 @@ +# Ears + +- type: marking + id: EarsShadowkin + bodyPart: HeadTop + markingCategory: HeadTop + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin + +- type: marking + id: EarsShadowkinStriped + bodyPart: HeadTop + markingCategory: HeadTop + speciesRestriction: [Shadowkin] + coloring: + default: + type: + !type:SkinColoring + layers: + shadowkin_stripes: + type: + !type:SimpleColoring + color: "#FFFFFF" + sprites: + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin_stripes + +# Tails + +- type: marking + id: TailShadowkin + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin + +- type: marking + id: TailShadowkinBig + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin_big + +- type: marking + id: TailShadowkinBigFluff + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin_big_fluff + +- type: marking + id: TailShadowkinShorter + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails32x32.rsi + state: shadowkin_shorter + +- type: marking + id: TailShadowkinMedium + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails32x32.rsi + state: shadowkin_medium \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml index 57b25798e74..0bff2347471 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml @@ -2,7 +2,7 @@ id: SlimeGradientLeftArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_arm @@ -11,7 +11,7 @@ id: SlimeGradientRightArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_arm @@ -20,7 +20,7 @@ id: SlimeGradientLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_leg @@ -29,7 +29,7 @@ id: SlimeGradientRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_leg @@ -38,7 +38,7 @@ id: SlimeGradientLeftFoot bodyPart: LFoot markingCategory: LeftFoot - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_foot @@ -47,7 +47,7 @@ id: SlimeGradientRightFoot bodyPart: RFoot markingCategory: RightFoot - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_foot @@ -56,7 +56,7 @@ id: SlimeGradientLeftHand bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_hand @@ -65,7 +65,7 @@ id: SlimeGradientRightHand bodyPart: RHand markingCategory: RightHand - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_hand diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml index 88307085397..93a16fcfd3c 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml @@ -2,7 +2,7 @@ id: TattooHiveChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -16,7 +16,7 @@ id: TattooNightlingChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -30,7 +30,7 @@ id: TattooSilverburghLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -44,7 +44,7 @@ id: TattooSilverburghRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -58,7 +58,7 @@ id: TattooCampbellLeftArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -72,7 +72,7 @@ id: TattooCampbellRightArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -86,7 +86,7 @@ id: TattooCampbellLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -100,7 +100,7 @@ id: TattooCampbellRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index c690bbb3117..dce408ed827 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -10,6 +10,7 @@ - PsionicInvisibility - Ghost - Normal + - Ethereal - type: ContentEye maxZoom: 8.916104, 8.916104 - type: Tag diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index e96633dde8b..a4bfe2289bd 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -21,6 +21,8 @@ - type: randomHumanoidSettings id: DeathSquad randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -59,6 +61,8 @@ - type: randomHumanoidSettings id: ERTLeader randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -486,6 +490,8 @@ - type: randomHumanoidSettings id: CBURNAgent + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: Loadout @@ -516,6 +522,8 @@ - type: randomHumanoidSettings id: CentcomOfficial + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -544,6 +552,8 @@ - type: randomHumanoidSettings id: SyndicateAgent + speciesBlacklist: + - Shadowkin components: - type: Loadout prototypes: [SyndicateOperativeGearExtremelyBasic] @@ -560,6 +570,8 @@ - type: randomHumanoidSettings id: NukeOp + speciesBlacklist: + - Shadowkin components: - type: NukeOperative - type: Psionic @@ -582,6 +594,8 @@ - type: randomHumanoidSettings id: Cluwne + speciesBlacklist: + - Shadowkin randomizeName: false components: - type: GhostRole diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 0086be81d9a..c92595ffc9d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -32,6 +32,7 @@ - PsionicInvisibility - Ghost - Normal + - Ethereal - type: Input context: "ghost" - type: Examiner diff --git a/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml new file mode 100644 index 00000000000..2a58fe5c1fe --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml @@ -0,0 +1,5 @@ +- type: entity + save: false + name: Urist McShadow + parent: MobShadowkinBase + id: MobShadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index c0d23c489c0..7294506bc88 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -152,7 +152,6 @@ - type: Identity - type: IdExaminable - type: Hands - - type: Internals - type: Inventory - type: InventorySlots - type: FloatingVisuals diff --git a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml new file mode 100644 index 00000000000..393cb0b8716 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml @@ -0,0 +1,304 @@ +- type: entity + save: false + parent: + - MobBloodstream + - MobAtmosStandard + - MobFlammable + - BaseMobSpecies + id: MobShadowkinBase + name: Urist McShadow + abstract: true + components: + - type: Destructible + thresholds: + - trigger: + !type:DamageTypeTrigger + damageType: Blunt + damage: 400 + behaviors: + - !type:GibBehavior {} + - !type:SpawnEntitiesBehavior + spawn: + ShadowkinShadow: + min: 1 + max: 1 + EffectFlashShadowkinShadeskip: + min: 1 + max: 1 + - trigger: + !type:DamageTypeTrigger + damageType: Heat + damage: 1500 + behaviors: + - !type:SpawnEntitiesBehavior + spawnInContainer: true + spawn: + Ash: + min: 1 + max: 1 + - !type:BurnBodyBehavior {} + - !type:PlaySoundBehavior + sound: + collection: MeatLaserImpact + - type: PassiveDamage # Slight passive regen. Assuming one damage type, comes out to about 4 damage a minute. + allowedStates: + - Alive + damageCap: 20 + damage: + types: + Heat: -0.07 + groups: + Brute: -0.07 + - type: StatusEffects + allowed: + - Stun + - KnockedDown + - SlowedDown + - Stutter + - SeeingRainbows + - Electrocution + - ForcedSleep + - TemporaryBlindness + - Drunk + - SlurredSpeech + - RatvarianLanguage + - PressureImmunity + - Muted + - Pacified + - StaminaModifier + - type: Blindable + - type: ThermalRegulator + metabolismHeat: 800 + radiatedHeat: 100 + implicitHeatRegulation: 500 + sweatHeatRegulation: 2000 + shiveringHeatRegulation: 2000 + normalBodyTemperature: 310.15 + thermalRegulationTemperatureThreshold: 25 + - type: Perishable + - type: FireVisuals + alternateState: Standing + - type: OfferItem + - type: LayingDown + - type: Shoving + - type: BloodstreamAffectedByMass + power: 0.6 + - type: Hunger + - type: Thirst + - type: Carriable + - type: HumanoidAppearance + species: Shadowkin + - type: Icon + sprite: Mobs/Species/Shadowkin/parts.rsi + state: full + - type: Body + prototype: Shadowkin + - type: Flammable + damage: + types: + Heat: 1.5 # burn more + - type: MobThresholds + thresholds: # Weak + 0: Alive + 80: Critical + 180: Dead + - type: SlowOnDamage + speedModifierThresholds: + 48: 0.85 + 64: 0.65 + - type: Damageable + damageContainer: Biological # Shadowkin + damageModifierSet: Shadowkin + - type: Barotrauma + damage: + types: + Blunt: 0.35 # per second, scales with pressure and other constants. + - type: Bloodstream + bloodlossDamage: + types: + Bloodloss: 1 + bloodlossHealDamage: + types: + Bloodloss: -0.25 + - type: Temperature + heatDamageThreshold: 330 + coldDamageThreshold: 195 + currentTemperature: 310.15 + specificHeat: 46 + coldDamage: + types: + Cold: 0.05 #per second, scales with temperature & other constants + heatDamage: + types: + Heat: 0.25 #per second, scales with temperature & other constants + - type: Fixtures + fixtures: + fix1: + shape: !type:PhysShapeCircle + radius: 0.35 + density: 130 #lower density + restitution: 0.0 + mask: + - MobMask + layer: + - MobLayer + - type: Sprite + netsync: false + noRot: true + drawdepth: Mobs + scale: 0.85, 0.85 + layers: + - map: ["enum.HumanoidVisualLayers.Chest"] + - map: ["enum.HumanoidVisualLayers.Head"] + - map: ["enum.HumanoidVisualLayers.Snout"] + - map: ["enum.HumanoidVisualLayers.Eyes"] + shader: unshaded + - map: ["enum.HumanoidVisualLayers.RArm"] + - map: ["enum.HumanoidVisualLayers.LArm"] + - map: ["enum.HumanoidVisualLayers.RLeg"] + - map: ["enum.HumanoidVisualLayers.LLeg"] + - shader: StencilClear + sprite: Mobs/Species/Human/parts.rsi + state: l_leg + - shader: StencilMask + map: ["enum.HumanoidVisualLayers.StencilMask"] + sprite: Mobs/Customization/masking_helpers.rsi + state: full + visible: false + - map: ["enum.HumanoidVisualLayers.LFoot"] + - map: ["enum.HumanoidVisualLayers.RFoot"] + - map: ["socks"] + - map: ["underpants"] + - map: ["undershirt"] + - map: ["jumpsuit"] + - map: ["enum.HumanoidVisualLayers.LHand"] + - map: ["enum.HumanoidVisualLayers.RHand"] + - map: ["enum.HumanoidVisualLayers.Handcuffs"] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: ["id"] + - map: ["gloves"] + - map: ["shoes"] + - map: ["ears"] + - map: ["outerClothing"] + - map: ["eyes"] + - map: ["belt"] + - map: ["neck"] + - map: ["back"] + - map: ["enum.HumanoidVisualLayers.FacialHair"] + - map: ["enum.HumanoidVisualLayers.Hair"] + - map: ["enum.HumanoidVisualLayers.HeadSide"] + - map: ["enum.HumanoidVisualLayers.HeadTop"] + - map: ["mask"] + - map: ["head"] + - map: ["pocket1"] + - map: ["pocket2"] + - map: ["enum.HumanoidVisualLayers.Tail"] + - type: MeleeWeapon + soundHit: + collection: AlienClaw + angle: 30 + animation: WeaponArcClaw + damage: + types: + Slash: 5 + - type: Vocal + sounds: + Male: MaleShadowkin + Female: FemaleShadowkin + Unsexed: MaleShadowkin + - type: TypingIndicator + proto: alien + - type: MovementSpeedModifier + baseWalkSpeed: 2.7 + baseSprintSpeed: 4.5 + - type: Flashable + eyeDamageChance: 0.3 + eyeDamage: 1 + durationMultiplier: 1.5 + - type: Speech + allowedEmotes: ['Mars', 'Wurble'] + - type: Shadowkin + - type: Psionic + mindbreakingFeedback: shadowkin-blackeye + manaGain: 0.25 + mana: 150 + maxMana: 250 + bypassManaCheck: true + removable: false + - type: InnatePsionicPowers + powersToAdd: + - ShadowkinPowers + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - Marish + understands: + - TauCetiBasic + - Marish + +- type: entity + save: false + parent: MobHumanDummy + id: MobShadowkinDummy + noSpawn: true + description: A dummy shadowkin meant to be used in character setup. + components: + - type: HumanoidAppearance + species: Shadowkin + - type: Sprite + netsync: false + noRot: true + drawdepth: Mobs + scale: 0.85, 0.85 # Small + layers: + - map: ["enum.HumanoidVisualLayers.Chest"] + - map: ["enum.HumanoidVisualLayers.Head"] + - map: ["enum.HumanoidVisualLayers.Snout"] + - map: ["enum.HumanoidVisualLayers.Eyes"] + shader: unshaded + - map: ["enum.HumanoidVisualLayers.RArm"] + - map: ["enum.HumanoidVisualLayers.LArm"] + - map: ["enum.HumanoidVisualLayers.RLeg"] + - map: ["enum.HumanoidVisualLayers.LLeg"] + - shader: StencilClear + sprite: Mobs/Species/Human/parts.rsi + state: l_leg + - shader: StencilMask + map: ["enum.HumanoidVisualLayers.StencilMask"] + sprite: Mobs/Customization/masking_helpers.rsi + state: full + visible: false + - map: ["enum.HumanoidVisualLayers.LFoot"] + - map: ["enum.HumanoidVisualLayers.RFoot"] + - map: ["socks"] + - map: ["underpants"] + - map: ["undershirt"] + - map: ["jumpsuit"] + - map: ["enum.HumanoidVisualLayers.LHand"] + - map: ["enum.HumanoidVisualLayers.RHand"] + - map: ["enum.HumanoidVisualLayers.Handcuffs"] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: ["id"] + - map: ["gloves"] + - map: ["shoes"] + - map: ["ears"] + - map: ["outerClothing"] + - map: ["eyes"] + - map: ["belt"] + - map: ["neck"] + - map: ["back"] + - map: ["enum.HumanoidVisualLayers.FacialHair"] + - map: ["enum.HumanoidVisualLayers.Hair"] + - map: ["enum.HumanoidVisualLayers.HeadSide"] + - map: ["enum.HumanoidVisualLayers.HeadTop"] + - map: ["mask"] + - map: ["head"] + - map: ["pocket1"] + - map: ["pocket2"] + - map: ["enum.HumanoidVisualLayers.Tail"] diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index ad8f3d5c719..3ff5a3955a9 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -57,6 +57,7 @@ - idcard - Belt - type: UnpoweredFlashlight + - type: EtherealLight - type: PointLight enabled: false radius: 1.5 diff --git a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml index d6d86c08b23..19aa6a3015a 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml @@ -285,6 +285,9 @@ - id: PetRock prob: 0.80 orGroup: Prize + - id: PlushieShadowkin + prob: 0.80 + orGroup: Prize # Uncommon - id: PrizeTicket60 prob: 0.50 diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index d7a5f3542dc..fc771414b4a 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1954,4 +1954,14 @@ size: Ginormous sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi - type: DisarmMalus - malus: 0 \ No newline at end of file + malus: 0 + +- type: entity + parent: BasePlushie + id: PlushieShadowkin + name: shadowkin plushie + description: A plushie of a Shadowkin. It's very soft. + components: + - type: Sprite + sprite: Objects/Fun/toys.rsi + state: shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml index bc0ed8f03cd..7af87ca40d6 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml @@ -14,6 +14,10 @@ - type: PhysicalComposition materialComposition: Bluespace: 100 + - type: EmitSoundOnUse + sound: + collection: RadiationPulse + - type: EtherealStunItem - type: Tag tags: - BluespaceCrystal diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index a02f94215b8..8fc465f4fb2 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -40,6 +40,7 @@ sprite: Objects/Misc/Lights/lights.rsi size: Normal heldPrefix: off + - type: EtherealLight - type: PointLight enabled: false radius: 3 diff --git a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml index a1005004946..838715f1da5 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml @@ -25,8 +25,7 @@ - type: MeleeSound soundGroups: Brute: - path: - "/Audio/Weapons/slash.ogg" + path: "/Audio/Weapons/slash.ogg" - type: Sprite sprite: Objects/Misc/kudzu.rsi state: kudzu_11 @@ -38,21 +37,19 @@ fix1: hard: false density: 7 - shape: - !type:PhysShapeAabb + shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" layer: - - MidImpassable + - MidImpassable - type: Damageable damageModifierSet: Wood - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Temperature heatDamage: types: @@ -69,14 +66,14 @@ Flammable: [Touch] Extinguish: [Touch] reactions: - - reagents: [WeedKiller, PlantBGone] - methods: [Touch] - effects: - - !type:HealthChange - scaleByQuantity: true - damage: - types: - Heat: 10 + - reagents: [WeedKiller, PlantBGone] + methods: [Touch] + effects: + - !type:HealthChange + scaleByQuantity: true + damage: + types: + Heat: 10 - type: AtmosExposed - type: Kudzu growthTickChance: 0.3 @@ -86,19 +83,19 @@ sprintSpeedModifier: 0.2 ignoreWhitelist: components: - - IgnoreKudzu + - IgnoreKudzu - type: Food requiredStomachs: 2 # ruminants have 4 stomachs but i dont care to give them literally 4 stomachs. 2 is good delay: 0.5 - type: FlavorProfile flavors: - - fiber + - fiber - type: SolutionContainerManager solutions: food: reagents: - - ReagentId: Nutriment - Quantity: 2 + - ReagentId: Nutriment + Quantity: 2 - type: entity id: WeakKudzu @@ -127,22 +124,22 @@ sprintSpeedModifier: 0.8 ignoreWhitelist: components: - - IgnoreKudzu + - IgnoreKudzu - type: RandomSpawner deleteSpawnerAfterSpawn: false rareChance: 0.15 offset: 0.2 chance: 0.05 prototypes: - - LightTree01 - - LightTree02 - - LightTree03 - - LightTree04 - - LightTree05 - - LightTree06 - - CrystalCyan + - LightTree01 + - LightTree02 + - LightTree03 + - LightTree04 + - LightTree05 + - LightTree06 + - CrystalCyan rarePrototypes: - - AnomalyFloraBulb + - AnomalyFloraBulb - type: entity id: KudzuFlowerAngry @@ -154,11 +151,11 @@ - type: RandomSpawner chance: 0.05 rarePrototypes: - - AnomalyFloraBulb - - AnomalyFloraBulb - - MobLuminousEntity - - MobLuminousObject - - MobLuminousPerson + - AnomalyFloraBulb + - AnomalyFloraBulb + - MobLuminousEntity + - MobLuminousObject + - MobLuminousPerson - type: entity id: FleshKudzu @@ -173,8 +170,7 @@ - type: MeleeSound soundGroups: Brute: - path: - "/Audio/Weapons/slash.ogg" + path: "/Audio/Weapons/slash.ogg" - type: Sprite sprite: Objects/Misc/fleshkudzu.rsi state: kudzu_11 @@ -186,20 +182,18 @@ fix1: hard: false density: 7 - shape: - !type:PhysShapeAabb + shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" layer: - - MidImpassable + - MidImpassable - type: Damageable - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: !type:DamageTrigger + damage: 40 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: DamageContacts damage: types: @@ -207,7 +201,7 @@ Piercing: 1.5 ignoreWhitelist: tags: - - Flesh + - Flesh - type: Kudzu growthTickChance: 0.1 spreadChance: 0.4 @@ -232,23 +226,23 @@ - type: Flammable fireSpread: true damage: - types: - Heat: 3 + types: + Heat: 3 - type: AtmosExposed - type: SpeedModifierContacts walkSpeedModifier: 0.3 sprintSpeedModifier: 0.3 ignoreWhitelist: tags: - - Flesh + - Flesh - type: Food # delightfully devilish ! delay: 0.5 - type: SolutionContainerManager solutions: food: reagents: - - ReagentId: Protein - Quantity: 2 + - ReagentId: Protein + Quantity: 2 - type: Respirator damage: types: @@ -260,7 +254,7 @@ - type: entity name: dark haze id: ShadowKudzu - parent: [ BaseKudzu, BaseShadow ] + parent: [BaseKudzu, BaseShadow] components: - type: Physics canCollide: false @@ -269,9 +263,9 @@ drawdepth: Effects sprite: Effects/spookysmoke.rsi layers: - - state: spookysmoke - color: "#793a80dd" - map: [base] + - state: spookysmoke + color: "#793a80dd" + map: [base] - type: Kudzu growthTickChance: 0.2 spreadChance: 0.99 @@ -281,10 +275,10 @@ offset: 0.2 chance: 0.45 prototypes: - - ShadowBasaltRandom + - ShadowBasaltRandom rarePrototypes: - - ShadowPortal - - ShadowKudzuLootSpawner + - ShadowPortal + - ShadowKudzuLootSpawner - type: Tag tags: - HideContextMenu @@ -292,10 +286,10 @@ - type: OptionsVisualizer visuals: base: - - options: Default - data: { state: spookysmoke } - - options: ReducedMotion - data: { state: spookysmoke_static } + - options: Default + data: { state: spookysmoke } + - options: ReducedMotion + data: { state: spookysmoke_static } - type: entity name: Haze @@ -304,3 +298,18 @@ components: - type: Kudzu spreadChance: 0 #appears during pulsation. It shouldnt spreading. + +- type: entity + name: Shadowkin Haze + id: ShadowkinShadow + parent: ShadowKudzuWeak + components: + - type: RandomSpawner + deleteSpawnerAfterSpawn: false + rareChance: 0 + offset: 0.2 + chance: 0.45 + prototypes: + - ShadowBasaltRandom + - type: TimedDespawn + lifetime: 30 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index 2a982ed6f79..cbb5dfee0a1 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -65,6 +65,7 @@ - type: Item sprite: Objects/Tools/flashlight.rsi storedRotation: -90 + - type: EtherealLight - type: PointLight enabled: false mask: /Textures/Effects/LightMasks/cone.png diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index 6b301a50eed..c6c55a916c7 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -31,6 +31,7 @@ state: glow shader: unshaded state: base + - type: EtherealLight - type: PointLight color: "#FFE4CE" # 5000K color temp energy: 0.8 diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 35434003d29..0088fc4306b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -806,6 +806,8 @@ - WeaponLaserCarbinePractice - Zipties - ShockCollar + - ShadowkinRestraints + # DeltaV - .38 special ammo - Add various .38 special ammo to security techfab - MagazineBoxSpecial - MagazineBoxSpecialPractice - SpeedLoaderSpecial @@ -1398,6 +1400,7 @@ staticRecipes: - PrizeBall - PlushieMothRandom + - PlushieShadowkin - PlushieMothMusician - PlushieMothBartender - PlushieBee diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index e98b40b9a0c..c79cfa2641c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -86,6 +86,7 @@ - type: Electrified enabled: false usesApcPower: true + - type: EtherealLight - type: PointLight enabled: false castShadows: false diff --git a/Resources/Prototypes/Guidebook/species.yml b/Resources/Prototypes/Guidebook/species.yml index d9d039ea436..f7b77b7ec6f 100644 --- a/Resources/Prototypes/Guidebook/species.yml +++ b/Resources/Prototypes/Guidebook/species.yml @@ -12,6 +12,7 @@ - SlimePerson - IPCs - Harpy + - Shadowkin - type: guideEntry id: Arachnid @@ -56,4 +57,9 @@ - type: guideEntry id: Harpy name: species-name-harpy - text: "/ServerInfo/Guidebook/Mobs/Harpy.xml" \ No newline at end of file + text: "/ServerInfo/Guidebook/Mobs/Harpy.xml" + +- type: guideEntry + id: Shadowkin + name: species-name-shadowkin + text: "/ServerInfo/Guidebook/Mobs/Shadowkin.xml" diff --git a/Resources/Prototypes/Language/Species-Specific/marish.yml b/Resources/Prototypes/Language/Species-Specific/marish.yml new file mode 100644 index 00000000000..872d67373d2 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/marish.yml @@ -0,0 +1,20 @@ +# Spoken by shadowkins. +- type: language + id: Marish + speech: + color: "#be3cc5" + fontId: Lymphatic + empathySpeech: true + speechVerbOverrides: + - chat-speech-verb-marish + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 # Replacements are really short + maxSyllables: 2 + replacement: + - mar + - mwrrr + - maaAr + - aarrr + - wrurrl + - mmar \ No newline at end of file diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index f7a31205706..3834c3b95c4 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -18,6 +18,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: ForensicMantisGear icon: "JobIconForensicMantis" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml index 00ffdde666f..0ca17947425 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml @@ -13,6 +13,15 @@ - !type:DepartmentTimeRequirement department: Security min: 21600 + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin + - !type:CharacterTraitRequirement + traits: + - ShadowkinBlackeye - type: startingGear id: PrisonerGear diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index 378d48b3fad..54275fc9c51 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -52,7 +52,7 @@ name: Pyrokinesis description: pyrokinesis-power-description actions: - - ActionPyrokinesis + - ActionPyrokinesis components: - type: PyrokinesisPower initializationFeedback: pyrokinesis-power-initialization-feedback @@ -64,7 +64,7 @@ name: Metapsionic Pulse description: metapsionic-power-description actions: - - ActionMetapsionic + - ActionMetapsionic components: - type: MetapsionicPower initializationFeedback: metapsionic-power-initialization-feedback @@ -77,7 +77,7 @@ name: Psionic Regeneration description: psionic-regeneration-power-description actions: - - ActionPsionicRegeneration + - ActionPsionicRegeneration components: - type: PsionicRegenerationPower initializationFeedback: psionic-regeneration-power-initialization-feedback @@ -90,7 +90,7 @@ name: Telegnosis description: telegnosis-power-description actions: - - ActionTelegnosis + - ActionTelegnosis components: - type: TelegnosisPower initializationFeedback: telegnosis-power-initialization-feedback @@ -103,7 +103,7 @@ name: Psionic Invisibility description: psionic-invisibility-power-description actions: - - ActionDispel + - ActionDispel components: - type: PsionicInvisibilityPower initializationFeedback: psionic-invisibility-power-initialization-feedback @@ -204,7 +204,7 @@ name: Shadeskip description: shadeskip-power-description actions: - - ActionShadeskip + - ActionShadeskip initializationFeedback: shadeskip-power-initialization-feedback metapsionicFeedback: shadeskip-power-metapsionic-feedback amplificationModifier: 1 @@ -214,7 +214,32 @@ name: Telekinetic Pulse description: telekinetic-pulse-power-description actions: - - ActionTelekineticPulse + - ActionTelekineticPulse initializationFeedback: telekinetic-pulse-power-initialization-feedback metapsionicFeedback: telekinetic-pulse-power-metapsionic-feedback - amplificationModifier: 1 \ No newline at end of file + amplificationModifier: 1 + +- type: psionicPower + id: ShadowkinPowers + name: Shadowkin Powers + description: shadowkin-powers-description + actions: + - ActionShadowkinShadeskip + - ActionDarkSwap + powerSlotCost: 1 + +- type: psionicPower + id: EtherealVisionPower + name: Ethereal Vision + description: ethereal-vision-powers-description + components: + - type: ShowEthereal + powerSlotCost: 0 + +- type: psionicPower + id: DarkSwapPower + name: DarkSwap + description: darkswap-power-description + actions: + - ActionDarkSwap + powerSlotCost: 1 diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 5d29f024ce8..abb33f832a4 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -511,6 +511,12 @@ shouldHave: true reagent: Protein amount: 0.5 + - !type:AdjustReagent + conditions: + - !type:OrganType + type: Shadowkin + reagent: Protein + amount: 0.5 - type: reagent id: Allicin diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml index 08d6d57fb94..47eaf30e5cc 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml @@ -11,4 +11,3 @@ doAfter: 5 - node: cuffscable entity: Cablecuffs - diff --git a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml index c600702b206..9e5e239c6f5 100644 --- a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml +++ b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml @@ -6,6 +6,14 @@ materials: PrizeTicket: 30 +- type: latheRecipe + id: PlushieShadowkin + result: PlushieShadowkin + applyMaterialDiscount: false + completetime: 0.1 + materials: + PrizeTicket: 50 + - type: latheRecipe id: PlushieMothRandom result: PlushieMothRandom diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 04c2ad1ec1f..98e8c530b87 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -758,9 +758,10 @@ result: GrenadeBlast completetime: 3 materials: - Steel: 450 - Plastic: 300 - Gold: 150 + Steel: 150 + Plastic: 100 + Gold: 50 + - type: latheRecipe id: GrenadeFlash result: GrenadeFlash @@ -872,3 +873,10 @@ materials: Plastic: 15 Uranium: 10 + +- type: latheRecipe + id: ShadowkinRestraints + result: ClothingOuterShadowkinRestraints + completetime: 6 + materials: + Steel: 300 diff --git a/Resources/Prototypes/Roles/Antags/Thief.yml b/Resources/Prototypes/Roles/Antags/Thief.yml index 131db8cf1da..2715e6d96d7 100644 --- a/Resources/Prototypes/Roles/Antags/Thief.yml +++ b/Resources/Prototypes/Roles/Antags/Thief.yml @@ -3,4 +3,4 @@ name: roles-antag-thief-name antagonist: true setPreference: true - objective: roles-antag-thief-objective + objective: roles-antag-thief-objective \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index fec2280ddc8..05b0553c78f 100644 --- a/Resources/Prototypes/Roles/Antags/traitor.yml +++ b/Resources/Prototypes/Roles/Antags/traitor.yml @@ -6,4 +6,4 @@ objective: roles-antag-syndicate-agent-objective requirements: - !type:CharacterOverallTimeRequirement # DeltaV - Playtime requirement - min: 86400 # DeltaV - 24 hours + min: 86400 # DeltaV - 24 hours \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index d4f8bdb0671..dcb2b6438a3 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -16,6 +16,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: ChaplainGear icon: "JobIconChaplain" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml index 8552073dcc6..3468d10f278 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml @@ -16,6 +16,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: LibrarianGear icon: "JobIconLibrarian" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index b132729432c..6b61fe856e7 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -18,6 +18,15 @@ min: 43200 # DeltaV - 12 hours - !type:CharacterOverallTimeRequirement min: 72000 # DeltaV - 20 hours + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin + - !type:CharacterTraitRequirement + traits: + - ShadowkinBlackeye weight: 10 startingGear: CMOGear icon: "JobIconChiefMedicalOfficer" diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index 46d91ee00ee..4fc9325f39d 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -19,6 +19,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin weight: 10 startingGear: ResearchDirectorGear icon: "JobIconResearchDirector" diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index 01f14f80994..18f6d04022c 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -6,7 +6,6 @@ requirements: - !type:CharacterDepartmentTimeRequirement department: Security - min: 14400 # DeltaV - 4 hours startingGear: SecurityOfficerGear icon: "JobIconSecurityOfficer" supervisors: job-supervisors-hos diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 3f0cb5ae1fb..41d04137bbe 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -110,4 +110,16 @@ - type: shader id: Flap kind: source - path: "/Textures/Shaders/flap.swsl" \ No newline at end of file + path: "/Textures/Shaders/flap.swsl" + + # Shadowkin shaders + +- type: shader + id: ColorTint + kind: source + path: "/Textures/Shaders/color_tint.swsl" + +- type: shader + id: Ethereal + kind: source + path: "/Textures/Shaders/ethereal.swsl" \ No newline at end of file diff --git a/Resources/Prototypes/SoundCollections/emotes.yml b/Resources/Prototypes/SoundCollections/emotes.yml index 35693a70a4d..f655a8cf6b4 100644 --- a/Resources/Prototypes/SoundCollections/emotes.yml +++ b/Resources/Prototypes/SoundCollections/emotes.yml @@ -86,3 +86,13 @@ - /Audio/Voice/IPC/whirr1.ogg - /Audio/Voice/IPC/whirr2.ogg - /Audio/Voice/IPC/whirr3.ogg + +- type: soundCollection + id: Mars + files: + - /Audio/Voice/Shadowkin/mar.ogg + +- type: soundCollection + id: Wurble + files: + - /Audio/Voice/Shadowkin/wurble.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Species/shadowkin.yml b/Resources/Prototypes/Species/shadowkin.yml new file mode 100644 index 00000000000..7043397d8e7 --- /dev/null +++ b/Resources/Prototypes/Species/shadowkin.yml @@ -0,0 +1,179 @@ +- type: species + id: Shadowkin + name: species-name-shadowkin + roundStart: true + prototype: MobShadowkin + sprites: MobShadowkinSprites + defaultSkinTone: "#FFFFFF" + markingLimits: MobShadowkinMarkingLimits + dollPrototype: MobShadowkinDummy + skinColoration: Hues + naming: First + maleFirstNames: names_shadowkin + femaleFirstNames: names_shadowkin + minAge: 18 + maxAge: 300 + youngAge: 20 + oldAge: 250 + sexes: + - Male + - Female + - Unsexed + minHeight: 0.65 + defaultHeight: 0.85 + maxHeight: 1.15 + minWidth: 0.6 + defaultWidth: 0.85 + maxWidth: 1.2 + +- type: speciesBaseSprites + id: MobShadowkinSprites + sprites: + Head: MobShadowkinHead + Snout: MobShadowkinAnyMarkingFollowSkin + HeadTop: MobShadowkinAnyMarkingFollowSkin + HeadSide: MobShadowkinAnyMarkingFollowSkin + Tail: MobShadowkinAnyMarkingFollowSkin + Chest: MobShadowkinTorso + Eyes: MobShadowkinEyes + LArm: MobShadowkinLArm + RArm: MobShadowkinRArm + LHand: MobShadowkinLHand + RHand: MobShadowkinRHand + LLeg: MobShadowkinLLeg + RLeg: MobShadowkinRLeg + LFoot: MobShadowkinLFoot + RFoot: MobShadowkinRFoot + +- type: markingPoints + id: MobShadowkinMarkingLimits + points: + Tail: + points: 1 + required: true + defaultMarkings: [TailShadowkin] + HeadTop: + points: 1 + required: true + defaultMarkings: [EarsShadowkin] + Chest: + points: 1 + required: false + RightLeg: + points: 2 + required: false + RightFoot: + points: 2 + required: false + LeftLeg: + points: 2 + required: false + LeftFoot: + points: 2 + required: false + RightArm: + points: 2 + required: false + RightHand: + points: 2 + required: false + LeftArm: + points: 2 + required: false + LeftHand: + points: 2 + required: false + +- type: humanoidBaseSprite + id: MobShadowkinAnyMarkingFollowSkin + markingsMatchSkin: true + +- type: humanoidBaseSprite + id: MobShadowkinHead + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_m + +- type: humanoidBaseSprite + id: MobShadowkinHeadMale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_m + +- type: humanoidBaseSprite + id: MobShadowkinHeadFemale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_f + +- type: humanoidBaseSprite + id: MobShadowkinTorso + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_m + +- type: humanoidBaseSprite + id: MobShadowkinTorsoMale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_m + +- type: humanoidBaseSprite + id: MobShadowkinTorsoFemale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_f + +- type: humanoidBaseSprite + id: MobShadowkinLLeg + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_leg + +- type: humanoidBaseSprite + id: MobShadowkinLHand + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_hand + +- type: humanoidBaseSprite + id: MobShadowkinEyes + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: eyes + +- type: humanoidBaseSprite + id: MobShadowkinLArm + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_arm + +- type: humanoidBaseSprite + id: MobShadowkinLFoot + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_foot + +- type: humanoidBaseSprite + id: MobShadowkinRLeg + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_leg + +- type: humanoidBaseSprite + id: MobShadowkinRHand + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_hand + +- type: humanoidBaseSprite + id: MobShadowkinRArm + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_arm + +- type: humanoidBaseSprite + id: MobShadowkinRFoot + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_foot \ No newline at end of file diff --git a/Resources/Prototypes/Species/species_weights.yml b/Resources/Prototypes/Species/species_weights.yml index 990a8ccecfa..d158862d38b 100644 --- a/Resources/Prototypes/Species/species_weights.yml +++ b/Resources/Prototypes/Species/species_weights.yml @@ -4,8 +4,9 @@ weights: Human: 5 Reptilian: 4 - SlimePerson: 4 + SlimePerson: 3 Oni: 3 #Nyanotrasen Oni, see Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml Felinid: 4 # Nyanotrasen - Felinid, see Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml - Vulpkanin: 3 # DeltaV - Vulpkanin, see Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml + Vulpkanin: 4 # DeltaV - Vulpkanin, see Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml Diona: 2 + Shadowkin: 0 diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 9961c849487..5e5028035f5 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -217,6 +217,7 @@ inverted: true species: - Vulpkanin # This trait functions exactly as-is for the Vulpkanin trait. + - Shadowkin components: - type: Flashable eyeDamageChance: 0.3 diff --git a/Resources/Prototypes/Traits/mental.yml b/Resources/Prototypes/Traits/mental.yml index 4a50c8c3972..8fe672c2212 100644 --- a/Resources/Prototypes/Traits/mental.yml +++ b/Resources/Prototypes/Traits/mental.yml @@ -27,6 +27,7 @@ inverted: true species: - Oni + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -64,6 +65,7 @@ inverted: true species: - Oni + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -97,6 +99,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -130,6 +138,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -163,6 +177,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -196,6 +216,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -228,6 +254,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 288386b8f4e..afaaf0b2cb4 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -305,6 +305,7 @@ species: - Felinid # Felinids already have cat claws. - Reptilian # Reptilians also have cat claws. + - Shadowkin # Shadowkins also have claws. # - Vulpkanin # Vulpkanin have "Blunt" claws. One could argue this trait "Sharpens" their claws. - !type:CharacterTraitRequirement inverted: true @@ -403,6 +404,7 @@ species: - Arachnid - Arachne + - Shadowkin - IPC components: - type: Sericulture diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 0e4868f19bc..0d6eb045ac6 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -22,16 +22,16 @@ components: - type: SelfAware analyzableTypes: - - Blunt - - Slash - - Piercing - - Heat - - Shock - - Cold - - Caustic + - Blunt + - Slash + - Piercing + - Heat + - Shock + - Cold + - Caustic detectableGroups: - - Airloss - - Toxin + - Airloss + - Toxin - type: trait id: HeavyweightDrunk @@ -186,6 +186,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -221,6 +227,12 @@ inverted: true traits: - LatentPsychic + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - type: trait id: NaturalTelepath @@ -252,6 +264,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - type: trait id: TrapAvoider @@ -261,10 +279,10 @@ - type: StepTriggerImmune whitelist: types: - - Shard - - Landmine - - Mousetrap - - SlipEntity + - Shard + - Landmine + - Mousetrap + - SlipEntity requirements: - !type:CharacterSpeciesRequirement inverted: true @@ -279,6 +297,8 @@ componentRemovals: - PsionicInsulation requirements: - - !type:CharacterSpeciesRequirement - species: - - IPC + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + species: + - IPC diff --git a/Resources/Prototypes/Traits/species.yml b/Resources/Prototypes/Traits/species.yml index 504cf469d46..17be3489fe1 100644 --- a/Resources/Prototypes/Traits/species.yml +++ b/Resources/Prototypes/Traits/species.yml @@ -60,3 +60,17 @@ traits: - Swashbuckler - Spearmaster + +- type: trait + id: ShadowkinBlackeye + category: Mental + points: 4 + componentRemovals: + - Shadowkin + components: + - type: Shadowkin + blackeyeSpawn: true + requirements: + - !type:CharacterSpeciesRequirement + species: + - Shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Voice/speech_emote_sounds.yml index a5d05e24b71..5c3f8ec6976 100644 --- a/Resources/Prototypes/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Voice/speech_emote_sounds.yml @@ -183,6 +183,90 @@ params: variation: 0.125 +- type: emoteSounds + id: MaleShadowkin + params: + variation: 0.125 + sounds: + Scream: + collection: SlimeMaleScreams + Laugh: + collection: MaleLaugh + Sneeze: + collection: MaleSneezes + Cough: + collection: MaleCoughs + Yawn: + collection: MaleYawn + Snore: + collection: Snores + Honk: + collection: BikeHorn + Sigh: + collection: MaleSigh + Crying: + collection: MaleCry + Whistle: + collection: Whistles + Weh: + collection: Weh + Hiss: + collection: FelinidHisses + Meow: + collection: FelinidMeows + Mew: + collection: FelinidMews + Growl: + collection: FelinidGrowls + Purr: + collection: FelinidPurrs + Mars: + collection: Mars + Wurble: + collection: Wurble + +- type: emoteSounds + id: FemaleShadowkin + params: + variation: 0.125 + sounds: + Scream: + collection: SlimeFemaleScreams + Laugh: + collection: MaleLaugh + Sneeze: + collection: FemaleSneezes + Cough: + collection: FemaleCoughs + Yawn: + collection: FemaleYawn + Snore: + collection: Snores + Honk: + collection: BikeHorn + Sigh: + collection: FemaleSigh + Crying: + collection: FemaleCry + Whistle: + collection: Whistles + Weh: + collection: Weh + Hiss: + collection: FelinidHisses + Meow: + collection: FelinidMeows + Mew: + collection: FelinidMews + Growl: + collection: FelinidGrowls + Purr: + collection: FelinidPurrs + Mars: + collection: Mars + Wurble: + collection: Wurble + - type: emoteSounds id: UnisexVox sounds: diff --git a/Resources/Prototypes/Voice/speech_emotes.yml b/Resources/Prototypes/Voice/speech_emotes.yml index ca4aa17229e..309794c1dcd 100644 --- a/Resources/Prototypes/Voice/speech_emotes.yml +++ b/Resources/Prototypes/Voice/speech_emotes.yml @@ -340,3 +340,19 @@ chatMessages: [ whirrs ] chatTriggers: - whirrs + +- type: emote + id: Mars + name: chat-emote-name-mars + category: Vocal + chatMessages: [ mars ] + chatTriggers: + - mars + +- type: emote + id: Wurble + name: chat-emote-name-wurble + category: Vocal + chatMessages: [ wurble ] + chatTriggers: + - wurble diff --git a/Resources/Prototypes/fonts.yml b/Resources/Prototypes/fonts.yml index 8e49fe8c355..c61a5fb8041 100644 --- a/Resources/Prototypes/fonts.yml +++ b/Resources/Prototypes/fonts.yml @@ -62,6 +62,10 @@ id: Noganas path: /Fonts/Noganas.ttf +- type: font + id: Lymphatic + path: /Fonts/Lymphatic.ttf + - type: font id: Cambria path: /Fonts/Cambria.ttf diff --git a/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml b/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml new file mode 100644 index 00000000000..f2f73ee6663 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml @@ -0,0 +1,36 @@ + +# Shadowkin + + + + + +Fluffy lil' guys. + +## Ability Differences + +- Need no air to survive +- Can Shadeskip +- Can travel to and from The Dark at will +- Dims nearby lights when in the The Dark +- When too low on energy, they may fall into a powerful sleep +- Can "speak" in the Empathy, which only other Shadowkin can understand +- Slightly less blunt damage +- A bit more slash damage +- Slightly more piercing damage +- Less cold damage +- Slightly more heat damage +- Near no cellular damage +- More bloodloss damage +- Slightly more shock damage +- More radiation damage + +## Physical Differences + +- Very large and brightly colored eyes with no pupils +- Sees the world through their eyes' tint +- Very large ears +- Very fluffy +- Can be Male, Female, or Unisex +- Can be 18-300 years old + \ No newline at end of file diff --git a/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt b/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt new file mode 100644 index 00000000000..e3cda6d77e2 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt @@ -0,0 +1,178 @@ +# Lore + +## Biology + +[color=#a88b5e]Physicality[/color] + +Shadowkin seem very similar to canine species when looked at. +While mammalian in nature, it is nearly impossible to find any sexual dimorphism between genders, if they even have any. +They seem to all share the same body shape and often voices. + +The head consists of a snout with a nose, sharp teeth in the mouth, and two big ears on top of the head. +In addition to the two big ears, Shadowkin have two smaller ears lower down, giving them an exceptionally good ability to detect where exactly sounds come from. + +Shadowkin eyes are usually very large and fully mono-colored; they have no discernible pupils, irises, or similar. +The eye of a Shadowkin is a solid orb of color and, perhaps, their most defining trait. + +All observations show Shadowkin do not seem to age after a certain point. +Some may look older or younger than others, but the aging process seems to stop when the Shadowkin is chronologically in their 20s. + +Unusually, compiled medical data on Shadowkin shows that they have no lungs, thus not needing to breathe. +The compiled medical data also shows that they have a large, more circular shape where their lungs would be. + +[color=#a88b5e]Mentality[/color] + +Shadowkin core personalities are mirrored by their eye color. +Some common colors and their meanings are: + +- Red: Determined to reach a goal, even if it means sacrificing others. +- Green: Eager to learn and fit in with others. +- Blue: Very calm and collected, or shy. + +Other colors also exist in addition to these but are not as common. +The other colors are just mixes of the three main colors. +For example, here are a couple of mixes: + +- Purple: Determined, yet probably won't harm, in some cases shy. +- Yellow: Quite eager to be number one, especially if related to knowledge. +- Orange: Shy and calm, yet will fight if provoked. +- White: Very robust, exceeds expectations in most things they do, but is quite boring to interact with generally. + +There are rumors other colors exist as well. +Shadowkin that lose their ability to travel between dimensions have [color=#000000]black eyes[/color] no matter their core personality. +These black-eyed Shadowkin make up the largest number of them living in Realspace. + +## History + +Shadowkin are creatures native to their so-called Pocket Dimension, a part of space that is unable to be accurately pinpointed and/or visited by most people. +From there, Shadowkin visit our worlds, and sometimes a large number of them are forced into our world in mass migrations and then live among us. +What exactly causes these mass migrations is unknown, but may be related to them losing their ability to travel between dimensions. + +[color=#a88b5e]Homeworld[/color] + +While no home system or world has been found, some Shadowkin talk about dreaming of their homeworld, describing a lush and green land with abundant resources. + +[color=#a88b5e]Racial/Government Status[/color] + +There currently exists no Shadowkin government, and with Shadowkin being as rare as they are, they tend to fit into currently established societies and civilizations, with varying degrees of success in the past. +In recent years, however, many cultures have taken to accepting Shadowkin as another galactic constant, going as far as allowing them lives similar to those that any of the other species would lead. + +Some systems early on took and experimented on Shadowkin, in an attempt to utilize their powers in various technology, and even recreating them with very experimental technology. +The tests failed and this was met with a lot of resistance from Shadowkin, many of them being killed or becoming Blackeyes in the process. +After this, those systems realized that Shadowkin were a sentient species, and deeply apologized for their actions. + +## Culture + +[color=#a88b5e]The Typical Experience[/color] + +Most times, life is pretty normal. A Shadowkin can have a job, go to the job to work, then spend time off with friends. +The fact is most times of the day, the small abilities one might have are nice, but not overly useful. Sure, a Shadowkin could dim the nearby lights, but at the same time, the Shadowkin could also just flick the light switch. +However, it can sometimes happen that when a Shadowkin slept in and needs to get to work quickly, they might just teleport to work. +Some people might be overly wary of Shadowkin, some might be drawn towards Shadowkin. +Many Shadowkin are used to not wearing clothes, which can lead to some awkward situations, but those are generally the same situations any other furred species would have. + +[color=#a88b5e]Language[/color] + +The only recorded spoken language of the Shadowkin is an unusual language named "Mar", named after the fact that every single word in this language is the word "mar". +Spoken in different tones, with more or less emphasis on the various parts, or by drawing out parts of this word, a multitude of different "mar"s can be created, but they follow no apparent conventions. +Shadowkin can perfectly understand each other though and discuss complex topics using only this one word. +The Shadowkin can hear the Mar language from anywhere and understand it via Empathy, yet do not know who it is from unless they are close enough to see the sender. +Other humanoid species are incapable of understanding or learning Mar, as they are unable to accentuate their speech in the same way or hear some of the silent, Empathic tones they use. +Shadowkin tend to learn one or more languages of the Realspace beings. +The capability in such learned languages depends fully on each single Shadowkin, where some have only very broken capabilities, while others are fluent in several languages. + +[color=#a88b5e]Naming Conventions[/color] + +Shadowkin tend to name themselves with a singular word, ranging from states of being, such as Lone and Collected, to words that describe their memories connected to their supposed homeworld, like Dreamer. +Name schemes of the humans are usually frowned upon, as they do not reflect upon the Shadowkin, resulting in reactions ranging anywhere from an actual frown to being entirely ignored. +Shadowkin following another species naming scheme are often Blackeyes, not fitting in with other Shadowkin as well. +Names hold a great deal for Shadowkin because of how closely they are often tied to describing who they are. +Following life-changing events, Shadowkin often change their names to reflect the new person they have become. + +[color=#a88b5e]Rumors and Speculation[/color] + +Shadowkin are beings from another dimension, capable of performing feats that others could only describe as "magic", with no scientific explanation. +It is speculated that Shadowkin actually have a whole nation in their dimension, and those that come to Realspace just simply refuse to talk about it. +Sometimes Shadowkin mention a "Hub", acting as evidence for this theory. + +Some believe Shadowkin are the result of experimentation, though them being taken and experimented on is a large piece of evidence denying this theory. + +Some believe Shadowkin are the result of an expedition crew that disappeared hundreds of years ago, even though the first Shadowkin sightings were long before that. +Obviously, this expedition ship performed time travel. + +## OOC Notes + +Extra information, should be read if playing Shadowkin, if not you should try to learn this in gameplay. + +[color=#a88b5e]History[/color] + +Shadowkin used to live on a very lush forest planet, using primitive technology to work with metal, create tools, and build stone structures. +According to the dreams, Shadowkin back then had forged tools and blades, but had no electricity, presumably putting them at a medieval technology level. +In a sudden event, everyone and everything organic suddenly found themselves in an alternate dimension, sucked straight out of their previous home. +The Shadowkin being ripped from their homes happened in three total "dimensional ripples". + +Now stuck in this alternate dimension, Shadowkin changed a lot. +They stopped aging and with time developed the ability to use the energy of this dimension to power their unusual "magic". +And even later, they learned how to leave the dimension and travel to Realspace and back again. +However, as the eons passes, memories became shady of their home. +Many Shadowkin forgot more and more about the thousands of years they had lived, reducing memory to focus on the more important closer time. +However in dreams, Shadowkin, even those born in Realspace, often have visions of their home planet. +Some events can trigger certain memories to return like meeting someone they knew from the past makes them remember in great detail who they are. +As such, it can happen that two Shadowkin can randomly meet and remember that they met hundreds of years ago, often confusing nearby unknowing humanoids. + +[color=#a88b5e]Biology[/color] + +If a Shadowkin personality changes drastically, their eye color will change as an effect, reflecting upon their new personality. +Shadowkin, being ageless entities, can look older or younger, but that is merely a visual representation of how they feel about their age. +22 years after birth a Shadowkin stops aging, and in all of their history no instance of them dying of old age has been recorded. + +[color=#a88b5e]Shadowkin energy[/color] + +Shadowkin have an odd organ in them, their "Core". +This organ is the source and storage place for their power. +Without it, they would be unable to use their abilities. +The Core is very sensitive to physical trauma, yet is very resilient to electrical or magical damage. +The Core can get irreversibly destroyed from overloading it, using too much power too quickly, which is what happens to the Blackeyes. +The Core can not be replaced, using a new one would require a new body, and trying to use another Shadowkin Core will result in death. + +The Shadowkin's ability to fall into a deep sleep is a method to recharge their energy at a significantly higher rate than idling. +Though while in this deep sleep, it is difficult to wake up, and cannot be woken up by anything other than themself. + +Shadowkin can know exactly how much energy they have, and feel differently based on how much they have. +They can also Empathically sense the energy of others nearby and can estimate how much energy they have based on their feelings. + +[color=#a88b5e]Shadowkin Abilities[/color] + +Shadowkin have a few abilities. +They can teleport, requiring a small amount of energy, but they can do it quickly. +They can teleport to and from The Dark, requiring much more energy than teleportation. +They can immediately fall into a deep sleep at any time. + +[color=#a88b5e]The Ritual[/color] + +When a Shadowkin reaches adulthood, they undergo a "Ritual". +During this Ritual they intentionally overload their energy reserves, forcing their capacity to expand rapidly in order to gain enough energy to travel between dimensions. +Most Shadowkin pass the Ritual and that's the end of it, but some Shadowkin perish or become Blackeyes during the ritual. + +[color=#a88b5e]Important Terminology[/color] + +[color=#a88b5e]Shadowkin:[/color] +The name given for your race. +While your race doesn't have a name for itself, this is how most of the Galaxy refers to you, so it's how you refer to yourself too. + +[color=#a88b5e]The Dark:[/color] +The other dimension where you are from. +There are theories that The Dark and Bluespace are connected, but you don't know the details about that. +The Dark is a dimension where strange rules apply. +For outsiders who somehow enter it, whatever they imagine being in there, they see there, if not too complex. +If an outsider comes with a willing Shadowkin they may see what the Shadowkin sees, being anything they imagine, or a reflection of the station. + +[color=#a88b5e]Blackeyes:[/color] +A Shadowkin who has undergone and failed the Ritual, or lost their ability via other methods. +The Blackeyes have completely black eyes, no matter their actual personality. +They choose a new name, which follows a favorite species' naming scheme. +They are often ignored or left alone more by other Shadowkin. +[color=#a88b5e]Note:[/color] Blackeyes correlation with black eyes is done entirely via roleplay, the game will not handle this for you. + +[color=#a88b5e]Mar:[/color] +A word used to verbally communicate feelings, emotions, wishes, hopes, ideas, and whatever, in addition to your Empathy. \ No newline at end of file diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png new file mode 100644 index 00000000000..7496c91c550 Binary files /dev/null and b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png differ diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png new file mode 100644 index 00000000000..265318a96f5 Binary files /dev/null and b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json new file mode 100644 index 00000000000..e4f5aa0f920 --- /dev/null +++ b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json @@ -0,0 +1,56 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/blob/master/icons/mob/clothing/back.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png new file mode 100644 index 00000000000..39fafeb1449 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/icon.png new file mode 100644 index 00000000000..8ef5aa68d09 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-left.png new file mode 100644 index 00000000000..7ecbd93e8ae Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png new file mode 100644 index 00000000000..26fd8e57a42 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json new file mode 100644 index 00000000000..555efbc7daa --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "JustAnOrange", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-EYES", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 00000000000..e8618fc6b7d Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png new file mode 100644 index 00000000000..def3161bb0a Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-left.png new file mode 100644 index 00000000000..5c15def7666 Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png new file mode 100644 index 00000000000..99c96d368ad Binary files /dev/null and b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json new file mode 100644 index 00000000000..57a0b994f9b --- /dev/null +++ b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "JustAnOrange", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png new file mode 100644 index 00000000000..3c2815e8c24 Binary files /dev/null and b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png differ diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/meta.json b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/meta.json new file mode 100644 index 00000000000..62b03fecafc --- /dev/null +++ b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + "size": { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "DEATHB4DEFEAT#4404 (801294818839756811)", + "states": [ + { + "name": "darkswap", + "directions": 1 + }, + { + "name": "rest", + "directions": 1 + }, + { + "name": "shadeskip", + "directions": 1 + } + ] +} diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/rest.png b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/rest.png new file mode 100644 index 00000000000..188b4ec5911 Binary files /dev/null and b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/rest.png differ diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png new file mode 100644 index 00000000000..b0f1bd8dc8c Binary files /dev/null and b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json new file mode 100644 index 00000000000..f642565f9ab --- /dev/null +++ b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json @@ -0,0 +1,43 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "DEATHB4DEFEAT#4404 (801294818839756811)", + "states": [ + { + "name": "power0", + "delays": [[0.8, 0.8, 0.8, 0.8, 0.8, 0.8]] + }, + { + "name": "power1", + "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.7]] + }, + { + "name": "power2", + "delays": [[0.6, 0.6, 0.6, 0.6, 0.6, 0.6]] + }, + { + "name": "power3", + "delays": [[0.5, 0.5, 0.5, 0.5, 0.5, 0.5]] + }, + { + "name": "power4", + "delays": [[0.4, 0.4, 0.4, 0.4, 0.4, 0.4]] + }, + { + "name": "power5", + "delays": [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]] + }, + { + "name": "power6", + "delays": [[0.2, 0.2, 0.2, 0.2, 0.2, 0.2]] + }, + { + "name": "power7", + "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1]] + } + ] +} diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png new file mode 100644 index 00000000000..ab370f753ed Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png new file mode 100644 index 00000000000..d72965eeec8 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power2.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power2.png new file mode 100644 index 00000000000..1b2c51575c7 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power2.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power3.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power3.png new file mode 100644 index 00000000000..0f93f925ac2 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power3.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power4.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power4.png new file mode 100644 index 00000000000..3f3035da0df Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power4.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power5.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power5.png new file mode 100644 index 00000000000..af3f7168618 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power5.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power6.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power6.png new file mode 100644 index 00000000000..9f7957c44f2 Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power6.png differ diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power7.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power7.png new file mode 100644 index 00000000000..ac9f17f55bf Binary files /dev/null and b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power7.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json new file mode 100644 index 00000000000..b67f4f34899 --- /dev/null +++ b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from S.P.L.U.R.T ears.dmi at commit https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/8b4abffbe538fbded19b44b989ebc6808748f31f", + "states": [ + { + "name": "shadowkin", + "directions": 4 + }, + { + "name": "shadowkin_stripes", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png new file mode 100644 index 00000000000..3aec926c5e2 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin_stripes.png b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin_stripes.png new file mode 100644 index 00000000000..ce1f04ce602 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin_stripes.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json new file mode 100644 index 00000000000..4731b9de3dd --- /dev/null +++ b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/96703f76bccd8fe6a96b78524efb97a9af661767 Shadowkin big fluff made by DSC@Cabbage#9633 (561159087765848084)", + "states": [ + { + "name": "shadowkin_shorter", + "directions": 4 + }, + { + "name": "shadowkin_medium", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png new file mode 100644 index 00000000000..ca162bb1b05 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png new file mode 100644 index 00000000000..979e4e6b26c Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/meta.json b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/meta.json new file mode 100644 index 00000000000..535c01bbaf1 --- /dev/null +++ b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + "size": { + "x": 64, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/96703f76bccd8fe6a96b78524efb97a9af661767 Shadowkin big fluff made by DSC@Cabbage#9633 (561159087765848084)", + "states": [ + { + "name": "shadowkin", + "directions": 4 + }, + { + "name": "shadowkin_big", + "directions": 4 + }, + { + "name": "shadowkin_big_fluff", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin.png new file mode 100644 index 00000000000..94ff21d9cbb Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big.png new file mode 100644 index 00000000000..988aa6e589f Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big.png differ diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big_fluff.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big_fluff.png new file mode 100644 index 00000000000..20ad696ccee Binary files /dev/null and b/Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big_fluff.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png new file mode 100644 index 00000000000..0d2ad309c74 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png new file mode 100644 index 00000000000..ac2806b79ce Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png new file mode 100644 index 00000000000..ac2d7893fdb Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png new file mode 100644 index 00000000000..6ff3ac86b76 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/eyes.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/eyes.png new file mode 100644 index 00000000000..f7c0a306aaa Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/eyes.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/heart.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/heart.png new file mode 100644 index 00000000000..1b79b529aee Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/heart.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png new file mode 100644 index 00000000000..482bb241022 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png new file mode 100644 index 00000000000..0a2e6ab25ae Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/lungs.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/lungs.png new file mode 100644 index 00000000000..a76c9fc1eb4 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/lungs.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json new file mode 100644 index 00000000000..1c9aebfb6d6 --- /dev/null +++ b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/commit/f309886bf3e29808206693e9142304260df134e9", + "states": [ + { + "name": "appendix" + }, + { + "name": "brain" + }, + { + "name": "core" + }, + { + "name": "ears" + }, + { + "name": "eyes" + }, + { + "name": "heart" + }, + { + "name": "kidneys" + }, + { + "name": "liver" + }, + { + "name": "lungs" + }, + { + "name": "stomach" + }, + { + "name": "tongue" + } + ] +} diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png new file mode 100644 index 00000000000..a0341750d32 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png new file mode 100644 index 00000000000..64306900f57 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png new file mode 100644 index 00000000000..20fd326f17f Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full-nomarkings.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full-nomarkings.png new file mode 100644 index 00000000000..b62d81fec79 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full-nomarkings.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full.png new file mode 100644 index 00000000000..253eb0c3c9b Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_f.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_f.png new file mode 100644 index 00000000000..9d99bd18903 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_f.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_m.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_m.png new file mode 100644 index 00000000000..9d99bd18903 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_m.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_arm.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_arm.png new file mode 100644 index 00000000000..078ca333f6d Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_arm.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png new file mode 100644 index 00000000000..30ad69dc3f0 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png new file mode 100644 index 00000000000..0cbc7371d14 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_leg.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_leg.png new file mode 100644 index 00000000000..b961dfc842a Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_leg.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json new file mode 100644 index 00000000000..a259ab696b8 --- /dev/null +++ b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json @@ -0,0 +1,71 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/5bc3ea02ce03a551c85017f1ddd411315a19a5ca#diff-519788fa2ca74299d1686a44d3ab2098b49ed5ab65d293ec742bead7d49f0b8d", + "states": [ + { + "name": "full", + "directions": 4 + }, + { + "name": "full-nomarkings", + "directions": 4 + }, + { + "name": "head_m", + "directions": 4 + }, + { + "name": "head_f", + "directions": 4 + }, + { + "name": "torso_m", + "directions": 4 + }, + { + "name": "torso_f", + "directions": 4 + }, + { + "name": "r_arm", + "directions": 4 + }, + { + "name": "l_arm", + "directions": 4 + }, + { + "name": "r_hand", + "directions": 4 + }, + { + "name": "l_hand", + "directions": 4 + }, + { + "name": "r_leg", + "directions": 4 + }, + { + "name": "r_foot", + "directions": 4 + }, + { + "name": "l_leg", + "directions": 4 + }, + { + "name": "l_foot", + "directions": 4 + }, + { + "name": "eyes", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png new file mode 100644 index 00000000000..c294120942c Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png new file mode 100644 index 00000000000..390e0a27ee3 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_hand.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_hand.png new file mode 100644 index 00000000000..331e33a587e Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_hand.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_leg.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_leg.png new file mode 100644 index 00000000000..6b0270f6343 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_leg.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_f.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_f.png new file mode 100644 index 00000000000..83cc63cdd29 Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_f.png differ diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png new file mode 100644 index 00000000000..dafc83b65eb Binary files /dev/null and b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png differ diff --git a/Resources/Textures/Objects/Fun/toys.rsi/meta.json b/Resources/Textures/Objects/Fun/toys.rsi/meta.json index 24345aadf38..cc03557e0b4 100644 --- a/Resources/Textures/Objects/Fun/toys.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toys.rsi/meta.json @@ -386,6 +386,9 @@ { "name": "beachb-inhand-right", "directions": 4 + }, + { + "name": "shadowkin" } ] } \ No newline at end of file diff --git a/Resources/Textures/Objects/Fun/toys.rsi/shadowkin.png b/Resources/Textures/Objects/Fun/toys.rsi/shadowkin.png new file mode 100644 index 00000000000..df4118ecd45 Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/shadowkin.png differ diff --git a/Resources/Textures/Shaders/color_tint.swsl b/Resources/Textures/Shaders/color_tint.swsl new file mode 100644 index 00000000000..a5449b2d4d6 --- /dev/null +++ b/Resources/Textures/Shaders/color_tint.swsl @@ -0,0 +1,56 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; +uniform lowp vec3 tint_color; // RGB color between 0 and 1 +uniform lowp float tint_amount; // Number between 0 and 1 + +// Function to convert RGB to HSV. +highp vec3 rgb2hsv(highp vec3 c) +{ + highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + highp vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + highp vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + highp float d = q.x - min(q.w, q.y); + /* float e = 1.0e-10; */ + highp float e = 0.0000000001; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +// Function to convert HSV to RGB. +highp vec3 hsv2rgb(highp vec3 c) +{ + highp vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void fragment() { + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // Convert color to HSV. + highp vec3 hsvTint = rgb2hsv(tint_color); + highp vec3 hsvColor = rgb2hsv(color.rgb); + + // Set the original hue to the tint hue as long as it's not greyscale. + if (hsvTint.y > 0.05 && hsvTint.z != 0.0) + { + hsvColor.x = hsvTint.x; + } + // Modify saturation based on tint color saturation, + // Halving it if it's higher and capping it at the original. + hsvColor.y = (hsvColor.y < hsvTint.y) ? + mix(hsvColor.y, hsvTint.y, 0.75) : mix(hsvColor.y, hsvTint.y, 0.35); + + // Modify value based on tint color value, but only if it's darker. + hsvColor.z = (mix(hsvColor.z, hsvTint.z, 0.85) <= hsvColor.z) ? + mix(hsvColor.z, hsvTint.z, 0.85) : hsvColor.z; + + // Convert back to RGB. + highp vec3 rgbColorMod = hsv2rgb(hsvColor); + + // Mix the final RGB product with the original color to the intensity of the tint. + color.rgb = mix(color.rgb, rgbColorMod, tint_amount); + + COLOR = color; +} \ No newline at end of file diff --git a/Resources/Textures/Shaders/ethereal.swsl b/Resources/Textures/Shaders/ethereal.swsl new file mode 100644 index 00000000000..dc9d971e1cd --- /dev/null +++ b/Resources/Textures/Shaders/ethereal.swsl @@ -0,0 +1,75 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; + +// Function to convert RGB to HSV. +highp vec3 rgb2hsv(highp vec3 c) +{ + highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + highp vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + highp vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + highp float d = q.x - min(q.w, q.y); + /* float e = 1.0e-10; */ + highp float e = 0.0000000001; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +// Function to convert HSV to RGB. +highp vec3 hsv2rgb(highp vec3 c) +{ + highp vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +// Random number function with potential negative values. +highp float rand(highp vec2 n) { + highp float r = 2.0 * (0.5 + 0.5 * fract (sin (dot (n.xy, vec2(12.9898, 78.233)))* 43758.5453)) - 1.0; + return r * (r < 0.0 ? 0.8 : 1.3); +} + +void fragment() { + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // Increase the contrast of the image if the luminance is low enough. + highp float luminance = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (luminance < 0.06) { + color.rgb *= 0.5; + } + + // Convert to HSV. + highp vec3 hsvColor = rgb2hsv(color.rgb); + + // Apply a breathing effect to the value of the image. + hsvColor.z *= mix(0.35, 0.7, (sin(TIME) * 0.65)); + + // Increase the saturation of the color, incorperating a random value, as long as the value is above 0.1. + if (hsvColor.z > 0.065) { + hsvColor.y *= (rand(FRAGCOORD.xy * (TIME * 0.15)) * 1.5) + 1.0; + } + + // Convert back to RGB. + color.rgb = hsv2rgb(hsvColor); + + + + // get distortion magnitude. hand crafted from a random jumble of trig functions + highp float w = sin(TIME + (FRAGCOORD.x + FRAGCOORD.y + 2.0*sin(TIME*0.3) * sin(TIME*0.3 + FRAGCOORD.x - FRAGCOORD.y)) ); + + // visualize distortion via: + // COLOR = vec4(w,w,w,1.0); + + w *= 5.0; + + highp vec4 background = zTextureSpec(SCREEN_TEXTURE, ( FRAGCOORD.xy + vec2(w) ) * SCREEN_PIXEL_SIZE ); + highp vec3 hsvBg = rgb2hsv(background.rgb); + hsvBg.x *= -1.0; + background.rgb = hsv2rgb(hsvBg); + + color.xyz = mix(background.xyz, color.xyz, 0.75); + + + + COLOR = color; +}