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:
Ed
2024-08-01 11:52:27 +03:00
committed by GitHub
parent 19365c0314
commit 56da23925f
40 changed files with 787 additions and 396 deletions

View File

@@ -10,7 +10,6 @@ public partial class CP14MagicEnergySystem
{
SubscribeLocalEvent<CP14MagicEnergyDrawComponent, MapInitEvent>(OnDrawMapInit);
SubscribeLocalEvent<CP14RandomAuraNodeComponent, MapInitEvent>(OnRandomRangeMapInit);
}
private void OnRandomRangeMapInit(Entity<CP14RandomAuraNodeComponent> random, ref MapInitEvent args)

View File

@@ -1,13 +1,15 @@
using Content.Server.Chat.Systems;
using Content.Shared._CP14.Magic;
using Content.Shared._CP14.Magic.Components;
using Content.Shared._CP14.Magic.Events;
using Content.Shared._CP14.MagicSpell;
using Content.Shared._CP14.MagicSpell.Components;
using Content.Shared._CP14.MagicSpell.Events;
using Robust.Server.GameObjects;
namespace Content.Server._CP14.Magic;
namespace Content.Server._CP14.MagicSpell;
public sealed partial class CP14MagicSystem : CP14SharedMagicSystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly TransformSystem _transform = default!;
public override void Initialize()
{
@@ -26,6 +28,7 @@ public sealed partial class CP14MagicSystem : CP14SharedMagicSystem
private void OnSpawnMagicVisualEffect(Entity<CP14MagicEffectCastingVisualComponent> ent, ref CP14StartCastMagicEffectEvent args)
{
var vfx = SpawnAttachedTo(ent.Comp.Proto, Transform(args.Performer).Coordinates);
_transform.SetParent(vfx, args.Performer);
ent.Comp.SpawnedEntity = vfx;
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
namespace Content.Shared._CP14.Magic.Events;
namespace Content.Shared._CP14.MagicSpell.Events;
[ByRefEvent]
public sealed class CP14BeforeCastMagicEffectEvent : CancellableEntityEventArgs

View File

@@ -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

View File

@@ -1,4 +1,4 @@
namespace Content.Shared._CP14.Magic;
namespace Content.Shared._CP14.MagicSpell.Events;
public interface ICP14DelayedMagicEffect // The speak n spell interface
{

View File

@@ -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));
}
}
}

View 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
{
}

View File

@@ -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
{
}

View File

@@ -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();
}

View File

@@ -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
{
}

View 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);
}
}

View File

@@ -0,0 +1,7 @@
cp14-magic-attuning-verb-text = Attune yourself
cp14-magic-attuning-verb-message = Some magic items require a creature to form a bond with them before their magical properties can be used.
cp14-magic-deattuning-verb-text = Break attune
cp14-magic-attune-oldest-forgot = Your connection to {$item} is weakening ...
cp14-magic-attune-oldest-forgot-end = Your connection to {$item} has broken...

View File

@@ -0,0 +1,7 @@
cp14-magic-attuning-verb-text = Настроиться на магический предмет
cp14-magic-attuning-verb-message = Некоторые магические предметы требуют того, чтобы существо образовало с ними связь, прежде чем оно сможет использовать их магические свойства.
cp14-magic-deattuning-verb-text = Разорвать связь с магическим предметом
cp14-magic-attune-oldest-forgot = Ваша связь с {$item} слабеет...
cp14-magic-attune-oldest-forgot-end = Ваша связь с {$item} оборвалась...

View File

