From c1fe31b63b8d30a8dc415a3036685de20d212daa Mon Sep 17 00:00:00 2001 From: Aviu00 <93730715+Aviu00@users.noreply.github.com> Date: Sat, 8 Jun 2024 13:01:49 +0000 Subject: [PATCH] More magic (#338) * - tweak: Magic tweaks. * - add: Rework some spells. * - fix: Some bugfixes. * - tweak: Less requirements. * - add: Loc. --- .../Systems/Actions/ChargeActionSystem.cs | 7 ++ Content.Server/Beam/BeamSystem.cs | 23 ++-- .../Components/ElectrifiedComponent.cs | 7 ++ .../Electrocution/ElectrocutionSystem.cs | 12 ++- Content.Server/Lightning/LightningSystem.cs | 17 ++- .../EntitySystems/GravityWellSystem.cs | 21 ++-- .../_White/Wizard/Magic/WizardSpellsSystem.cs | 102 +++++++++++------- Resources/Locale/en-US/_white/wizard.ftl | 1 + Resources/Locale/ru-RU/_white/wizard.ftl | 2 + .../Prototypes/Entities/Effects/lightning.yml | 19 +++- Resources/Prototypes/Magic/knock_spell.yml | 4 +- Resources/Prototypes/Magic/white.yml | 34 ++---- .../_White/Objects/Scrolls/scrolls.yml | 3 +- 13 files changed, 168 insertions(+), 84 deletions(-) create mode 100644 Resources/Locale/en-US/_white/wizard.ftl diff --git a/Content.Client/UserInterface/Systems/Actions/ChargeActionSystem.cs b/Content.Client/UserInterface/Systems/Actions/ChargeActionSystem.cs index d14c075c29..69660882a6 100644 --- a/Content.Client/UserInterface/Systems/Actions/ChargeActionSystem.cs +++ b/Content.Client/UserInterface/Systems/Actions/ChargeActionSystem.cs @@ -2,6 +2,8 @@ using Content.Shared._White.Wizard; using Content.Shared._White.Wizard.Charging; using Content.Shared.Actions; +using Content.Shared.Mobs.Systems; +using Content.Shared.StatusEffect; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; @@ -24,6 +26,8 @@ public sealed class ChargeActionSystem : SharedChargingSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; private ActionUIController? _controller; @@ -55,6 +59,9 @@ public sealed class ChargeActionSystem : SharedChargingSystem if (_playerManager.LocalEntity is not { } user) return; + if (!_mobState.IsAlive(user) || _statusEffects.HasStatusEffect(user, "Incorporeal")) + return; + if (!_timing.IsFirstTimePredicted || _controller == null || _controller.SelectingTargetFor is not { } actionId) return; diff --git a/Content.Server/Beam/BeamSystem.cs b/Content.Server/Beam/BeamSystem.cs index 33f2f252d9..a3e5c75e26 100644 --- a/Content.Server/Beam/BeamSystem.cs +++ b/Content.Server/Beam/BeamSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Server.Beam.Components; +using Content.Server.Electrocution; using Content.Shared.Beam; using Content.Shared.Beam.Components; using Content.Shared.Physics; @@ -65,7 +66,7 @@ public sealed class BeamSystem : SharedBeamSystem /// The virtual beam controller that this beam will use. If one doesn't exist it will be created here. /// Optional sprite state for the if it needs a dynamic one /// Optional shader for the and if it needs something other than default - private void CreateBeam(string prototype, + private IEnumerable CreateBeam(string prototype, Angle userAngle, Vector2 calculatedDistance, MapCoordinates beamStartPos, @@ -78,8 +79,10 @@ public sealed class BeamSystem : SharedBeamSystem var ent = Spawn(prototype, beamSpawnPos); var shape = new EdgeShape(distanceCorrection, new Vector2(0,0)); + yield return ent; + if (!TryComp(ent, out var physics) || !TryComp(ent, out var beam)) - return; + yield break; FixturesComponent? manager = null; _fixture.TryCreateFixture( @@ -120,6 +123,7 @@ public sealed class BeamSystem : SharedBeamSystem { beamSpawnPos = beamSpawnPos.Offset(calculatedDistance.Normalized()); var newEnt = Spawn(prototype, beamSpawnPos); + yield return newEnt; var ev = new BeamVisualizerEvent(GetNetEntity(newEnt), distanceLength, userAngle, bodyState, shader); RaiseNetworkEvent(ev); @@ -139,10 +143,10 @@ public sealed class BeamSystem : SharedBeamSystem /// Optional sprite state for the if a default one is not given /// Optional shader for the if a default one is not given /// - public void TryCreateBeam(EntityUid user, EntityUid target, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null) + public IEnumerable TryCreateBeam(EntityUid user, EntityUid target, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null) { if (Deleted(user) || Deleted(target)) - return; + yield break; var userMapPos = Transform(user).MapPosition; var targetMapPos = Transform(target).MapPosition; @@ -152,14 +156,14 @@ public sealed class BeamSystem : SharedBeamSystem var userAngle = calculatedDistance.ToWorldAngle(); if (userMapPos.MapId != targetMapPos.MapId) - return; + yield break; //Where the start of the beam will spawn var beamStartPos = userMapPos.Offset(calculatedDistance.Normalized()); //Don't divide by zero if (calculatedDistance.Length() == 0) - return; + yield break; if (controller != null && TryComp(controller, out var controllerBeamComp)) { @@ -169,7 +173,12 @@ public sealed class BeamSystem : SharedBeamSystem var distanceCorrection = calculatedDistance - calculatedDistance.Normalized(); - CreateBeam(bodyPrototype, userAngle, calculatedDistance, beamStartPos, distanceCorrection, controller, bodyState, shader); + var ents = CreateBeam(bodyPrototype, userAngle, calculatedDistance, beamStartPos, distanceCorrection, controller, bodyState, shader); + + foreach (var ent in ents) + { + yield return ent; + } var ev = new CreateBeamSuccessEvent(user, target); RaiseLocalEvent(user, ev); diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs index 65a539eb08..6b281ebb8d 100644 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs @@ -85,4 +85,11 @@ public sealed partial class ElectrifiedComponent : Component [DataField("shockVolume")] public float ShockVolume = 20; + + // WD EDIT START + [DataField] + public bool IgnoreInsulation; + + public EntityUid? Caster; + // WD EDIT END } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index f61e586de4..48f503c68a 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -219,7 +219,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem _appearance.SetData(uid, ElectrifiedVisuals.IsPowered, true); siemens *= electrified.SiemensCoefficient; - if (!DoCommonElectrocutionAttempt(targetUid, uid, ref siemens) || siemens <= 0) + if (targetUid != electrified.Caster && !DoCommonElectrocutionAttempt(targetUid, uid, ref siemens, electrified.IgnoreInsulation) || siemens <= 0) // WD EDIT return false; // If electrocution would fail, do nothing. var targets = new List<(EntityUid entity, int depth)>(); @@ -230,13 +230,19 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem for (var i = targets.Count - 1; i >= 0; i--) { var (entity, depth) = targets[i]; + + if (entity == electrified.Caster) // WD + continue; + lastRet = TryDoElectrocution( entity, uid, (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth)), TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)), true, - electrified.SiemensCoefficient + electrified.SiemensCoefficient, + null, // WD EDIT START + electrified.IgnoreInsulation // WD EDIT END ); } @@ -518,4 +524,4 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem _audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume)); } -} \ No newline at end of file +} diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 6f5a86b0bb..2286555325 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Server.Beam; using Content.Server.Beam.Components; +using Content.Server.Electrocution; using Content.Server.Lightning.Components; using Content.Shared.Lightning; using Robust.Shared.Random; @@ -45,10 +46,16 @@ public sealed class LightningSystem : SharedLightningSystem /// Where the lightning fires to /// The prototype for the lightning to be created /// if the lightnings being fired should trigger lightning events. - public void ShootLightning(EntityUid user, EntityUid target, string lightningPrototype = "Lightning", bool triggerLightningEvents = true) + public void ShootLightning(EntityUid user, EntityUid target, string lightningPrototype = "Lightning", bool triggerLightningEvents = true, EntityUid? caster = null) // WD EDIT { var spriteState = LightningRandomizer(); - _beam.TryCreateBeam(user, target, lightningPrototype, spriteState); + var ents = _beam.TryCreateBeam(user, target, lightningPrototype, spriteState); // WD EDIT START + foreach (var ent in ents) + { + if (TryComp(ent, out ElectrifiedComponent? electrified)) + electrified.Caster = caster; + } + // WD EDIT END if (triggerLightningEvents) // we don't want certain prototypes to trigger lightning level events { @@ -66,7 +73,7 @@ public sealed class LightningSystem : SharedLightningSystem /// The prototype for the lightning to be created /// how many times to recursively fire lightning bolts from the target points of the first shot. /// if the lightnings being fired should trigger lightning events. - public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true) + public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true, EntityUid? caster = null) // WD EDIT { //To Do: add support to different priority target tablem for different lightning types //To Do: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent) @@ -89,10 +96,10 @@ public sealed class LightningSystem : SharedLightningSystem if (!_random.Prob(curTarget.HitProbability)) //Chance to ignore target continue; - ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents); + ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents, caster); // WD EDIT if (arcDepth - targets[count].LightningResistance > 0) { - ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents); + ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents, caster); // WD EDIT } shootCount++; } diff --git a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs index f1d0af6f90..d66d78abfd 100644 --- a/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs +++ b/Content.Server/Singularity/EntitySystems/GravityWellSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.Components; using Content.Server.Singularity.Components; +using Content.Server.Stunnable; using Content.Shared.Ghost; using Content.Shared.Singularity.EntitySystems; using Robust.Shared.Map; @@ -24,6 +25,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly StunSystem _stun = default!; // WD EDIT #endregion Dependencies /// @@ -141,10 +143,10 @@ public sealed class GravityWellSystem : SharedGravityWellSystem /// The base radial velocity that will be added to entities within range towards the center of the gravitational pulse. /// The base tangential velocity that will be added to entities within countrclockwise around the center of the gravitational pulse. /// (optional) The transform of the entity at the epicenter of the gravitational pulse. - public void GravPulse(EntityUid uid, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, TransformComponent? xform = null) + public void GravPulse(EntityUid uid, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, TransformComponent? xform = null, float stunTime = 0f, List? ignore = null) { if (Resolve(uid, ref xform)) - GravPulse(xform.Coordinates, maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV); + GravPulse(xform.Coordinates, maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV, stunTime, ignore); } /// @@ -165,8 +167,8 @@ public sealed class GravityWellSystem : SharedGravityWellSystem /// The minimum distance at which entities can be affected by the gravity pulse. /// The base radial velocity that will be added to entities within range towards the center of the gravitational pulse. /// The base tangential velocity that will be added to entities within countrclockwise around the center of the gravitational pulse. - public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f) - => GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV); + public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, float stunTime = 0f, List? ignore = null) + => GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV, stunTime, ignore); /// /// Causes a gravitational pulse, shoving around all entities within some distance of an epicenter. @@ -175,7 +177,7 @@ public sealed class GravityWellSystem : SharedGravityWellSystem /// The maximum distance at which entities can be affected by the gravity pulse. /// The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors. /// The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter. - public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV) + public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3 baseMatrixDeltaV, float stunTime = 0f, List? ignore = null) { if (mapPos == MapCoordinates.Nullspace) return; // No gravpulses in nullspace please. @@ -187,6 +189,9 @@ public sealed class GravityWellSystem : SharedGravityWellSystem foreach(var entity in _lookup.GetEntitiesInRange(mapPos.MapId, epicenter, maxRange, flags: LookupFlags.Dynamic | LookupFlags.Sundries)) { + if (ignore?.Contains(entity) is true) + continue; + if (!bodyQuery.TryGetComponent(entity, out var physics) || physics.BodyType == BodyType.Static) { @@ -206,6 +211,8 @@ public sealed class GravityWellSystem : SharedGravityWellSystem var scaling = (1f / distance2) * physics.Mass; // TODO: Variable falloff gradiants. _physics.ApplyLinearImpulse(entity, (displacement * baseMatrixDeltaV) * scaling, body: physics); + if (stunTime > 0f) + _stun.TryParalyze(entity, TimeSpan.FromSeconds(stunTime), true); } } @@ -217,12 +224,12 @@ public sealed class GravityWellSystem : SharedGravityWellSystem /// The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors. /// The base amount of velocity that will be added to entities in range towards the epicenter of the pulse. /// The base amount of velocity that will be added to entities in range counterclockwise relative to the epicenter of the pulse. - public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f) + public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, float stunTime = 0f, List? ignore = null) => GravPulse(mapPos, maxRange, minRange, new Matrix3( baseRadialDeltaV, +baseTangentialDeltaV, 0.0f, -baseTangentialDeltaV, baseRadialDeltaV, 0.0f, 0.0f, 0.0f, 1.0f - )); + ), stunTime, ignore); #endregion GravPulse diff --git a/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs b/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs index eed87e0d7d..2b717366aa 100644 --- a/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs +++ b/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using Content.Server._White.IncorporealSystem; using Content.Server._White.Wizard.Magic.Amaterasu; @@ -12,6 +12,7 @@ using Content.Server.Emp; using Content.Server.Lightning; using Content.Server.Magic; using Content.Server.Singularity.EntitySystems; +using Content.Server.Standing; using Content.Server.Weapons.Ranged.Systems; using Content.Shared._White.Wizard; using Content.Shared._White.Wizard.Magic; @@ -62,6 +63,7 @@ public sealed class WizardSpellsSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly EmpSystem _empSystem = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; #endregion @@ -89,7 +91,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnInstantRecallSpell(InstantRecallSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; if (!TryComp(msg.Performer, out var handsComponent)) @@ -110,11 +112,13 @@ public sealed class WizardSpellsSystem : EntitySystem } recallComponent.Item = handsComponent.ActiveHandEntity.Value; - _popupSystem.PopupEntity($"Сопряжено с {MetaData(handsComponent.ActiveHandEntity.Value).EntityName}", msg.Performer, msg.Performer); + _popupSystem.PopupEntity($"Сопряжено с {MetaData(handsComponent.ActiveHandEntity.Value).EntityName}", + msg.Performer, msg.Performer); return; } - if (handsComponent.ActiveHandEntity == null && recallComponent.Item != null) + if (handsComponent.ActiveHandEntity == null && recallComponent.Item != null && + Exists(recallComponent.Item.Value)) { var coordsItem = Transform(recallComponent.Item.Value).Coordinates; var coordsPerformer = Transform(msg.Performer).Coordinates; @@ -139,12 +143,12 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnMimeTouchSpell(MimeTouchSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; if (!HasComp(msg.Target)) { - _popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer); + _popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer); return; } @@ -164,7 +168,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnBananaTouchSpell(BananaTouchSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; if (!HasComp(msg.Target)) @@ -188,7 +192,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnCluwneCurseSpell(CluwneCurseSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; if (!HasComp(msg.Target)) @@ -197,8 +201,7 @@ public sealed class WizardSpellsSystem : EntitySystem return; } - var cluwne = EnsureComp(msg.Target); - cluwne.KnockChance = 0.2f; + EnsureComp(msg.Target); Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates); @@ -212,7 +215,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnEmpSpell(EmpSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; var coords = _transformSystem.ToMapCoordinates(Transform(msg.Performer).Coordinates); @@ -229,7 +232,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnJauntSpell(EtherealJauntSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; if (_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal")) @@ -240,7 +243,8 @@ public sealed class WizardSpellsSystem : EntitySystem Spawn("AdminInstantEffectSmoke10", Transform(msg.Performer).Coordinates); - _statusEffectsSystem.TryAddStatusEffect(msg.Performer, "Incorporeal", TimeSpan.FromSeconds(10), false); + _statusEffectsSystem.TryAddStatusEffect(msg.Performer, "Incorporeal", + TimeSpan.FromSeconds(10), false); msg.Handled = true; Speak(msg); @@ -252,7 +256,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnBlinkSpell(BlinkSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; var transform = Transform(msg.Performer); @@ -291,8 +295,8 @@ public sealed class WizardSpellsSystem : EntitySystem _audio.PlayPvs("/Audio/White/Cult/veilin.ogg", coords); _audio.PlayPvs("/Audio/White/Cult/veilout.ogg", oldCoords); - Spawn("AdminInstantEffectSmoke10", oldCoords); - Spawn("AdminInstantEffectSmoke10", coords); + Spawn("AdminInstantEffectSmoke3", oldCoords); + Spawn("AdminInstantEffectSmoke3", coords); msg.Handled = true; Speak(msg); @@ -304,7 +308,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnForcewallSpell(ForceWallSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; switch (msg.ActionUseType) @@ -372,7 +376,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnCardsSpell(CardsSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; var result = true; @@ -413,7 +417,8 @@ public sealed class WizardSpellsSystem : EntitySystem var ent = Spawn(msg.Prototype, spawnCoords); - var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem); + var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - + spawnCoords.ToMapPos(EntityManager, _transformSystem); var randomizedDirection = direction + new Vector2(_random.Next(-2, 2), _random.Next(-2, 2)); _throwingSystem.TryThrow(ent, randomizedDirection, 60, msg.Performer); @@ -425,14 +430,15 @@ public sealed class WizardSpellsSystem : EntitySystem { var xform = Transform(msg.Performer); - var count = 10 * msg.ChargeLevel; + var count = 10 + 10 * msg.ChargeLevel; var angleStep = 360f / count; for (var i = 0; i < count; i++) { var angle = i * angleStep; - var direction = new Vector2(MathF.Cos(MathHelper.DegreesToRadians(angle)), MathF.Sin(MathHelper.DegreesToRadians(angle))); + var direction = new Vector2(MathF.Cos(MathHelper.DegreesToRadians(angle)), + MathF.Sin(MathHelper.DegreesToRadians(angle))); foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos)) { @@ -453,7 +459,7 @@ public sealed class WizardSpellsSystem : EntitySystem { if (!HasComp(msg.TargetUid)) { - _popupSystem.PopupEntity("Работает только на предметах.", msg.Performer, msg.Performer); + _popupSystem.PopupEntity("В карту можно превратить только предметы.", msg.Performer, msg.Performer); return false; } @@ -470,7 +476,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnFireballSpell(FireballSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; var result = true; @@ -513,7 +519,8 @@ public sealed class WizardSpellsSystem : EntitySystem userVelocity = physics.LinearVelocity; var ent = Spawn(msg.Prototype, spawnCoords); - var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem); + var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - + spawnCoords.ToMapPos(EntityManager, _transformSystem); _gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer); } } @@ -554,9 +561,11 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnForceSpell(ForceSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; + var result = true; + switch (msg.ActionUseType) { case ActionUseType.Default: @@ -566,28 +575,41 @@ public sealed class WizardSpellsSystem : EntitySystem ForceSpellCharge(msg); break; case ActionUseType.AltUse: - ForceSpellAlt(msg); + result = ForceSpellAlt(msg); break; } + if (!result) + return; + SetCooldown(msg.Action, msg.ActionUseType); msg.Handled = true; Speak(msg); } - private void ForceSpellDefault(ForceSpellEvent msg) + private bool ForceSpellAlt(ForceSpellEvent msg) { - Spawn("AdminInstantEffectMinusGravityWell", msg.Target); + if (!HasComp(msg.TargetUid) || !HasComp(msg.TargetUid)) + { + _popupSystem.PopupEntity("Невозможно это притянуть!", msg.Performer, msg.Performer); + return false; + } + + _throwingSystem.TryThrow(msg.TargetUid, Transform(msg.Performer).Coordinates, 5f); + _standing.TryLieDown(msg.TargetUid); + + return true; } private void ForceSpellCharge(ForceSpellEvent msg) { - _gravityWell.GravPulse(msg.Performer, 15, 0, -80 * msg.ChargeLevel, -2 * msg.ChargeLevel); + _gravityWell.GravPulse(msg.Performer, 15, 0, -80 * msg.ChargeLevel, -2 * msg.ChargeLevel, null, msg.ChargeLevel, + new() {msg.Performer}); } - private void ForceSpellAlt(ForceSpellEvent msg) + private void ForceSpellDefault(ForceSpellEvent msg) { - _gravityWell.GravPulse(msg.Target, 10, 0, 200, 10); + _gravityWell.GravPulse(msg.Target, 10, 0, 200, 10, 2f, new() {msg.Performer}); } #endregion @@ -596,7 +618,7 @@ public sealed class WizardSpellsSystem : EntitySystem private void OnArcSpell(ArcSpellEvent msg) { - if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer)) + if (!CanCast(msg)) return; var result = true; @@ -632,7 +654,7 @@ public sealed class WizardSpellsSystem : EntitySystem var entityUids = entitiesToHit.ToList(); foreach (var entity in entityUids) { - _lightning.ShootLightning(msg.Performer, entity); + _lightning.ShootLightning(msg.Performer, entity, "WizardLightning", true, msg.Performer); } return entityUids.Count != 0; @@ -640,7 +662,8 @@ public sealed class WizardSpellsSystem : EntitySystem private void ArcSpellCharge(ArcSpellEvent msg) { - _lightning.ShootRandomLightnings(msg.Performer, 2 * msg.ChargeLevel, msg.ChargeLevel * 2, arcDepth: 2); + _lightning.ShootRandomLightnings(msg.Performer, 2f * msg.ChargeLevel, msg.ChargeLevel * 2, "WizardLightning", 1, + caster: msg.Performer); } private void ArcSpellAlt(ArcSpellEvent msg) @@ -660,7 +683,8 @@ public sealed class WizardSpellsSystem : EntitySystem userVelocity = physics.LinearVelocity; var ent = Spawn(msg.Prototype, spawnCoords); - var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem); + var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - + spawnCoords.ToMapPos(EntityManager, _transformSystem); _gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer); } } @@ -669,6 +693,12 @@ public sealed class WizardSpellsSystem : EntitySystem #region Helpers + private bool CanCast(BaseActionEvent msg) + { + return !msg.Handled && CheckRequirements(msg.Action, msg.Performer) && + !_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal"); + } + private void Speak(BaseActionEvent args) { if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) @@ -743,7 +773,7 @@ public sealed class WizardSpellsSystem : EntitySystem if (!hasReqs) { args.Cancelled = true; - _popupSystem.PopupEntity("Missing Requirements! You need to wear your robe and hat!", args.Performer, args.Performer); + _popupSystem.PopupEntity(Loc.GetString("magic-component-missing-req"), args.Performer, args.Performer); } } diff --git a/Resources/Locale/en-US/_white/wizard.ftl b/Resources/Locale/en-US/_white/wizard.ftl new file mode 100644 index 0000000000..1b9f41348a --- /dev/null +++ b/Resources/Locale/en-US/_white/wizard.ftl @@ -0,0 +1 @@ +magic-component-missing-req = Missing Requirements! You need to wear your robe and hat! diff --git a/Resources/Locale/ru-RU/_white/wizard.ftl b/Resources/Locale/ru-RU/_white/wizard.ftl index 833d76c8e8..e0f380e29f 100644 --- a/Resources/Locale/ru-RU/_white/wizard.ftl +++ b/Resources/Locale/ru-RU/_white/wizard.ftl @@ -15,3 +15,5 @@ wizard-welcome = Вы - космический волшебник. Федера wizard-no-more-threat-announcement-shuttle-call = Судя по данным наших датчиков дальнего действия, магическая угроза была устранена. Эвакуационный шаттл скоро прибудет. Время прибытия: {$time} {$units}. Вы можете отозвать его, чтобы продлить смену. wizard-no-more-threat-announcement = Судя по данным наших датчиков дальнего действия, магическая угроза была устранена. Шаттл уже вызван. + +magic-component-missing-req = Недостающие требования! Вам необходимо надеть мантию и шляпу! diff --git a/Resources/Prototypes/Entities/Effects/lightning.yml b/Resources/Prototypes/Entities/Effects/lightning.yml index 7afd1c07a0..11e6c6c0f0 100644 --- a/Resources/Prototypes/Entities/Effects/lightning.yml +++ b/Resources/Prototypes/Entities/Effects/lightning.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: lightning id: BaseLightning abstract: true @@ -60,6 +60,9 @@ castShadows: false - type: Lightning canArc: false + - type: Electrified + requirePower: false + ignoreInsulation: true shockDamage: 30 - type: entity @@ -159,3 +162,17 @@ softness: 1 autoRot: true castShadows: false + +- type: entity + name: wizard lightning + id: WizardLightning + parent: BaseLightning + noSpawn: true + components: + - type: Electrified + requirePower: false + ignoreInsulation: true + shockDamage: 40 + - type: Lightning + canArc: true + lightningPrototype: WizardLightning diff --git a/Resources/Prototypes/Magic/knock_spell.yml b/Resources/Prototypes/Magic/knock_spell.yml index 131fbb8910..612e7b175f 100644 --- a/Resources/Prototypes/Magic/knock_spell.yml +++ b/Resources/Prototypes/Magic/knock_spell.yml @@ -4,8 +4,10 @@ description: This spell opens nearby doors. noSpawn: true components: + - type: Magic + requiresClothes: false - type: InstantAction - useDelay: 10 + useDelay: 8 itemIconStyle: BigAction checkCanInteract: false icon: diff --git a/Resources/Prototypes/Magic/white.yml b/Resources/Prototypes/Magic/white.yml index 1be2773391..d44b9a4da8 100644 --- a/Resources/Prototypes/Magic/white.yml +++ b/Resources/Prototypes/Magic/white.yml @@ -38,8 +38,6 @@ name: Force noSpawn: true components: - - type: Magic - requiresClothes: true - type: WorldTargetAction itemIconStyle: BigAction useDelay: 60 @@ -63,8 +61,8 @@ speech: "EL DRITCH!" - type: VariableUseDelay useDelay: 6 - altUseDelay: 12 - chargeUseDelay: 40 + altUseDelay: 2 + chargeUseDelay: 20 - type: entity id: ActionFireballSpell @@ -93,8 +91,8 @@ posData: !type:TargetCasterPos speech: action-speech-spell-fireball - type: VariableUseDelay - useDelay: 6 - altUseDelay: 12 + useDelay: 5 + altUseDelay: 10 chargeUseDelay: 40 - type: entity @@ -102,8 +100,6 @@ name: Cards noSpawn: true components: - - type: Magic - requiresClothes: true - type: WorldTargetAction itemIconStyle: BigAction useDelay: 60 @@ -128,7 +124,7 @@ posData: !type:TargetCasterPos speech: "SHIZO NERO!" - type: VariableUseDelay - useDelay: 6 + useDelay: 4 altUseDelay: 1 chargeUseDelay: 40 @@ -220,12 +216,10 @@ name: Cluwne Curse noSpawn: true components: - - type: Magic - requiresClothes: true - type: EntityTargetAction canTargetSelf: false - range: 2 - useDelay: 400 + range: 3 + useDelay: 60 itemIconStyle: BigAction icon: sprite: Objects/Magic/magicactions.rsi @@ -238,12 +232,10 @@ name: Banana Touch noSpawn: true components: - - type: Magic - requiresClothes: true - type: EntityTargetAction canTargetSelf: false - range: 2 - useDelay: 200 + range: 3 + useDelay: 30 itemIconStyle: BigAction icon: sprite: Objects/Magic/magicactions.rsi @@ -256,12 +248,10 @@ name: Mime Touch noSpawn: true components: - - type: Magic - requiresClothes: true - type: EntityTargetAction canTargetSelf: false - range: 2 - useDelay: 200 + range: 3 + useDelay: 30 itemIconStyle: BigAction icon: sprite: Objects/Magic/magicactions.rsi @@ -275,8 +265,6 @@ noSpawn: true components: - type: InstantRecall - - type: Magic - requiresClothes: true - type: InstantAction useDelay: 10 itemIconStyle: BigAction diff --git a/Resources/Prototypes/_White/Objects/Scrolls/scrolls.yml b/Resources/Prototypes/_White/Objects/Scrolls/scrolls.yml index 3e334f0e16..951bf29e77 100644 --- a/Resources/Prototypes/_White/Objects/Scrolls/scrolls.yml +++ b/Resources/Prototypes/_White/Objects/Scrolls/scrolls.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: BaseScroll parent: BaseItem name: "Magic Scroll" @@ -11,6 +11,7 @@ layers: - state: scroll - type: Scroll + learnTime: 1 useSound: path: /Audio/White/Items/scroll/use.ogg afterUseSound: