Magic Items (#370)
* attuning to items start * verbs attuning, forgot oldest attuning * Update CP14SharedMagicAttuningSystem.cs * some fixes * add deattuning * fix popups on owner * file restructurization * MORE FOLDER RENAMING GODD * magic via holding yea! * fix deattuning * vfx bugfixs * wearing magic items * Cure wounds spell * refactor spells * more redo * finish refactor
This commit is contained in:
@@ -1,130 +0,0 @@
|
||||
using Content.Shared._CP14.Magic.Components.Spells;
|
||||
using Content.Shared._CP14.Magic.Events;
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Shared._CP14.Magic;
|
||||
|
||||
public partial class CP14SharedMagicSystem
|
||||
{
|
||||
private void InitializeSpells()
|
||||
{
|
||||
// Instants
|
||||
SubscribeLocalEvent<CP14DelayedSpawnEntitiesSpellComponent, CP14DelayedInstantActionDoAfterEvent>(OnCastEntitiesSpawn);
|
||||
SubscribeLocalEvent<CP14DelayedSelfEntityEffectSpellComponent, CP14DelayedInstantActionDoAfterEvent>(OnCastSelfEntityEffects);
|
||||
//Entity Target
|
||||
SubscribeLocalEvent<CP14DelayedApplyEntityEffectsSpellComponent, CP14DelayedEntityTargetActionDoAfterEvent>(OnCastApplyEntityEffects);
|
||||
//World Target
|
||||
SubscribeLocalEvent<CP14DelayedProjectileSpellComponent, CP14DelayedWorldTargetActionDoAfterEvent>(OnCastProjectileSpell);
|
||||
SubscribeLocalEvent<CP14DelayedSpawnOnWorldTargetSpellComponent, CP14DelayedWorldTargetActionDoAfterEvent>(OnCastSpawnOnPoint);
|
||||
}
|
||||
|
||||
//TODO: Fuck,there's a lot of code repetition here that needs to be squeezed together somehow. Event calls, checks, and all this stuff
|
||||
private void OnCastEntitiesSpawn(Entity<CP14DelayedSpawnEntitiesSpellComponent> spell, ref CP14DelayedInstantActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(spell, ref stopEv);
|
||||
|
||||
if (args.Cancelled || args.Handled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
foreach (var spawn in spell.Comp.Spawns)
|
||||
{
|
||||
SpawnAtPosition(spawn, Transform(args.User).Coordinates);
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
}
|
||||
|
||||
private void OnCastSelfEntityEffects(Entity<CP14DelayedSelfEntityEffectSpellComponent> spell, ref CP14DelayedInstantActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(spell, ref stopEv);
|
||||
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
foreach (var effect in spell.Comp.Effects)
|
||||
{
|
||||
effect.Effect(new EntityEffectBaseArgs(args.User, EntityManager));
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
}
|
||||
|
||||
private void OnCastApplyEntityEffects(Entity<CP14DelayedApplyEntityEffectsSpellComponent> spell, ref CP14DelayedEntityTargetActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(spell, ref stopEv);
|
||||
|
||||
if (args.Cancelled || args.Handled || args.Target == null)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
foreach (var effect in spell.Comp.Effects)
|
||||
{
|
||||
effect.Effect(new EntityEffectBaseArgs(args.Target.Value, EntityManager));
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
}
|
||||
|
||||
private void OnCastProjectileSpell(Entity<CP14DelayedProjectileSpellComponent> spell, ref CP14DelayedWorldTargetActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(spell, ref stopEv);
|
||||
|
||||
if (args.Cancelled || args.Handled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var xform = Transform(args.User);
|
||||
var fromCoords = xform.Coordinates;
|
||||
var toCoords = GetCoordinates(args.Target);
|
||||
var userVelocity = _physics.GetMapLinearVelocity(args.User);
|
||||
|
||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
||||
var fromMap = fromCoords.ToMap(EntityManager, _transform);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(fromMap, out var gridUid, out _)
|
||||
? fromCoords.WithEntityId(gridUid, EntityManager)
|
||||
: new(_mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position);
|
||||
|
||||
var ent = Spawn(spell.Comp.Prototype, spawnCoords);
|
||||
var direction = toCoords.ToMapPos(EntityManager, _transform) -
|
||||
spawnCoords.ToMapPos(EntityManager, _transform);
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, args.User, args.User);
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
}
|
||||
|
||||
private void OnCastSpawnOnPoint(Entity<CP14DelayedSpawnOnWorldTargetSpellComponent> spell, ref CP14DelayedWorldTargetActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(spell, ref stopEv);
|
||||
|
||||
if (args.Cancelled || args.Handled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var xform = Transform(args.User);
|
||||
var toCoords = GetCoordinates(args.Target);
|
||||
|
||||
foreach (var spawn in spell.Comp.Spawns)
|
||||
{
|
||||
SpawnAtPosition(spawn, toCoords);
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of effects for delayed actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicSystem))]
|
||||
public sealed partial class CP14DelayedApplyEntityEffectsSpellComponent : Component
|
||||
{
|
||||
[DataField(required: true, serverOnly: true)]
|
||||
public List<EntityEffect> Effects = new();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of effects for delayed actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicSystem))]
|
||||
public sealed partial class CP14DelayedProjectileSpellComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What entity should be spawned.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Prototype;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of effects for delayed actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicSystem))]
|
||||
public sealed partial class CP14DelayedSelfEntityEffectSpellComponent : Component
|
||||
{
|
||||
[DataField(required: true, serverOnly: true)]
|
||||
public List<EntityEffect> Effects = new();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of effects for delayed actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicSystem))]
|
||||
public sealed partial class CP14DelayedSpawnEntitiesSpellComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What entities should be spawned.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public HashSet<EntProtoId> Spawns = new();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of effects for delayed actions.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicSystem))]
|
||||
public sealed partial class CP14DelayedSpawnOnWorldTargetSpellComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What entities should be spawned.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public HashSet<EntProtoId> Spawns = new();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Content.Shared._CP14.MagicAttuning;
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the fact that this subject can be focused on (Magical attune as a mechanic from DnD.)
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicAttuningSystem))]
|
||||
public sealed partial class CP14MagicAttuningItemComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// how long it takes to focus on that object
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan FocusTime = TimeSpan.FromSeconds(5f);
|
||||
|
||||
public Entity<CP14MagicAttuningMindComponent>? Link = null;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Content.Shared._CP14.MagicAttuning;
|
||||
|
||||
/// <summary>
|
||||
/// A mind that can focus on objects
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedMagicAttuningSystem))]
|
||||
public sealed partial class CP14MagicAttuningMindComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public int MaxAttuning = 3;
|
||||
/// <summary>
|
||||
/// The entities that this being is focused on
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntityUid> AttunedTo = new();
|
||||
|
||||
/// <summary>
|
||||
/// cheat: if added to an entity with MindContainer, automatically copied to the mind, removing it from the body. This is to make it easy to add the component to prototype creatures.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool AutoCopyToMind = false;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._CP14.MagicAttuning;
|
||||
|
||||
/// <summary>
|
||||
/// This system controls the customization to magic items by the players.
|
||||
/// </summary>
|
||||
public sealed partial class CP14SharedMagicAttuningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14MagicAttuningItemComponent, GetVerbsEvent<InteractionVerb>>(OnInteractionVerb);
|
||||
SubscribeLocalEvent<CP14MagicAttuningMindComponent, CP14MagicAttuneDoAfterEvent>(OnAttuneDoAfter);
|
||||
SubscribeLocalEvent<CP14MagicAttuningMindComponent, MindAddedMessage>(OnMindAdded);
|
||||
}
|
||||
|
||||
private void OnMindAdded(Entity<CP14MagicAttuningMindComponent> ent, ref MindAddedMessage args)
|
||||
{
|
||||
if (!ent.Comp.AutoCopyToMind)
|
||||
return;
|
||||
|
||||
if (HasComp<MindComponent>(ent))
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetMind(ent, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
var attuneMind = AddComp<CP14MagicAttuningMindComponent>(mindId);
|
||||
attuneMind.MaxAttuning = ent.Comp.MaxAttuning;
|
||||
}
|
||||
|
||||
public bool IsAttunedTo(EntityUid mind, EntityUid item)
|
||||
{
|
||||
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
|
||||
return false;
|
||||
|
||||
if (!TryComp<CP14MagicAttuningMindComponent>(mind, out var attuningMind))
|
||||
return false;
|
||||
|
||||
return attuningMind.AttunedTo.Contains(item);
|
||||
}
|
||||
|
||||
private void OnInteractionVerb(Entity<CP14MagicAttuningItemComponent> attuningItem, ref GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetMind(args.User, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14MagicAttuningMindComponent>(mindId, out var attumingMind))
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
if (attumingMind.AttunedTo.Contains(args.Target))
|
||||
{
|
||||
args.Verbs.Add(new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
RemoveAttune((mindId, attumingMind), attuningItem);
|
||||
},
|
||||
Text = Loc.GetString("cp14-magic-deattuning-verb-text"),
|
||||
Message = Loc.GetString("cp14-magic-attuning-verb-message"),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Verbs.Add(new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
TryStartAttune(user, attuningItem);
|
||||
},
|
||||
Text = Loc.GetString("cp14-magic-attuning-verb-text"),
|
||||
Message = Loc.GetString("cp14-magic-attuning-verb-message"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryStartAttune(EntityUid user, Entity<CP14MagicAttuningItemComponent> item)
|
||||
{
|
||||
if (!_mind.TryGetMind(user, out var mindId, out var mind))
|
||||
return false;
|
||||
|
||||
if (!TryComp<CP14MagicAttuningMindComponent>(mindId, out var attuningMind))
|
||||
return false;
|
||||
|
||||
if (attuningMind.MaxAttuning <= 0)
|
||||
return false;
|
||||
|
||||
//if there's an overabundance of ties, we report that the oldest one is torn.
|
||||
if (attuningMind.AttunedTo.Count >= attuningMind.MaxAttuning)
|
||||
{
|
||||
var oldestAttune = attuningMind.AttunedTo[0];
|
||||
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(oldestAttune).EntityName)), user, user);
|
||||
}
|
||||
|
||||
//we notify the current owner of the item that someone is cutting ties.
|
||||
if (item.Comp.Link is not null &&
|
||||
item.Comp.Link.Value.Owner != mindId &&
|
||||
TryComp<MindComponent>(item.Comp.Link.Value.Owner, out var ownerMind) &&
|
||||
ownerMind.OwnedEntity is not null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(item).EntityName)), ownerMind.OwnedEntity.Value, ownerMind.OwnedEntity.Value);
|
||||
}
|
||||
|
||||
var doAfterArgs = new DoAfterArgs(EntityManager,
|
||||
user,
|
||||
item.Comp.FocusTime,
|
||||
new CP14MagicAttuneDoAfterEvent(),
|
||||
mindId,
|
||||
item)
|
||||
{
|
||||
BreakOnDamage = true,
|
||||
BreakOnMove = true,
|
||||
DistanceThreshold = 2f,
|
||||
BlockDuplicate = true,
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnAttuneDoAfter(Entity<CP14MagicAttuningMindComponent> ent, ref CP14MagicAttuneDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || args.Target is null)
|
||||
return;
|
||||
|
||||
if (ent.Comp.AttunedTo.Count >= ent.Comp.MaxAttuning)
|
||||
{
|
||||
var oldestAttune = ent.Comp.AttunedTo[0];
|
||||
RemoveAttune(ent, oldestAttune);
|
||||
}
|
||||
|
||||
AddAttune(ent, args.Target.Value);
|
||||
}
|
||||
|
||||
private void RemoveAttune(Entity<CP14MagicAttuningMindComponent> attuningMind, EntityUid item)
|
||||
{
|
||||
if (!attuningMind.Comp.AttunedTo.Contains(item))
|
||||
return;
|
||||
|
||||
attuningMind.Comp.AttunedTo.Remove(item);
|
||||
|
||||
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
|
||||
return;
|
||||
|
||||
if (!TryComp<MindComponent>(attuningMind, out var mind))
|
||||
return;
|
||||
|
||||
attuningItem.Link = null;
|
||||
|
||||
var ev = new RemovedAttuneFromMindEvent(attuningMind, mind.OwnedEntity, item);
|
||||
RaiseLocalEvent(attuningMind, ev);
|
||||
RaiseLocalEvent(item, ev);
|
||||
|
||||
if (mind.OwnedEntity is not null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot-end", ("item", MetaData(item).EntityName)), mind.OwnedEntity.Value, mind.OwnedEntity.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAttune(Entity<CP14MagicAttuningMindComponent> attuningMind, EntityUid item)
|
||||
{
|
||||
if (attuningMind.Comp.AttunedTo.Contains(item))
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
|
||||
return;
|
||||
|
||||
if (!TryComp<MindComponent>(attuningMind, out var mind))
|
||||
return;
|
||||
|
||||
if (attuningItem.Link is not null)
|
||||
RemoveAttune(attuningItem.Link.Value, item);
|
||||
|
||||
attuningMind.Comp.AttunedTo.Add(item);
|
||||
attuningItem.Link = attuningMind;
|
||||
|
||||
|
||||
var ev = new AddedAttuneToMindEvent(attuningMind, mind.OwnedEntity, item);
|
||||
RaiseLocalEvent(attuningMind, ev);
|
||||
RaiseLocalEvent(item, ev);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class CP14MagicAttuneDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// is evoked on both the item and the mind when a new connection between them appears.
|
||||
/// </summary>
|
||||
public sealed class AddedAttuneToMindEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Mind;
|
||||
public readonly EntityUid? User;
|
||||
public readonly EntityUid Item;
|
||||
|
||||
public AddedAttuneToMindEvent(EntityUid mind, EntityUid? user, EntityUid item)
|
||||
{
|
||||
Mind = mind;
|
||||
User = user;
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// is evoked on both the item and the mind when the connection is broken
|
||||
/// </summary>
|
||||
public sealed class RemovedAttuneFromMindEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Mind;
|
||||
public readonly EntityUid? User;
|
||||
public readonly EntityUid Item;
|
||||
|
||||
public RemovedAttuneFromMindEvent(EntityUid mind, EntityUid? user, EntityUid item)
|
||||
{
|
||||
Mind = mind;
|
||||
User = user;
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using Content.Shared._CP14.Magic.Components;
|
||||
using Content.Shared._CP14.Magic.Events;
|
||||
using Content.Shared._CP14.MagicEnergy;
|
||||
using Content.Shared._CP14.MagicEnergy.Components;
|
||||
using Content.Shared._CP14.MagicSpell.Components;
|
||||
using Content.Shared._CP14.MagicSpell.Events;
|
||||
using Content.Shared._CP14.MagicSpell.Spells;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Speech.Muting;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
@@ -13,8 +13,11 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared._CP14.Magic;
|
||||
namespace Content.Shared._CP14.MagicSpell;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles the basic mechanics of spell use, such as doAfter, event invocation, and energy spending.
|
||||
/// </summary>
|
||||
public partial class CP14SharedMagicSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
@@ -26,7 +29,6 @@ public partial class CP14SharedMagicSystem : EntitySystem
|
||||
[Dependency] private readonly SharedCP14MagicEnergySystem _magicEnergy = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -34,6 +36,10 @@ public partial class CP14SharedMagicSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<CP14MagicEffectComponent, CP14BeforeCastMagicEffectEvent>(OnBeforeCastMagicEffect);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicEffectComponent, CP14DelayedInstantActionDoAfterEvent>(OnDelayedInstantActionDoAfter);
|
||||
SubscribeLocalEvent<CP14MagicEffectComponent, CP14DelayedEntityTargetActionDoAfterEvent>(OnDelayedEntityTargetDoAfter);
|
||||
SubscribeLocalEvent<CP14MagicEffectComponent, CP14DelayedWorldTargetActionDoAfterEvent>(OnDelayedWorldTargetDoAfter);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicEffectSomaticAspectComponent, CP14BeforeCastMagicEffectEvent>(OnSomaticAspectBeforeCast);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicEffectVerbalAspectComponent, CP14BeforeCastMagicEffectEvent>(OnVerbalAspectBeforeCast);
|
||||
@@ -44,8 +50,57 @@ public partial class CP14SharedMagicSystem : EntitySystem
|
||||
SubscribeLocalEvent<CP14DelayedInstantActionEvent>(OnInstantAction);
|
||||
SubscribeLocalEvent<CP14DelayedEntityTargetActionEvent>(OnEntityTargetAction);
|
||||
SubscribeLocalEvent<CP14DelayedWorldTargetActionEvent>(OnWorldTargetAction);
|
||||
}
|
||||
|
||||
InitializeSpells();
|
||||
private void OnDelayedWorldTargetDoAfter(Entity<CP14MagicEffectComponent> ent, ref CP14DelayedWorldTargetActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(ent, ref stopEv);
|
||||
|
||||
if (args.Cancelled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
foreach (var effect in ent.Comp.Effects)
|
||||
{
|
||||
effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, null, GetCoordinates(args.Target)));
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
}
|
||||
|
||||
private void OnDelayedEntityTargetDoAfter(Entity<CP14MagicEffectComponent> ent, ref CP14DelayedEntityTargetActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(ent, ref stopEv);
|
||||
|
||||
if (args.Cancelled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
foreach (var effect in ent.Comp.Effects)
|
||||
{
|
||||
effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, args.Target, null));
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
}
|
||||
|
||||
private void OnDelayedInstantActionDoAfter(Entity<CP14MagicEffectComponent> ent, ref CP14DelayedInstantActionDoAfterEvent args)
|
||||
{
|
||||
var stopEv = new CP14StopCastMagicEffectEvent();
|
||||
RaiseLocalEvent(ent, ref stopEv);
|
||||
|
||||
if (args.Cancelled || !_net.IsServer)
|
||||
return;
|
||||
|
||||
foreach (var effect in ent.Comp.Effects)
|
||||
{
|
||||
effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, args.User, Transform(args.User).Coordinates));
|
||||
}
|
||||
|
||||
var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User};
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
}
|
||||
|
||||
private void OnSomaticAspectBeforeCast(Entity<CP14MagicEffectSomaticAspectComponent> ent, ref CP14BeforeCastMagicEffectEvent args)
|
||||
@@ -85,6 +140,9 @@ public partial class CP14SharedMagicSystem : EntitySystem
|
||||
|
||||
private void OnVerbalAspectAfterCast(Entity<CP14MagicEffectVerbalAspectComponent> ent, ref CP14AfterCastMagicEffectEvent args)
|
||||
{
|
||||
if (_net.IsClient)
|
||||
return;
|
||||
|
||||
var ev = new CP14VerbalAspectSpeechEvent
|
||||
{
|
||||
Performer = args.Performer,
|
||||
@@ -1,6 +1,6 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components;
|
||||
namespace Content.Shared._CP14.MagicSpell.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary entity that exists while the spell is cast, and disappears at the end. For visual special effects.
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared._CP14.MagicSpell.Spells;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components;
|
||||
namespace Content.Shared._CP14.MagicSpell.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Restricts the use of this action, by spending mana or user requirements.
|
||||
@@ -13,4 +14,7 @@ public sealed partial class CP14MagicEffectComponent : Component
|
||||
|
||||
[DataField]
|
||||
public bool Safe = false;
|
||||
|
||||
[DataField]
|
||||
public List<CP14SpellEffect> Effects = new();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Content.Shared._CP14.Magic.Components;
|
||||
namespace Content.Shared._CP14.MagicSpell.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires the user to have at least one free hand to use this spell
|
||||
@@ -1,6 +1,4 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Components;
|
||||
namespace Content.Shared._CP14.MagicSpell.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Requires the user to be able to speak in order to use this spell. Also forces the user to use certain phrases at the beginning and end of a spell cast
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Content.Shared._CP14.Magic.Events;
|
||||
namespace Content.Shared._CP14.MagicSpell.Events;
|
||||
|
||||
[ByRefEvent]
|
||||
public sealed class CP14BeforeCastMagicEffectEvent : CancellableEntityEventArgs
|
||||
@@ -3,7 +3,7 @@ using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._CP14.Magic.Events;
|
||||
namespace Content.Shared._CP14.MagicSpell.Events;
|
||||
|
||||
//World target
|
||||
public sealed partial class CP14DelayedWorldTargetActionEvent : WorldTargetActionEvent, ICP14DelayedMagicEffect
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Content.Shared._CP14.Magic;
|
||||
namespace Content.Shared._CP14.MagicSpell.Events;
|
||||
|
||||
public interface ICP14DelayedMagicEffect // The speak n spell interface
|
||||
{
|
||||
@@ -0,0 +1,22 @@
|
||||
using Content.Shared.EntityEffects;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpell.Spells;
|
||||
|
||||
public sealed partial class CP14SpellApplyEntityEffect : CP14SpellEffect
|
||||
{
|
||||
[DataField(required: true, serverOnly: true)]
|
||||
public List<EntityEffect> Effects = new();
|
||||
|
||||
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
if (args.Target is null)
|
||||
return;
|
||||
|
||||
var targetEntity = args.Target.Value;
|
||||
|
||||
foreach (var effect in Effects)
|
||||
{
|
||||
effect.Effect(new EntityEffectBaseArgs(targetEntity, entManager));
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs
Normal file
25
Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpell.Spells;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[MeansImplicitUse]
|
||||
public abstract partial class CP14SpellEffect
|
||||
{
|
||||
public abstract void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args);
|
||||
}
|
||||
|
||||
public record class CP14SpellEffectBaseArgs
|
||||
{
|
||||
public EntityUid? User;
|
||||
public EntityUid? Target;
|
||||
public EntityCoordinates? Position;
|
||||
|
||||
public CP14SpellEffectBaseArgs(EntityUid? user, EntityUid? target, EntityCoordinates? position)
|
||||
{
|
||||
User = user;
|
||||
Target = target;
|
||||
Position = position;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpell.Spells;
|
||||
|
||||
public sealed partial class CP14SpellProjectile : CP14SpellEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Prototype;
|
||||
|
||||
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
EntityCoordinates? targetPoint = null;
|
||||
if (args.Position is not null)
|
||||
targetPoint = args.Position.Value;
|
||||
else if (args.Target is not null && entManager.TryGetComponent<TransformComponent>(args.Target.Value, out var transformComponent))
|
||||
targetPoint = transformComponent.Coordinates;
|
||||
|
||||
if (targetPoint is null)
|
||||
return;
|
||||
|
||||
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var gunSystem = entManager.System<SharedGunSystem>();
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
if (!entManager.TryGetComponent<TransformComponent>(args.User, out var xform))
|
||||
return;
|
||||
|
||||
var fromCoords = xform.Coordinates;
|
||||
|
||||
if (fromCoords == targetPoint)
|
||||
return;
|
||||
|
||||
var userVelocity = physics.GetMapLinearVelocity(args.User.Value);
|
||||
|
||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
||||
var fromMap = transform.ToMapCoordinates(fromCoords);
|
||||
var spawnCoords = mapManager.TryFindGridAt(fromMap, out var gridUid, out _)
|
||||
? transform.WithEntityId(fromCoords, gridUid)
|
||||
: new(mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position);
|
||||
|
||||
|
||||
var ent = entManager.SpawnAtPosition(Prototype, spawnCoords);
|
||||
var direction = targetPoint.Value.ToMapPos(entManager, transform) -
|
||||
spawnCoords.ToMapPos(entManager, transform);
|
||||
gunSystem.ShootProjectile(ent, direction, userVelocity, args.User.Value, args.User);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpell.Spells;
|
||||
|
||||
public sealed partial class CP14SpellSpawnEntity : CP14SpellEffect
|
||||
{
|
||||
[DataField]
|
||||
public List<EntProtoId> Spawns = new();
|
||||
|
||||
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
EntityCoordinates? targetPoint = null;
|
||||
if (args.Position is not null)
|
||||
targetPoint = args.Position.Value;
|
||||
else if (args.Target is not null && entManager.TryGetComponent<TransformComponent>(args.Target.Value, out var transformComponent))
|
||||
targetPoint = transformComponent.Coordinates;
|
||||
|
||||
if (targetPoint is null)
|
||||
return;
|
||||
|
||||
foreach (var spawn in Spawns)
|
||||
{
|
||||
entManager.SpawnAtPosition(spawn, targetPoint.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Shared._CP14.MagicSpellStorage;
|
||||
|
||||
/// <summary>
|
||||
/// Denotes that this item's spells can be accessed while holding it in your hand
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SpellStorageSystem))]
|
||||
public sealed partial class CP14SpellStorageAccessHoldingComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Shared._CP14.MagicSpellStorage;
|
||||
|
||||
/// <summary>
|
||||
/// Denotes that this item's spells can be accessed while wearing it in your body
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SpellStorageSystem))]
|
||||
public sealed partial class CP14SpellStorageAccessWearingComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpellStorage;
|
||||
|
||||
/// <summary>
|
||||
/// A component that allows you to store spells in items
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SpellStorageSystem))]
|
||||
public sealed partial class CP14SpellStorageComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// list of spell prototypes used for initialization.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntProtoId> Spells = new();
|
||||
|
||||
/// <summary>
|
||||
/// created after the initialization of spell entities.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<EntityUid> SpellEntities = new();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Shared._CP14.MagicSpellStorage;
|
||||
|
||||
/// <summary>
|
||||
/// The ability to access spellcasting is limited by the attuning requirement
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SpellStorageSystem))]
|
||||
public sealed partial class CP14SpellStorageRequireAttuneComponent : Component
|
||||
{
|
||||
}
|
||||
109
Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs
Normal file
109
Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Content.Shared._CP14.MagicAttuning;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Mind;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpellStorage;
|
||||
|
||||
/// <summary>
|
||||
/// this part of the system is responsible for storing spells in items, and the methods players use to obtain them.
|
||||
/// </summary>
|
||||
public sealed partial class CP14SpellStorageSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly CP14SharedMagicAttuningSystem _attuning = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<CP14SpellStorageComponent, MapInitEvent>(OnMagicStorageInit);
|
||||
|
||||
SubscribeLocalEvent<CP14SpellStorageAccessHoldingComponent, GotEquippedHandEvent>(OnEquippedHand);
|
||||
SubscribeLocalEvent<CP14SpellStorageAccessHoldingComponent, AddedAttuneToMindEvent>(OnHandAddedAttune);
|
||||
|
||||
SubscribeLocalEvent<CP14SpellStorageAccessWearingComponent, ClothingGotEquippedEvent>(OnClothingEquipped);
|
||||
SubscribeLocalEvent<CP14SpellStorageAccessWearingComponent, ClothingGotUnequippedEvent>(OnClothingUnequipped);
|
||||
|
||||
SubscribeLocalEvent<CP14SpellStorageRequireAttuneComponent, RemovedAttuneFromMindEvent>(OnRemovedAttune);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When we initialize, we create action entities, and add them to this item.
|
||||
/// </summary>
|
||||
private void OnMagicStorageInit(Entity<CP14SpellStorageComponent> mStorage, ref MapInitEvent args)
|
||||
{
|
||||
foreach (var spell in mStorage.Comp.Spells)
|
||||
{
|
||||
var spellEnt = _actionContainer.AddAction(mStorage, spell);
|
||||
if (spellEnt is null)
|
||||
continue;
|
||||
|
||||
mStorage.Comp.SpellEntities.Add(spellEnt.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEquippedHand(Entity<CP14SpellStorageAccessHoldingComponent> ent, ref GotEquippedHandEvent args)
|
||||
{
|
||||
if (!TryComp<CP14SpellStorageComponent>(ent, out var spellStorage))
|
||||
return;
|
||||
|
||||
TryGrantAccess((ent, spellStorage), args.User);
|
||||
}
|
||||
|
||||
private void OnHandAddedAttune(Entity<CP14SpellStorageAccessHoldingComponent> ent, ref AddedAttuneToMindEvent args)
|
||||
{
|
||||
if (!TryComp<CP14SpellStorageComponent>(ent, out var spellStorage))
|
||||
return;
|
||||
|
||||
if (args.User is null)
|
||||
return;
|
||||
|
||||
if (!_hands.IsHolding(args.User.Value, ent))
|
||||
return;
|
||||
|
||||
TryGrantAccess((ent, spellStorage), args.User.Value);
|
||||
}
|
||||
|
||||
private void OnClothingEquipped(Entity<CP14SpellStorageAccessWearingComponent> ent, ref ClothingGotEquippedEvent args)
|
||||
{
|
||||
if (!TryComp<CP14SpellStorageComponent>(ent, out var spellStorage))
|
||||
return;
|
||||
|
||||
TryGrantAccess((ent, spellStorage), args.Wearer);
|
||||
}
|
||||
|
||||
private void OnClothingUnequipped(Entity<CP14SpellStorageAccessWearingComponent> ent, ref ClothingGotUnequippedEvent args)
|
||||
{
|
||||
_actions.RemoveProvidedActions(args.Wearer, ent);
|
||||
}
|
||||
|
||||
private bool TryGrantAccess(Entity<CP14SpellStorageComponent> storage, EntityUid user)
|
||||
{
|
||||
if (!_mind.TryGetMind(user, out var mindId, out var mind))
|
||||
return false;
|
||||
|
||||
if (mind.OwnedEntity is null)
|
||||
return false;
|
||||
|
||||
if (TryComp<CP14SpellStorageRequireAttuneComponent>(storage, out var reqAttune))
|
||||
{
|
||||
if (!_attuning.IsAttunedTo(mindId, storage))
|
||||
return false;
|
||||
}
|
||||
|
||||
_actions.GrantActions(user, storage.Comp.SpellEntities, storage);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnRemovedAttune(Entity<CP14SpellStorageRequireAttuneComponent> ent, ref RemovedAttuneFromMindEvent args)
|
||||
{
|
||||
if (args.User is null)
|
||||
return;
|
||||
|
||||
_actions.RemoveProvidedActions(args.User.Value, ent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user