Spells stuff (#153)

* -add: Create and remove blood spells using verbs.

* - add: Blood spells now have charges and disappear.

* - add: Localize spells.

* - add: New blood spells.
This commit is contained in:
Aviu00
2024-03-01 17:44:27 +09:00
committed by GitHub
parent 9b5dd4481d
commit 71ca698a7c
20 changed files with 498 additions and 43 deletions

View File

@@ -0,0 +1,87 @@
using System.Linq;
using Content.Client._White.UserInterface.Radial;
using Content.Shared._White.Cult;
using Content.Shared.Actions;
using Content.Shared._White.Cult.Components;
using Robust.Client.Player;
using Robust.Client.Utility;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
namespace Content.Client._White.Cult.UI.SpellRemover;
public sealed class SpellRemoverBUI : BoundUserInterface
{
private RadialContainer? _radialContainer;
public SpellRemoverBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
base.Open();
_radialContainer = new RadialContainer();
_radialContainer.Closed += Close;
var entityManager = IoCManager.Resolve<IEntityManager>();
var ent = IoCManager.Resolve<IPlayerManager>().LocalEntity;
if (ent == null || !entityManager.TryGetComponent(ent, out CultistComponent? comp) ||
comp.SelectedEmpowers.Count == 0)
{
Close();
return;
}
var metaQuery = entityManager.GetEntityQuery<MetaDataComponent>();
var instantQuery = entityManager.GetEntityQuery<InstantActionComponent>();
var targetQuery = entityManager.GetEntityQuery<EntityTargetActionComponent>();
foreach (var action in comp.SelectedEmpowers)
{
if (action == null)
continue;
var spell = entityManager.GetEntity(action.Value);
if (!entityManager.EntityExists(spell))
continue;
SpriteSpecifier? icon;
if (instantQuery.TryGetComponent(spell, out var instantComp))
icon = instantComp.Icon;
else
{
if (!targetQuery.TryGetComponent(spell, out var targetComp))
continue;
icon = targetComp.Icon;
}
if (icon == null)
continue;
var texture = icon.Frame0();
if (!metaQuery.TryGetComponent(spell, out var meta))
continue;
var button = _radialContainer.AddButton(meta.EntityName, texture);
button.Controller.OnPressed += _ =>
{
SendMessage(new CultEmpowerRemoveBuiMessage(action.Value));
Close();
};
}
_radialContainer.OpenAttachedLocalPlayer();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_radialContainer?.Close();
}
}

View File

@@ -179,7 +179,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
foreach (var empower in component.SelectedEmpowers) foreach (var empower in component.SelectedEmpowers)
{ {
_actions.RemoveAction(uid, empower); _actions.RemoveAction(uid, GetEntity(empower));
} }
RemoveCultistAppearance(uid); RemoveCultistAppearance(uid);

View File