@@ -0,0 +1,62 @@
- type: entity
id: CP14ActionSpellCureWounds
name: Cure wounds
description: You touch the creature, healing its body from physical damage
components:
- type: CP14MagicEffect
manaCost: 10
effects:
- !type:CP14SpellSpawnEntity
spawns:
- CP14CureWoundsImpactEffect
- !type:CP14SpellApplyEntityEffect
effects:
- !type:HealthChange
damage:
types:
Slash: -8
Blunt: -8
Piercing: -8
Heat: -8
- !type:Jitter
- type: CP14MagicEffectVerbalAspect
startSpeech: "Et curabuntur..."
endSpeech: "vulnera tua"
- type: CP14MagicEffectSomaticAspect
- type: CP14MagicEffectCastingVisual
proto: CP14CureWoundsRune
- type: EntityTargetAction
useDelay: 5
itemIconStyle: BigAction
interactOnMiss: false
sound: !type:SoundPathSpecifier
path: /Audio/Magic/rumble.ogg
icon:
sprite: _CP14/Effects/Magic/spells_icons.rsi
state: cure_wounds
event: !type:CP14DelayedEntityTargetActionEvent
delay: 2
- type: entity
id: CP14CureWoundsRune
noSpawn: true
parent: CP14BaseMagicRune
components:
- type: PointLight
color: "#328643"
- type: Sprite
layers:
- state: sun
color: "#79b330"
shader: unshaded
- type: entity
id: CP14CureWoundsImpactEffect
parent: CP14BaseMagicImpact
noSpawn: true
components:
- type: Sprite
layers:
- state: particles_up
color: "#79b330"
shader: unshaded

View File

@@ -1,153 +0,0 @@
- type: entity
id: CP14ActionSpellHealingWord
name: Healing word
description: bruh
components:
- type: CP14MagicEffect
manaCost: 10
- type: EntityTargetAction
useDelay: 1
itemIconStyle: BigAction
interactOnMiss: true
sound: !type:SoundPathSpecifier
path: /Audio/Magic/rumble.ogg
icon:
sprite: Objects/Magic/magicactions.rsi
state: gib
event: !type:CP14DelayedEntityTargetActionEvent
- type: CP14DelayedApplyEntityEffectsSpell
effects:
- !type:HealthChange
damage:
types:
Slash: -5
Blunt: -5
Piercing: -5
Heat: -10
- !type:Jitter
- type: entity
id: CP14ActionSpellFireball
name: Fireball
description: Fires an explosive fireball towards the clicked location.
components:
- type: CP14MagicEffect
manaCost: 70
- type: CP14MagicEffectVerbalAspect
startSpeech: "O tenebrae, ubi lux non penetrat..."
endSpeech: "Quaeso, quemdam inter vos quaero!"
- type: CP14MagicEffectSomaticAspect
- type: CP14MagicEffectCastingVisual
proto: CP14FireballRune
- type: WorldTargetAction
useDelay: 3
itemIconStyle: BigAction
checkCanAccess: false
raiseOnUser: true
range: 60
sound: !type:SoundPathSpecifier
path: /Audio/Magic/fireball.ogg
icon:
sprite: Objects/Magic/magicactions.rsi
state: fireball
event: !type:CP14DelayedWorldTargetActionEvent
delay: 3
- type: CP14DelayedProjectileSpell
prototype: ProjectileFireball
- type: entity
id: CP14ActionSpellSelfHeal
name: Self Heal
description: Toggles your suit's phase cloak. Beware that if you are hit, all abilities are disabled for 5 seconds, including your cloak!
components:
- type: InstantAction
# have to plan (un)cloaking ahead of time
useDelay: 5
priority: -9
event: !type:CP14DelayedInstantActionEvent
- type: CP14DelayedSelfEntityEffectSpell
effects:
- !type:HealthChange
damage:
types:
Slash: -5
Blunt: -5
Piercing: -5
Heat: -10
- !type:Jitter
- type: entity
id: CP14ActionSpelldaggerSpawn
name: Gravity well
description: Toggles your suit's phase cloak. Beware that if you are hit, all abilities are disabled for 5 seconds, including your cloak!
components:
- type: CP14MagicEffect
manaCost: 20
- type: WorldTargetAction
useDelay: 1
itemIconStyle: BigAction
checkCanAccess: false
raiseOnUser: true
range: 4
sound: !type:SoundPathSpecifier
path: /Audio/Magic/fireball.ogg
icon:
sprite: Objects/Magic/magicactions.rsi
state: fireball
event: !type:CP14DelayedWorldTargetActionEvent
- type: CP14DelayedSpawnOnWorldTargetSpell
spawns:
- AdminInstantEffectGravityWell
- type: listing
id: CP14SpellbookJutter
name: jit
description: jit
productAction: CP14ActionSpellHealingWord
cost:
WizCoin: 1
categories:
- SpellbookOffensive
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: CP14SpellbookFireball
name: fifire
description: fifire
productAction: CP14ActionSpellFireball
cost:
WizCoin: 1
categories:
- SpellbookOffensive
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: CP14SpellbookFireball3
name: selfheal
description: selfheal
productAction: CP14ActionSpellSelfHeal
cost:
WizCoin: 1
categories:
- SpellbookOffensive
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: CP14SpellbookFireball33
name: Gravity Well
description: well...
productAction: CP14ActionSpelldaggerSpawn
cost:
WizCoin: 1
categories:
- SpellbookOffensive
conditions:
- !type:ListingLimitedStockCondition
stock: 1

