Timestop spell (#370)
* - tweak: Tweak mindswap. * - fix: Mindswap and cult fixes. * - add: Timestop v1. * - add: Timestop spell. * - remove: What? * - fix: Fixes.
This commit is contained in:
@@ -29,6 +29,7 @@ using Content.Shared._White.Mood;
|
||||
using Content.Shared.Cloning;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server._White.Cult.GameRule;
|
||||
@@ -50,6 +51,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly GulagSystem _gulag = default!;
|
||||
[Dependency] private readonly BloodSpearSystem _bloodSpear = default!;
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
|
||||
private const int PlayerPerCultist = 10;
|
||||
private int _minStartingCultists;
|
||||
@@ -160,22 +162,18 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
|
||||
private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
|
||||
{
|
||||
RaiseLocalEvent(uid, new MoodEffectEvent("CultFocused"));
|
||||
|
||||
var query = QueryActiveRules();
|
||||
while (query.MoveNext(out _, out var cult, out _))
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(uid, out var mindComponent))
|
||||
return;
|
||||
|
||||
if (!mindComponent.HasMind)
|
||||
return;
|
||||
|
||||
cult.CurrentCultists.Add(component);
|
||||
|
||||
var name = Name(uid);
|
||||
|
||||
if (TryComp<ActorComponent>(uid, out var actor) && !cult.CultistsCache.ContainsKey(name))
|
||||
if (TryComp<ActorComponent>(uid, out var actor))
|
||||
{
|
||||
cult.CultistsCache.Add(name, actor.PlayerSession.Name);
|
||||
cult.CultistsCache.TryAdd(name, actor.PlayerSession.Name);
|
||||
}
|
||||
|
||||
UpdateCultistsAppearance(cult);
|
||||
@@ -203,17 +201,23 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
while (query.MoveNext(out _, out var cult, out _))
|
||||
{
|
||||
cult.CurrentCultists.Remove(component);
|
||||
|
||||
_bloodSpear.DetachSpearFromUser((uid, component));
|
||||
|
||||
foreach (var empower in component.SelectedEmpowers)
|
||||
{
|
||||
_actions.RemoveAction(uid, GetEntity(empower));
|
||||
}
|
||||
|
||||
RemoveCultistAppearance(uid);
|
||||
CheckRoundShouldEnd();
|
||||
}
|
||||
|
||||
if (!TerminatingOrDeleted(uid))
|
||||
{
|
||||
RemoveAllCultistItems(uid);
|
||||
RemoveCultistAppearance(uid);
|
||||
RaiseLocalEvent(uid, new MoodRemoveEffectEvent("CultFocused"));
|
||||
}
|
||||
|
||||
_bloodSpear.DetachSpearFromUser((uid, component));
|
||||
|
||||
foreach (var empower in component.SelectedEmpowers)
|
||||
{
|
||||
_actions.RemoveAction(uid, GetEntity(empower));
|
||||
}
|
||||
|
||||
CheckRoundShouldEnd();
|
||||
}
|
||||
|
||||
private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev)
|
||||
@@ -422,8 +426,6 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
_factionSystem.RemoveFaction(cultist, "NanoTrasen", false);
|
||||
_factionSystem.AddFaction(cultist, "Cultist");
|
||||
|
||||
RaiseLocalEvent(cultist, new MoodEffectEvent("CultFocused"));
|
||||
|
||||
if (_inventorySystem.TryGetSlotEntity(cultist, "back", out var backPack))
|
||||
{
|
||||
foreach (var itemPrototype in rule.StartingItems)
|
||||
@@ -442,6 +444,20 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveAllCultistItems(EntityUid uid)
|
||||
{
|
||||
if (!_inventorySystem.TryGetContainerSlotEnumerator(uid, out var enumerator))
|
||||
return;
|
||||
|
||||
while (enumerator.MoveNext(out var container))
|
||||
{
|
||||
if (container.ContainedEntity != null && HasComp<CultItemComponent>(container.ContainedEntity.Value))
|
||||
{
|
||||
_container.Remove(container.ContainedEntity.Value, container, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TransferRole(EntityUid transferFrom, EntityUid transferTo)
|
||||
{
|
||||
if (HasComp<PentagramComponent>(transferFrom))
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
using System.Threading;
|
||||
using Content.Server._White.Cult.GameRule;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared._White.Mood;
|
||||
using Content.Shared.Jittering;
|
||||
using Content.Shared.Mind;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
@@ -60,26 +53,10 @@ public sealed partial class DeconvertCultist : ReagentEffect
|
||||
|
||||
cultist.HolyConvertToken = null;
|
||||
|
||||
var inventory = entityManager.System<InventorySystem>();
|
||||
var containerSystem = entityManager.System<ContainerSystem>();
|
||||
if (!inventory.TryGetContainerSlotEnumerator(uid, out var enumerator))
|
||||
return;
|
||||
|
||||
while (enumerator.MoveNext(out var container))
|
||||
{
|
||||
if (container.ContainedEntity != null &&
|
||||
entityManager.HasComponent<CultItemComponent>(container.ContainedEntity.Value))
|
||||
{
|
||||
containerSystem.Remove(container.ContainedEntity.Value, container, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
entityManager.RemoveComponent<CultistComponent>(uid);
|
||||
entityManager.RemoveComponent<PentagramComponent>(uid);
|
||||
|
||||
var cultRuleSystem = entityManager.System<CultRuleSystem>();
|
||||
cultRuleSystem.RemoveObjectiveAndRole(uid);
|
||||
|
||||
entityManager.EventBus.RaiseLocalEvent(uid, new MoodRemoveEffectEvent("CultFocused"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,7 @@ public sealed partial class CultRobeModifierComponent : Component
|
||||
public string DamageModifierSetId = "CultRobe";
|
||||
|
||||
public string? StoredDamageSetId { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public bool Active;
|
||||
}
|
||||
|
||||
@@ -29,18 +29,22 @@ public sealed class CultRobeModifierSystem : EntitySystem
|
||||
if (args.Slot != "outerClothing")
|
||||
return;
|
||||
|
||||
component.Active = true;
|
||||
|
||||
ModifySpeed(args.Equipee, component, true);
|
||||
ModifyDamage(args.Equipee, component, true);
|
||||
}
|
||||
|
||||
private void OnUnequip(EntityUid uid, CultRobeModifierComponent component, GotUnequippedEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.Equipee))
|
||||
if (!component.Active)
|
||||
return;
|
||||
|
||||
if (args.Slot != "outerClothing")
|
||||
return;
|
||||
|
||||
component.Active = false;
|
||||
|
||||
ModifySpeed(args.Equipee, component, false);
|
||||
ModifyDamage(args.Equipee, component, false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server._White.Cult;
|
||||
using Content.Server._White.IncorporealSystem;
|
||||
using Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
using Content.Server._White.Wizard.Magic.Other;
|
||||
@@ -24,7 +25,6 @@ using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Magic;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Borer;
|
||||
using Content.Shared.Changeling;
|
||||
using Content.Shared.Cluwne;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Hands.Components;
|
||||
@@ -36,6 +36,7 @@ using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Magic;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
@@ -85,6 +86,7 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StopTimeSpellEvent>(OnTimeStop);
|
||||
SubscribeLocalEvent<MindswapSpellEvent>(OnMindswapSpell);
|
||||
SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
|
||||
SubscribeLocalEvent<InstantRecallSpellEvent>(OnInstantRecallSpell);
|
||||
@@ -103,6 +105,25 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
SubscribeLocalEvent<MagicComponent, BeforeCastSpellEvent>(OnBeforeCastSpell);
|
||||
}
|
||||
|
||||
#region Timestop
|
||||
|
||||
private void OnTimeStop(StopTimeSpellEvent msg)
|
||||
{
|
||||
if (!CanCast(msg))
|
||||
return;
|
||||
|
||||
var ent = Spawn(msg.Prototype, Transform(msg.Performer).Coordinates);
|
||||
_transformSystem.AttachToGridOrMap(ent);
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mindswap
|
||||
|
||||
private void OnMindswapSpell(MindswapSpellEvent msg)
|
||||
@@ -113,17 +134,12 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
var target = msg.Target;
|
||||
var uid = msg.Performer;
|
||||
|
||||
if (HasComp<ChangelingComponent>(target) || HasComp<RevolutionaryComponent>(target) ||
|
||||
HasComp<CultistComponent>(target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Не работает на культистов, генокрадов и революционеров.", uid, uid,
|
||||
PopupType.MediumCaution);
|
||||
if (!TryComp(target, out MobStateComponent? mobState) || mobState.CurrentState != MobState.Alive)
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp(target, out InfestedBorerComponent? borer) && borer.ControllingBrain)
|
||||
{
|
||||
_popupSystem.PopupEntity("Им уже кто-то управляет.", uid, uid, PopupType.MediumCaution);
|
||||
_popupSystem.PopupEntity(Loc.GetString("mindswap-borer-failed"), uid, uid, PopupType.MediumCaution);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +154,7 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
if (targetHasMind)
|
||||
{
|
||||
_mindSystem.TransferTo(targetMindId, uid, mind: targetMind);
|
||||
_popupSystem.PopupEntity(Loc.GetString("Ваш разум подменили!"), uid, uid, PopupType.LargeCaution);
|
||||
_popupSystem.PopupEntity(Loc.GetString("mindswap-success"), uid, uid, PopupType.LargeCaution);
|
||||
}
|
||||
|
||||
TransferAllMagicActions(uid, target);
|
||||
@@ -149,23 +165,11 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
|
||||
var hasWiz = HasComp<WizardComponent>(uid);
|
||||
var targetHasWiz = HasComp<WizardComponent>(target);
|
||||
|
||||
if (hasWiz == targetHasWiz)
|
||||
return;
|
||||
|
||||
if (hasWiz)
|
||||
{
|
||||
RemComp<WizardComponent>(uid);
|
||||
EnsureComp<WizardComponent>(target);
|
||||
}
|
||||
|
||||
if (targetHasWiz)
|
||||
{
|
||||
RemComp<WizardComponent>(target);
|
||||
EnsureComp<WizardComponent>(uid);
|
||||
}
|
||||
SwapComponent<WizardComponent>(uid, target);
|
||||
SwapComponent<RevolutionaryComponent>(uid, target);
|
||||
SwapComponent<HeadRevolutionaryComponent>(uid, target);
|
||||
SwapComponent<PentagramComponent>(uid, target);
|
||||
SwapComponent<CultistComponent>(uid, target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -919,5 +923,26 @@ public sealed class WizardSpellsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void SwapComponent<T>(EntityUid uid1, EntityUid uid2) where T : Component, new()
|
||||
{
|
||||
var hasComp = HasComp<T>(uid1);
|
||||
var targetHasComp = HasComp<T>(uid2);
|
||||
|
||||
if (hasComp == targetHasComp)
|
||||
return;
|
||||
|
||||
if (hasComp)
|
||||
{
|
||||
EnsureComp<T>(uid2);
|
||||
RemComp<T>(uid1);
|
||||
}
|
||||
|
||||
if (targetHasComp)
|
||||
{
|
||||
EnsureComp<T>(uid1);
|
||||
RemComp<T>(uid2);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Wizard.Timestop;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Gravity;
|
||||
@@ -78,6 +79,8 @@ namespace Content.Shared.Throwing
|
||||
|
||||
private void OnSleep(EntityUid uid, ThrownItemComponent thrownItem, ref PhysicsSleepEvent @event)
|
||||
{
|
||||
if (HasComp<FrozenComponent>(uid)) // WD
|
||||
return;
|
||||
StopThrow(uid, thrownItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Timestop;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class FreezeContactsComponent : Component
|
||||
{
|
||||
}
|
||||
183
Content.Shared/_White/Wizard/Timestop/FreezeContactsSystem.cs
Normal file
183
Content.Shared/_White/Wizard/Timestop/FreezeContactsSystem.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Emoting;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Spawners;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Timestop;
|
||||
|
||||
public sealed class FreezeContactsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FreezeContactsComponent, StartCollideEvent>(OnEntityEnter);
|
||||
SubscribeLocalEvent<FreezeContactsComponent, EndCollideEvent>(OnEntityExit);
|
||||
SubscribeLocalEvent<FrozenComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<FrozenComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<FrozenComponent, PreventCollideEvent>(OnPreventCollide);
|
||||
SubscribeLocalEvent<FrozenComponent, EntGotInsertedIntoContainerMessage>(OnGetInserted);
|
||||
|
||||
SubscribeLocalEvent<FrozenComponent, SpeakAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, EmoteAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, ChangeDirectionAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, UpdateCanMoveEvent>(OnMoveAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, InteractionAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, UseAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, ThrowAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, DropAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, AttackAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, PickupAttemptEvent>(OnAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, IsEquippingAttemptEvent>(OnEquipAttempt);
|
||||
SubscribeLocalEvent<FrozenComponent, IsUnequippingAttemptEvent>(OnUnequipAttempt);
|
||||
}
|
||||
|
||||
private void OnMoveAttempt(EntityUid uid, FrozenComponent component, UpdateCanMoveEvent args)
|
||||
{
|
||||
if (component.LifeStage > ComponentLifeStage.Running)
|
||||
return;
|
||||
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnAttempt(EntityUid uid, FrozenComponent component, CancellableEntityEventArgs args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnEquipAttempt(EntityUid uid, FrozenComponent component, IsEquippingAttemptEvent args)
|
||||
{
|
||||
// is this a self-equip, or are they being stripped?
|
||||
if (args.Equipee == uid)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnUnequipAttempt(EntityUid uid, FrozenComponent component, IsUnequippingAttemptEvent args)
|
||||
{
|
||||
// is this a self-equip, or are they being stripped?
|
||||
if (args.Unequipee == uid)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnGetInserted(Entity<FrozenComponent> ent, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
RemCompDeferred<FrozenComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnPreventCollide(Entity<FrozenComponent> ent, ref PreventCollideEvent args)
|
||||
{
|
||||
if (args.OurBody.BodyType == BodyType.Dynamic && !HasComp<FreezeContactsComponent>(args.OtherEntity))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnRemove(Entity<FrozenComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
|
||||
_blocker.UpdateCanMove(uid);
|
||||
|
||||
if (_container.IsEntityOrParentInContainer(uid))
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out PhysicsComponent? physics))
|
||||
return;
|
||||
|
||||
_physics.SetLinearVelocity(uid, comp.OldLinearVelocity, false, body: physics);
|
||||
_physics.SetAngularVelocity(uid, comp.OldAngularVelocity, body: physics);
|
||||
}
|
||||
|
||||
private void OnInit(Entity<FrozenComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
|
||||
_blocker.UpdateCanMove(uid);
|
||||
|
||||
if (!TryComp(uid, out PhysicsComponent? physics))
|
||||
return;
|
||||
|
||||
comp.OldLinearVelocity = physics.LinearVelocity;
|
||||
comp.OldAngularVelocity = physics.AngularVelocity;
|
||||
|
||||
_physics.SetLinearVelocity(uid, Vector2.Zero, false, body: physics);
|
||||
_physics.SetAngularVelocity(uid, 0f, body: physics);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = AllEntityQuery<FrozenComponent, FixturesComponent, PhysicsComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var frozen, out var fixtures, out var physics))
|
||||
{
|
||||
frozen.Lifetime -= frameTime;
|
||||
|
||||
if (physics.LinearVelocity != Vector2.Zero)
|
||||
_physics.SetLinearVelocity(uid, Vector2.Zero, manager: fixtures, body: physics);
|
||||
|
||||
if (physics.AngularVelocity != 0f)
|
||||
_physics.SetAngularVelocity(uid, 0f, manager: fixtures, body: physics);
|
||||
|
||||
if (frozen.Lifetime > 0)
|
||||
continue;
|
||||
|
||||
RemCompDeferred<FrozenComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEntityExit(Entity<FreezeContactsComponent> ent, ref EndCollideEvent args)
|
||||
{
|
||||
if (IsTouchingFrozenContacts(args.OtherEntity, args.OtherBody))
|
||||
return;
|
||||
|
||||
RemCompDeferred<FrozenComponent>(args.OtherEntity);
|
||||
}
|
||||
|
||||
private void OnEntityEnter(Entity<FreezeContactsComponent> ent, ref StartCollideEvent args)
|
||||
{
|
||||
var hadFrozen = HasComp<FrozenComponent>(args.OtherEntity);
|
||||
var frozen = EnsureComp<FrozenComponent>(args.OtherEntity);
|
||||
|
||||
if (!TryComp(ent, out TimedDespawnComponent? timedDespawn))
|
||||
return;
|
||||
|
||||
frozen.Lifetime = timedDespawn.Lifetime;
|
||||
|
||||
if (TryComp(args.OtherEntity, out TimedDespawnComponent? otherTimedDespawn))
|
||||
otherTimedDespawn.Lifetime += timedDespawn.Lifetime;
|
||||
|
||||
if (hadFrozen)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.OtherEntity, out ThrownItemComponent? thrownItem))
|
||||
return;
|
||||
|
||||
if (thrownItem.LandTime != null)
|
||||
thrownItem.LandTime = thrownItem.LandTime.Value + TimeSpan.FromSeconds(timedDespawn.Lifetime);
|
||||
|
||||
if (thrownItem.ThrownTime != null)
|
||||
thrownItem.ThrownTime = thrownItem.ThrownTime.Value + TimeSpan.FromSeconds(timedDespawn.Lifetime);
|
||||
}
|
||||
|
||||
private bool IsTouchingFrozenContacts(EntityUid uid, PhysicsComponent body)
|
||||
{
|
||||
return _physics.GetContactingEntities(uid, body).Any(HasComp<FreezeContactsComponent>);
|
||||
}
|
||||
}
|
||||
17
Content.Shared/_White/Wizard/Timestop/FrozenComponent.cs
Normal file
17
Content.Shared/_White/Wizard/Timestop/FrozenComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Timestop;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class FrozenComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public float Lifetime = 10f;
|
||||
|
||||
[ViewVariables]
|
||||
public Vector2 OldLinearVelocity;
|
||||
|
||||
[ViewVariables]
|
||||
public float OldAngularVelocity;
|
||||
}
|
||||
@@ -184,4 +184,13 @@ public sealed partial class MindswapSpellEvent : EntityTargetActionEvent, ISpeak
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class StopTimeSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
BIN
Resources/Audio/White/Magic/timeparadox2.ogg
Normal file
BIN
Resources/Audio/White/Magic/timeparadox2.ogg
Normal file
Binary file not shown.
@@ -17,6 +17,7 @@ scroll-component-recall = призыв
|
||||
scroll-component-teleport = телепортацию
|
||||
scroll-component-smite = кару
|
||||
scroll-component-mindswap = подмену сознания
|
||||
scroll-component-timestop = остановку времени
|
||||
|
||||
ent-BaseScroll = магический свиток
|
||||
.desc = Этот древний пергамент, ставший реликвией в арканных преданиях, хранит в себе бесчисленные мистические заклятия и забытые заклинания.
|
||||
@@ -52,3 +53,5 @@ ent-ScrollSmite = свиток кары
|
||||
.desc = { ent-BaseScroll.desc }
|
||||
ent-ScrollMindswap = свиток подмены сознания
|
||||
.desc = { ent-BaseScroll.desc }
|
||||
ent-ScrollTimestop = свиток остановки времени
|
||||
.desc = { ent-BaseScroll.desc }
|
||||
|
||||
@@ -43,6 +43,9 @@ spellbook-smite-desc = { ent-ActionSmite.desc }
|
||||
spellbook-mindswap-name = { ent-ActionMindswapSpell }
|
||||
spellbook-mindswap-desc = { ent-ActionMindswapSpell.desc }
|
||||
|
||||
spellbook-timestop-name = { ent-ActionTimestopSpell }
|
||||
spellbook-timestop-desc = { ent-ActionTimestopSpell.desc }
|
||||
|
||||
spellbook-hardsuit-name = Скафандр волшебника
|
||||
spellbook-hardsuit-desc = Украшенный магическими драгоценными камнями скафандр, функционирующий так же, как и обычная мантия волшебника, но в то же время является пригодным для использования в космосе и бронированным. Небольшое замедление. Теперь вы можете произносить заклинания в космосе и местах с низкой температурой! Имеет функцию энергетического щита,который защищает от всех снарядов. Щит разряжается при получении урона и автоматически заряжается.
|
||||
|
||||
|
||||
@@ -44,4 +44,7 @@ ent-ActionSmite = Кара
|
||||
.desc = Заряжает вашу руку мерзкой энергией, которую можно использовать для взрыва жертв. Заклинание требует, чтобы вы коснулись своей цели, поэтому вы не сможете использовать его в наручниках или будучи оглушённым. Не работает без волшебной мантии и шляпы.
|
||||
|
||||
ent-ActionMindswapSpell = Подмена сознания
|
||||
.desc = Позволяет заклинателю переключаться между телами с целью. Вы должны быть рядом с целью, в которую хотите перейти, после чего вы оба будете нокаутированы. Не работает на культистов, генокрадов и революционеров.
|
||||
.desc = Позволяет заклинателю переключаться между телами с целью. Вы должны быть рядом с целью, в которую хотите перейти, после чего вы оба будете нокаутированы.
|
||||
|
||||
ent-ActionTimestopSpell = Остановка времени
|
||||
.desc = Останавливает время в радиусе вокруг вас. Находящиеся под воздействием существа не смогут двигаться, а пролетающие мимо снаряды будут остановлены до окончания остановки времени. Не работает без волшебной мантии и шляпы.
|
||||
|
||||
@@ -36,6 +36,9 @@ ent-ClothingOuterRealWizardFancy = мантия волшебника
|
||||
ent-ClothingHeadHelmetWizardHelmArmored = шлем мага
|
||||
.desc = Странный головной убор, который наверняка принадлежит настоящему магу. Не обладает свойствами волшебной шляпы.
|
||||
|
||||
mindswap-success = Ваш разум подменили!
|
||||
mindswap-borer-failed = Его разумом кто-то управляет.
|
||||
|
||||
store-currency-display-spell-point = Очки заклинаний
|
||||
|
||||
store-category-spells-attack = Атакующие заклинания
|
||||
|
||||
@@ -330,6 +330,7 @@
|
||||
components:
|
||||
- MindContainer
|
||||
- ActionContainer
|
||||
- MobState
|
||||
canTargetSelf: false
|
||||
checkCanInteract: false
|
||||
useDelay: 60
|
||||
@@ -339,3 +340,24 @@
|
||||
state: mindswap
|
||||
event: !type:MindswapSpellEvent
|
||||
speech: "GIN'YU CAPAN!"
|
||||
|
||||
- type: entity
|
||||
id: ActionTimestopSpell
|
||||
name: Stop time
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: InstantAction
|
||||
alwaysPlaySound: false
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/White/Magic/timeparadox2.ogg
|
||||
useDelay: 50
|
||||
itemIconStyle: BigAction
|
||||
checkCanInteract: false
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: time
|
||||
event: !type:StopTimeSpellEvent
|
||||
prototype: Timestop
|
||||
speech: "TOKI YO TOMARE!"
|
||||
|
||||
31
Resources/Prototypes/_White/Entities/Effects/timestop.yml
Normal file
31
Resources/Prototypes/_White/Entities/Effects/timestop.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
- type: entity
|
||||
id: Timestop
|
||||
name: "chronofield"
|
||||
description: "ZA WARUDO"
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: FloorObjects
|
||||
sprite: White/Effects/timestop.rsi
|
||||
state: icon
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 2.5
|
||||
density: 0
|
||||
hard: false
|
||||
mask:
|
||||
- None
|
||||
layer:
|
||||
- Impassable
|
||||
- HighImpassable
|
||||
- MidImpassable
|
||||
- LowImpassable
|
||||
- Opaque
|
||||
- BulletImpassable
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: TimedDespawn
|
||||
lifetime: 10
|
||||
- type: FreezeContacts
|
||||
@@ -160,3 +160,12 @@
|
||||
- type: Scroll
|
||||
actionId: ActionMindswapSpell
|
||||
learnPopup: scroll-component-mindswap
|
||||
|
||||
- type: entity
|
||||
id: ScrollTimestop
|
||||
parent: BaseScroll
|
||||
name: "Stop time scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionTimestopSpell
|
||||
learnPopup: scroll-component-timestop
|
||||
|
||||
@@ -30,6 +30,22 @@
|
||||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
|
||||
- type: listing
|
||||
id: SpellBookTimestop
|
||||
name: spellbook-timestop-name
|
||||
description: spellbook-timestop-desc
|
||||
productEntity: ScrollTimestop
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: time
|
||||
cost:
|
||||
SpellPoint: 2
|
||||
categories:
|
||||
- DefenceSpells
|
||||
conditions:
|
||||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
|
||||
- type: listing
|
||||
id: SpellBookKnock
|
||||
name: spellbook-knock-name
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
},
|
||||
{
|
||||
"name": "mindswap"
|
||||
},
|
||||
{
|
||||
"name": "time"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/time.png
Normal file
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/time.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Resources/Textures/White/Effects/timestop.rsi/icon.png
Normal file
BIN
Resources/Textures/White/Effects/timestop.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 568 KiB |
37
Resources/Textures/White/Effects/timestop.rsi/meta.json
Normal file
37
Resources/Textures/White/Effects/timestop.rsi/meta.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/pull/49264/commits/d0dffe7ca643db2624424fdcebf45863f85c0448",
|
||||
"size": {
|
||||
"x": 160,
|
||||
"y": 160
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user