Spellblade update (#346)

* - tweak: Don't close eui too quickly.

* - add: Spellblade update.

* - fix: Cult teleport spell.
This commit is contained in:
Aviu00
2024-06-11 20:07:47 +00:00
committed by GitHub
parent ee41166fc0
commit 4d09ed9245
31 changed files with 583 additions and 65 deletions

View File

@@ -3,23 +3,19 @@ using Content.Client._White.Cult.UI.TeleportRunesList;
using Content.Client.Eui;
using Content.Shared.Eui;
using Content.Shared._White.Cult.UI;
using JetBrains.Annotations;
namespace Content.Client._White.Cult.UI.TeleportSpell;
public sealed class TeleportSpellEui : BaseEui
[UsedImplicitly]
public sealed class CultTeleportSpellEui : BaseEui
{
private TeleportRunesListWindow _window;
public TeleportSpellEui()
{
_window = new TeleportRunesListWindow();
}
private readonly TeleportRunesListWindow _window = new();
public override void Opened()
{
_window.OpenCentered();
_window.ItemSelected += (index, _) => SendMessage(new TeleportSpellTargetRuneSelected(){RuneUid = index});
_window.ItemSelected += (index, _) => SendMessage(new TeleportSpellTargetRuneSelected {RuneUid = index});
_window.OnClose += () => SendMessage(new CloseEuiMessage());
base.Opened();
@@ -33,7 +29,8 @@ public sealed class TeleportSpellEui : BaseEui
public override void HandleState(EuiStateBase state)
{
if(state is not TeleportSpellEuiState cast) return;
if (state is not CultTeleportSpellEuiState cast)
return;
_window.Clear();
_window.PopulateList(cast.Runes.Keys.ToList(), cast.Runes.Values.ToList());

View File

@@ -0,0 +1,57 @@
using Content.Client._White.UserInterface.Radial;
using Content.Shared._White.Wizard.SpellBlade;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client._White.Wizard.SpellBlade;
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
public sealed class SpellBladeBUI(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private RadialContainer? _aspectSelector;
protected override void Open()
{
base.Open();
if (!_entityManager.TryGetComponent(Owner, out SpellBladeComponent? spellBlade) ||
spellBlade.ChosenAspect != string.Empty)
return;
var spriteSystem = _entityManager.System<SpriteSystem>();
_aspectSelector = new RadialContainer();
_aspectSelector.Closed += Close;
foreach (var aspect in spellBlade.Aspects)
{
if (!_prototypeManager.TryIndex(aspect, out var proto))
continue;
var button = _aspectSelector.AddButton(proto.Name,
spriteSystem.GetPrototypeIcon(proto).Default);
button.Tooltip = proto.Description;
button.Controller.OnPressed += _ =>
{
SendMessage(new SpellBladeSystemMessage(aspect));
_aspectSelector.Close();
};
}
_aspectSelector.OpenAttachedLocalPlayer();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_aspectSelector?.Close();
}
}

View File

@@ -0,0 +1,7 @@
using Content.Shared._White.Wizard.SpellBlade;
namespace Content.Client._White.Wizard.SpellBlade;
public sealed class SpellBladeSystem : SharedSpellBladeSystem
{
}

View File

@@ -8,7 +8,7 @@ using JetBrains.Annotations;
namespace Content.Client._White.Wizard.TeleportSpell;
[UsedImplicitly]
public sealed class TeleportSpellEui : BaseEui
public sealed class WizardTeleportSpellEui : BaseEui
{
private readonly TeleportRunesListWindow _window = new();
@@ -30,7 +30,7 @@ public sealed class TeleportSpellEui : BaseEui
public override void HandleState(EuiStateBase state)
{
if (state is not TeleportSpellEuiState cast)
if (state is not WizardTeleportSpellEuiState cast)
return;
_window.Clear();

View File

@@ -1,3 +1,4 @@
using Content.Server._White.Wizard.SpellBlade;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
using Content.Server.IgnitionSource;
@@ -49,6 +50,7 @@ namespace Content.Server.Atmos.EntitySystems
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SpellBladeSystem _spellBlade = default!; // WD
public const float MinimumFireStacks = -10f;
public const float MaximumFireStacks = 20f;
@@ -84,13 +86,19 @@ namespace Content.Server.Atmos.EntitySystems
private void OnMeleeHit(EntityUid uid, IgniteOnMeleeHitComponent component, MeleeHitEvent args)
{
// WD START
var fireStacks = component.FireStacks;
if (args.Direction != null) // Heavy attack
fireStacks *= 0.5f;
// WD END
foreach (var entity in args.HitEntities)
{
if (!TryComp<FlammableComponent>(entity, out var flammable))
continue;
AdjustFireStacks(entity, component.FireStacks, flammable);
if (component.FireStacks >= 0)
AdjustFireStacks(entity, fireStacks, flammable); // WD EDIT
if (fireStacks >= 0) // WD EDIT
Ignite(entity, args.Weapon, flammable, args.User);
}
}
@@ -203,8 +211,15 @@ namespace Content.Server.Atmos.EntitySystems
if (!flammable.OnFire && !otherFlammable.OnFire)
return; // Neither are on fire
// WD START
var weHold = _spellBlade.IsHoldingItemWithComponent<FireAspectComponent>(uid);
var theyHold = _spellBlade.IsHoldingItemWithComponent<FireAspectComponent>(otherUid);
// WD END
if (flammable.OnFire && otherFlammable.OnFire)
{
if (weHold && !theyHold || theyHold && !weHold) // WD
return;
// Both are on fire -> equalize fire stacks.
var avg = (flammable.FireStacks + otherFlammable.FireStacks) / 2;
flammable.FireStacks = flammable.CanExtinguish ? avg : Math.Max(flammable.FireStacks, avg);
@@ -217,6 +232,8 @@ namespace Content.Server.Atmos.EntitySystems
// Only one is on fire -> attempt to spread the fire.
if (flammable.OnFire)
{
if (theyHold) // WD
return;
otherFlammable.FireStacks += flammable.FireStacks / 2;
Ignite(otherUid, uid, otherFlammable);
if (flammable.CanExtinguish)
@@ -227,6 +244,8 @@ namespace Content.Server.Atmos.EntitySystems
}
else
{
if (weHold) // WD
return;
flammable.FireStacks += otherFlammable.FireStacks / 2;
Ignite(uid, otherUid, flammable);
if (otherFlammable.CanExtinguish)
@@ -436,7 +455,8 @@ namespace Content.Server.Atmos.EntitySystems
if (TryComp(uid, out TemperatureComponent? temp))
_temperatureSystem.ChangeHeat(uid, 12500 * damageScale, false, temp);
_damageableSystem.TryChangeDamage(uid, flammable.Damage * damageScale, interruptsDoAfters: false);
if (!_spellBlade.IsHoldingItemWithComponent<FireAspectComponent>(uid)) // WD EDIT
_damageableSystem.TryChangeDamage(uid, flammable.Damage * damageScale, interruptsDoAfters: false);
AdjustFireStacks(uid, flammable.FirestackFade * (flammable.Resisting ? 10f : 1f), flammable);
}

View File

@@ -1,4 +1,5 @@
using System.Linq;
using Content.Server._White.Wizard.SpellBlade;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
@@ -22,6 +23,7 @@ public sealed class TemperatureSystem : EntitySystem
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SpellBladeSystem _spellBlade = default!; // WD
/// <summary>
/// All the components that will have their damage updated at the end of the tick.
@@ -266,6 +268,17 @@ public sealed class TemperatureSystem : EntitySystem
if (temperature.CurrentTemperature >= heatDamageThreshold)
{
// WD START
if (_spellBlade.IsHoldingItemWithComponent<FireAspectComponent>(uid))
{
if (!temperature.TakingDamage)
return;
_adminLogger.Add(LogType.Temperature,
$"{ToPrettyString(uid):entity} stopped taking temperature damage");
temperature.TakingDamage = false;
return;
}
// WD END
if (!temperature.TakingDamage)
{
_adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} started taking high temperature damage");
@@ -278,7 +291,8 @@ public sealed class TemperatureSystem : EntitySystem
}
else if (temperature.CurrentTemperature <= coldDamageThreshold)
{
if (TryComp(uid, out VoidAdaptationComponent? voidAdaptation)) // WD
// WD START
if (TryComp(uid, out VoidAdaptationComponent? voidAdaptation))
{
if (temperature.TakingDamage)
{
@@ -291,6 +305,17 @@ public sealed class TemperatureSystem : EntitySystem
return;
}
if (_spellBlade.IsHoldingItemWithComponent<FrostAspectComponent>(uid))
{
if (!temperature.TakingDamage)
return;
_adminLogger.Add(LogType.Temperature,
$"{ToPrettyString(uid):entity} stopped taking temperature damage");
temperature.TakingDamage = false;
return;
}
// WD END
if (!temperature.TakingDamage)
{
_adminLogger.Add(LogType.Temperature, $"{ToPrettyString(uid):entity} started taking low temperature damage");

View File

@@ -1,3 +1,4 @@
using Content.Server._White.Wizard.SpellBlade;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.Changeling;
@@ -10,6 +11,7 @@ namespace Content.Server._White.ChangeTemperatureOnCollide;
public sealed class LowTemperatureSlowdownSystem : EntitySystem
{
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
[Dependency] private readonly SpellBladeSystem _spellBlade = default!;
public override void Initialize()
{
@@ -22,7 +24,8 @@ public sealed class LowTemperatureSlowdownSystem : EntitySystem
private void OnMoveSpeedRefresh(EntityUid uid, TemperatureComponent component,
RefreshMovementSpeedModifiersEvent args)
{
var modifier = HasComp<GodmodeComponent>(uid) || HasComp<VoidAdaptationComponent>(uid) || !component.Slowdown
var modifier = _spellBlade.IsHoldingItemWithComponent<FrostAspectComponent>(uid) ||
HasComp<GodmodeComponent>(uid) || HasComp<VoidAdaptationComponent>(uid) || !component.Slowdown
? 1f
: GetSpeedModifier(component.CurrentTemperature);
args.ModifySpeed(modifier, modifier);
@@ -32,7 +35,7 @@ public sealed class LowTemperatureSlowdownSystem : EntitySystem
OnTemperatureChangeEvent args)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
if(GetSpeedModifier(args.LastTemperature) == GetSpeedModifier(args.CurrentTemperature))
if (GetSpeedModifier(args.LastTemperature) == GetSpeedModifier(args.CurrentTemperature))
return;
_movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid, component);

View File

@@ -148,7 +148,7 @@ public partial class CultSystem
_bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false);
var eui = new TeleportSpellEui(args.Performer, args.Target);
var eui = new CultTeleportSpellEui(args.Performer, args.Target);
_euiManager.OpenEui(eui, actor.PlayerSession);
eui.StateDirty();

View File

@@ -631,6 +631,18 @@ public sealed partial class CultSystem : EntitySystem
}
private bool Teleport(EntityUid rune, EntityUid user, List<EntityUid>? victims = null)
{
if (!OpenTeleportUi(user, rune))
return false;
_entityManager.EnsureComponent<CultTeleportRuneProviderComponent>(user, out var providerComponent);
providerComponent.Targets = victims;
providerComponent.BaseRune = rune;
return true;
}
private bool OpenTeleportUi(EntityUid user, EntityUid? exceptRune = null)
{
var runesQuery = EntityQueryEnumerator<CultRuneTeleportComponent>();
var list = new List<int>();
@@ -641,7 +653,7 @@ public sealed partial class CultSystem : EntitySystem
if (teleportComponent.Label == null)
continue;
if (runeUid == rune)
if (runeUid == exceptRune)
continue;
if (!int.TryParse(runeUid.ToString(), out var intValue))
@@ -665,10 +677,6 @@ public sealed partial class CultSystem : EntitySystem
return false;
}
_entityManager.EnsureComponent<CultTeleportRuneProviderComponent>(user, out var providerComponent);
providerComponent.Targets = victims;
providerComponent.BaseRune = rune;
_ui.SetUiState(ui, new TeleportRunesListWindowBUIState(list, labels));
if (_ui.IsUiOpen(user, ui.UiKey))

View File

@@ -10,7 +10,7 @@ using Robust.Shared.Timing;
namespace Content.Server._White.Cult.UI;
public sealed class TeleportSpellEui : BaseEui
public sealed class CultTeleportSpellEui : BaseEui
{
[Dependency] private readonly EntityManager _entityManager = default!;
private readonly SharedTransformSystem _transformSystem;
@@ -22,7 +22,7 @@ public sealed class TeleportSpellEui : BaseEui
private bool _used;
public TeleportSpellEui(EntityUid performer, EntityUid target)
public CultTeleportSpellEui(EntityUid performer, EntityUid target)
{
IoCManager.InjectDependencies(this);
@@ -39,7 +39,7 @@ public sealed class TeleportSpellEui : BaseEui
public override EuiStateBase GetNewState()
{
var runesQuery = _entityManager.EntityQueryEnumerator<CultRuneTeleportComponent>();
var state = new TeleportSpellEuiState();
var state = new CultTeleportSpellEuiState();
while (runesQuery.MoveNext(out var runeUid, out var rune))
{
@@ -110,4 +110,4 @@ public sealed class TeleportSpellEui : BaseEui
_transformSystem.SetCoordinates(_target, runeTransform.Coordinates);
Close();
}
}
}

View File

@@ -105,7 +105,7 @@ public sealed class WizardSpellsSystem : EntitySystem
if (!TryComp(msg.Performer, out ActorComponent? actor))
return;
var eui = new TeleportSpellEui(msg.Performer);
var eui = new WizardTeleportSpellEui(msg.Performer);
_euiManager.OpenEui(eui, actor.PlayerSession);
eui.StateDirty();

View File

@@ -0,0 +1,6 @@
namespace Content.Server._White.Wizard.SpellBlade;
[RegisterComponent]
public sealed partial class FireAspectComponent : Component
{
}

View File

@@ -0,0 +1,13 @@
using Content.Shared.Atmos;
namespace Content.Server._White.Wizard.SpellBlade;
[RegisterComponent]
public sealed partial class FrostAspectComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float TemperatureOnHit = 100;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MinTemperature = Atmospherics.TCMB;
}

View File

@@ -0,0 +1,22 @@
namespace Content.Server._White.Wizard.SpellBlade;
[RegisterComponent]
public sealed partial class LightningAspectComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float Range = 2f;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int BoltCount = 3;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public string LightningPrototype = "WeakWizardLightning";
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int ArcDepth = 2;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan ShockRate = TimeSpan.FromSeconds(10);
public TimeSpan NextShock;
}

View File

@@ -0,0 +1,79 @@
using Content.Server.Atmos.Components;
using Content.Server.Lightning;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared._White.Wizard.SpellBlade;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Timing;
namespace Content.Server._White.Wizard.SpellBlade;
public sealed class SpellBladeSystem : SharedSpellBladeSystem
{
[Dependency] private readonly TemperatureSystem _temperature = default!;
[Dependency] private readonly LightningSystem _lightning = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FrostAspectComponent, MeleeHitEvent>(OnFrostMeleeHit);
SubscribeLocalEvent<LightningAspectComponent, MeleeHitEvent>(OnLightningMeleeHit);
}
private void OnLightningMeleeHit(Entity<LightningAspectComponent> ent, ref MeleeHitEvent args)
{
if (args.Direction != null || args.HitEntities.Count != 1)
return;
if (ent.Comp.NextShock > _timing.CurTime)
return;
ent.Comp.NextShock = _timing.CurTime + ent.Comp.ShockRate;
_lightning.ShootRandomLightnings(args.HitEntities[0], ent.Comp.Range, ent.Comp.BoltCount,
ent.Comp.LightningPrototype, ent.Comp.ArcDepth, false, args.User);
}
private void OnFrostMeleeHit(Entity<FrostAspectComponent> ent, ref MeleeHitEvent args)
{
var temp = ent.Comp.TemperatureOnHit;
if (args.Direction != null) // Heavy attack
temp *= 0.5f;
foreach (var entity in args.HitEntities)
{
if (!TryComp<TemperatureComponent>(entity, out var temperature))
continue;
var curTemp = temperature.CurrentTemperature;
var newTemp = curTemp - temp;
newTemp = curTemp < ent.Comp.MinTemperature
? MathF.Min(curTemp, newTemp)
: Math.Max(newTemp, ent.Comp.MinTemperature);
_temperature.ForceChangeTemperature(entity, newTemp, temperature);
}
}
protected override void ApplyFireAspect(EntityUid uid)
{
var ignite = EnsureComp<IgniteOnMeleeHitComponent>(uid);
ignite.FireStacks = 2f;
EnsureComp<FireAspectComponent>(uid);
}
protected override void ApplyFrostAspect(EntityUid uid)
{
var ignite = EnsureComp<IgniteOnMeleeHitComponent>(uid);
ignite.FireStacks = -5f;
EnsureComp<FrostAspectComponent>(uid);
}
protected override void ApplyLightningAspect(EntityUid uid)
{
EnsureComp<LightningAspectComponent>(uid);
}
}

View File

@@ -1,14 +1,12 @@
using System.Linq;
using Content.Server.EUI;
using Content.Server.EUI;
using Content.Server.Popups;
using Content.Shared._White.Wizard.Teleport;
using Content.Shared.Eui;
using Robust.Shared.Timing;
using TeleportSpellEuiState = Content.Shared._White.Wizard.Teleport.TeleportSpellEuiState;
namespace Content.Server._White.Wizard.Teleport;
public sealed class TeleportSpellEui : BaseEui
public sealed class WizardTeleportSpellEui : BaseEui
{
[Dependency] private readonly EntityManager _entityManager = default!;
private readonly SharedTransformSystem _transformSystem;
@@ -19,7 +17,7 @@ public sealed class TeleportSpellEui : BaseEui
private bool _used;
public TeleportSpellEui(EntityUid performer)
public WizardTeleportSpellEui(EntityUid performer)
{
IoCManager.InjectDependencies(this);
@@ -29,13 +27,13 @@ public sealed class TeleportSpellEui : BaseEui
_performer = performer;
Timer.Spawn(TimeSpan.FromSeconds(10), Close);
Timer.Spawn(TimeSpan.FromSeconds(60), Close);
}
public override EuiStateBase GetNewState()
{
var locationQuery = _entityManager.EntityQueryEnumerator<TeleportLocationComponent, TransformComponent>();
var state = new TeleportSpellEuiState();
var state = new WizardTeleportSpellEuiState();
while (locationQuery.MoveNext(out var locationUid, out var locationComponent, out var transformComponent))
{

View File

@@ -533,6 +533,11 @@ public abstract partial class SharedGunSystem : EntitySystem
Dirty(gun);
}
// WD EDIT
public void SetUseKey(GunComponent gun, bool useKey)
{
gun.UseKey = useKey;
}
public void SetProjectileSpeed(EntityUid weapon, float projectileSpeed)
{
if(!TryComp<GunComponent>(weapon, out var gunComponent))

View File

@@ -1,10 +1,11 @@
using System.Numerics;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared._White.BetrayalDagger;
[RegisterComponent]
[RegisterComponent, NetworkedComponent]
public sealed partial class BlinkComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadWrite)]

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Serialization;
namespace Content.Shared._White.Cult.UI;
[Serializable, NetSerializable]
public sealed class TeleportSpellEuiState : EuiStateBase
public sealed class CultTeleportSpellEuiState : EuiStateBase
{
public Dictionary<int, string> Runes = new();
}

View File

@@ -0,0 +1,115 @@
using System.Linq;
using Content.Shared._White.BetrayalDagger;
using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.UserInterface;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
namespace Content.Shared._White.Wizard.SpellBlade;
public abstract class SharedSpellBladeSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpellBladeComponent, SpellBladeSystemMessage>(OnMessage);
SubscribeLocalEvent<SpellBladeComponent, ActivatableUIOpenAttemptEvent>(OnOpenAttempt);
SubscribeLocalEvent<SpellBladeComponent, ExaminedEvent>(OnExamined);
}
private void OnExamined(Entity<SpellBladeComponent> ent, ref ExaminedEvent args)
{
if (ent.Comp.ChosenAspect == string.Empty)
{
args.PushMarkup("Аспект не выбран.");
return;
}
var proto = _prototypeManager.Index(ent.Comp.ChosenAspect);
args.PushMarkup($"Выбранный аспект: {proto.Name}");
}
private void OnOpenAttempt(Entity<SpellBladeComponent> ent, ref ActivatableUIOpenAttemptEvent args)
{
if (ent.Comp.ChosenAspect == string.Empty)
return;
_popup.PopupEntity("Аспект уже выбран.", args.User, args.User);
args.Cancel();
}
private void OnMessage(Entity<SpellBladeComponent> ent, ref SpellBladeSystemMessage args)
{
if (ent.Comp.ChosenAspect != string.Empty)
return;
switch (args.ProtoId)
{
case "AspectFire":
ApplyFireAspect(ent);
break;
case "AspectFrost":
ApplyFrostAspect(ent);
break;
case "AspectLightning":
ApplyLightningAspect(ent);
break;
case "AspectBluespace":
ApplyBluespaceAspect(ent);
break;
case "AspectMagicMissile":
ApplyMagicMissileAspect(ent);
break;
default:
return;
}
ent.Comp.ChosenAspect = args.ProtoId;
_audio.PlayPvs(ent.Comp.AspectChosenSound, ent);
Dirty(ent);
}
protected virtual void ApplyFireAspect(EntityUid uid) { }
protected virtual void ApplyFrostAspect(EntityUid uid) { }
protected virtual void ApplyLightningAspect(EntityUid uid) { }
private void ApplyBluespaceAspect(EntityUid uid)
{
var blink = EnsureComp<BlinkComponent>(uid);
blink.Distance = 15f;
blink.BlinkRate = 1f;
}
private void ApplyMagicMissileAspect(EntityUid uid)
{
var gun = EnsureComp<GunComponent>(uid);
_gun.SetUseKey(gun, false);
_gun.SetSound(uid, new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/Magic/staff_healing.ogg"));
_gun.SetFireRate(uid, 1.2f);
var ammoProvider = EnsureComp<BasicEntityAmmoProviderComponent>(uid);
ammoProvider.Proto = "ProjectileMagicMissile";
}
public bool IsHoldingItemWithComponent<T>(EntityUid uid) where T : Component
{
return _hands.EnumerateHeld(uid).Any(HasComp<T>);
}
}

View File

@@ -0,0 +1,39 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Wizard.SpellBlade;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class SpellBladeComponent : Component
{
[ViewVariables, AutoNetworkedField]
public string ChosenAspect = string.Empty;
[DataField]
public List<EntProtoId> Aspects = new()
{
"AspectFire",
"AspectFrost",
"AspectLightning",
"AspectBluespace",
"AspectMagicMissile"
};
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier AspectChosenSound = new SoundPathSpecifier("/Audio/White/Magic/spellblade-aspect.ogg");
}
[Serializable, NetSerializable]
public sealed class SpellBladeSystemMessage(EntProtoId protoId) : BoundUserInterfaceMessage
{
public EntProtoId ProtoId = protoId;
}
[Serializable, NetSerializable]
public enum SpellBladeUiKey : byte
{
Key
}

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Serialization;
namespace Content.Shared._White.Wizard.Teleport;
[Serializable, NetSerializable]
public sealed class TeleportSpellEuiState : EuiStateBase
public sealed class WizardTeleportSpellEuiState : EuiStateBase
{
public Dictionary<int, string> Locations = new();
}

Binary file not shown.

View File

@@ -176,3 +176,16 @@
- type: Lightning
canArc: true
lightningPrototype: WizardLightning
- type: entity
name: wizard lightning
id: WeakWizardLightning
parent: BaseLightning
noSpawn: true
components:
- type: Electrified
requirePower: false
ignoreInsulation: true
- type: Lightning
canArc: true
lightningPrototype: WeakWizardLightning

View File

@@ -236,3 +236,44 @@
stunAmount: 2
knockdownAmount: 2
- type: TeslaProjectile
- type: entity
id: ProjectileMagicMissile
name: magic missile
description: asdf
parent: BaseBullet
noSpawn: true
components:
- type: Sprite
sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
layers:
- state: spell
color: pink
- type: Projectile
damage:
groups:
Burn: 0
- type: StaminaDamageOnCollide
damage: 60
- type: Ammo
muzzleFlash: null
- type: Trail
splineIteratorType: CatmullRom
splineRendererType: Continuous
creationMethod: OnMove
lengthStep: 0.1
scale: 0.05, 0.0
lifetime: 1
randomWalk: 0.1, 0.001
gravity: 0.0, 0.0
texturePath: /Textures/White/Effects/Trails/Continuous/trail.png
gradientIteratorType: Linear
gradient:
- 1, 0, 0, 1
- 1, 1, 0, 0.85
- 0, 1, 0, 0.7
- 0, 1, 1, 0.55
- 0, 0, 1, 0.4
- 1, 0, 1, 0.25
- 1, 0, 0, 0.1
optionsConcealable: true

View File

@@ -36,7 +36,6 @@
- HolyKatana
- MultiverseBlade
- VorpalScythe
- SpellBlade
- PossessedBlade
- ChainsawHand
- HolyWhip
@@ -195,31 +194,6 @@
sprite: White/Objects/Weapons/Chaplain/scythe-inhands.rsi
- type: HolyWeapon
- type: entity
name: клинок заклинаний
parent: HolyKatana
id: SpellBlade
description: Клинок, с шансом 20% наносящий критический удар.
components:
- type: Sprite
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
- type: MeleeWeapon
wideAnimationRotation: 135
damage:
types:
Slash: 18
- type: Clothing
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
slots:
- back
- suitStorage
- type: Crit
critChance: 20
critMultiplier: 2.5
- type: Item
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
- type: HolyWeapon
- type: entity
name: одержимый клинок
parent: HolyKatana

View File

@@ -21,3 +21,33 @@
reflectProb: 0.4
- type: Item
sprite: White/Objects/Weapons/Chaplain/hfrequency.rsi
- type: entity
name: клинок заклинаний
parent: Katana
id: SpellBlade
description: Магический клинок, наделяемый силой одного из пяти аспектов.
components:
- type: Sprite
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
- type: MeleeWeapon
wideAnimationRotation: 135
damage:
types:
Slash: 30
- type: Clothing
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
slots:
- back
- suitStorage
- type: Item
sprite: White/Objects/Weapons/Chaplain/spellblade.rsi
- type: UserInterface
interfaces:
- key: enum.SpellBladeUiKey.Key
type: SpellBladeBUI
- type: ActivatableUI
key: enum.SpellBladeUiKey.Key
inHandsOnly: true
closeOnHandDeselect: true
- type: SpellBlade

View File

@@ -0,0 +1,49 @@
- type: entity
name: Огонь
description: Клинок заклинаний наделяется способностью поджигать врагов. И тот, кто удерживает клинок в руках, становится неуязвимым к огню и высокой температуре.
id: AspectFire
noSpawn: true
components:
- type: Sprite
sprite: Objects/Magic/magicactions.rsi
state: fireball
- type: entity
name: Холод
description: Клинок заклинаний наделяется замораживать врагов. И тот, кто удерживает клинок в руках, становится неуязвимым к низкой температуре.
id: AspectFrost
noSpawn: true
components:
- type: Sprite
sprite: Objects/Magic/magicactions.rsi
state: icebeam_active
- type: entity
name: Молния
description: Клинок заклинаний наделяется способностью каждые 10 секунд при ударе излучать шоковый заряд, поражающий ближайшие цели.
id: AspectLightning
noSpawn: true
components:
- type: Sprite
sprite: Objects/Magic/magicactions.rsi
state: thunder
- type: entity
name: Блюспейс
description: Клинок заклинаний наделяется способностью телепортации на далёкое расстояние.
id: AspectBluespace
noSpawn: true
components:
- type: Sprite
sprite: Objects/Magic/magicactions.rsi
state: blink
- type: entity
name: Магическая Стрела
description: Клинок заклинаний наделяется способностью стрелять оглушающей магической стрелой.
id: AspectMagicMissile
noSpawn: true
components:
- type: Sprite
sprite: Objects/Magic/magicactions.rsi
state: magicmissile

View File

@@ -247,3 +247,16 @@
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: SpellBookSpellBlade
name: spellbook-spellblade-name
description: spellbook-spellblade-desc
productEntity: SpellBlade
cost:
SpellPoint: 2
categories:
- MagicItems
conditions:
- !type:ListingLimitedStockCondition
stock: 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -54,6 +54,9 @@
},
{
"name": "teleport"
},
{
"name": "icebeam_active"
}
]
}