@@ -4,6 +4,7 @@ using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Emp; using Content.Server.Emp;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server._White.Cult.UI; using Content.Server._White.Cult.UI;
using Content.Shared._White.Chaplain;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
@@ -15,8 +16,11 @@ using Content.Shared.Mobs.Components;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.StatusEffect; using Content.Shared.StatusEffect;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Actions; using Content.Shared._White.Cult.Actions;
using Content.Shared.Actions;
using Content.Shared.Cuffs.Components;
using Content.Shared.DoAfter;
using Content.Shared.Mindshield.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -38,38 +42,71 @@ public partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultTwistedConstructionActionEvent>(OnTwistedConstructionAction); SubscribeLocalEvent<CultistComponent, CultTwistedConstructionActionEvent>(OnTwistedConstructionAction);
SubscribeLocalEvent<CultistComponent, CultSummonDaggerActionEvent>(OnSummonDaggerAction); SubscribeLocalEvent<CultistComponent, CultSummonDaggerActionEvent>(OnSummonDaggerAction);
SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles); SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles);
SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseTargetActionEvent>(OnElectromagneticPulse); SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseInstantActionEvent>(OnElectromagneticPulse);
SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment); SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment);
SubscribeLocalEvent<CultistComponent, CultConcealPresenceWorldActionEvent>(OnConcealPresence); SubscribeLocalEvent<CultistComponent, CultConcealPresenceWorldActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites); SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites);
SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport); SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport);
SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget); SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget);
SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved);
SubscribeLocalEvent<CultistComponent, ShacklesEvent>(OnShackles);
}
private void OnShackles(Entity<CultistComponent> ent, ref ShacklesEvent args)
{
if (args.Cancelled || args.Target == null)
return;
if (!TryComp(args.Target, out CuffableComponent? cuffable) || cuffable.Container.ContainedEntities.Count > 0)
return;
var cuffs = Spawn("ShadowShackles", Transform(ent).Coordinates);
if (!_cuffable.TryAddNewCuffs(args.Target.Value, args.User, cuffs, cuffable))
QueueDel(cuffs);
}
private void OnActionRemoved(Entity<CultistComponent> ent, ref ActionGettingRemovedEvent args)
{
ent.Comp.SelectedEmpowers.Remove(GetNetEntity(args.Action));
Dirty(ent);
} }
private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args) private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args)
{ {
if (args.Target == uid || !HasComp<StatusEffectsComponent>(args.Target)) if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
HasComp<HolyComponent>(args.Target) || !TryComp<StatusEffectsComponent>(args.Target, out var status))
return; return;
if (_stunSystem.TryStun(args.Target, TimeSpan.FromSeconds(6), true)) if (HasComp<MindShieldComponent>(args.Target))
{ {
args.Handled = true; _popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer);
return;
} }
if (!_stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(6), true, status) &
!_statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(12), true, "Muted",
status))
return;
_bloodstreamSystem.TryModifyBloodLevel(uid, -10, bloodstream, createPuddle: false);
args.Handled = true;
} }
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out _) || !TryComp<ActorComponent>(uid, out var actor)) if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) || !TryComp<ActorComponent>(uid, out var actor))
return; return;
if (!TryComp<CultistComponent>(args.Target, out _) && if (!TryComp<CultistComponent>(args.Target, out _) &&
!(TryComp<MobStateComponent>(args.Target, out var mobStateComponent) && !(TryComp<MobStateComponent>(args.Target, out var mobStateComponent) &&
mobStateComponent.CurrentState is not MobState.Alive)) mobStateComponent.CurrentState is not MobState.Alive))
{ {
_popupSystem.PopupEntity("Цель должна быть культистом или лежать", args.Performer); _popupSystem.PopupEntity("Цель должна быть культистом или лежать.", args.Performer, args.Performer);
return; return;
} }
_bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false);
var eui = new TeleportSpellEui(args.Performer, args.Target); var eui = new TeleportSpellEui(args.Performer, args.Target);
_euiManager.OpenEui(eui, actor.PlayerSession); _euiManager.OpenEui(eui, actor.PlayerSession);
eui.StateDirty(); eui.StateDirty();
@@ -148,10 +185,10 @@ public partial class CultSystem
CultistComponent component, CultistComponent component,
CultSummonCombatEquipmentTargetActionEvent args) CultSummonCombatEquipmentTargetActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out _)) if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream))
return; return;
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); _bloodstreamSystem.TryModifyBloodLevel(uid, -20, bloodstream, createPuddle: false);
var coordinates = Transform(uid).Coordinates; var coordinates = Transform(uid).Coordinates;
var helmet = Spawn("ClothingHeadHelmetCult", coordinates); var helmet = Spawn("ClothingHeadHelmetCult", coordinates);
@@ -177,30 +214,42 @@ public partial class CultSystem
private void OnElectromagneticPulse( private void OnElectromagneticPulse(
EntityUid uid, EntityUid uid,
CultistComponent component, CultistComponent component,
CultElectromagneticPulseTargetActionEvent args) CultElectromagneticPulseInstantActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out _)) if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream))
return; return;
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); _bloodstreamSystem.TryModifyBloodLevel(uid, -10, bloodstream, createPuddle: false);
var xform = Transform(uid); _empSystem.EmpPulse(_transform.GetMapCoordinates(uid), 5, 100000, 10f);
_empSystem.EmpPulse(xform.MapPosition, 10, 100000, 10f);
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false);
args.Handled = true; args.Handled = true;
} }
private void OnShadowShackles(EntityUid uid, CultistComponent component, CultShadowShacklesTargetActionEvent args) private void OnShadowShackles(EntityUid uid, CultistComponent component, CultShadowShacklesTargetActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out _)) if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream))
return; return;
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); _bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false);
var cuffs = Spawn("CultistCuffs", Transform(uid).Coordinates); if (!HasComp<HolyComponent>(args.Target) &&
_handsSystem.TryPickupAnyHand(uid, cuffs); _statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(10), true, "Muted"))
{
_popupSystem.PopupEntity("Цель обезмолвлена.", args.Performer, args.Performer);
args.Handled = true;
}
if (!TryComp(args.Target, out CuffableComponent? cuffs) || cuffs.Container.ContainedEntities.Count > 0)
return;
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, TimeSpan.FromSeconds(2),
new ShacklesEvent(), args.Performer, args.Target)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnDamage = true
});
args.Handled = true; args.Handled = true;
} }
@@ -229,14 +278,14 @@ public partial class CultSystem
var material = _entityManager.SpawnEntity(RunicMetalPrototypeId, transform); var material = _entityManager.SpawnEntity(RunicMetalPrototypeId, transform);
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -15, bloodstreamComponent, false); _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -stack.Count / 2f, bloodstreamComponent, false);
if (!_entityManager.TryGetComponent<StackComponent>(material, out var stackNew)) if (!_entityManager.TryGetComponent<StackComponent>(material, out var stackNew))
return; return;
stackNew.Count = count; stackNew.Count = count;
_popupSystem.PopupEntity(Loc.GetString("Конвертируем сталь в руиник металл!"), args.Performer, args.Performer); _popupSystem.PopupEntity("Конвертируем сталь в руинический металл!", args.Performer, args.Performer);
args.Handled = true; args.Handled = true;
} }
@@ -251,7 +300,7 @@ public partial class CultSystem
var xform = Transform(args.Performer).Coordinates; var xform = Transform(args.Performer).Coordinates;
var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform); var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform);
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -30, bloodstreamComponent, false); _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -20, bloodstreamComponent, false);
_handsSystem.TryPickupAnyHand(args.Performer, dagger); _handsSystem.TryPickupAnyHand(args.Performer, dagger);
args.Handled = true; args.Handled = true;
} }

