Refactor magic speak system to be a component added to actions (#36328)
This commit is contained in:
@@ -21,13 +21,6 @@ public sealed class MagicSystem : SharedMagicSystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpeakSpellEvent>(OnSpellSpoken);
|
||||
}
|
||||
|
||||
private void OnSpellSpoken(ref SpeakSpellEvent args)
|
||||
{
|
||||
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), InGameICChatType.Speak, false);
|
||||
}
|
||||
|
||||
public override void OnVoidApplause(VoidApplauseSpellEvent ev)
|
||||
|
||||
39
Content.Server/Speech/EntitySystems/SpeakOnActionSystem.cs
Normal file
39
Content.Server/Speech/EntitySystems/SpeakOnActionSystem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Content.Shared.Speech.Muting;
|
||||
using Content.Shared.Actions.Events;
|
||||
|
||||
|
||||
namespace Content.Server.Speech.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// As soon as the chat refactor moves to Shared
|
||||
/// the logic here can move to the shared <see cref="SharedSpeakOnActionSystem"/>
|
||||
/// </summary>
|
||||
public sealed class SpeakOnActionSystem : SharedSpeakOnActionSystem
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpeakOnActionComponent, ActionPerformedEvent>(OnActionPerformed);
|
||||
}
|
||||
|
||||
private void OnActionPerformed(Entity<SpeakOnActionComponent> ent, ref ActionPerformedEvent args)
|
||||
{
|
||||
var user = args.Performer;
|
||||
|
||||
// If we can't speak, we can't speak
|
||||
if (!HasComp<SpeechComponent>(user) || HasComp<MutedComponent>(user))
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ent.Comp.Sentence))
|
||||
return;
|
||||
|
||||
_chat.TrySendInGameICMessage(user, Loc.GetString(ent.Comp.Sentence), InGameICChatType.Speak, false);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Content.Shared.Magic.Events;
|
||||
/// <summary>
|
||||
/// Spell that uses the magic of ECS to add & remove components. Components are first removed, then added.
|
||||
/// </summary>
|
||||
public sealed partial class ChangeComponentsSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class ChangeComponentsSpellEvent : EntityTargetActionEvent
|
||||
{
|
||||
// TODO allow it to set component data-fields?
|
||||
// for now a Hackish way to do that is to remove & add, but that doesn't allow you to selectively set specific data fields.
|
||||
@@ -19,9 +19,4 @@ public sealed partial class ChangeComponentsSpellEvent : EntityTargetActionEvent
|
||||
[AlwaysPushInheritance]
|
||||
public HashSet<string> ToRemove = new();
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
|
||||
[DataField]
|
||||
public bool DoSpeech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Adds provided Charge to the held wand
|
||||
/// </summary>
|
||||
public sealed partial class ChargeSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
public sealed partial class ChargeSpellEvent : InstantActionEvent
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public int Charge;
|
||||
|
||||
[DataField]
|
||||
public string WandTag = "WizardWand";
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
public sealed partial class InstantSpawnSpellEvent : InstantActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// What entity should be spawned.
|
||||
@@ -14,9 +14,6 @@ public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakS
|
||||
[DataField]
|
||||
public bool PreventCollideWithCaster = true;
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the targeted spawn positons; may lead to multiple entities being spawned.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
public sealed partial class KnockSpellEvent : InstantActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The range this spell opens doors in
|
||||
@@ -11,7 +11,4 @@ public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Range = 10f;
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -2,14 +2,11 @@ using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class MindSwapSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class MindSwapSpellEvent : EntityTargetActionEvent
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan PerformerStunDuration = TimeSpan.FromSeconds(10);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan TargetStunDuration = TimeSpan.FromSeconds(10);
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// What entity should be spawned.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Prototype;
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class RandomGlobalSpawnSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
public sealed partial class RandomGlobalSpawnSpellEvent : InstantActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of prototypes this spell can spawn, will select one randomly
|
||||
@@ -18,9 +18,6 @@ public sealed partial class RandomGlobalSpawnSpellEvent : InstantActionEvent, IS
|
||||
[DataField]
|
||||
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Magic/staff_animation.ogg");
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should this Global spawn spell turn its targets into a Survivor Antagonist?
|
||||
/// Ignores the caster for this.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class SmiteSpellEvent : EntityTargetActionEvent
|
||||
{
|
||||
// TODO: Make part of gib method
|
||||
/// <summary>
|
||||
@@ -10,7 +10,4 @@ public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpe
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool DeleteNonBrainParts = true;
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
[ByRefEvent]
|
||||
public readonly struct SpeakSpellEvent(EntityUid performer, string speech)
|
||||
{
|
||||
public readonly EntityUid Performer = performer;
|
||||
public readonly string Speech = speech;
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
// TODO: Can probably just be an entity or something
|
||||
public sealed partial class TeleportSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class TeleportSpellEvent : WorldTargetActionEvent
|
||||
{
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
|
||||
// TODO: Move to magic component
|
||||
// TODO: Maybe not since sound specifier is a thing
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Magic.Events;
|
||||
|
||||
public sealed partial class VoidApplauseSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class VoidApplauseSpellEvent : EntityTargetActionEvent
|
||||
{
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Emote to use.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Storage;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Shared.Magic.Events;
|
||||
|
||||
// TODO: This class needs combining with InstantSpawnSpellEvent
|
||||
|
||||
public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of prototypes this spell will spawn
|
||||
@@ -28,7 +28,4 @@ public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpea
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float? Lifetime;
|
||||
|
||||
[DataField]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Shared.Magic;
|
||||
|
||||
public interface ISpeakSpell // The speak n spell interface
|
||||
{
|
||||
/// <summary>
|
||||
/// Localized string spoken by the caster when casting this spell.
|
||||
/// </summary>
|
||||
public string? Speech { get; }
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
@@ -141,7 +140,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
SpawnSpellHelper(args.Prototype, position, args.Performer, preventCollide: args.PreventCollideWithCaster);
|
||||
}
|
||||
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -235,7 +233,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
var targetMapCoords = args.Target;
|
||||
|
||||
WorldSpawnSpellHelper(args.Prototypes, targetMapCoords, args.Performer, args.Lifetime, args.Offset);
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -271,7 +268,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
var xform = Transform(ev.Performer);
|
||||
var fromCoords = xform.Coordinates;
|
||||
@@ -299,8 +295,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
if (ev.DoSpeech)
|
||||
Speak(ev);
|
||||
|
||||
RemoveComponents(ev.Target, ev.ToRemove);
|
||||
AddComponents(ev.Target, ev.ToAdd);
|
||||
@@ -324,7 +318,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
|
||||
_transform.SetCoordinates(args.Performer, args.Target);
|
||||
_transform.AttachToGridOrMap(args.Performer, transform);
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -334,7 +327,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
_transform.SwapPositions(ev.Performer, ev.Target);
|
||||
}
|
||||
@@ -392,7 +384,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
var direction = _transform.GetMapCoordinates(ev.Target, Transform(ev.Target)).Position - _transform.GetMapCoordinates(ev.Performer, Transform(ev.Performer)).Position;
|
||||
var impulseVector = direction * 10000;
|
||||
@@ -418,7 +409,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
Speak(args);
|
||||
|
||||
var transform = Transform(args.Performer);
|
||||
|
||||
@@ -457,7 +447,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
}
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
if (wand == null || !TryComp<BasicEntityAmmoProviderComponent>(wand, out var basicAmmoComp) || basicAmmoComp.Count == null)
|
||||
return;
|
||||
@@ -475,7 +464,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
var allHumans = _mind.GetAliveHumans();
|
||||
|
||||
@@ -509,7 +497,6 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
Speak(ev);
|
||||
|
||||
// Need performer mind, but target mind is unnecessary, such as taking over a NPC
|
||||
// Need to get target mind before putting performer mind into their body if they have one
|
||||
@@ -535,14 +522,4 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
// End Spells
|
||||
#endregion
|
||||
|
||||
// When any spell is cast it will raise this as an event, so then it can be played in server or something. At least until chat gets moved to shared
|
||||
// TODO: Temp until chat is in shared
|
||||
private void Speak(BaseActionEvent args)
|
||||
{
|
||||
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
|
||||
return;
|
||||
|
||||
var ev = new SpeakSpellEvent(args.Performer, speak.Speech);
|
||||
RaiseLocalEvent(ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
17
Content.Shared/Speech/Components/SpeakOnActionComponent.cs
Normal file
17
Content.Shared/Speech/Components/SpeakOnActionComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Speech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Action components which should write a message to ICChat on use
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedSpeakOnActionSystem))]
|
||||
public sealed partial class SpeakOnActionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ftl id of the sentence that the user will speak.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public LocId? Sentence;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Content.Shared.Chasm;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Speech.Muting;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.Speech.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Once the chat refactor has happened, move the code from
|
||||
/// <see cref="Content.Server.Speech.EntitySystems.SpeakOnUseSystem"/>
|
||||
/// to here and set this class to sealed.
|
||||
/// </summary>
|
||||
public abstract class SharedSpeakOnActionSystem : EntitySystem;
|
||||
@@ -8,3 +8,4 @@ action-speech-spell-summon-magic = RYGOIN FEMA-VERECO
|
||||
action-speech-spell-mind-swap = GIN'YU CAPAN!
|
||||
action-speech-spell-cluwne = !KNOH
|
||||
action-speech-spell-slip = SLEE PARRI!
|
||||
action-speech-spell-charge = DI'RI CEL!
|
||||
@@ -74,5 +74,3 @@
|
||||
- BlockMovement
|
||||
- Item
|
||||
- MeleeRequiresWield
|
||||
speech: action-speech-spell-animate
|
||||
doSpeech: false
|
||||
|
||||
@@ -158,7 +158,8 @@
|
||||
orGroup: Guns
|
||||
- id: RevolverCapGunFake
|
||||
orGroup: Guns
|
||||
speech: action-speech-spell-summon-guns
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-summon-guns
|
||||
|
||||
- type: entity
|
||||
id: ActionSummonMagic
|
||||
@@ -205,4 +206,5 @@
|
||||
orGroup: Magics
|
||||
- id: RGBStaff
|
||||
orGroup: Magics
|
||||
speech: action-speech-spell-summon-magic
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-summon-magic
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
event: !type:InstantSpawnSpellEvent
|
||||
prototype: WallForce
|
||||
posData: !type:TargetInFront
|
||||
speech: action-speech-spell-forcewall
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-forcewall
|
||||
|
||||
@@ -12,4 +12,5 @@
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: knock
|
||||
event: !type:KnockSpellEvent
|
||||
speech: action-speech-spell-knock
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-knock
|
||||
|
||||
@@ -18,4 +18,5 @@
|
||||
sprite: Mobs/Species/Human/organs.rsi
|
||||
state: brain
|
||||
event: !type:MindSwapSpellEvent
|
||||
speech: action-speech-spell-mind-swap
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-mind-swap
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
state: fireball
|
||||
event: !type:ProjectileSpellEvent
|
||||
prototype: ProjectileFireball
|
||||
speech: action-speech-spell-fireball
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-fireball
|
||||
- type: ActionUpgrade
|
||||
effectedLevels:
|
||||
2: ActionFireballII
|
||||
@@ -41,7 +42,8 @@
|
||||
state: fireball
|
||||
event: !type:ProjectileSpellEvent
|
||||
prototype: ProjectileFireball
|
||||
speech: action-speech-spell-fireball
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-fireball
|
||||
|
||||
- type: entity
|
||||
id: ActionFireballIII
|
||||
@@ -61,4 +63,5 @@
|
||||
state: fireball
|
||||
event: !type:ProjectileSpellEvent
|
||||
prototype: ProjectileFireball
|
||||
speech: action-speech-spell-fireball
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-fireball
|
||||
|
||||
@@ -15,4 +15,5 @@
|
||||
- id: MobCarpMagic
|
||||
amount: 3
|
||||
offset: 0, 1
|
||||
speech: action-speech-spell-summon-magicarp
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-summon-magicarp
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: gib
|
||||
event: !type:SmiteSpellEvent
|
||||
speech: action-speech-spell-smite
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-smite
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
|
||||
@@ -47,9 +48,10 @@
|
||||
sprite: Clothing/Mask/cluwne.rsi
|
||||
state: icon
|
||||
event: !type:ChangeComponentsSpellEvent
|
||||
speech: action-speech-spell-cluwne
|
||||
toAdd:
|
||||
- type: Cluwne
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-cluwne
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
|
||||
@@ -72,10 +74,11 @@
|
||||
sprite: Objects/Specific/Janitorial/soap.rsi
|
||||
state: omega-4
|
||||
event: !type:ChangeComponentsSpellEvent
|
||||
speech: action-speech-spell-slip
|
||||
toAdd:
|
||||
- type: Slippery
|
||||
- type: StepTrigger
|
||||
requiredTriggeredSpeed: -1
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-slip
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
|
||||
@@ -11,4 +11,5 @@
|
||||
state: nothing
|
||||
event: !type:ChargeSpellEvent
|
||||
charge: 1
|
||||
speech: DI'RI CEL!
|
||||
- type: SpeakOnAction
|
||||
sentence: action-speech-spell-charge
|
||||
|
||||
Reference in New Issue
Block a user