diff --git a/Content.Client/_White/Overlays/NightVisionOverlay.cs b/Content.Client/_White/Overlays/NightVisionOverlay.cs index 6f616c061c..ffc98c1294 100644 --- a/Content.Client/_White/Overlays/NightVisionOverlay.cs +++ b/Content.Client/_White/Overlays/NightVisionOverlay.cs @@ -31,19 +31,32 @@ namespace Content.Client._White.Overlays var handle = args.WorldHandle; - if (!_entityManager.TryGetComponent(_playerManager.LocalSession?.AttachedEntity, - out var component)) + Color? color = null; + + if (_entityManager.TryGetComponent(_playerManager.LocalSession?.AttachedEntity, + out var component) && component.IsActive) { - return; + _shader.SetParameter("tint", component.Tint); + _shader.SetParameter("luminance_threshold", component.Strength); + _shader.SetParameter("noise_amount", component.Noise); + color = component.Color; + } + else if (_entityManager.TryGetComponent( + _playerManager.LocalSession?.AttachedEntity, out var tempNvComp)) + { + _shader.SetParameter("tint", tempNvComp.Tint); + _shader.SetParameter("luminance_threshold", tempNvComp.Strength); + _shader.SetParameter("noise_amount", tempNvComp.Noise); + color = tempNvComp.Color; } + if (color == null) + return; + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); - _shader.SetParameter("tint", component.Tint); - _shader.SetParameter("luminance_threshold", component.Strength); - _shader.SetParameter("noise_amount", component.Noise); handle.UseShader(_shader); - handle.DrawRect(args.WorldBounds, component.Color); + handle.DrawRect(args.WorldBounds, color.Value); handle.UseShader(null); } } diff --git a/Content.Client/_White/Overlays/NightVisionSystem.cs b/Content.Client/_White/Overlays/NightVisionSystem.cs index 4da1d57626..00934c5765 100644 --- a/Content.Client/_White/Overlays/NightVisionSystem.cs +++ b/Content.Client/_White/Overlays/NightVisionSystem.cs @@ -23,23 +23,56 @@ public sealed class NightVisionSystem : SharedNightVisionSystem SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnRestart); + SubscribeLocalEvent(OnTempInit); + SubscribeLocalEvent(OnTempRemove); + SubscribeLocalEvent(OnTempPlayerAttached); + SubscribeLocalEvent(OnTempPlayerDetached); + _overlay = new NightVisionOverlay(); } + private void OnTempPlayerAttached(Entity ent, ref PlayerAttachedEvent args) + { + UpdateNightVision(args.Player, true); + } + + private void OnTempPlayerDetached(Entity ent, ref PlayerDetachedEvent args) + { + UpdateNightVision(args.Player, false); + } + + private void OnTempRemove(Entity ent, ref ComponentRemove args) + { + if (TryComp(ent, out NightVisionComponent? nightVision) && nightVision.IsActive) + return; + + UpdateNightVision(ent, false); + } + + private void OnTempInit(Entity ent, ref ComponentInit args) + { + UpdateNightVision(ent, true); + } + private void OnPlayerAttached(EntityUid uid, NightVisionComponent component, PlayerAttachedEvent args) { - if (_player.LocalSession != args.Player) + if (!component.IsActive && HasComp(args.Entity)) return; - UpdateNightVision(component.IsActive); + UpdateNightVision(args.Player, component.IsActive); } private void OnPlayerDetached(EntityUid uid, NightVisionComponent component, PlayerDetachedEvent args) { - if (_player.LocalSession != args.Player) + UpdateNightVision(args.Player, false); + } + + private void UpdateNightVision(ICommonSession player, bool active) + { + if (_player.LocalSession != player) return; - UpdateNightVision(false); + UpdateNightVision(active); } protected override void UpdateNightVision(EntityUid uid, bool active) diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 252d29811c..544a84f755 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared._White.Mood; +using Content.Shared.Changeling; using Robust.Shared.Containers; namespace Content.Server.Atmos.EntitySystems @@ -234,6 +235,12 @@ namespace Content.Server.Atmos.EntitySystems if (pressure > Atmospherics.WarningLowPressure) goto default; + if (TryComp(uid, out VoidAdaptationComponent? voidAdaptation)) // WD + { + voidAdaptation.ChemMultiplier = 0.75f; + goto default; + } + // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false); diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 7b7cb580a1..17b547f1d7 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -22,7 +22,8 @@ using Content.Shared.Mobs.Components; // WD using Content.Shared.Mobs.Systems; using Content.Shared.Popups; // WD using Content.Shared._White.CPR.Events; -using Content.Shared._White.Mood; // WD +using Content.Shared._White.Mood; +using Content.Shared.Changeling; // WD using JetBrains.Annotations; using Robust.Server.Audio; // WD using Robust.Shared.Audio; // WD @@ -97,6 +98,14 @@ public sealed class RespiratorSystem : EntitySystem if (respirator.Saturation < respirator.SuffocationThreshold) { + if (TryComp(uid, out VoidAdaptationComponent? voidAdaptation)) + { + voidAdaptation.ChemMultiplier = 0.75f; + StopSuffocation(uid, respirator); + respirator.SuffocationCycles = 0; + continue; + } + if (_gameTiming.CurTime >= respirator.LastGaspPopupTime + respirator.GaspPopupCooldown) { respirator.LastGaspPopupTime = _gameTiming.CurTime; diff --git a/Content.Server/Changeling/ChangelingRuleSystem.cs b/Content.Server/Changeling/ChangelingRuleSystem.cs index 04b85cc92a..32a68b8da2 100644 --- a/Content.Server/Changeling/ChangelingRuleSystem.cs +++ b/Content.Server/Changeling/ChangelingRuleSystem.cs @@ -40,7 +40,7 @@ public sealed class ChangelingRuleSystem : GameRuleSystem(OnAdrenalineSacs); SubscribeLocalEvent(OnFleshMend); SubscribeLocalEvent(OnBiodegrade); + SubscribeLocalEvent(OnEyesight); + SubscribeLocalEvent(OnDissonantShriek); SubscribeLocalEvent(OnArmBlade); SubscribeLocalEvent(OnShield); @@ -109,6 +118,9 @@ public sealed partial class ChangelingSystem SubscribeLocalEvent(OnLesserFormDoAfter); SubscribeLocalEvent(OnTransformUiMessage); + + SubscribeLocalEvent(OnEyesightPurchased); + SubscribeLocalEvent(OnVoidAdaptationPurchased); } #region Data @@ -259,7 +271,8 @@ public sealed partial class ChangelingSystem new RegenerateDoAfterEvent(), args.Performer, args.Performer, args.Performer) { - RequireCanInteract = false + RequireCanInteract = false, + Hidden = true }); component.IsRegenerating = true; @@ -267,7 +280,7 @@ public sealed partial class ChangelingSystem private void OnLesserForm(EntityUid uid, ChangelingComponent component, LesserFormActionEvent args) { - if (_mobStateSystem.IsDead(uid) || component.IsRegenerating) + if (!_mobStateSystem.IsAlive(uid)) { _popup.PopupEntity(Loc.GetString("changeling-popup-cant-perform"), uid, uid); return; @@ -282,7 +295,8 @@ public sealed partial class ChangelingSystem _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, component.LesserFormDelay, new LesserFormDoAfterEvent(), args.Performer, args.Performer) { - BreakOnUserMove = true + BreakOnUserMove = true, + RequireCanInteract = false }); } @@ -490,7 +504,7 @@ public sealed partial class ChangelingSystem if (!TakeChemicals(uid, component, 20)) return; - _solutionContainer.TryAddReagent(injectable.Value, "Omnizine", 25); + _solutionContainer.TryAddReagent(injectable.Value, "Ichor", 10); if (TryComp(uid, out BloodstreamComponent? bloodstream)) { _blood.TryModifyBleedAmount(uid, -bloodstream.BleedAmount, bloodstream); @@ -501,7 +515,7 @@ public sealed partial class ChangelingSystem private void OnBiodegrade(EntityUid uid, ChangelingComponent component, BiodegradeActionEvent args) { - if (_mobStateSystem.IsDead(uid)) + if (!_mobStateSystem.IsAlive(uid)) return; if (!TryComp(uid, out CuffableComponent? cuffs) || cuffs.Container.ContainedEntities.Count < 1) @@ -512,7 +526,7 @@ public sealed partial class ChangelingSystem var lastAddedCuffs = cuffs.LastAddedCuffs; - _cuffable.Uncuff(uid, lastAddedCuffs, lastAddedCuffs); + _cuffable.Uncuff(uid, null, lastAddedCuffs); Del(lastAddedCuffs); @@ -523,14 +537,14 @@ public sealed partial class ChangelingSystem private void OnArmBlade(EntityUid uid, ChangelingComponent component, ArmbladeActionEvent args) { - SpawnOrDeleteItem(uid, "ArmBlade"); + SpawnOrDeleteItem(uid, component, "ArmBlade", 20); args.Handled = true; } private void OnShield(EntityUid uid, ChangelingComponent component, OrganicShieldActionEvent args) { - SpawnOrDeleteItem(uid, "OrganicShield"); + SpawnOrDeleteItem(uid, component, "OrganicShield", 20); args.Handled = true; } @@ -538,43 +552,83 @@ public sealed partial class ChangelingSystem private void OnArmor(EntityUid uid, ChangelingComponent component, ChitinousArmorActionEvent args) { const string outerName = "outerClothing"; - const string protoName = "ClothingOuterChangeling"; + const string headName = "head"; + const string armorName = "ClothingOuterChangeling"; + const string helmetName = "ClothingHeadHelmetLing"; - if (!_inventorySystem.TryGetSlotEntity(uid, outerName, out var outerEnt)) + _inventorySystem.TryUnequip(uid, outerName, out var outer, true, true); + _inventorySystem.TryUnequip(uid, headName, out var helmet, true, true); + + if (TryComp(outer, out MetaDataComponent? metaData) && metaData.EntityPrototype is {ID: armorName}) { - _inventorySystem.SpawnItemInSlot(uid, outerName, protoName, silent: true); + args.Handled = true; return; } - if (!TryComp(outerEnt, out var meta)) + if (!TakeChemicals(uid, component, 20)) { - _inventorySystem.SpawnItemInSlot(uid, outerName, protoName, silent: true); + if (outer != null) + _inventorySystem.TryEquip(uid, outer.Value, outerName, true, true); + + if (helmet != null) + _inventorySystem.TryEquip(uid, helmet.Value, headName, true, true); + return; } - if (meta.EntityPrototype == null) - { - _inventorySystem.SpawnItemInSlot(uid, outerName, protoName, silent: true); - return; - } - - if (meta.EntityPrototype.ID == protoName) - { - _inventorySystem.TryUnequip(uid, outerName, out var removedItem, force: true); - QueueDel(removedItem); - return; - } - - _inventorySystem.TryUnequip(uid, outerName, out _); - - _inventorySystem.SpawnItemInSlot(uid, outerName, protoName, silent: true); + _inventorySystem.SpawnItemInSlot(uid, outerName, armorName, true, true); + _inventorySystem.SpawnItemInSlot(uid, headName, helmetName, true, true); args.Handled = true; } private void OnTentacleArm(EntityUid uid, ChangelingComponent component, TentacleArmActionEvent args) { - SpawnOrDeleteItem(uid, "TentacleArmGun"); + SpawnOrDeleteItem(uid, component, "TentacleArmGun", 10); + + args.Handled = true; + } + + private void OnEyesightPurchased(Entity ent, ref AugmentedEyesightPurchasedEvent args) + { + EnsureComp(ent); + EnsureComp(ent); + } + + private void OnVoidAdaptationPurchased(Entity ent, ref VoidAdaptationPurchasedEvent args) + { + EnsureComp(ent); + } + + private void OnEyesight(Entity ent, ref AugmentedEyesightActionEvent args) + { + if (!_mobStateSystem.IsAlive(ent)) + return; + + args.Handled = true; + + if (HasComp(ent)) + { + RemComp(ent); + EnsureComp(ent); + EnsureComp(ent); + return; + } + + EnsureComp(ent); + RemComp(ent); + RemComp(ent); + } + + private void OnDissonantShriek(Entity ent, ref DissonantShriekActionEvent args) + { + if (!_mobStateSystem.IsAlive(ent)) + return; + + if (!TakeChemicals(ent, ent.Comp, 20)) + return; + + _empSystem.EmpPulse(_transform.GetMapCoordinates(ent), 5, 100000, 10f); args.Handled = true; } @@ -612,6 +666,20 @@ public sealed partial class ChangelingSystem component.AbsorbedCount++; + _chemicalsSystem.AddChemicals(uid, component, 50); + + if (_container.TryGetContainer(uid, ImplanterComponent.ImplantSlotId, out var implantContainer)) + { + foreach (var implant in implantContainer.ContainedEntities) + { + if (!TryComp(implant, out var store) || store.Preset != "StorePresetChangeling") + continue; + + store.Refunds = true; + store.RefundAllowed = true; + } + } + if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is { } pulled && TryComp(pulled, out SharedPullableComponent? pullable)) { @@ -653,6 +721,7 @@ public sealed partial class ChangelingSystem { if (args.Handled || args.Cancelled || args.Target == null) { + component.IsRegenerating = false; return; } @@ -664,7 +733,10 @@ public sealed partial class ChangelingSystem } if (!TakeChemicals(uid, component, 15)) + { + component.IsRegenerating = false; return; + } _rejuvenate.PerformRejuvenate(args.Target.Value); @@ -682,14 +754,14 @@ public sealed partial class ChangelingSystem if (args.Handled || args.Cancelled) return; + if (!TakeChemicals(uid, component, 5)) + return; + var polymorphEntity = _polymorph.PolymorphEntity(args.User, "MonkeyChangeling"); if (polymorphEntity == null) return; - if (!TakeChemicals(uid, component, 5)) - return; - var toAdd = new ChangelingComponent { HiveName = component.HiveName, @@ -701,6 +773,8 @@ public sealed partial class ChangelingSystem EntityManager.AddComponent(polymorphEntity.Value, toAdd); + TransferComponents(uid, polymorphEntity.Value); + _implantSystem.TransferImplants(uid, polymorphEntity.Value); _actionContainerSystem.TransferAllActionsFiltered(uid, polymorphEntity.Value, polymorphEntity.Value); _action.GrantContainedActions(polymorphEntity.Value, polymorphEntity.Value); @@ -830,12 +904,6 @@ public sealed partial class ChangelingSystem _identity.QueueIdentityUpdate(polymorphEntity.Value); - if (HasComp(target)) - EnsureComp(polymorphEntity.Value); - - if (HasComp(target)) - EnsureComp(polymorphEntity.Value); - if (TryComp(target, out ChangelingComponent? lingComp)) { var toAdd = new ChangelingComponent @@ -850,18 +918,7 @@ public sealed partial class ChangelingSystem _chemicalsSystem.UpdateAlert(polymorphEntity.Value, toAdd); } - if (TryComp(target, out NpcFactionMemberComponent? factionMember)) - { - _faction.ClearFactions(polymorphEntity.Value); - foreach (var faction in factionMember.Factions) - { - _faction.AddFaction(polymorphEntity.Value, faction); - } - } - - _nukeOps.TransferRole(target, polymorphEntity.Value); - - _cult.TransferRole(target, polymorphEntity.Value); + TransferComponents(target, polymorphEntity.Value); _implantSystem.TransferImplants(target, polymorphEntity.Value); _actionContainerSystem.TransferAllActionsFiltered(target, polymorphEntity.Value, polymorphEntity.Value); @@ -870,6 +927,50 @@ public sealed partial class ChangelingSystem return polymorphEntity; } + private void TransferComponents(EntityUid from, EntityUid to) + { + if (HasComp(from)) + EnsureComp(to); + + if (HasComp(from)) + EnsureComp(to); + + if (HasComp(from)) + EnsureComp(to); + + if (HasComp(from)) + EnsureComp(to); + + if (HasComp(from)) + EnsureComp(to); + + if (TryComp(from, out TemporaryNightVisionComponent? nvComp)) + { + var toAdd = new TemporaryNightVisionComponent + { + Color = nvComp.Color, + Tint = nvComp.Tint, + Strength = nvComp.Strength, + Noise = nvComp.Noise + }; + + EntityManager.AddComponent(to, toAdd); + } + + if (TryComp(from, out NpcFactionMemberComponent? factionMember)) + { + _faction.ClearFactions(to); + foreach (var faction in factionMember.Factions) + { + _faction.AddFaction(to, faction); + } + } + + _nukeOps.TransferRole(from, to); + + _cult.TransferRole(from, to); + } + private void TransferDna(EntityUid target, string dna) { if (!TryComp(target, out var dnaComponent)) @@ -932,7 +1033,7 @@ public sealed partial class ChangelingSystem Dirty(target, targetHumanoid); } - private void SpawnOrDeleteItem(EntityUid target, string prototypeName) + private void SpawnOrDeleteItem(EntityUid target, ChangelingComponent component, string prototypeName, int chemicals) { foreach (var eHand in _handsSystem.EnumerateHands(target)) { @@ -952,6 +1053,9 @@ public sealed partial class ChangelingSystem return; } + if (!TakeChemicals(target, component, chemicals)) + return; + var item = Spawn(prototypeName, Transform(target).Coordinates); if (!_handsSystem.TryPickup(target, item, hand, animate: false)) diff --git a/Content.Server/Changeling/ChangelingSystem.Shop.cs b/Content.Server/Changeling/ChangelingSystem.Shop.cs index b0b6eb789f..d24e908dd0 100644 --- a/Content.Server/Changeling/ChangelingSystem.Shop.cs +++ b/Content.Server/Changeling/ChangelingSystem.Shop.cs @@ -1,9 +1,10 @@ -using Content.Server.Store.Components; -using Content.Server.Store.Systems; +using Content.Server.Flash.Components; +using Content.Server.Store.Components; +using Content.Shared._White.Overlays; using Content.Shared.Changeling; +using Content.Shared.Eye.Blinding.Components; using Content.Shared.Implants.Components; -using Robust.Server.GameStates; -using Robust.Server.Placement; +using Content.Shared.Inventory; namespace Content.Server.Changeling; @@ -11,7 +12,35 @@ public sealed partial class ChangelingSystem { private void InitializeShop() { - SubscribeLocalEvent(OnShop); + SubscribeLocalEvent(OnShop); + SubscribeLocalEvent(OnChangelingRefund); + SubscribeLocalEvent>(OnRefund); + } + + private void OnRefund(Entity ent, + ref InventoryRelayedEvent args) + { + QueueDel(ent); + } + + private void OnChangelingRefund(Entity ent, ref ChangelingRefundEvent args) + { + RemComp(ent); + RemComp(ent); + RemComp(ent); + RemComp(ent); + + foreach (var hand in _handsSystem.EnumerateHands(ent)) + { + if (hand.HeldEntity != null && HasComp(hand.HeldEntity.Value)) + QueueDel(hand.HeldEntity.Value); + } + + if (!TryComp(args.Store, out StoreComponent? storeComponent)) + return; + + storeComponent.Refunds = false; + _storeSystem.DisableRefund(args.Store, storeComponent); } private void OnShop(EntityUid uid, SubdermalImplantComponent component, ChangelingShopActionEvent args) diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index f05ae7874a..baaf258f93 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -5,6 +5,7 @@ using Content.Server.PDA.Ringer; using Content.Server.Stack; using Content.Server.Store.Components; using Content.Shared.Actions; +using Content.Shared.Changeling; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; @@ -364,6 +365,7 @@ public sealed partial class StoreSystem // Reset store back to its original state RefreshAllListings(component); component.BalanceSpent = new(); + RaiseLocalEvent(buyer, new ChangelingRefundEvent {Store = uid}); // WD UpdateUserInterface(buyer, uid, component); } diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index fed90a4aec..e5f918d71c 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Body.Components; using Content.Server.Temperature.Components; using Content.Shared.Alert; using Content.Shared.Atmos; +using Content.Shared.Changeling; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Inventory; @@ -279,6 +280,19 @@ public sealed class TemperatureSystem : EntitySystem } else if (temperature.CurrentTemperature <= coldDamageThreshold) { + if (TryComp(uid, out VoidAdaptationComponent? voidAdaptation)) // WD + { + if (temperature.TakingDamage) + { + _adminLogger.Add(LogType.Temperature, + $"{ToPrettyString(uid):entity} stopped taking temperature damage"); + temperature.TakingDamage = false; + } + + voidAdaptation.ChemMultiplier = 0.75f; + return; + } + if (!temperature.TakingDamage) { _adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} started taking low temperature damage"); diff --git a/Content.Server/_White/ChangeTemperatureOnCollide/LowTemperatureSlowdownSystem.cs b/Content.Server/_White/ChangeTemperatureOnCollide/LowTemperatureSlowdownSystem.cs index 9c5fc8dada..da4f986857 100644 --- a/Content.Server/_White/ChangeTemperatureOnCollide/LowTemperatureSlowdownSystem.cs +++ b/Content.Server/_White/ChangeTemperatureOnCollide/LowTemperatureSlowdownSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; +using Content.Shared.Changeling; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; @@ -20,7 +21,9 @@ public sealed class LowTemperatureSlowdownSystem : EntitySystem private void OnMoveSpeedRefresh(EntityUid uid, TemperatureComponent component, RefreshMovementSpeedModifiersEvent args) { - var modifier = !component.Slowdown ? 1f : GetSpeedModifier(component.CurrentTemperature); + var modifier = HasComp(uid) || !component.Slowdown + ? 1f + : GetSpeedModifier(component.CurrentTemperature); args.ModifySpeed(modifier, modifier); } diff --git a/Content.Shared/Changeling/ChangelingComponent.cs b/Content.Shared/Changeling/ChangelingComponent.cs index 9b6a1c5ba9..68171218cb 100644 --- a/Content.Shared/Changeling/ChangelingComponent.cs +++ b/Content.Shared/Changeling/ChangelingComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Humanoid; +using Content.Shared.Humanoid; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -9,7 +9,7 @@ namespace Content.Shared.Changeling; public sealed partial class ChangelingComponent : Component { [DataField("chemRegenRate")] - public int ChemicalRegenRate = 2; + public int ChemicalRegenRate = 4; [DataField("chemicalCap")] public int ChemicalCapacity = 75; @@ -24,7 +24,7 @@ public sealed partial class ChangelingComponent : Component public float Accumulator; [ViewVariables(VVAccess.ReadOnly)] - public float UpdateDelay = 6f; + public float UpdateDelay = 4f; [ViewVariables(VVAccess.ReadOnly)] public bool IsRegenerating; @@ -48,13 +48,13 @@ public sealed partial class ChangelingComponent : Component public float AbsorbDnaDelay = 10f; [ViewVariables(VVAccess.ReadWrite), DataField("TransformDelay")] - public float TransformDelay = 2f; + public float TransformDelay; [ViewVariables(VVAccess.ReadWrite), DataField("RegenerateDelay")] public float RegenerateDelay = 60f; [ViewVariables(VVAccess.ReadWrite), DataField("LesserFormDelay")] - public float LesserFormDelay = 5f; + public float LesserFormDelay; public bool IsInited; } diff --git a/Content.Shared/Changeling/ChemicalsSystem.cs b/Content.Shared/Changeling/ChemicalsSystem.cs index 304892d81b..761cb75b58 100644 --- a/Content.Shared/Changeling/ChemicalsSystem.cs +++ b/Content.Shared/Changeling/ChemicalsSystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.Alert; +using Content.Shared.Alert; +using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; using Robust.Shared.Network; @@ -10,24 +11,38 @@ public sealed class ChemicalsSystem : EntitySystem [Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly INetManager _net = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>( + OnChemRegenModify); + SubscribeLocalEvent(OnVoidAdaptationChemRegenModify); + } + + private void OnVoidAdaptationChemRegenModify(Entity ent, ref ChemRegenModifyEvent args) + { + args.Multiplier *= ent.Comp.ChemMultiplier; + ent.Comp.ChemMultiplier = 1f; + } + + private void OnChemRegenModify(Entity ent, + ref InventoryRelayedEvent args) + { + args.Args.Multiplier *= ent.Comp.Multiplier; + } + public bool AddChemicals(EntityUid uid, ChangelingComponent component, int quantity) { + var capacity = component.ChemicalCapacity; if (_mobStateSystem.IsDead(uid)) + capacity /= 2; + + if (component.ChemicalsBalance >= capacity) return false; - var toAdd = quantity; + component.ChemicalsBalance = Math.Min(component.ChemicalsBalance + quantity, capacity); - if (component.ChemicalsBalance == component.ChemicalCapacity) - return false; - - if (component.ChemicalsBalance + toAdd > component.ChemicalCapacity) - { - var overflow = component.ChemicalsBalance + toAdd - component.ChemicalCapacity; - toAdd -= overflow; - component.ChemicalsBalance += toAdd; - } - - component.ChemicalsBalance += toAdd; Dirty(uid, component); UpdateAlert(uid, component); @@ -69,11 +84,11 @@ public sealed class ChemicalsSystem : EntitySystem if(component.Accumulator < component.UpdateDelay) continue; - if (component.IsRegenerating) - continue; - component.Accumulator = 0; - AddChemicals(uid, component, component.ChemicalRegenRate); + var ev = new ChemRegenModifyEvent(); + RaiseLocalEvent(uid, ev); + var chemicals = (int) MathF.Round(component.ChemicalRegenRate * ev.Multiplier); + AddChemicals(uid, component, chemicals); } } diff --git a/Content.Shared/Changeling/ClothingModifyChemicalRegenComponent.cs b/Content.Shared/Changeling/ClothingModifyChemicalRegenComponent.cs new file mode 100644 index 0000000000..41e2a013a0 --- /dev/null +++ b/Content.Shared/Changeling/ClothingModifyChemicalRegenComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Changeling; + +[RegisterComponent] +public sealed partial class ClothingModifyChemicalRegenComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Multiplier = 0.75f; +} diff --git a/Content.Shared/Changeling/DeleteOnChangelingRefundComponent.cs b/Content.Shared/Changeling/DeleteOnChangelingRefundComponent.cs new file mode 100644 index 0000000000..a1c86fcf23 --- /dev/null +++ b/Content.Shared/Changeling/DeleteOnChangelingRefundComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Changeling; + +[RegisterComponent] +public sealed partial class DeleteOnChangelingRefundComponent : Component +{ +} diff --git a/Content.Shared/Changeling/SharedChangeling.cs b/Content.Shared/Changeling/SharedChangeling.cs index 6ddd5ea630..0c01f1c7a3 100644 --- a/Content.Shared/Changeling/SharedChangeling.cs +++ b/Content.Shared/Changeling/SharedChangeling.cs @@ -1,5 +1,6 @@ -using Content.Shared.Actions; +using Content.Shared.Actions; using Content.Shared.DoAfter; +using Content.Shared.Inventory; using Robust.Shared.Serialization; namespace Content.Shared.Changeling; @@ -97,3 +98,34 @@ public sealed partial class BiodegradeActionEvent : InstantActionEvent { } +public sealed partial class AugmentedEyesightActionEvent : InstantActionEvent +{ +} + +[Serializable, NetSerializable] +public sealed class AugmentedEyesightPurchasedEvent : EntityEventArgs +{ +} + +public sealed partial class DissonantShriekActionEvent : InstantActionEvent +{ +} + +[Serializable, NetSerializable] +public sealed class VoidAdaptationPurchasedEvent : EntityEventArgs +{ +} + +public sealed class ChemRegenModifyEvent : EntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots => ~SlotFlags.POCKET; + + public float Multiplier = 1f; +} + +public sealed class ChangelingRefundEvent : EntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots => SlotFlags.All; + + public EntityUid Store; +} diff --git a/Content.Shared/Changeling/VoidAdaptationComponent.cs b/Content.Shared/Changeling/VoidAdaptationComponent.cs new file mode 100644 index 0000000000..def52a4c22 --- /dev/null +++ b/Content.Shared/Changeling/VoidAdaptationComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Changeling; + +[RegisterComponent] +public sealed partial class VoidAdaptationComponent : Component +{ + public float ChemMultiplier = 1f; +} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index bb5413bfb2..4c70107a7b 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -1,4 +1,5 @@ using Content.Shared._White.StaminaProtection; +using Content.Shared.Changeling; using Content.Shared.Chemistry; using Content.Shared.Damage; using Content.Shared.Electrocution; @@ -29,6 +30,8 @@ public partial class InventorySystem SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); // WD SubscribeLocalEvent(RelayInventoryEvent); // WD + SubscribeLocalEvent(RelayInventoryEvent); // WD + SubscribeLocalEvent(RelayInventoryEvent); // WD SubscribeLocalEvent(RelayInventoryEvent); // by-ref events diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index b05f7914a4..e9b7bb82fd 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -132,7 +132,6 @@ public partial class ListingData : IEquatable, ICloneable Description != listing.Description || ProductEntity != listing.ProductEntity || ProductAction != listing.ProductAction || - ProductEvent != listing.ProductEvent || RestockTime != listing.RestockTime) return false; diff --git a/Content.Shared/_Miracle/Systems/SharedNightVisionSystem.cs b/Content.Shared/_Miracle/Systems/SharedNightVisionSystem.cs index 2354f17dd9..b414de944d 100644 --- a/Content.Shared/_Miracle/Systems/SharedNightVisionSystem.cs +++ b/Content.Shared/_Miracle/Systems/SharedNightVisionSystem.cs @@ -23,12 +23,20 @@ public abstract class SharedNightVisionSystem : EntitySystem private void OnRemove(EntityUid uid, NightVisionComponent component, ComponentRemove args) { _actions.RemoveAction(uid, component.ToggleActionEntity); + + if (HasComp(uid)) + return; + UpdateNightVision(uid, false); } private void OnInit(EntityUid uid, NightVisionComponent component, ComponentInit args) { _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); + + if (!component.IsActive && HasComp(uid)) + return; + UpdateNightVision(uid, component.IsActive); } @@ -41,8 +49,12 @@ public abstract class SharedNightVisionSystem : EntitySystem component.IsActive = !component.IsActive; _audio.PlayPredicted(component.ToggleSound, uid, uid); - UpdateNightVision(uid, component.IsActive); args.Handled = true; + + if (!component.IsActive && HasComp(uid)) + return; + + UpdateNightVision(uid, component.IsActive); } } diff --git a/Content.Shared/_White/Overlays/TemporaryNightVisionComponent.cs b/Content.Shared/_White/Overlays/TemporaryNightVisionComponent.cs new file mode 100644 index 0000000000..cc4d4aba22 --- /dev/null +++ b/Content.Shared/_White/Overlays/TemporaryNightVisionComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._White.Overlays; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TemporaryNightVisionComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public Vector3 Tint = new(0.3f, 0.3f, 0.3f); + + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public float Strength = 2f; + + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public float Noise = 0.5f; + + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public Color Color = Color.FromHex("#FB9898"); +} diff --git a/Resources/Locale/ru-RU/white/changeling/changeling-entities.ftl b/Resources/Locale/ru-RU/white/changeling/changeling-entities.ftl index 22ef824925..4d155a6359 100644 --- a/Resources/Locale/ru-RU/white/changeling/changeling-entities.ftl +++ b/Resources/Locale/ru-RU/white/changeling/changeling-entities.ftl @@ -1,9 +1,18 @@ -changeling-ability-fleshmend = Восстановление тканей +changeling-ability-fleshmend = Восстановление тканей changeling-ability-fleshmend-desc = Быстро вылечить большую часть повреждений changeling-ability-biodegrade = Биоразложение changeling-ability-biodegrade-desc = Растворяет наручники и прочие сдерживающие элементы. +changeling-ability-eyesight = Аугментация зрения +changeling-ability-eyesight-desc = Развивает переключаемое ночное зрение. Когда способность неактивна, защищает вас от флешек и яркого света, например от сварки. + +changeling-ability-dissonant-shriek = Диссонирующий вопль +changeling-ability-dissonant-shriek-desc = Испускает заряд ЭМИ, который нарушает работу всего оборудования вокруг. + +changeling-ability-void-adaptation = Пустотная адаптация +changeling-ability-void-adaptation-desc = Наделяет пассивной способностью, позволяющей сопротивляться холоду, низкому давлению и потребности в кислороде. При использовании замедляет регенерацию химикатов на 25%. + changeling-ability-adrenaline-sacks = Мешки с адреналином changeling-ability-adrenaline-sacks-desc = Дает кратковременный прилив адреналина в крови. diff --git a/Resources/Prototypes/Actions/changeling.yml b/Resources/Prototypes/Actions/changeling.yml index 77df3acc27..4786036641 100644 --- a/Resources/Prototypes/Actions/changeling.yml +++ b/Resources/Prototypes/Actions/changeling.yml @@ -21,7 +21,7 @@ icon: White/Actions/changeling.rsi/absorb.png event: !type:AbsorbDnaActionEvent canTargetSelf: false - useDelay: 10 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -34,7 +34,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/transform.png event: !type:TransformActionEvent - useDelay: 30 + useDelay: 1 - type: entity id: ActionChangelingRegenerate @@ -47,8 +47,7 @@ icon: White/Actions/changeling.rsi/reviving_stasis.png event: !type:RegenerateActionEvent checkCanInteract: false - useDelay: 120 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionChangelingLesserForm @@ -60,7 +59,8 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/lesser_form.png event: !type:LesserFormActionEvent - useDelay: 90 + useDelay: 1 + checkCanInteract: false - type: LesserFormRestricted - type: entity @@ -74,8 +74,7 @@ icon: White/Actions/changeling.rsi/sting_extract.png event: !type:ExtractionStingActionEvent canTargetSelf: false - useDelay: 40 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionTransformSting @@ -88,8 +87,7 @@ icon: White/Actions/changeling.rsi/sting_transform.png event: !type:TransformStingActionEvent canTargetSelf: false - useDelay: 120 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionBlindSting @@ -102,8 +100,7 @@ icon: White/Actions/changeling.rsi/sting_blind.png event: !type:BlindStingActionEvent canTargetSelf: false - useDelay: 30 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionMuteSting @@ -116,8 +113,7 @@ icon: White/Actions/changeling.rsi/sting_mute.png event: !type:MuteStingActionEvent canTargetSelf: false - useDelay: 30 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionHallucinationSting @@ -130,8 +126,7 @@ icon: White/Actions/changeling.rsi/sting_hallucination.png event: !type:HallucinationStingActionEvent canTargetSelf: false - useDelay: 30 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionCryoSting @@ -144,8 +139,7 @@ icon: White/Actions/changeling.rsi/sting_cryo.png event: !type:CryoStingActionEvent canTargetSelf: false - useDelay: 30 - - type: LesserFormRestricted + useDelay: 1 - type: entity id: ActionAdrenalineSacs @@ -157,7 +151,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/adrenaline_sacs.png event: !type:AdrenalineSacsActionEvent - useDelay: 60 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -170,7 +164,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/fleshmend.png event: !type:FleshmendActionEvent - useDelay: 60 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -183,7 +177,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/arm_blade.png event: !type:ArmbladeActionEvent - useDelay: 10 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -196,7 +190,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/shield.png event: !type:OrganicShieldActionEvent - useDelay: 10 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -209,7 +203,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/armor.png event: !type:ChitinousArmorActionEvent - useDelay: 10 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -222,7 +216,7 @@ itemIconStyle: NoItem icon: White/Actions/changeling.rsi/tentacle_arm.png event: !type:TentacleArmActionEvent - useDelay: 10 + useDelay: 1 - type: LesserFormRestricted - type: entity @@ -236,4 +230,30 @@ checkCanInteract: false icon: White/Actions/changeling.rsi/biodegrade.png event: !type:BiodegradeActionEvent - useDelay: 120 + useDelay: 1 + +- type: entity + id: ActionAugmentedEyesight + name: changeling-ability-eyesight + description: changeling-ability-eyesight-desc + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + checkCanInteract: false + icon: White/Actions/changeling.rsi/augmented_eyesight.png + event: !type:AugmentedEyesightActionEvent + +- type: entity + id: ActionDissonantShriek + name: changeling-ability-dissonant-shriek + description: changeling-ability-dissonant-shriek-desc + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + checkCanInteract: false + icon: White/Actions/changeling.rsi/dissonant_shriek.png + event: !type:DissonantShriekActionEvent + useDelay: 1 + - type: LesserFormRestricted diff --git a/Resources/Prototypes/Catalog/changeling_catalog.yml b/Resources/Prototypes/Catalog/changeling_catalog.yml index 7d8bbcaff9..4536dd403e 100644 --- a/Resources/Prototypes/Catalog/changeling_catalog.yml +++ b/Resources/Prototypes/Catalog/changeling_catalog.yml @@ -64,6 +64,20 @@ - !type:ListingLimitedStockCondition stock: 1 +- type: listing + id: ChangelingVoidAdaptation + name: changeling-ability-void-adaptation + description: changeling-ability-void-adaptation-desc + icon: { sprite: /Textures/White/Actions/changeling.rsi, state: organic_suit } + productEvent: !type:VoidAdaptationPurchasedEvent + cost: + ChangelingPoint: 2 + categories: + - ChangelingAbilities + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + # Stings - type: listing id: ChangelingExtractionString @@ -182,3 +196,30 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 + +- type: listing + id: ChangelingAugmentedEyesight + name: changeling-ability-eyesight + description: changeling-ability-eyesight-desc + productAction: ActionAugmentedEyesight + productEvent: !type:AugmentedEyesightPurchasedEvent + cost: + ChangelingPoint: 2 + categories: + - ChangelingBoosters + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: ChangelingDissonantShriek + name: changeling-ability-dissonant-shriek + description: changeling-ability-dissonant-shriek-desc + productAction: ActionDissonantShriek + cost: + ChangelingPoint: 1 + categories: + - ChangelingBoosters + conditions: + - !type:ListingLimitedStockCondition + stock: 1 diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index e38cb3af71..4dc929b3b6 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -288,10 +288,13 @@ - type: Armor #admeme item so it should be fine being overpowered while helmets are still intentionally kneecapped. modifiers: coefficients: - Blunt: 0.5 - Slash: 0.5 - Piercing: 0.5 - Heat: 0.9 + Blunt: 0.8 + Slash: 0.8 + Piercing: 0.85 + Heat: 0.8 + - type: Unremoveable + deleteOnDrop: true + - type: DeleteOnChangelingRefund #ERT HELMETS #ERT Leader Helmet diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index a826bd5586..5793210b3a 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -171,8 +171,11 @@ Heat: 0.5 - type: ExplosionResistance damageCoefficient: 0.9 + - type: GroupExamine - type: Unremoveable deleteOnDrop: true + - type: ClothingModifyChemicalRegen + - type: DeleteOnChangelingRefund - type: entity parent: ClothingOuterArmorHeavy diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index aef48e42d6..47b4084ca7 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -143,6 +143,7 @@ Piercing: 1.5 - type: Unremoveable deleteOnDrop: true + - type: DeleteOnChangelingRefund - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index d93be773f7..9970bec869 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -298,6 +298,7 @@ volume: -10 - type: Unremoveable deleteOnDrop: true + - type: DeleteOnChangelingRefund # Admeme diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml index 19f74b4e81..99cbebea07 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml @@ -28,3 +28,4 @@ - type: Unremoveable deleteOnDrop: true - type: ToolForcePowered + - type: DeleteOnChangelingRefund diff --git a/Resources/Prototypes/Reagents/biological.yml b/Resources/Prototypes/Reagents/biological.yml index 978ec717f9..574a36fa0f 100644 --- a/Resources/Prototypes/Reagents/biological.yml +++ b/Resources/Prototypes/Reagents/biological.yml @@ -137,10 +137,6 @@ color: "#f4692e" recognizable: true metabolisms: - Drink: - effects: - - !type:SatiateThirst - factor: 1.5 # Dragon doesn't require airloss healing, so omnizine is still best for humans. Medicine: effects: diff --git a/Resources/Textures/White/Actions/changeling.rsi/augmented_eyesight.png b/Resources/Textures/White/Actions/changeling.rsi/augmented_eyesight.png new file mode 100644 index 0000000000..e7f166ddac Binary files /dev/null and b/Resources/Textures/White/Actions/changeling.rsi/augmented_eyesight.png differ diff --git a/Resources/Textures/White/Actions/changeling.rsi/dissonant_shriek.png b/Resources/Textures/White/Actions/changeling.rsi/dissonant_shriek.png new file mode 100644 index 0000000000..eef3cf0983 Binary files /dev/null and b/Resources/Textures/White/Actions/changeling.rsi/dissonant_shriek.png differ diff --git a/Resources/Textures/White/Actions/changeling.rsi/meta.json b/Resources/Textures/White/Actions/changeling.rsi/meta.json index bbb9992932..31d5234cb0 100644 --- a/Resources/Textures/White/Actions/changeling.rsi/meta.json +++ b/Resources/Textures/White/Actions/changeling.rsi/meta.json @@ -1,4 +1,4 @@ -{ +{ "version": 1, "license": "CC-BY-SA-3.0", "copyright": "By SS14 White Dream", @@ -22,6 +22,15 @@ { "name": "biodegrade" }, + { + "name": "augmented_eyesight" + }, + { + "name": "dissonant_shriek" + }, + { + "name": "organic_suit" + }, { "name": "fleshmend" }, diff --git a/Resources/Textures/White/Actions/changeling.rsi/organic_suit.png b/Resources/Textures/White/Actions/changeling.rsi/organic_suit.png new file mode 100644 index 0000000000..4a257a170e Binary files /dev/null and b/Resources/Textures/White/Actions/changeling.rsi/organic_suit.png differ