View File

@@ -11,7 +11,6 @@ using Content.Server.Weapons.Ranged.Systems;
using Content.Server._White.Cult.GameRule; using Content.Server._White.Cult.GameRule;
using Content.Server._White.Cult.Runes.Comps; using Content.Server._White.Cult.Runes.Comps;
using Content.Shared._White.Chaplain; using Content.Shared._White.Chaplain;
using Content.Shared.Actions;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
@@ -28,16 +27,15 @@ using Content.Shared.Popups;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using Content.Shared.Rejuvenate; using Content.Shared.Rejuvenate;
using Content.Shared.Roles.Jobs;
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components; using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Runes; using Content.Shared._White.Cult.Runes;
using Content.Shared._White.Cult.UI; using Content.Shared._White.Cult.UI;
using Content.Shared.Cuffs;
using Content.Shared.Mindshield.Components; using Content.Shared.Mindshield.Components;
using Content.Shared.Pulling; using Content.Shared.Pulling;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
@@ -65,6 +63,7 @@ public sealed partial class CultSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly FlammableSystem _flammableSystem = default!; [Dependency] private readonly FlammableSystem _flammableSystem = default!;
[Dependency] private readonly SharedPullingSystem _pulling = default!; [Dependency] private readonly SharedPullingSystem _pulling = default!;
[Dependency] private readonly SharedCuffableSystem _cuffable = default!;
public override void Initialize() public override void Initialize()
@@ -110,6 +109,7 @@ public sealed partial class CultSystem : EntitySystem
InitializeBarrierSystem(); InitializeBarrierSystem();
InitializeConstructsAbilities(); InitializeConstructsAbilities();
InitializeActions(); InitializeActions();
InitializeVerb();
} }
private float _timeToDraw; private float _timeToDraw;
@@ -518,6 +518,14 @@ public sealed partial class CultSystem : EntitySystem
_stunSystem.TryStun(target, TimeSpan.FromSeconds(2f), false); _stunSystem.TryStun(target, TimeSpan.FromSeconds(2f), false);
HealCultist(target); HealCultist(target);
if (TryComp(target, out CuffableComponent? cuffs) && cuffs.Container.ContainedEntities.Count >= 1)
{
var lastAddedCuffs = cuffs.LastAddedCuffs;
_cuffable.Uncuff(target, user, lastAddedCuffs);
}
_statusEffectsSystem.TryRemoveStatusEffect(target, "Muted");
return true; return true;
} }
@@ -1143,17 +1151,19 @@ public sealed partial class CultSystem : EntitySystem
if (component.IsRune) if (component.IsRune)
{ {
if (comp.SelectedEmpowers.Count > component.MaxAllowedCultistActions) if (comp.SelectedEmpowers.Count >= component.MaxAllowedCultistActions)
{ {
_popupSystem.PopupEntity(Loc.GetString("cult-too-much-empowers"), uid); _popupSystem.PopupEntity(Loc.GetString("cult-too-much-empowers"), uid);
return; return;
} }
comp.SelectedEmpowers.Add(_actionsSystem.AddAction(playerEntity.Value, action)); comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(playerEntity.Value, action)));
Dirty(playerEntity.Value, comp);
} }
else if (comp.SelectedEmpowers.Count < component.MinRequiredCultistActions) else if (comp.SelectedEmpowers.Count < component.MinRequiredCultistActions)
{ {
comp.SelectedEmpowers.Add(_actionsSystem.AddAction(playerEntity.Value, action)); comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(playerEntity.Value, action)));
Dirty(playerEntity.Value, comp);
} }
} }

