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:
Aviu00
2024-06-20 16:24:10 +00:00
committed by GitHub
parent fd06a8b1d3
commit 6a7ad10d72
23 changed files with 446 additions and 71 deletions

View File

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

View File

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

View File

@@ -10,4 +10,7 @@ public sealed partial class CultRobeModifierComponent : Component
public string DamageModifierSetId = "CultRobe";
public string? StoredDamageSetId { get; set; }
[ViewVariables]
public bool Active;
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Wizard.Timestop;
[RegisterComponent, NetworkedComponent]
public sealed partial class FreezeContactsComponent : Component
{
}

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

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

View File

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

Binary file not shown.

View File

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

View File

@@ -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 = Украшенный магическими драгоценными камнями скафандр, функционирующий так же, как и обычная мантия волшебника, но в то же время является пригодным для использования в космосе и бронированным. Небольшое замедление. Теперь вы можете произносить заклинания в космосе и местах с низкой температурой! Имеет функцию энергетического щита,который защищает от всех снарядов. Щит разряжается при получении урона и автоматически заряжается.

View File

@@ -44,4 +44,7 @@ ent-ActionSmite = Кара
.desc = Заряжает вашу руку мерзкой энергией, которую можно использовать для взрыва жертв. Заклинание требует, чтобы вы коснулись своей цели, поэтому вы не сможете использовать его в наручниках или будучи оглушённым. Не работает без волшебной мантии и шляпы.
ent-ActionMindswapSpell = Подмена сознания
.desc = Позволяет заклинателю переключаться между телами с целью. Вы должны быть рядом с целью, в которую хотите перейти, после чего вы оба будете нокаутированы. Не работает на культистов, генокрадов и революционеров.
.desc = Позволяет заклинателю переключаться между телами с целью. Вы должны быть рядом с целью, в которую хотите перейти, после чего вы оба будете нокаутированы.
ent-ActionTimestopSpell = Остановка времени
.desc = Останавливает время в радиусе вокруг вас. Находящиеся под воздействием существа не смогут двигаться, а пролетающие мимо снаряды будут остановлены до окончания остановки времени. Не работает без волшебной мантии и шляпы.

View File

@@ -36,6 +36,9 @@ ent-ClothingOuterRealWizardFancy = мантия волшебника
ent-ClothingHeadHelmetWizardHelmArmored = шлем мага
.desc = Странный головной убор, который наверняка принадлежит настоящему магу. Не обладает свойствами волшебной шляпы.
mindswap-success = Ваш разум подменили!
mindswap-borer-failed = Его разумом кто-то управляет.
store-currency-display-spell-point = Очки заклинаний
store-category-spells-attack = Атакующие заклинания

View File

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

View 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

View File

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

View File

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

View File

@@ -60,6 +60,9 @@
},
{
"name": "mindswap"
},
{
"name": "time"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

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