View File

@@ -5,6 +5,7 @@
abstract: true
components:
- type: Sprite
noRot: true
drawDepth: FloorTiles
sprite: _CP14/Effects/Magic/cast_rune.rsi
- type: Tag
@@ -16,16 +17,19 @@
netsync: false
- type: entity
id: CP14FireballRune
parent: CP14BaseMagicRune
id: CP14BaseMagicImpact
name: magic impact
description: manifestation of magical energy in the physical plane
abstract: true
components:
- type: PointLight
color: "#d47d26"
- type: TimedDespawn
lifetime: 1.6
- type: AnimationPlayer
- type: EffectVisuals
- type: Tag
tags:
- HideContextMenu
- type: Sprite
layers:
- state: medium_circle
color: "#d47d26"
shader: unshaded
- state: sun
color: "#d47d26"
shader: unshaded
drawdepth: Effects
sprite: _CP14/Effects/Magic/cast_impact.rsi
noRot: true

View File

@@ -7,4 +7,11 @@
sprite: Objects/Specific/Hydroponics/seeds.rsi
state: seed
- type: CP14Seed
plantProto: CP14PlantWheat
plantProto: CP14PlantWheat
- type: CP14MagicAttuningItem
focusTime: 1
- type: CP14SpellStorageAccessHolding
- type: CP14SpellStorage
spells:
- CP14ActionSpellCureWounds
- type: CP14SpellStorageRequireAttune

View File

@@ -211,10 +211,12 @@
maxEnergy: 100
energy: 100
- type: CP14MagicEnergyDraw
energy: 5
delay: 5
energy: 1
delay: 6 # 10m to full restore
- type: CP14MagicUnsafeDamage
- type: CP14MagicUnsafeSleep
- type: CP14MagicAttuningMind
autoCopyToMind: true
- type: entity

View File

@@ -5,25 +5,25 @@
minPlayers: 0
stations:
Empty:
stationProto: StandardNanotrasenStation
stationProto: StandardStationArena
components:
- type: StationNameSetup
mapNameTemplate: "Empty"
- type: StationJobs
availableJobs:
CP14Adventurer: [ -1, -1 ] #CrystallPunk Dev replacement
CP14Adventurer: [ -1, -1 ]
- type: gameMap
id: Dev
mapName: Dev
mapPath: /Maps/_CP14/dev_map.yml #CrystallPunk Dev replacement
mapPath: /Maps/_CP14/dev_map.yml
minPlayers: 0
stations:
Dev:
stationProto: StandardNanotrasenStation
stationProto: StandardStationArena
components:
- type: StationNameSetup
mapNameTemplate: "Dev"
- type: StationJobs
availableJobs:
CP14Adventurer: [ -1, -1 ] #CrystallPunk Dev replacement
CP14Adventurer: [ -1, -1 ]

View File

@@ -0,0 +1,26 @@
{
"version": 1,
"size": {
"x": 48,
"y": 48
},
"license": "All rights reserved for the CrystallPunk14 project only",
"copyright": "Created by TheShuEd for CrystallPunk",
"states": [
{
"name": "particles_up",
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,14 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "All rights reserved for the CrystallPunk14 project only",
"copyright": "Created by TheShuEd",
"states": [
{
"name": "cure_wounds"
}
]
}