View File

@@ -0,0 +1,115 @@
using System.Linq;
using Content.Server.Body.Components;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared.DoAfter;
using Content.Shared.Verbs;
using Robust.Shared.Player;
namespace Content.Server._White.Cult.Runes.Systems;
public sealed partial class CultSystem
{
public void InitializeVerb()
{
SubscribeLocalEvent<CultistComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
}
private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args)
{
var entity = GetEntity(args.ActionType);
ent.Comp.SelectedEmpowers.Remove(args.ActionType);
_actionsSystem.RemoveAction(entity);
Dirty(ent);
}
private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args)
{
if (args.Cancelled || comp.SelectedEmpowers.Count >= 1)
return;
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell));
if (action == null)
return;
var howMuchBloodTake = HasComp<CultBuffComponent>(ent) ? -10f : -20f;
if (!TryComp<BloodstreamComponent>(ent, out var bloodstreamComponent))
return;
_bloodstreamSystem.TryModifyBloodLevel(ent, howMuchBloodTake, bloodstreamComponent, createPuddle: false);
comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(ent, args.Spell)));
Dirty(ent, comp);
}
private void OnCultistEmpowerSelected(EntityUid ent, CultistComponent comp, CultEmpowerSelectedBuiMessage args)
{
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.ActionType));
if (action == null)
return;
if (comp.SelectedEmpowers.Count >= 1)
{
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent);
return;
}
var creationTime = HasComp<CultBuffComponent>(ent) ? 2.5f : 5f;
_doAfterSystem.TryStartDoAfter(
new DoAfterArgs(_entityManager, ent, creationTime, new SpellCreatedEvent {Spell = action}, ent)
{
BreakOnDamage = true,
BreakOnUserMove = true
});
}
private void OnGetVerbs(Entity<CultistComponent> ent, ref GetVerbsEvent<Verb> args)
{
if (ent.Owner != args.User || !TryComp<ActorComponent>(args.User, out var actor))
return;
var createSpellVerb = new Verb
{
Text = Loc.GetString("verb-spell-create-text"),
Message = Loc.GetString("verb-spell-create-message"),
Category = VerbCategory.Cult,
Act = () =>
{
_ui.TryOpen(ent, CultEmpowerUiKey.Key, actor.PlayerSession);
}
};
var removeSpellVerb = new Verb
{
Text = Loc.GetString("verb-spell-remove-text"),
Message = Loc.GetString("verb-spell-remove-message"),
Category = VerbCategory.Cult,
Act = () =>
{
RemoveSpell(ent, actor.PlayerSession);
}
};
args.Verbs.Add(createSpellVerb);
args.Verbs.Add(removeSpellVerb);
}
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
{
if (ent.Comp.SelectedEmpowers.Count == 0)
{
_popupSystem.PopupEntity(Loc.GetString("verb-spell-remove-no-spells"), ent);
return;
}
_ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session);
}
}

