Better melee combat (#542)
* - add: NextMobAttack, EquipCooldown. * - fix: Some combat fixes. * - add: Telebaton. * - add: Stun baton rework. * - tweak: Reduce melee range. * - add: Rework melee block system. * - add: ExaminedEvent.
This commit is contained in:
@@ -76,7 +76,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
}
|
||||
}
|
||||
|
||||
if (weapon.Attacking || weapon.NextAttack > Timing.CurTime)
|
||||
if (weapon.Attacking) // WD EDIT
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -124,10 +124,31 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
target = screen.GetDamageableClickedEntity(mousePos); // WD EDIT
|
||||
}
|
||||
|
||||
// WD START
|
||||
if (target == null)
|
||||
{
|
||||
if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
else if (IsMob(target.Value))
|
||||
{
|
||||
if (weapon.NextMobAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weapon.NextAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
// WD END
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(coordinates)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime)
|
||||
return;
|
||||
|
||||
// WD START
|
||||
if (HasComp<BlinkComponent>(weaponUid))
|
||||
{
|
||||
@@ -176,6 +197,24 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
if (Interaction.CombatModeCanHandInteract(entity, target))
|
||||
return;
|
||||
|
||||
// WD START
|
||||
if (target == null)
|
||||
{
|
||||
if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
else if (IsMob(target.Value))
|
||||
{
|
||||
if (weapon.NextMobAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weapon.NextAttack > Timing.CurTime)
|
||||
return;
|
||||
}
|
||||
// WD END
|
||||
|
||||
RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Stunnable;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Server.Temperature.Systems;
|
||||
using Content.Server.Damage.Components;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
@@ -17,6 +17,7 @@ using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Robust.Server.Audio;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
@@ -32,7 +33,8 @@ public sealed class HypospraySystem : SharedHypospraySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
|
||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
|
||||
}
|
||||
|
||||
@@ -68,6 +70,9 @@ public sealed class HypospraySystem : SharedHypospraySystem
|
||||
|
||||
public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD
|
||||
return;
|
||||
|
||||
if (!args.HitEntities.Any())
|
||||
return;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Projectiles;
|
||||
@@ -28,7 +29,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SolutionInjectOnProjectileHitComponent, ProjectileHitEvent>(HandleProjectileHit);
|
||||
SubscribeLocalEvent<SolutionInjectOnEmbedComponent, EmbedEvent>(HandleEmbed);
|
||||
SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(HandleMeleeHit);
|
||||
SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(HandleMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
}
|
||||
|
||||
private void HandleProjectileHit(Entity<SolutionInjectOnProjectileHitComponent> entity, ref ProjectileHitEvent args)
|
||||
@@ -43,6 +45,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
|
||||
|
||||
private void HandleMeleeHit(Entity<MeleeChemicalInjectorComponent> entity, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD
|
||||
return;
|
||||
// MeleeHitEvent is weird, so we have to filter to make sure we actually
|
||||
// hit something and aren't just examining the weapon.
|
||||
if (args.IsHit)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
@@ -28,7 +29,8 @@ public sealed partial class PuddleSystem
|
||||
|
||||
SubscribeLocalEvent<SpillableComponent, LandEvent>(SpillOnLand);
|
||||
// Openable handles the event if it's closed
|
||||
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: [typeof(OpenableSystem)]);
|
||||
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(
|
||||
SplashOnMeleeHit, before: new[] {typeof(MeleeBlockSystem)}, after:[typeof(OpenableSystem)]); // WD EDIT
|
||||
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<SpillableComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.DoAfter;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Forensics.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Forensics;
|
||||
@@ -27,7 +28,8 @@ namespace Content.Server.Forensics
|
||||
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit);
|
||||
|
||||
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
|
||||
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
||||
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter);
|
||||
@@ -61,6 +63,8 @@ namespace Content.Server.Forensics
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, ForensicsComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD EDIT
|
||||
return;
|
||||
if((args.BaseDamage.DamageDict.TryGetValue("Blunt", out var bluntDamage) && bluntDamage.Value > 0) ||
|
||||
(args.BaseDamage.DamageDict.TryGetValue("Slash", out var slashDamage) && slashDamage.Value > 0) ||
|
||||
(args.BaseDamage.DamageDict.TryGetValue("Piercing", out var pierceDamage) && pierceDamage.Value > 0))
|
||||
|
||||
@@ -101,7 +101,7 @@ public sealed partial class NPCCombatSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (weapon.NextAttack > curTime || !Enabled)
|
||||
if (weapon.NextAttack > curTime || weapon.NextMobAttack > curTime || !Enabled) // WD EDIT
|
||||
return;
|
||||
|
||||
if (_random.Prob(component.MissChance) &&
|
||||
|
||||
@@ -143,7 +143,7 @@ public sealed class NPCJukeSystem : EntitySystem
|
||||
if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon))
|
||||
return;
|
||||
|
||||
var cdRemaining = weapon.NextAttack - _timing.CurTime;
|
||||
var cdRemaining = weapon.NextMobAttack - _timing.CurTime; // WD EDIT
|
||||
var attackCooldown = TimeSpan.FromSeconds(1f / _melee.GetAttackRate(weaponUid, uid, weapon));
|
||||
|
||||
// Might as well get in range.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Cloning;
|
||||
using Content.Server.Drone.Components;
|
||||
using Content.Server.Emoting.Systems;
|
||||
using Content.Server.Speech.EntitySystems;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Cloning;
|
||||
using Content.Shared.Damage;
|
||||
@@ -56,7 +57,8 @@ namespace Content.Server.Zombies
|
||||
SubscribeLocalEvent<ZombieComponent, EmoteEvent>(OnEmote, before:
|
||||
new[] { typeof(VocalSystem), typeof(BodyEmotesSystem) });
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<ZombieComponent, MobStateChangedEvent>(OnMobState);
|
||||
SubscribeLocalEvent<ZombieComponent, CloningEvent>(OnZombieCloning);
|
||||
SubscribeLocalEvent<ZombieComponent, TryingToSleepEvent>(OnSleepAttempt);
|
||||
@@ -207,6 +209,9 @@ namespace Content.Server.Zombies
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, ZombieComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD EDIT
|
||||
return;
|
||||
|
||||
if (!TryComp<ZombieComponent>(args.User, out _))
|
||||
return;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed class ReturnItemOnThrowSystem : EntitySystem
|
||||
if (!HasComp<MobStateComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (!_stun.IsParalyzed(args.Target) && !isCultist && !_holyWeapon.IsHoldingHolyWeapon(args.Target))
|
||||
if (!isCultist && !_holyWeapon.IsHoldingHolyWeapon(args.Target))
|
||||
{
|
||||
_stun.TryParalyze(args.Target, TimeSpan.FromSeconds(component.StunTime), true);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Mind.Components;
|
||||
@@ -22,7 +23,7 @@ public partial class CultSystem
|
||||
SubscribeLocalEvent<ConstructShellComponent, ComponentInit>(OnShellInit);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ComponentRemove>(OnShellRemove);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ConstructFormSelectedEvent>(OnShellSelected);
|
||||
SubscribeLocalEvent<ConstructComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ConstructComponent, MeleeHitEvent>(OnMeleeHit, before: new []{typeof(MeleeBlockSystem)});
|
||||
}
|
||||
|
||||
private void OnMeleeHit(Entity<ConstructComponent> ent, ref MeleeHitEvent args)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Examine;
|
||||
@@ -25,7 +26,7 @@ public sealed class CritSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CritComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<CritComponent, MeleeHitEvent>(HandleHit);
|
||||
SubscribeLocalEvent<CritComponent, MeleeHitEvent>(HandleHit, before: new [] {typeof(MeleeBlockSystem)});
|
||||
SubscribeLocalEvent<CritComponent, GetMeleeAttackRateEvent>(GetMeleeAttackRate);
|
||||
SubscribeLocalEvent<BloodLustComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMoveSpeed);
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Content.Server._White.Other.MeleeBlockSystem;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class MeleeBlockComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BlockChance = 0.4f;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._White.Other.MeleeBlockSystem;
|
||||
|
||||
public sealed class MeleeBlockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, MeleeBlockAttemptEvent>(OnBlockAttempt);
|
||||
}
|
||||
|
||||
private void OnBlockAttempt(Entity<HandsComponent> ent, ref MeleeBlockAttemptEvent args)
|
||||
{
|
||||
if (ent.Owner == args.Attacker ||
|
||||
!TryComp(ent.Comp.ActiveHandEntity, out MeleeBlockComponent? blockComponent) ||
|
||||
!_random.Prob(blockComponent.BlockChance))
|
||||
return;
|
||||
|
||||
args.Blocked = true;
|
||||
|
||||
_popupSystem.PopupEntity("заблокировал!", ent);
|
||||
|
||||
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/block_metal1.ogg"), ent,
|
||||
AudioParams.Default.WithVariation(0.25f));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
@@ -15,7 +16,7 @@ public sealed class RandomDamageSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RandomDamageComponent, MeleeHitEvent>(HandleHit);
|
||||
SubscribeLocalEvent<RandomDamageComponent, MeleeHitEvent>(HandleHit, before: new [] {typeof(MeleeBlockSystem)});
|
||||
}
|
||||
|
||||
private void HandleHit(Entity<RandomDamageComponent> ent, ref MeleeHitEvent args)
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Content.Server._White.Stunprod;
|
||||
public sealed partial class StunprodComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float EnergyPerUse { get; set; } = 72;
|
||||
public float EnergyPerUse { get; set; } = 144;
|
||||
|
||||
[DataField]
|
||||
public bool HasHeldPrefix = true;
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Content.Shared.Alert
|
||||
MoodDead,
|
||||
CultBuffed,
|
||||
Knockdown,
|
||||
RecentlyBlocked,
|
||||
//WD end
|
||||
PilotingShuttle,
|
||||
Peckish,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
@@ -19,8 +20,33 @@ public sealed partial class BlockingSystem
|
||||
SubscribeLocalEvent<BlockingUserComponent, ContainerGettingInsertedAttemptEvent>(OnInsertAttempt);
|
||||
SubscribeLocalEvent<BlockingUserComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
||||
SubscribeLocalEvent<BlockingUserComponent, EntityTerminatingEvent>(OnEntityTerminating);
|
||||
|
||||
SubscribeLocalEvent<BlockingUserComponent, MeleeBlockAttemptEvent>(OnMeleeBlockAttempt); // WD
|
||||
}
|
||||
|
||||
// WD START
|
||||
private void OnMeleeBlockAttempt(Entity<BlockingUserComponent> ent, ref MeleeBlockAttemptEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var uid = ent.Comp.BlockingItem;
|
||||
if (!TryComp(uid, out BlockingComponent? blocking) || !blocking.IsBlocking)
|
||||
return;
|
||||
|
||||
if (TryComp(uid.Value, out ItemToggleComponent? toggle) && !toggle.Activated)
|
||||
return;
|
||||
|
||||
if (!TryComp(uid.Value, out DamageableComponent? damageable))
|
||||
return;
|
||||
|
||||
_audio.PlayPredicted(blocking.BlockSound, ent, args.Attacker);
|
||||
_popupSystem.PopupPredicted(Loc.GetString("melee-block-event-blocked"), ent, args.Attacker);
|
||||
_damageable.TryChangeDamage(uid.Value, args.Damage, damageable: damageable);
|
||||
args.Handled = true;
|
||||
}
|
||||
// WD END
|
||||
|
||||
private void OnParentChanged(EntityUid uid, BlockingUserComponent component, ref EntParentChangedMessage args)
|
||||
{
|
||||
UserStopBlocking(uid, component);
|
||||
|
||||
@@ -135,6 +135,9 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD
|
||||
return;
|
||||
|
||||
if (!args.IsHit ||
|
||||
!args.HitEntities.Any() ||
|
||||
component.Damage <= 0f)
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Wieldable;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Item.ItemToggle;
|
||||
/// <summary>
|
||||
@@ -22,6 +23,7 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPointLightSystem _light = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -240,6 +242,9 @@ public abstract class SharedItemToggleSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggledEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted) // WD
|
||||
return;
|
||||
|
||||
if (args.Activated)
|
||||
{
|
||||
if (activeSound.ActiveSound != null && activeSound.PlayingStream == null)
|
||||
|
||||
@@ -9,11 +9,11 @@ public sealed partial class JitteringComponent : Component
|
||||
{
|
||||
[AutoNetworkedField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Amplitude { get; set; }
|
||||
public float Amplitude { get; set; } = 10f;
|
||||
|
||||
[AutoNetworkedField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Frequency { get; set; }
|
||||
public float Frequency { get; set; } = 4f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 LastJitter { get; set; }
|
||||
|
||||
@@ -78,7 +78,7 @@ public abstract partial class SharedStandingStateSystem : EntitySystem
|
||||
|
||||
RaiseNetworkEvent(new CheckAutoGetUpEvent()); // WD EDIT
|
||||
|
||||
if (_stun.IsParalyzed(uid))
|
||||
if (HasComp<KnockedDownComponent>(uid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,13 +265,6 @@ public abstract class SharedStunSystem : EntitySystem
|
||||
args.Modifier *= KnockDownModifier;
|
||||
}*/
|
||||
|
||||
//WD EDIT START
|
||||
public bool IsParalyzed(EntityUid uid)
|
||||
{
|
||||
return HasComp<StunnedComponent>(uid) || HasComp<KnockedDownComponent>(uid);
|
||||
}
|
||||
//WD EDIT END
|
||||
|
||||
#region Attempt Event Handling
|
||||
|
||||
private void OnMoveAttempt(EntityUid uid, StunnedComponent stunned, UpdateCanMoveEvent args)
|
||||
|
||||
@@ -97,6 +97,10 @@ public record struct GetMeleeAttackRateEvent(EntityUid Weapon, float Rate, float
|
||||
public record struct GetHeavyDamageModifierEvent(EntityUid Weapon, FixedPoint2 DamageModifier, float Multipliers, EntityUid User);
|
||||
|
||||
// WD START
|
||||
[ByRefEvent]
|
||||
public record struct MeleeBlockAttemptEvent(EntityUid Attacker, bool Blocked = false);
|
||||
public sealed class MeleeBlockAttemptEvent(EntityUid attacker, DamageSpecifier damage) : HandledEntityEventArgs
|
||||
{
|
||||
public EntityUid Attacker = attacker;
|
||||
|
||||
public DamageSpecifier Damage = damage;
|
||||
}
|
||||
// WD END
|
||||
|
||||
@@ -64,6 +64,9 @@ public sealed class MeleeThrowOnHitSystem : EntitySystem
|
||||
|
||||
private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD
|
||||
return;
|
||||
|
||||
var (_, comp) = ent;
|
||||
if (!args.IsHit)
|
||||
return;
|
||||
|
||||
@@ -70,7 +70,10 @@ public sealed partial class MeleeWeaponComponent : Component
|
||||
public bool CanAttackSelf = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public bool CanMiss = true;
|
||||
public bool CanBeBlocked = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public bool CanMiss;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? AttackWhitelist;
|
||||
@@ -78,6 +81,14 @@ public sealed partial class MeleeWeaponComponent : Component
|
||||
[DataField]
|
||||
public EntityWhitelist? AttackBlacklist;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextMobAttack;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public float? EquipCooldown;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public bool IgnoreResistances;
|
||||
|
||||
@@ -114,12 +125,11 @@ public sealed partial class MeleeWeaponComponent : Component
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||
public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1);
|
||||
|
||||
// TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2
|
||||
/// <summary>
|
||||
/// Nearest edge range to hit an entity.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||
public float Range = 1.5f;
|
||||
public float Range = 1.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Total width of the angle for wide attacks.
|
||||
|
||||
@@ -22,6 +22,7 @@ using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Content.Shared._White;
|
||||
using Content.Shared._White.Implants.NeuroControl;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -94,14 +95,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.NextAttack > Timing.CurTime)
|
||||
if (component.NextAttack > Timing.CurTime || component.NextMobAttack > Timing.CurTime) // WD EDIT
|
||||
Log.Warning($"Initializing a map that contains an entity that is on cooldown. Entity: {ToPrettyString(uid)}");
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnMeleeShotAttempted(EntityUid uid, MeleeWeaponComponent comp, ref ShotAttemptedEvent args)
|
||||
{
|
||||
if (comp.NextAttack > Timing.CurTime)
|
||||
if (comp.NextAttack > Timing.CurTime || comp.NextMobAttack > Timing.CurTime) // WD EDIT
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
@@ -110,16 +111,28 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
if (!TryComp<GunComponent>(uid, out var gun))
|
||||
return;
|
||||
|
||||
// WD EDIT START
|
||||
var dirty = false;
|
||||
if (gun.NextFire > component.NextAttack)
|
||||
{
|
||||
component.NextAttack = gun.NextFire;
|
||||
Dirty(uid, component);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (gun.NextFire > component.NextMobAttack)
|
||||
{
|
||||
component.NextMobAttack = gun.NextFire;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
Dirty(uid, component);
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args)
|
||||
{
|
||||
var attackRate = GetAttackRate(uid, args.User, component);
|
||||
var attackRate = component.EquipCooldown ?? GetAttackRate(uid, args.User, component); // WD EDIT
|
||||
if (attackRate.Equals(0f))
|
||||
return;
|
||||
|
||||
@@ -133,11 +146,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
var curTime = Timing.CurTime;
|
||||
var minimum = curTime + TimeSpan.FromSeconds(1 / attackRate);
|
||||
|
||||
if (minimum < component.NextAttack)
|
||||
return;
|
||||
|
||||
component.NextAttack = minimum;
|
||||
Dirty(uid, component);
|
||||
// WD EDIT START
|
||||
var dirty = false;
|
||||
if (minimum > component.NextAttack)
|
||||
{
|
||||
component.NextAttack = minimum;
|
||||
dirty = true;
|
||||
}
|
||||
if (minimum > component.NextMobAttack)
|
||||
{
|
||||
component.NextMobAttack = minimum;
|
||||
dirty = true;
|
||||
}
|
||||
if (dirty)
|
||||
Dirty(uid, component);
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args)
|
||||
@@ -339,6 +362,13 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
return AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(targetXform.Coordinates)), null);
|
||||
}
|
||||
|
||||
private enum UpdateNextAttack : byte // WD
|
||||
{
|
||||
Mob,
|
||||
NonMob,
|
||||
Both
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a windup is finished and an attack is tried.
|
||||
/// </summary>
|
||||
@@ -347,16 +377,16 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
var curTime = Timing.CurTime;
|
||||
|
||||
if (weapon.NextAttack > curTime)
|
||||
return false;
|
||||
|
||||
if (!CombatMode.IsInCombatMode(user))
|
||||
return false;
|
||||
|
||||
var update = UpdateNextAttack.Both; // WD
|
||||
switch (attack)
|
||||
{
|
||||
case LightAttackEvent light:
|
||||
var lightTarget = GetEntity(light.Target);
|
||||
update = lightTarget == null ? UpdateNextAttack.Both :
|
||||
IsMob(lightTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD
|
||||
|
||||
if (!Blocker.CanAttack(user, lightTarget, (weaponUid, weapon)))
|
||||
return false;
|
||||
@@ -392,6 +422,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
break;
|
||||
case DisarmAttackEvent disarm:
|
||||
var disarmTarget = GetEntity(disarm.Target);
|
||||
update = disarmTarget == null ? UpdateNextAttack.Both :
|
||||
IsMob(disarmTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD
|
||||
|
||||
if (!Blocker.CanAttack(user, disarmTarget, (weaponUid, weapon), true))
|
||||
return false;
|
||||
@@ -405,23 +437,52 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
// Windup time checked elsewhere.
|
||||
var fireRate = TimeSpan.FromSeconds(1f / GetAttackRate(weaponUid, user, weapon));
|
||||
|
||||
if (attack is LightAttackEvent _)
|
||||
{
|
||||
fireRate *= 0.8f;
|
||||
}
|
||||
|
||||
var swings = 0;
|
||||
|
||||
// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
|
||||
if (weapon.NextAttack < curTime)
|
||||
weapon.NextAttack = curTime;
|
||||
|
||||
while (weapon.NextAttack <= curTime)
|
||||
// WD EDIT START
|
||||
switch(update)
|
||||
{
|
||||
weapon.NextAttack += fireRate;
|
||||
swings++;
|
||||
case UpdateNextAttack.Mob:
|
||||
if (weapon.NextMobAttack > curTime)
|
||||
return false;
|
||||
break;
|
||||
case UpdateNextAttack.NonMob:
|
||||
if (weapon.NextAttack > curTime)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
if (weapon.NextAttack > curTime || weapon.NextMobAttack > curTime)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (update != UpdateNextAttack.Mob)
|
||||
{
|
||||
// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
|
||||
if (weapon.NextAttack < curTime)
|
||||
weapon.NextAttack = curTime;
|
||||
|
||||
while (weapon.NextAttack <= curTime)
|
||||
{
|
||||
weapon.NextAttack += fireRate;
|
||||
swings++;
|
||||
}
|
||||
}
|
||||
|
||||
if (update != UpdateNextAttack.NonMob)
|
||||
{
|
||||
if (weapon.NextMobAttack < curTime)
|
||||
weapon.NextMobAttack = curTime;
|
||||
|
||||
while (weapon.NextMobAttack <= curTime)
|
||||
{
|
||||
weapon.NextMobAttack += fireRate;
|
||||
if (update == UpdateNextAttack.Mob)
|
||||
swings++;
|
||||
}
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
Dirty(weaponUid, weapon);
|
||||
|
||||
// Do this AFTER attack so it doesn't spam every tick
|
||||
@@ -452,7 +513,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
? weapon.MissAnimation
|
||||
: weapon.Animation;
|
||||
if (miss)
|
||||
{
|
||||
weapon.NextAttack -= fireRate / 2f;
|
||||
weapon.NextMobAttack -= fireRate / 2f;
|
||||
}
|
||||
// WD EDIT END
|
||||
break;
|
||||
case DisarmAttackEvent disarm:
|
||||
@@ -520,13 +584,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
|
||||
|
||||
// WD START
|
||||
var blockEvent = new MeleeBlockAttemptEvent(user);
|
||||
RaiseLocalEvent(target.Value, ref blockEvent);
|
||||
if (blockEvent.Blocked)
|
||||
return;
|
||||
// WD END
|
||||
|
||||
// Raise event before doing damage so we can cancel damage if the event is handled
|
||||
var hitEvent = new MeleeHitEvent(new List<EntityUid> { target.Value }, user, meleeUid, damage, null);
|
||||
RaiseLocalEvent(meleeUid, hitEvent);
|
||||
@@ -663,19 +720,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
|
||||
|
||||
// WD START
|
||||
foreach (var target in new List<EntityUid>(targets))
|
||||
{
|
||||
var blockEvent = new MeleeBlockAttemptEvent(user);
|
||||
RaiseLocalEvent(target, ref blockEvent);
|
||||
if (blockEvent.Blocked)
|
||||
targets.Remove(target);
|
||||
}
|
||||
|
||||
if (targets.Count == 0)
|
||||
return true;
|
||||
// WD END
|
||||
|
||||
// Raise event before doing damage so we can cancel damage if the event is handled
|
||||
var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage, direction);
|
||||
RaiseLocalEvent(meleeUid, hitEvent);
|
||||
@@ -899,4 +943,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
|
||||
Dirty(uid, meleeWeapon);
|
||||
}
|
||||
|
||||
protected bool IsMob(EntityUid uid)
|
||||
{
|
||||
return HasComp<InputMoverComponent>(uid) || HasComp<MobMoverComponent>(uid) || HasComp<CombatModeComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +120,23 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
if (!TryComp<MeleeWeaponComponent>(uid, out var melee))
|
||||
return;
|
||||
|
||||
// WD EDIT START
|
||||
var dirty = false;
|
||||
if (melee.NextAttack > component.NextFire)
|
||||
{
|
||||
component.NextFire = melee.NextAttack;
|
||||
Dirty(uid, component);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (melee.NextMobAttack > component.NextFire)
|
||||
{
|
||||
component.NextFire = melee.NextMobAttack;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
Dirty(uid, component);
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args)
|
||||
|
||||
8
Content.Shared/_White/Blocking/BlockBlockerComponent.cs
Normal file
8
Content.Shared/_White/Blocking/BlockBlockerComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Blocking;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class BlockBlockerComponent : Component
|
||||
{
|
||||
}
|
||||
16
Content.Shared/_White/Blocking/MeleeBlockComponent.cs
Normal file
16
Content.Shared/_White/Blocking/MeleeBlockComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared._White.Blocking;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class MeleeBlockComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(3.1);
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier BlockSound = new SoundPathSpecifier("/Audio/Weapons/block_metal1.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.25f)
|
||||
};
|
||||
}
|
||||
97
Content.Shared/_White/Blocking/MeleeBlockSystem.cs
Normal file
97
Content.Shared/_White/Blocking/MeleeBlockSystem.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Content.Shared._White.BetrayalDagger;
|
||||
using Content.Shared.Blocking;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._White.Blocking;
|
||||
|
||||
public sealed class MeleeBlockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, MeleeBlockAttemptEvent>(OnBlockAttempt,
|
||||
after: new[] {typeof(BlockingSystem)});
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, MeleeHitEvent>(OnHit,
|
||||
before: new[] {typeof(StaminaSystem), typeof(MeleeThrowOnHitSystem)},
|
||||
after: new[] {typeof(BackstabSystem)});
|
||||
SubscribeLocalEvent<MeleeBlockComponent, ExaminedEvent>(OnExamine);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<MeleeBlockComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("melee-block-component-delay", ("delay", ent.Comp.Delay.TotalSeconds)));
|
||||
}
|
||||
|
||||
private void OnHit(Entity<MeleeWeaponComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (!ent.Comp.CanBeBlocked || !args.IsHit || args.Handled)
|
||||
return;
|
||||
|
||||
if (args.Direction != null || args.HitEntities.Count != 1) // Heavy attacks are unblockable
|
||||
return;
|
||||
|
||||
var hitEntity = args.HitEntities[0];
|
||||
|
||||
if (hitEntity == args.User)
|
||||
return;
|
||||
|
||||
var ev = new MeleeBlockAttemptEvent(args.User, args.BaseDamage + args.BonusDamage);
|
||||
RaiseLocalEvent(hitEntity, ev);
|
||||
if (ev.Handled)
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnBlockAttempt(Entity<HandsComponent> ent, ref MeleeBlockAttemptEvent args)
|
||||
{
|
||||
if (args.Handled || HasComp<BlockBlockerComponent>(ent))
|
||||
return;
|
||||
|
||||
if (!TryComp(ent, out StatusEffectsComponent? statusEffects))
|
||||
return;
|
||||
|
||||
var uid = ent.Comp.ActiveHandEntity;
|
||||
if (!TryComp(uid, out MeleeBlockComponent? block))
|
||||
return;
|
||||
|
||||
if (TryComp(uid.Value, out ItemToggleComponent? toggle) && !toggle.Activated)
|
||||
return;
|
||||
|
||||
_audio.PlayPredicted(block.BlockSound, ent, args.Attacker);
|
||||
_popupSystem.PopupPredicted(Loc.GetString("melee-block-event-blocked"), ent, args.Attacker);
|
||||
_damageable.TryChangeDamage(uid.Value, args.Damage);
|
||||
TryBlockBlocking(ent, block.Delay, true, statusEffects);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public bool TryBlockBlocking(EntityUid uid, TimeSpan time, bool refresh,
|
||||
StatusEffectsComponent? status = null)
|
||||
{
|
||||
if (time <= TimeSpan.Zero)
|
||||
return false;
|
||||
|
||||
if (!Resolve(uid, ref status, false))
|
||||
return false;
|
||||
|
||||
return _statusEffect.TryAddStatusEffect<BlockBlockerComponent>(uid, "RecentlyBlocked", time, refresh);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Item.DelayedKnockdown;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class DelayedKnockdownComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan KnockdownTime = TimeSpan.FromSeconds(5);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan KnockdownMoment = TimeSpan.MaxValue;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Content.Shared._White.Item.DelayedKnockdown;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class DelayedKnockdownOnHitComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(2);
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan KnockdownTime = TimeSpan.FromSeconds(5);
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan StutterTime = TimeSpan.FromSeconds(16);
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan JitterTime = TimeSpan.FromSeconds(40);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Jittering;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._White.Item.DelayedKnockdown;
|
||||
|
||||
public sealed class DelayedKnockdownOnHitSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly SharedJitteringSystem _jitter = default!;
|
||||
[Dependency] private readonly SharedStutteringSystem _stutter = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DelayedKnockdownOnHitComponent, StaminaMeleeHitEvent>(OnHit);
|
||||
}
|
||||
|
||||
private void OnHit(Entity<DelayedKnockdownOnHitComponent> ent, ref StaminaMeleeHitEvent args)
|
||||
{
|
||||
var jitterTime = ent.Comp.JitterTime;
|
||||
var stutterTime = ent.Comp.StutterTime;
|
||||
foreach (var (uid, _) in args.HitList)
|
||||
{
|
||||
if (!TryComp(uid, out StatusEffectsComponent? statusEffects))
|
||||
continue;
|
||||
|
||||
if (!TryComp(uid, out StandingStateComponent? standingState) || !standingState.CanLieDown)
|
||||
continue;
|
||||
|
||||
if (jitterTime > TimeSpan.Zero)
|
||||
_jitter.DoJitter(uid, jitterTime, true, status: statusEffects);
|
||||
if (stutterTime > TimeSpan.Zero)
|
||||
_stutter.DoStutter(uid, stutterTime, true, statusEffects);
|
||||
if (HasComp<KnockedDownComponent>(uid))
|
||||
continue;
|
||||
var delayedKnockdown = EnsureComp<DelayedKnockdownComponent>(uid);
|
||||
delayedKnockdown.KnockdownTime = TimeSpan.FromSeconds(Math.Max(ent.Comp.KnockdownTime.TotalSeconds,
|
||||
delayedKnockdown.KnockdownTime.TotalSeconds));
|
||||
var knockdownMoment = _timing.CurTime + ent.Comp.Delay;
|
||||
if (knockdownMoment < delayedKnockdown.KnockdownMoment)
|
||||
delayedKnockdown.KnockdownMoment = knockdownMoment;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<DelayedKnockdownComponent>();
|
||||
while (query.MoveNext(out var uid, out var delayedKnockdown))
|
||||
{
|
||||
if (delayedKnockdown.KnockdownMoment > _timing.CurTime)
|
||||
continue;
|
||||
|
||||
_stun.TryKnockdown(uid, delayedKnockdown.KnockdownTime, true);
|
||||
RemCompDeferred<DelayedKnockdownComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._White.Item.Telebaton;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TelebatonComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan KnockdownTime = TimeSpan.FromSeconds(1.5f);
|
||||
}
|
||||
56
Content.Shared/_White/Item/Telebaton/TelebatonSystem.cs
Normal file
56
Content.Shared/_White/Item/Telebaton/TelebatonSystem.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
|
||||
namespace Content.Shared._White.Item.Telebaton;
|
||||
|
||||
public sealed class TelebatonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly SharedItemToggleSystem _itemToggle = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TelebatonComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<TelebatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
|
||||
SubscribeLocalEvent<TelebatonComponent, ItemToggledEvent>(ToggleDone);
|
||||
SubscribeLocalEvent<TelebatonComponent, StaminaMeleeHitEvent>(OnHit);
|
||||
}
|
||||
|
||||
private void OnHit(Entity<TelebatonComponent> ent, ref StaminaMeleeHitEvent args)
|
||||
{
|
||||
var time = ent.Comp.KnockdownTime;
|
||||
if (time <= TimeSpan.Zero)
|
||||
return;
|
||||
|
||||
foreach (var (uid, _) in args.HitList)
|
||||
{
|
||||
_stun.TryKnockdown(uid, time, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStaminaHitAttempt(Entity<TelebatonComponent> entity, ref StaminaDamageOnHitAttemptEvent args)
|
||||
{
|
||||
if (!_itemToggle.IsActivated(entity.Owner))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<TelebatonComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
var onMsg = _itemToggle.IsActivated(entity.Owner)
|
||||
? Loc.GetString("comp-telebaton-examined-on")
|
||||
: Loc.GetString("comp-telebaton-examined-off");
|
||||
args.PushMarkup(onMsg);
|
||||
}
|
||||
|
||||
private void ToggleDone(Entity<TelebatonComponent> entity, ref ItemToggledEvent args)
|
||||
{
|
||||
_item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off");
|
||||
}
|
||||
}
|
||||
BIN
Resources/Audio/White/Weapons/woodhit.ogg
Normal file
BIN
Resources/Audio/White/Weapons/woodhit.ogg
Normal file
Binary file not shown.
5
Resources/Locale/ru-RU/_white/object/telebaton.ftl
Normal file
5
Resources/Locale/ru-RU/_white/object/telebaton.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
ent-Telebaton = телескопическая дубинка
|
||||
.desc = Компактное, но надежное оружие личной обороны. В сложенном состоянии может быть скрыто.
|
||||
|
||||
comp-telebaton-examined-on = Дубинка в боевом положении.
|
||||
comp-telebaton-examined-off = Дубинка сложена.
|
||||
@@ -17,3 +17,8 @@ carry-start = { $carrier } пытается взять вас на руки!
|
||||
|
||||
alerts-knockdown-name = Лежу
|
||||
alerts-knockdown-desc = Не могу встать.
|
||||
|
||||
melee-block-event-blocked = заблокировал!
|
||||
alerts-blocked-name = Атака заблокирована
|
||||
alerts-blocked-desc = Невозможно блокировать некоторое время.
|
||||
melee-block-component-delay = Может блокировать атаку ближнего боя каждые {$delay} секунд.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
- alertType: Corporeal
|
||||
- alertType: Stun
|
||||
- alertType: Knockdown
|
||||
- alertType: RecentlyBlocked
|
||||
- category: Breathing # Vox gang not calling this oxygen
|
||||
- category: Pressure
|
||||
- alertType: Bleed
|
||||
@@ -494,6 +495,13 @@
|
||||
name: alerts-knockdown-name
|
||||
description: alerts-knockdown-desc
|
||||
|
||||
# WD-EDIT
|
||||
- type: alert
|
||||
id: RecentlyBlocked
|
||||
icons: [ /Textures/Objects/Weapons/Melee/shields.rsi/buckler-icon.png ]
|
||||
name: alerts-blocked-name
|
||||
description: alerts-blocked-desc
|
||||
|
||||
# WD-EDIT
|
||||
- type: alert
|
||||
id: Bleeding
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- name: StationCharter
|
||||
#- name: TelescopicBaton
|
||||
- type: entity
|
||||
@@ -77,6 +78,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalEngineering
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -88,6 +90,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -99,6 +102,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -110,6 +114,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -121,6 +126,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalMedical
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -132,6 +138,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- name: StationCharter
|
||||
#- name: TelescopicBaton
|
||||
- type: entity
|
||||
@@ -86,6 +87,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalEngineering
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -97,6 +99,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -108,6 +111,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -119,6 +123,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalMedical
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -130,6 +135,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- name: StationCharter
|
||||
#- name: TelescopicBaton
|
||||
- type: entity
|
||||
@@ -114,6 +115,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalEngineering
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -125,6 +127,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -136,6 +139,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -147,6 +151,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalMedical
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -158,6 +163,7 @@
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
@@ -180,6 +186,7 @@
|
||||
contents:
|
||||
- id: BoxSurvivalSecurity
|
||||
- id: Flash
|
||||
- id: Telebaton
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
- type: Item
|
||||
size: Small
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- type: Sprite
|
||||
state: icon
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
- type: Spillable
|
||||
solution: drink
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
- type: Spillable
|
||||
solution: drink
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
maxTransferAmount: 5
|
||||
- type: Drink
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
acts: [ "Destruction" ]
|
||||
# packet contents can be splashed when open
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
solution: food
|
||||
# soup weapon!
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
- type: UseDelay
|
||||
delay: 1.0
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
wideAnimationRotation: 180
|
||||
soundHit:
|
||||
collection: ToySqueak
|
||||
@@ -992,6 +993,7 @@
|
||||
sprite: Objects/Fun/toys.rsi
|
||||
state: foamblade
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
attackRate: 1.5
|
||||
angle: 0
|
||||
animation: WeaponArcThrust
|
||||
@@ -1325,6 +1327,7 @@
|
||||
sprite: Objects/Weapons/Melee/cutlass.rsi
|
||||
state: foam_icon
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
attackRate: 1.5
|
||||
range: 2.0
|
||||
angle: 0
|
||||
@@ -1383,6 +1386,7 @@
|
||||
- type: DisarmMalus
|
||||
malus: 0
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundHit:
|
||||
collection: RubberHammer
|
||||
params:
|
||||
@@ -1424,6 +1428,7 @@
|
||||
params:
|
||||
variation: 0.125
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundHit:
|
||||
collection: Parp
|
||||
params:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
tags:
|
||||
- Handcuffs
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
wideAnimationRotation: 90
|
||||
resetOnHandSelected: false
|
||||
animation: WeaponArcDisarm
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
Slash: 1
|
||||
Piercing: 1
|
||||
Heat: 1
|
||||
- type: MeleeBlock
|
||||
- type: Damageable
|
||||
damageContainer: Shield
|
||||
- type: Destructible
|
||||
@@ -412,6 +413,7 @@
|
||||
description: Exotic energy shield, when folded, can even fit in your pocket.
|
||||
components:
|
||||
- type: ItemToggle
|
||||
predictable: false # WD EDIT
|
||||
soundActivate:
|
||||
path: /Audio/Weapons/ebladeon.ogg
|
||||
soundDeactivate:
|
||||
@@ -420,10 +422,9 @@
|
||||
path: /Audio/Machines/button.ogg
|
||||
params:
|
||||
variation: 0.250
|
||||
# Causes sound issues in combination with Rechargeable
|
||||
#- type: ItemToggleActiveSound
|
||||
# activeSound:
|
||||
# path: /Audio/Weapons/ebladehum.ogg
|
||||
- type: ItemToggleActiveSound
|
||||
activeSound:
|
||||
path: /Audio/Weapons/ebladehum.ogg
|
||||
- type: ItemToggleSize
|
||||
activatedSize: Huge
|
||||
- type: ItemToggleDisarmMalus
|
||||
@@ -480,6 +481,7 @@
|
||||
flatReductions:
|
||||
Heat: 1
|
||||
Piercing: 1
|
||||
- type: MeleeBlock
|
||||
- type: Appearance
|
||||
- type: Damageable
|
||||
damageContainer: Shield
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
- type: Spillable
|
||||
solution: absorbed
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
- type: Label
|
||||
originalName: jug
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
- type: Spillable
|
||||
solution: drink
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
shape:
|
||||
- 0,0,0,0
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- type: Item
|
||||
sprite: Objects/Specific/Chemistry/beaker.rsi
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
@@ -108,6 +109,7 @@
|
||||
- type: Item
|
||||
sprite: Objects/Specific/Chemistry/beaker.rsi
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
- key: enum.TransferAmountUiKey.Key
|
||||
type: TransferAmountBoundUserInterface
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundNoDamage:
|
||||
path: "/Audio/Effects/Fluids/splat.ogg"
|
||||
damage:
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
Quantity: 8
|
||||
maxVol: 8 #uses less fuel than a welder, so this isnt as bad as it looks
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
wideAnimationRotation: 180
|
||||
damage:
|
||||
types:
|
||||
@@ -201,6 +202,7 @@
|
||||
- state: zippo-inhand-right-flame
|
||||
shader: unshaded
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
wideAnimationRotation: 180
|
||||
damage:
|
||||
types:
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
critChance: 50
|
||||
critMultiplier: 2
|
||||
isBloodDagger: true
|
||||
- type: MeleeBlock
|
||||
delay: 12.1
|
||||
|
||||
- type: entity
|
||||
name: предательский нож
|
||||
@@ -61,3 +63,5 @@
|
||||
- type: Backstab
|
||||
- type: Blink
|
||||
blinkRate: 0.33
|
||||
- type: MeleeBlock
|
||||
delay: 12.1
|
||||
|
||||
@@ -84,6 +84,12 @@
|
||||
- Energy
|
||||
- type: IgnitionSource
|
||||
temperature: 700
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
blockSound:
|
||||
path: /Audio/Weapons/eblade1.ogg
|
||||
params:
|
||||
variation: 0.250
|
||||
|
||||
- type: entity
|
||||
name: energy sword
|
||||
@@ -199,6 +205,8 @@
|
||||
- id: EnergyDagger
|
||||
sound:
|
||||
path: /Audio/Effects/unwrap.ogg
|
||||
- type: MeleeBlock
|
||||
delay: 12.1
|
||||
|
||||
- type: entity
|
||||
name: energy cutlass
|
||||
@@ -267,6 +275,8 @@
|
||||
deconstructionTarget: null
|
||||
graph: EnergyDoubleSwordGraph
|
||||
node: desword
|
||||
- type: MeleeBlock
|
||||
delay: 3.1
|
||||
|
||||
- type: entity
|
||||
parent: EnergySwordBase
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
animation: WeaponArcThrust
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
range: 2 # Spears are long
|
||||
range: 1.8 # Spears are long
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
- type: entity
|
||||
name: stun prod
|
||||
parent: BaseItem
|
||||
id: Stunprod
|
||||
id: StunprodBase
|
||||
description: A stun prod for illegal incapacitation.
|
||||
abstract: true
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/stunprod.rsi
|
||||
layers:
|
||||
- state: stunprod_nocell
|
||||
map: [ "enum.ToggleVisuals.Layer" ]
|
||||
- type: ItemToggle
|
||||
soundActivate:
|
||||
collection: sparks
|
||||
@@ -27,41 +24,36 @@
|
||||
types:
|
||||
Blunt: 0
|
||||
- type: Stunprod
|
||||
- type: DelayedKnockdownOnHit
|
||||
delay: 4
|
||||
- type: MeleeWeapon
|
||||
attackRate: 0.4
|
||||
canHeavyAttack: false
|
||||
equipCooldown: 1
|
||||
wideAnimationRotation: -135
|
||||
damage:
|
||||
types:
|
||||
Blunt: 9
|
||||
Blunt: 12
|
||||
angle: 0
|
||||
animation: WeaponArcThrust
|
||||
- type: StaminaDamageOnHit
|
||||
damage: 40
|
||||
damage: 60
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: StaminaDamageOnCollide
|
||||
damage: 40
|
||||
damage: 30
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: UseDelay
|
||||
- type: Item
|
||||
heldPrefix: off
|
||||
size: Normal
|
||||
- type: Clothing
|
||||
sprite: Objects/Weapons/Melee/stunprod.rsi
|
||||
quickEquip: false
|
||||
slots:
|
||||
- back
|
||||
shape:
|
||||
- 0,0,2,0
|
||||
storedRotation: 44
|
||||
- type: DisarmMalus
|
||||
malus: 0.225
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ToggleVisuals.Toggled:
|
||||
enum.ToggleVisuals.Layer:
|
||||
nocell: {state: stunprod_nocell}
|
||||
True: {state: stunprod_on}
|
||||
False: {state: stunprod_off}
|
||||
- type: StaticPrice
|
||||
price: 100
|
||||
price: 40
|
||||
- type: PowerCellSlot
|
||||
cellSlotId: cell_slot
|
||||
- type: ItemSlots
|
||||
@@ -71,6 +63,32 @@
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
cell_slot: !type:ContainerSlot {}
|
||||
|
||||
- type: entity
|
||||
name: stun prod
|
||||
parent: StunprodBase
|
||||
id: Stunprod
|
||||
description: A stun prod for illegal incapacitation.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/stunprod.rsi
|
||||
layers:
|
||||
- state: stunprod_nocell
|
||||
map: [ "enum.ToggleVisuals.Layer" ]
|
||||
- type: Item
|
||||
sprite: Objects/Weapons/Melee/stunprod.rsi
|
||||
- type: Clothing
|
||||
sprite: Objects/Weapons/Melee/stunprod.rsi
|
||||
quickEquip: false
|
||||
slots:
|
||||
- back
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ToggleVisuals.Toggled:
|
||||
enum.ToggleVisuals.Layer:
|
||||
nocell: {state: stunprod_nocell}
|
||||
True: {state: stunprod_on}
|
||||
False: {state: stunprod_off}
|
||||
- type: Construction
|
||||
deconstructionTarget: cuffs
|
||||
graph: StunprodGraph
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
tags:
|
||||
- CaptainSabre
|
||||
- type: DisarmMalus
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
|
||||
- type: entity
|
||||
name: katana
|
||||
@@ -94,6 +96,8 @@
|
||||
- Belt
|
||||
- SuitStorage
|
||||
- type: Reflect
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
|
||||
- type: entity
|
||||
name: machete
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- state: stunbaton_off
|
||||
map: [ "enum.ToggleVisuals.Layer" ]
|
||||
- type: Stunbaton
|
||||
energyPerUse: 50
|
||||
energyPerUse: 100
|
||||
- type: ItemToggle
|
||||
predictable: false
|
||||
soundActivate:
|
||||
@@ -30,19 +30,20 @@
|
||||
types:
|
||||
Blunt: 0
|
||||
- type: MeleeWeapon
|
||||
attackRate: 1.25
|
||||
attackRate: 0.4
|
||||
canHeavyAttack: false
|
||||
equipCooldown: 1
|
||||
wideAnimationRotation: -135
|
||||
damage:
|
||||
types:
|
||||
Blunt: 7
|
||||
Blunt: 12
|
||||
bluntStaminaDamageFactor: 2.0
|
||||
angle: 60
|
||||
- type: StaminaDamageOnHit
|
||||
damage: 40
|
||||
damage: 60
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: StaminaDamageOnCollide
|
||||
damage: 40
|
||||
damage: 30
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: ExaminableBattery
|
||||
- type: Battery
|
||||
@@ -87,7 +88,8 @@
|
||||
- type: GuideHelp
|
||||
guides:
|
||||
- Security
|
||||
- type: StunLock # Wd EDIT
|
||||
- type: StunLock # WD EDIT
|
||||
- type: DelayedKnockdownOnHit # WD EDIT
|
||||
|
||||
- type: entity
|
||||
name: truncheon
|
||||
@@ -142,6 +144,7 @@
|
||||
maxCharges: 7
|
||||
charges: 7
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
canHeavyAttack: false
|
||||
wideAnimationRotation: 180
|
||||
damage:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- type: Item
|
||||
size: Ginormous
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
canHeavyAttack: false
|
||||
canMiss: false
|
||||
attackRate: 2
|
||||
|
||||
@@ -35,14 +35,17 @@
|
||||
animation: WeaponArcThrust
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
range: 2
|
||||
range: 1.8
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
Piercing: 40
|
||||
- type: Item
|
||||
sprite: White/Cult/Entities/blood_spear.rsi
|
||||
size: Ginormous
|
||||
storedRotation: 44
|
||||
size: Huge
|
||||
shape:
|
||||
- 0,0,5,0
|
||||
- type: Clothing
|
||||
slots:
|
||||
- back
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
- type: MeleeWeapon
|
||||
soundHit:
|
||||
path: /Audio/White/Items/hit/chainhit.ogg
|
||||
range: 2.5
|
||||
range: 2.2
|
||||
damage:
|
||||
types:
|
||||
Blunt: 18
|
||||
@@ -309,6 +309,7 @@
|
||||
- type: UseDelay
|
||||
- type: DisarmMalus
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
- type: HolyWeapon
|
||||
|
||||
- type: entity
|
||||
@@ -348,7 +349,6 @@
|
||||
animation: WeaponArcThrust
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
range: 2
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
|
||||
@@ -9,16 +9,6 @@
|
||||
- type: Sprite
|
||||
sprite: White/Objects/Weapons/experimental_stunbaton.rsi
|
||||
- type: Stunbaton
|
||||
- type: MeleeWeapon
|
||||
damage:
|
||||
types:
|
||||
Blunt: 10
|
||||
bluntStaminaDamageFactor: 2.0
|
||||
angle: 70
|
||||
- type: StaminaDamageOnHit
|
||||
damage: 45
|
||||
- type: StaminaDamageOnCollide
|
||||
damage: 45
|
||||
- type: Battery
|
||||
maxCharge: 2000
|
||||
startingCharge: 2000
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
parent: StunprodBase
|
||||
id: Snatcherprod
|
||||
name: хваталка
|
||||
description: Искрится жаждой воровства и коварства.
|
||||
@@ -9,48 +9,8 @@
|
||||
layers:
|
||||
- state: snatcherprod_nocell
|
||||
map: [ "enum.ToggleVisuals.Layer" ]
|
||||
- type: ItemToggle
|
||||
soundActivate:
|
||||
collection: sparks
|
||||
params:
|
||||
variation: 0.250
|
||||
soundDeactivate:
|
||||
collection: sparks
|
||||
params:
|
||||
variation: 0.250
|
||||
soundFailToActivate:
|
||||
path: /Audio/Machines/button.ogg
|
||||
params:
|
||||
variation: 0.250
|
||||
- type: ItemToggleMeleeWeapon
|
||||
activatedDamage:
|
||||
types:
|
||||
Blunt: 0
|
||||
- type: MeleeWeapon
|
||||
canHeavyAttack: false
|
||||
wideAnimationRotation: -135
|
||||
damage:
|
||||
types:
|
||||
Blunt: 9
|
||||
angle: 0
|
||||
animation: WeaponArcThrust
|
||||
- type: StaminaDamageOnHit
|
||||
damage: 40
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: StaminaDamageOnCollide
|
||||
damage: 40
|
||||
sound: /Audio/Weapons/egloves.ogg
|
||||
- type: UseDelay
|
||||
- type: Item
|
||||
heldPrefix: off
|
||||
size: Normal
|
||||
- type: Clothing
|
||||
quickEquip: false
|
||||
slots:
|
||||
- back
|
||||
- type: DisarmMalus
|
||||
malus: 0.225
|
||||
- type: Appearance
|
||||
sprite: White/Objects/Weapons/snatcherprod.rsi
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ToggleVisuals.Toggled:
|
||||
@@ -58,19 +18,7 @@
|
||||
nocell: {state: snatcherprod_nocell}
|
||||
True: {state: snatcherprod_on}
|
||||
False: {state: snatcherprod_off}
|
||||
- type: StaticPrice
|
||||
price: 100
|
||||
- type: PowerCellSlot
|
||||
cellSlotId: cell_slot
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
cell_slot:
|
||||
name: power-cell-slot-component-slot-name-default
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
cell_slot: !type:ContainerSlot {}
|
||||
- type: Snatcherprod
|
||||
- type: Stunprod
|
||||
- type: Construction
|
||||
deconstructionTarget: cuffs
|
||||
graph: StunprodGraph
|
||||
@@ -86,7 +34,10 @@
|
||||
sprite: White/Objects/Weapons/prod.rsi
|
||||
state: prod_unfinished
|
||||
- type: Item
|
||||
size: Normal
|
||||
size: Small
|
||||
shape:
|
||||
- 0,0,1,0
|
||||
storedRotation: 44
|
||||
- type: Construction
|
||||
deconstructionTarget: cuffs
|
||||
graph: StunprodGraph
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
- type: entity
|
||||
id: Telebaton
|
||||
name: telescopic baton
|
||||
parent: BaseItem
|
||||
description: A compact yet robust personal defense weapon. Can be concealed when folded.
|
||||
components:
|
||||
- type: ItemToggle
|
||||
soundActivate:
|
||||
path: /Audio/Weapons/telescopicon.ogg
|
||||
params:
|
||||
volume: -5
|
||||
soundDeactivate:
|
||||
path: /Audio/Weapons/telescopicoff.ogg
|
||||
params:
|
||||
volume: -5
|
||||
- type: ItemToggleSize
|
||||
activatedSize: Large
|
||||
activatedShape:
|
||||
- 0, 0, 3, 0
|
||||
- type: DisarmMalus
|
||||
malus: 0.225
|
||||
- type: Sprite
|
||||
sprite: White/Objects/Weapons/telebaton.rsi
|
||||
layers:
|
||||
- state: telebaton_off
|
||||
map: [ "enum.ToggleVisuals.Layer" ]
|
||||
- type: ItemToggleMeleeWeapon
|
||||
activatedDamage:
|
||||
types:
|
||||
Blunt: 12
|
||||
deactivatedSecret: true
|
||||
- type: MeleeWeapon
|
||||
canHeavyAttack: false
|
||||
equipCooldown: 1
|
||||
attackRate: 0.25
|
||||
wideAnimationRotation: -135
|
||||
damage:
|
||||
types:
|
||||
Blunt: 0
|
||||
bluntStaminaDamageFactor: 0.0 # so blunt doesn't deal stamina damage at all
|
||||
- type: StaminaDamageOnHit
|
||||
damage: 55
|
||||
sound: /Audio/White/Weapons/woodhit.ogg
|
||||
- type: UseDelay
|
||||
- type: Item
|
||||
heldPrefix: off
|
||||
sprite: White/Objects/Weapons/telebaton.rsi
|
||||
size: Small
|
||||
storedRotation: 44
|
||||
shape:
|
||||
- 0, 0, 1, 0
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ToggleVisuals.Toggled:
|
||||
enum.ToggleVisuals.Layer:
|
||||
True: {state: telebaton_on}
|
||||
False: {state: telebaton_off}
|
||||
- type: StaticPrice
|
||||
price: 150
|
||||
- type: Telebaton
|
||||
@@ -395,6 +395,7 @@
|
||||
- type: UseDelay
|
||||
delay: 2
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundHit:
|
||||
path: /Audio/White/Fluff/Omntns/gavel.ogg
|
||||
damage:
|
||||
@@ -726,6 +727,7 @@
|
||||
Quantity: 1
|
||||
maxVol: 1
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
damage:
|
||||
types:
|
||||
Blunt: 0 #this feels hacky, but is needed for burn damage while active (i think)
|
||||
|
||||
@@ -20,5 +20,6 @@
|
||||
sound:
|
||||
path: /Textures/White/Fluff/Forg/ryan.ogg
|
||||
- type: MeleeWeapon
|
||||
canBeBlocked: false
|
||||
soundHit:
|
||||
path: /Audio/Items/Toys/weh.ogg
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
storedRotation: 44
|
||||
shape:
|
||||
- 0, 0, 3, 0
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
|
||||
- type: entity
|
||||
name: клинок заклинаний
|
||||
@@ -55,6 +57,8 @@
|
||||
inHandsOnly: true
|
||||
closeOnHandDeselect: true
|
||||
- type: SpellBlade
|
||||
- type: MeleeBlock
|
||||
delay: 6.1
|
||||
|
||||
- type: entity
|
||||
name: мьёльнир
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
description: spellbook-hfrequency-desc
|
||||
productEntity: HighFrequencyBlade
|
||||
cost:
|
||||
SpellPoint: 1
|
||||
SpellPoint: 2
|
||||
categories:
|
||||
- MagicItems
|
||||
conditions:
|
||||
@@ -302,7 +302,7 @@
|
||||
description: spellbook-spellblade-desc
|
||||
productEntity: SpellBlade
|
||||
cost:
|
||||
SpellPoint: 1
|
||||
SpellPoint: 2
|
||||
categories:
|
||||
- MagicItems
|
||||
conditions:
|
||||
|
||||
@@ -82,5 +82,10 @@
|
||||
id: Incorporeal
|
||||
alwaysAllowed: true
|
||||
|
||||
- type: statusEffect
|
||||
id: RecentlyBlocked
|
||||
alwaysAllowed: true
|
||||
alert: RecentlyBlocked
|
||||
|
||||
- type: statusEffect
|
||||
id: BloodLoss
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4397d63a55dac7d0536eb9bcc0a0f68634858c50",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "telebaton_off"
|
||||
},
|
||||
{
|
||||
"name": "telebaton_on"
|
||||
},
|
||||
{
|
||||
"name": "on-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "on-inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 356 B |
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
Binary file not shown.
|
After Width: | Height: | Size: 202 B |
Binary file not shown.
|
After Width: | Height: | Size: 252 B |
Reference in New Issue
Block a user