View File

@@ -102,6 +102,13 @@ public sealed class RequestPerformActionEvent : EntityEventArgs
} }
} }
// WD START
public sealed partial class ActionGettingRemovedEvent : EntityEventArgs
{
public EntityUid Action;
}
// WD END
/// <summary> /// <summary>
/// This is the type of event that gets raised when an <see cref="InstantAction"/> is performed. The <see /// This is the type of event that gets raised when an <see cref="InstantAction"/> is performed. The <see
/// cref="Performer"/> field is automatically filled out by the <see cref="SharedActionsSystem"/>. /// cref="Performer"/> field is automatically filled out by the <see cref="SharedActionsSystem"/>.

View File

@@ -144,6 +144,11 @@ public abstract partial class BaseActionComponent : Component
/// </summary> /// </summary>
[DataField("autoPopulate")] public bool AutoPopulate = true; [DataField("autoPopulate")] public bool AutoPopulate = true;
// WD START
[DataField]
public bool RemoveOnNoCharges;
// WD END
/// <summary> /// <summary>
/// Temporary actions are deleted when they get removed a <see cref="ActionsComponent"/>. /// Temporary actions are deleted when they get removed a <see cref="ActionsComponent"/>.
/// </summary> /// </summary>

View File

@@ -15,6 +15,7 @@ using Robust.Shared.Map;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Shared.Rejuvenate; using Content.Shared.Rejuvenate;
using Robust.Shared.Network;
namespace Content.Shared.Actions; namespace Content.Shared.Actions;
@@ -29,6 +30,7 @@ public abstract class SharedActionsSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!;
[Dependency] private readonly INetManager _net = default!; // WD
public override void Initialize() public override void Initialize()
{ {
@@ -537,6 +539,13 @@ public abstract class SharedActionsSystem : EntitySystem
action.Charges--; action.Charges--;
if (action is { Charges: 0, RenewCharges: false }) if (action is { Charges: 0, RenewCharges: false })
action.Enabled = false; action.Enabled = false;
// WD START
if (action is {Charges: 0, RemoveOnNoCharges: true})
{
RaiseLocalEvent(performer, new ActionGettingRemovedEvent {Action = actionId});
RemoveAction(performer, actionId, component, action);
}
// WD END
} }
action.Cooldown = null; action.Cooldown = null;
@@ -816,7 +825,7 @@ public abstract class SharedActionsSystem : EntitySystem
Dirty(actionId.Value, action); Dirty(actionId.Value, action);
Dirty(performer, comp); Dirty(performer, comp);
ActionRemoved(performer, actionId.Value, comp, action); ActionRemoved(performer, actionId.Value, comp, action);
if (action.Temporary) if (action.Temporary && (!_net.IsClient || action.ClientExclusive)) // WD EDIT
QueueDel(actionId.Value); QueueDel(actionId.Value);
} }

View File

@@ -680,8 +680,13 @@ namespace Content.Shared.Cuffs
if (cuff.BreakOnRemove) if (cuff.BreakOnRemove)
{ {
QueueDel(cuffsToRemove); QueueDel(cuffsToRemove);
var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates); // WD EDIT START
_hands.PickupOrDrop(user, trash); if (cuff.BrokenPrototype != null)
{
var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates);
_hands.PickupOrDrop(user, trash);
}
// WD EDIT END
} }
else else
{ {

View File

@@ -41,6 +41,9 @@ namespace Content.Shared.Verbs
public static readonly VerbCategory MeatyOre = public static readonly VerbCategory MeatyOre =
new("MeatyOre", null); new("MeatyOre", null);
public static readonly VerbCategory Cult =
new("verb-categories-cult", "/Textures/White/Cult/interface.rsi/icon.png");
public static readonly VerbCategory Antag = public static readonly VerbCategory Antag =
new("verb-categories-antag", "/Textures/Interface/VerbIcons/antag-e_sword-temp.192dpi.png", iconsOnly: true) { Columns = 5 }; new("verb-categories-antag", "/Textures/Interface/VerbIcons/antag-e_sword-temp.192dpi.png", iconsOnly: true) { Columns = 5 };

View File

@@ -18,7 +18,7 @@ public sealed partial class CultTeleportTargetActionEvent : EntityTargetActionEv
{ {
} }
public sealed partial class CultElectromagneticPulseTargetActionEvent : EntityTargetActionEvent public sealed partial class CultElectromagneticPulseInstantActionEvent : InstantActionEvent
{ {
} }

View File

@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Cult.Actions;
[Serializable, NetSerializable]
public sealed partial class ShacklesEvent : SimpleDoAfterEvent
{
}

View File

@@ -7,7 +7,7 @@ namespace Content.Shared._White.Cult.Components;
/// <summary> /// <summary>
/// This is used for tagging a mob as a cultist. /// This is used for tagging a mob as a cultist.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CultistComponent : Component public sealed partial class CultistComponent : Component
{ {
[DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))] [DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))]
@@ -18,22 +18,28 @@ public sealed partial class CultistComponent : Component
public CancellationTokenSource? HolyConvertToken; public CancellationTokenSource? HolyConvertToken;
[NonSerialized] [AutoNetworkedField]
public List<EntityUid?> SelectedEmpowers = new(); public List<NetEntity?> SelectedEmpowers = new();
public static string SummonCultDaggerAction = "InstantActionSummonCultDagger"; public static string SummonCultDaggerAction = "InstantActionSummonCultDagger";
public static string BloodRitesAction = "InstantActionBloodRites"; public static string BloodRitesAction = "InstantActionBloodRites";
public static string EmpPulseAction = "InstantActionEmpPulse";
public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction"; public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction";
public static string CultTeleportAction = "ActionCultTeleport"; public static string CultTeleportAction = "ActionCultTeleport";
public static string CultSummonCombatEquipmentAction = "ActionCultSummonCombatEquipment"; public static string CultSummonCombatEquipmentAction = "ActionCultSummonCombatEquipment";
public static string CultStunAction = "ActionCultStun";
public static string CultShadowShacklesAction = "ActionCultShadowShackles";
public static List<string> CultistActions = new() public static List<string> CultistActions = new()
{ {
SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction, SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction,
CultSummonCombatEquipmentAction CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, CultShadowShacklesAction
}; };
} }

View File

@@ -0,0 +1,27 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Cult;
[Serializable, NetSerializable]
public sealed partial class SpellCreatedEvent : SimpleDoAfterEvent
{
public string Spell = "";
}
[Serializable, NetSerializable]
public sealed class CultEmpowerRemoveBuiMessage : BoundUserInterfaceMessage
{
public NetEntity ActionType;
public CultEmpowerRemoveBuiMessage(NetEntity actionType)
{
ActionType = actionType;
}
}
[Serializable, NetSerializable]
public enum CultEmpowerRemoveUiKey : byte
{
Key
}

View File

@@ -10,3 +10,28 @@ wraith-phase-action-name = Фазовый Сдвиг
wraith-phase-action-description = Это заклинание позволяет проходить сквозь стены, подобно бесплотному полету волшебника. wraith-phase-action-description = Это заклинание позволяет проходить сквозь стены, подобно бесплотному полету волшебника.
juggernaut-create-wall-action-name = Щит juggernaut-create-wall-action-name = Щит
juggernaut-create-wall-action-description = Это заклинание создает временное, невидимое, силовое поле для защиты себя и союзников от подавляющего огня. juggernaut-create-wall-action-description = Это заклинание создает временное, невидимое, силовое поле для защиты себя и союзников от подавляющего огня.
ent-ActionCultTwistedConstruction = Искажённое Воздействие
.desc = Зловещее заклинание, которое используют для превращения металла в рунический металл.
ent-ActionCultTeleport = Телепорт
.desc = Полезное заклинание, которое телепортирует культистов в выбранное место.
ent-ActionCultSummonCombatEquipment = Призыв Боевого Снаряжения
.desc = Важное заклинание, которое позволяет вам вызвать полный набор боевого снаряжения.
ent-InstantActionSummonCultDagger = Призыв Ритуального Кинжала
.desc = Призывает ритуальный кинжал.
ent-InstantActionBloodRites = Кровавые Обряды
.desc = Высасывает кровь и исцеляет вас.
ent-ActionCultStun = Оглушение
.desc = Сильное заклинание, которое оглушает и обезмолвливает жертв.
ent-ActionCultShadowShackles = Теневые Узы
.desc = Бесшумное заклинание, которое наложит на человека теневые наручники и заставит вашу жертву замолчать на 10 секунд.
ent-InstantActionEmpPulse = Электромагнитный Импульс
.desc = Большое заклинание, которое позволяет пользователю направлять темную энергию в ЭМИ.

View File

@@ -0,0 +1,9 @@
verb-categories-cult = Культ
verb-spell-create-text = Создать заклинание крови
verb-spell-create-message = Вы можете создать одно заклинание крови без руны могущества.
verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше одного заклинания крови.
verb-spell-remove-text = Удалить заклинание крови
verb-spell-remove-message = Убрать любое из созданных заклинаний крови.
verb-spell-remove-no-spells = Заклинания крови отсутствуют.

View File

@@ -212,6 +212,10 @@
type: TeleportRunesListWindowBUI type: TeleportRunesListWindowBUI
- key: enum.SummonCultistUiKey.Key - key: enum.SummonCultistUiKey.Key
type: SummonCultistListWindowBUI type: SummonCultistListWindowBUI
- key: enum.CultEmpowerUiKey.Key
type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI
# WD-EDIT END # WD-EDIT END
- type: Puller - type: Puller
- type: Butcherable - type: Butcherable

View File

@@ -41,6 +41,10 @@
type: TeleportRunesListWindowBUI type: TeleportRunesListWindowBUI
- key: enum.SummonCultistUiKey.Key - key: enum.SummonCultistUiKey.Key
type: SummonCultistListWindowBUI type: SummonCultistListWindowBUI
- key: enum.CultEmpowerUiKey.Key
type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI
# WD-EDIT END # WD-EDIT END
- type: Sprite - type: Sprite
scale: 0.9, 0.9 scale: 0.9, 0.9

View File

@@ -5,13 +5,15 @@
noSpawn: true noSpawn: true
components: components:
- type: EntityTargetAction - type: EntityTargetAction
useDelay: 50
canTargetSelf: false canTargetSelf: false
icon: icon:
sprite: /Textures/Objects/Materials/Sheets/metal.rsi sprite: /Textures/Objects/Materials/Sheets/metal.rsi
state: steel state: steel
event: !type:CultTwistedConstructionActionEvent event: !type:CultTwistedConstructionActionEvent
itemIconStyle: BigAction itemIconStyle: BigAction
charges: 1
temporary: true
removeOnNoCharges: true
- type: entity - type: entity
id: ActionCultTeleport id: ActionCultTeleport
@@ -20,7 +22,6 @@
noSpawn: true noSpawn: true
components: components:
- type: EntityTargetAction - type: EntityTargetAction
useDelay: 30
whitelist: whitelist:
components: components:
- HumanoidAppearance - HumanoidAppearance
@@ -33,6 +34,9 @@
state: teleport state: teleport
event: !type:CultTeleportTargetActionEvent event: !type:CultTeleportTargetActionEvent
itemIconStyle: BigAction itemIconStyle: BigAction
charges: 1
temporary: true
removeOnNoCharges: true
- type: entity - type: entity
id: ActionCultSummonCombatEquipment id: ActionCultSummonCombatEquipment
@@ -41,7 +45,6 @@
noSpawn: true noSpawn: true
components: components:
- type: EntityTargetAction - type: EntityTargetAction
useDelay: 300
whitelist: whitelist:
components: components:
- HumanoidAppearance - HumanoidAppearance
@@ -54,6 +57,53 @@
state: armor state: armor
event: !type:CultSummonCombatEquipmentTargetActionEvent event: !type:CultSummonCombatEquipmentTargetActionEvent
itemIconStyle: BigAction itemIconStyle: BigAction
charges: 1
temporary: true
removeOnNoCharges: true
- type: entity
id: ActionCultStun
name: Stun
description: A potent spell that will stun and mute victims.
noSpawn: true
components:
- type: EntityTargetAction
whitelist:
components:
- HumanoidAppearance
canTargetSelf: false
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: stun
event: !type:CultStunTargetActionEvent
itemIconStyle: BigAction
charges: 1
temporary: true
removeOnNoCharges: true
- type: entity
id: ActionCultShadowShackles
name: Shadow Shackles
description: A stealthy spell that will summon shadowy handcuffs on a person, and temporarily silence your victim for 10 seconds.
noSpawn: true
components:
- type: EntityTargetAction
whitelist:
components:
- HumanoidAppearance
canTargetSelf: false
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: shackles
event: !type:CultShadowShacklesTargetActionEvent
itemIconStyle: BigAction
charges: 4
temporary: true
removeOnNoCharges: true
- type: entity - type: entity
id: InstantActionSummonCultDagger id: InstantActionSummonCultDagger
@@ -66,7 +116,9 @@
sprite: /Textures/White/Cult/interface.rsi sprite: /Textures/White/Cult/interface.rsi
state: icon state: icon
event: !type:CultSummonDaggerActionEvent event: !type:CultSummonDaggerActionEvent
useDelay: 200 charges: 1
temporary: true
removeOnNoCharges: true
- type: entity - type: entity
id: InstantActionBloodRites id: InstantActionBloodRites
@@ -79,4 +131,21 @@
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: blood_rites state: blood_rites
event: !type:CultBloodRitesInstantActionEvent event: !type:CultBloodRitesInstantActionEvent
useDelay: 35 charges: 4
temporary: true
removeOnNoCharges: true
- type: entity
id: InstantActionEmpPulse
name: Electromagnetic Pulse
description: A large spell that allows a user to channel dark energy into an EMP.
noSpawn: true
components:
- type: InstantAction
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: emp
event: !type:CultElectromagneticPulseInstantActionEvent
charges: 1
temporary: true
removeOnNoCharges: true

View File

@@ -0,0 +1,12 @@
- type: entity
name: shadow shackles
description: Shackles that bind the wrists with sinister magic.
id: ShadowShackles
parent: Handcuffs
components:
- type: Handcuff
breakoutTime: 15
color: black
breakOnRemove: true
- type: Sprite
color: black