Merge remote-tracking branch 'upstream/master' into upstream
This commit is contained in:
@@ -123,10 +123,10 @@ public abstract class SharedTentacleGun : EntitySystem
|
||||
foreach (var activeItem in _handsSystem.EnumerateHeld(args.Embedded))
|
||||
{
|
||||
if(!TryComp<PhysicsComponent>(activeItem, out var physicsComponent))
|
||||
return;
|
||||
continue;
|
||||
|
||||
var coords = Transform(args.Embedded).Coordinates;
|
||||
_handsSystem.TryDrop(args.Embedded, coords);
|
||||
_handsSystem.TryDrop(args.Embedded, activeItem, coords);
|
||||
|
||||
var force = physicsComponent.Mass * 2.5f / 2;
|
||||
|
||||
@@ -137,7 +137,7 @@ public abstract class SharedTentacleGun : EntitySystem
|
||||
|
||||
private bool PullMob(ProjectileEmbedEvent args)
|
||||
{
|
||||
var stunTime = _random.Next(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5));
|
||||
var stunTime = _random.Next(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3));
|
||||
|
||||
if (!_stunSystem.TryParalyze(args.Embedded, stunTime, true))
|
||||
return false;
|
||||
|
||||
42
Content.Shared/Chemistry/Components/PatchComponent.cs
Normal file
42
Content.Shared/Chemistry/Components/PatchComponent.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class PatchDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements draw/inject behavior for droppers and syringes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can optionally support both
|
||||
/// injection and drawing or just injection. Can inject/draw reagents from solution
|
||||
/// containers, and can directly inject into a mobs bloodstream.
|
||||
/// </remarks>
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class PatchComponent : Component
|
||||
{
|
||||
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
public FixedPoint2 CurrentVolume;
|
||||
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
public FixedPoint2 TotalVolume;
|
||||
|
||||
[DataField("solutionName")]
|
||||
public string SolutionName = "patch";
|
||||
|
||||
[DataField("onlyMobs")]
|
||||
public bool OnlyMobs = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("delay")]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(5);
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
@@ -7,12 +8,15 @@ using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Localizations;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Shared.Hands.EntitySystems;
|
||||
|
||||
public abstract partial class SharedHandsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!; // WD
|
||||
|
||||
private void InitializeInteractions()
|
||||
{
|
||||
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
|
||||
@@ -95,7 +99,17 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity)
|
||||
{
|
||||
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null)
|
||||
// WD EDIT START
|
||||
{
|
||||
if (HasComp<BoltBarrageComponent>(hands.ActiveHandEntity))
|
||||
{
|
||||
if (_net.IsServer)
|
||||
QueueDel(hands.ActiveHandEntity.Value);
|
||||
return false;
|
||||
}
|
||||
TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands);
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
// always send to server.
|
||||
return false;
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
|
||||
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
||||
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
||||
public static readonly BoundKeyFunction LieDown = "LieDown"; // WD EDIT
|
||||
|
||||
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
||||
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
||||
|
||||
@@ -46,7 +46,7 @@ public abstract class SharedLatheSystem : EntitySystem
|
||||
var adjustedAmount = AdjustMaterial(needed, recipe.ApplyMaterialDiscount, component.MaterialUseMultiplier);
|
||||
|
||||
var gridUid =
|
||||
HasComp<BluespaceSiloComponent>(uid) &&
|
||||
HasComp<BluespaceStorageComponent>(uid) &&
|
||||
TryComp<TransformComponent>(uid, out var transformComponent)
|
||||
? transformComponent.GridUid
|
||||
: null;
|
||||
|
||||
@@ -296,7 +296,7 @@ public abstract class SharedMaterialStorageSystem : EntitySystem
|
||||
var multiplier = TryComp<StackComponent>(toInsert, out var stackComponent) ? stackComponent.Count : 1;
|
||||
var totalVolume = 0;
|
||||
|
||||
var gridUid = HasComp<BluespaceSiloComponent>(receiver) &&
|
||||
var gridUid = HasComp<BluespaceStorageComponent>(receiver) &&
|
||||
TryComp<TransformComponent>(receiver, out var transformComponent)
|
||||
? transformComponent.GridUid
|
||||
: null;
|
||||
@@ -348,8 +348,10 @@ public abstract class SharedMaterialStorageSystem : EntitySystem
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
var ev = new GetMaterialWhitelistEvent(uid);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
component.MaterialWhiteList = ev.Whitelist;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ public sealed partial class ProjectileComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? Weapon;
|
||||
|
||||
/// <summary>
|
||||
/// Entity being targeted by crosshair.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
public EntityUid? Target;
|
||||
|
||||
/// <summary>
|
||||
/// The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves.
|
||||
/// </summary>
|
||||
|
||||
@@ -334,6 +334,12 @@ public sealed class ImpactEffectEvent : EntityEventArgs
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a projectile
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ProjectileCollideAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a projectile but can reflect it
|
||||
/// </summary>
|
||||
@@ -350,4 +356,4 @@ public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target
|
||||
/// Raised after a projectile has dealt it's damage.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture);
|
||||
public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture);
|
||||
@@ -1,24 +1,47 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(StandingStateSystem))]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(StandingStateSystem))]
|
||||
public sealed partial class StandingStateComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||
public SoundSpecifier DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall");
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Standing { get; set; } = true;
|
||||
public StandingState CurrentState { get; set; } = StandingState.Standing; // WD EDIT
|
||||
|
||||
/// <summary>
|
||||
/// Time required to get up.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan StandingUpTime { get; set; } = TimeSpan.FromSeconds(1); // WD EDIT
|
||||
|
||||
// WD EDIT
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanLieDown = false;
|
||||
|
||||
/// <summary>
|
||||
/// List of fixtures that had their collision mask changed when the entity was downed.
|
||||
/// Required for re-adding the collision mask.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<string> ChangedFixtures = new();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ChangeStandingStateEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
public enum StandingState
|
||||
{
|
||||
Lying,
|
||||
GettingUp,
|
||||
Standing
|
||||
}
|
||||
@@ -1,166 +1,324 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Rotation;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
namespace Content.Shared.Standing;
|
||||
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
{
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!; // WD EDIT
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!; // WD EDIT
|
||||
|
||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
|
||||
// WD EDIT START
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
base.Initialize();
|
||||
|
||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
SubscribeLocalEvent<StandingStateComponent, StandingUpDoAfterEvent>(OnStandingUpDoAfter);
|
||||
SubscribeLocalEvent<StandingStateComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
|
||||
SubscribeLocalEvent<StandingStateComponent, ProjectileCollideAttemptEvent>(OnProjectileCollideAttempt);
|
||||
SubscribeLocalEvent<StandingStateComponent, HitscanHitAttemptEvent>(OnHitscanHitAttempt);
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
SubscribeNetworkEvent<ChangeStandingStateEvent>(OnChangeState);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.LieDown, InputCmdHandler.FromDelegate(ChangeLyingState))
|
||||
.Register<StandingStateSystem>();
|
||||
}
|
||||
|
||||
|
||||
private void OnRefreshMovementSpeed(
|
||||
EntityUid uid,
|
||||
StandingStateComponent component,
|
||||
RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
if (IsDown(uid))
|
||||
args.ModifySpeed(0.4f, 0.4f);
|
||||
else
|
||||
args.ModifySpeed(1f, 1f);
|
||||
}
|
||||
|
||||
private void OnProjectileCollideAttempt(EntityUid uid, StandingStateComponent component, ref ProjectileCollideAttemptEvent args)
|
||||
{
|
||||
if (component.CurrentState is StandingState.Standing)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Component.Target.HasValue || args.Component.Target != uid)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHitscanHitAttempt(EntityUid uid, StandingStateComponent component, ref HitscanHitAttemptEvent args)
|
||||
{
|
||||
if (component.CurrentState is StandingState.Standing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Target.HasValue || args.Target != uid)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !standingState.Standing;
|
||||
private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component, StandingUpDoAfterEvent args)
|
||||
{
|
||||
Stand(uid);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
private void OnChangeState(ChangeStandingStateEvent ev, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!args.SenderSession.AttachedEntity.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
var uid = args.SenderSession.AttachedEntity.Value;
|
||||
|
||||
if (IsDown(uid))
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
TryStandUp(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
TryLieDown(uid);
|
||||
}
|
||||
}
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
/// <summary>
|
||||
/// Send an update event when player pressed keybind.
|
||||
/// </summary>
|
||||
private void ChangeLyingState(ICommonSession? session)
|
||||
{
|
||||
if (session?.AttachedEntity == null ||
|
||||
!TryComp(session.AttachedEntity, out StandingStateComponent? standing) ||
|
||||
!standing.CanLieDown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!standingState.Standing)
|
||||
return true;
|
||||
RaiseNetworkEvent(new ChangeStandingStateEvent());
|
||||
}
|
||||
|
||||
public bool TryStandUp(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// This is just to avoid most callers doing this manually saving boilerplate
|
||||
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
|
||||
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
|
||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
||||
if (dropHeldItems && hands != null)
|
||||
if (standingState.CurrentState is not StandingState.Lying)
|
||||
return false;
|
||||
|
||||
standingState.CurrentState = StandingState.GettingUp;
|
||||
var doargs = new DoAfterArgs(EntityManager, uid, standingState.StandingUpTime,
|
||||
new StandingUpDoAfterEvent(), uid)
|
||||
{
|
||||
BreakOnUserMove = false,
|
||||
BreakOnDamage = false,
|
||||
BreakOnHandChange = false
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doargs);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryLieDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
if (standingState.CurrentState is not StandingState.Standing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Down(uid, true, false, standingState);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WD EDIT END
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
return standingState.CurrentState is StandingState.Lying or StandingState.GettingUp;
|
||||
}
|
||||
|
||||
public bool Down(
|
||||
EntityUid uid,
|
||||
bool playSound = true,
|
||||
bool dropHeldItems = true,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
|
||||
if (standingState.CurrentState is StandingState.Lying or StandingState.GettingUp)
|
||||
return true;
|
||||
|
||||
// This is just to avoid most callers doing this manually saving boilerplate
|
||||
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
|
||||
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
|
||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
||||
if (dropHeldItems && hands != null)
|
||||
{
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent());
|
||||
}
|
||||
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
|
||||
standingState.CurrentState = StandingState.Lying;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new DownedEvent());
|
||||
|
||||
// Seemed like the best place to put it
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
// Change collision masks to allow going under certain entities like flaps and tables
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent(), false);
|
||||
}
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer,
|
||||
manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// check if component was just added or streamed to client
|
||||
// if true, no need to play sound - mob was down before player could seen that
|
||||
if (standingState.LifeStage <= ComponentLifeStage.Starting)
|
||||
return true;
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
_audio.PlayPredicted(standingState.DownSound, uid, uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stand(
|
||||
EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (standingState.CurrentState is StandingState.Standing)
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
|
||||
standingState.Standing = false;
|
||||
Dirty(standingState);
|
||||
RaiseLocalEvent(uid, new DownedEvent(), false);
|
||||
|
||||
// Seemed like the best place to put it
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
// Change collision masks to allow going under certain entities like flaps and tables
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// check if component was just added or streamed to client
|
||||
// if true, no need to play sound - mob was down before player could seen that
|
||||
if (standingState.LifeStage <= ComponentLifeStage.Starting)
|
||||
return true;
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
_audio.PlayPredicted(standingState.DownSound, uid, uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stand(EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
standingState.CurrentState = StandingState.Standing;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent());
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (standingState.Standing)
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.Standing = true;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent(), false);
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
{
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer,
|
||||
fixtureComponent);
|
||||
}
|
||||
}
|
||||
standingState.ChangedFixtures.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DropHandItemsEvent : EventArgs
|
||||
{
|
||||
}
|
||||
standingState.ChangedFixtures.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a down attempt.
|
||||
/// </summary>
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a stand attempt.
|
||||
/// </summary>
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity becomes standing
|
||||
/// </summary>
|
||||
public sealed class StoodEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is not standing
|
||||
/// </summary>
|
||||
public sealed class DownedEvent : EntityEventArgs
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DropHandItemsEvent : EventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a down attempt.
|
||||
/// </summary>
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a stand attempt.
|
||||
/// </summary>
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity becomes standing
|
||||
/// </summary>
|
||||
public sealed class StoodEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is not standing
|
||||
/// </summary>
|
||||
public sealed class DownedEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class StandingUpDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
namespace Content.Shared.Weapons.Melee.Components;
|
||||
@@ -8,6 +9,7 @@ namespace Content.Shared.Weapons.Melee.Components;
|
||||
/// Plays the specified sound upon receiving damage of the specified type.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent, AutoGenerateComponentState] // WD
|
||||
public sealed partial class MeleeSoundComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -16,6 +18,7 @@ public sealed partial class MeleeSoundComponent : Component
|
||||
/// </summary>
|
||||
[DataField("soundGroups",
|
||||
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageGroupPrototype>))]
|
||||
[AutoNetworkedField] // WD
|
||||
public Dictionary<string, SoundSpecifier>? SoundGroups;
|
||||
|
||||
/// <summary>
|
||||
@@ -24,10 +27,12 @@ public sealed partial class MeleeSoundComponent : Component
|
||||
/// </summary>
|
||||
[DataField("soundTypes",
|
||||
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageTypePrototype>))]
|
||||
[AutoNetworkedField] // WD
|
||||
public Dictionary<string, SoundSpecifier>? SoundTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Sound that plays if no damage is done.
|
||||
/// </summary>
|
||||
[AutoNetworkedField] // WD
|
||||
[DataField("noDamageSound")] public SoundSpecifier? NoDamageSound;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Content.Shared._White;
|
||||
using Content.Shared._White.MagGloves;
|
||||
using Content.Shared._White.Chaplain;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
@@ -229,6 +229,9 @@ public sealed partial class GunComponent : Component
|
||||
public bool ClumsyProof = false;
|
||||
|
||||
// WD START
|
||||
|
||||
public EntityUid? Target;
|
||||
|
||||
[DataField("forceThrowingAngle")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ForceThrowingAngle;
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Content.Shared.Weapons.Ranged.Events;
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RequestShootEvent : EntityEventArgs
|
||||
{
|
||||
public NetEntity? Target;
|
||||
public NetEntity Gun;
|
||||
public NetCoordinates Coordinates;
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
return;
|
||||
|
||||
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
||||
gun.Target = GetEntity(msg.Target);
|
||||
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
||||
AttemptShoot(user.Value, ent, gun);
|
||||
}
|
||||
@@ -558,6 +559,12 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
[ByRefEvent]
|
||||
public record struct AttemptShootEvent(EntityUid User, string? Message, bool Cancelled = false, bool ThrowItems = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a hitscan
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct HitscanHitAttemptEvent(EntityUid HitEntity, EntityUid? Target, bool Cancelled = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on the gun after firing.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
@@ -31,6 +32,9 @@ namespace Content.Shared.Whitelist
|
||||
[NonSerialized]
|
||||
private List<ComponentRegistration>? _registrations = null;
|
||||
|
||||
[DataField(customTypeSerializer:typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||
public List<string>? Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Tags that are allowed in the whitelist.
|
||||
/// </summary>
|
||||
|
||||
@@ -52,6 +52,6 @@ public sealed class BackstabSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var message = Loc.GetString("backstab-damage-betrayal-dagger", ("damage", damage));
|
||||
_popup.PopupClient(message, args.User, args.User, PopupType.MediumCaution);
|
||||
_popup.PopupEntity(message, args.User, args.User, PopupType.MediumCaution);
|
||||
}
|
||||
}
|
||||
|
||||
48
Content.Shared/_White/Body/BodyRejuvenateSystem.cs
Normal file
48
Content.Shared/_White/Body/BodyRejuvenateSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._White.Body;
|
||||
|
||||
public sealed class BodyRejuvenateSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<BodyComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
}
|
||||
|
||||
private void OnRejuvenate(Entity<BodyComponent> ent, ref RejuvenateEvent args)
|
||||
{
|
||||
if (ent.Comp.Prototype == null)
|
||||
return;
|
||||
|
||||
var prototype = _prototypeManager.Index(ent.Comp.Prototype.Value);
|
||||
var protoSlots = prototype.Slots.Values.ToList();
|
||||
|
||||
foreach (var (id, component) in _body.GetBodyChildren(ent.Owner, ent.Comp))
|
||||
{
|
||||
foreach (var organSlot in component.Organs.Values)
|
||||
{
|
||||
if (!_container.TryGetContainer(id, SharedBodySystem.GetOrganContainerId(organSlot.Id),
|
||||
out var container))
|
||||
continue;
|
||||
|
||||
if (container.Count > 0)
|
||||
continue;
|
||||
|
||||
var organ = protoSlots.Where(x => x.Organs.ContainsKey(organSlot.Id))
|
||||
.Select(x => x.Organs[organSlot.Id]).FirstOrDefault();
|
||||
|
||||
TrySpawnInContainer(organ, id, SharedBodySystem.GetOrganContainerId(organSlot.Id), out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,23 @@ public sealed partial class CultSummonCombatEquipmentTargetActionEvent : EntityT
|
||||
{
|
||||
}
|
||||
|
||||
public sealed partial class CultConcealPresenceWorldActionEvent : WorldTargetActionEvent
|
||||
[Virtual]
|
||||
public partial class CultConcealPresenceInstantActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed partial class CultConcealInstantActionEvent : CultConcealPresenceInstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed partial class CultRevealInstantActionEvent : CultConcealPresenceInstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed partial class CultBloodRitesInstantActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed partial class CultBloodSpearRecallInstantActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
17
Content.Shared/_White/Cult/Components/BloodSpearComponent.cs
Normal file
17
Content.Shared/_White/Cult/Components/BloodSpearComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BloodSpearComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public Entity<CultistComponent>? User;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId Action;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ShatterSound = new SoundCollectionSpecifier("GlassBreak");
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BoltBarrageComponent : Component
|
||||
{
|
||||
public bool Unremoveable = true;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ConcealPresenceSpellComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public bool Revealing;
|
||||
|
||||
[DataField(required: true), NonSerialized]
|
||||
public InstantActionEvent? ConcealEvent;
|
||||
|
||||
[DataField(required: true), NonSerialized]
|
||||
public InstantActionEvent? RevealEvent;
|
||||
|
||||
[DataField]
|
||||
public SpriteSpecifier? ConcealIcon;
|
||||
|
||||
[DataField]
|
||||
public SpriteSpecifier? RevealIcon;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class ConcealableComponent : Component
|
||||
{
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
public bool Concealed;
|
||||
|
||||
[DataField]
|
||||
public bool ExaminableWhileConcealed;
|
||||
|
||||
[DataField]
|
||||
public bool IconSmooth;
|
||||
|
||||
[DataField]
|
||||
public bool InteractionOutline;
|
||||
|
||||
[DataField]
|
||||
public ResPath? ConcealedSprite;
|
||||
|
||||
[DataField]
|
||||
public ResPath? RevealedSprite;
|
||||
|
||||
[DataField]
|
||||
public bool ChangeMeta;
|
||||
|
||||
[DataField]
|
||||
public string ConcealedName = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public string ConcealedDesc = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public string RevealedName = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public string RevealedDesc = string.Empty;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ConcealableAppearance
|
||||
{
|
||||
Concealed
|
||||
}
|
||||
@@ -9,8 +9,8 @@ public sealed partial class CultEmpowerComponent : Component
|
||||
[DataField("isRune")]
|
||||
public bool IsRune;
|
||||
|
||||
public int MaxAllowedCultistActions = 4;
|
||||
public int MinRequiredCultistActions = 1;
|
||||
public int MaxAllowedCultistActions = 5;
|
||||
public int MinRequiredCultistActions = 2;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Threading;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
@@ -8,7 +11,7 @@ namespace Content.Shared._White.Cult.Components;
|
||||
/// This is used for tagging a mob as a cultist.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class CultistComponent : Component
|
||||
public sealed partial class CultistComponent : ShowCultHudComponent
|
||||
{
|
||||
[DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))]
|
||||
public SoundSpecifier? CultistGreetSound = new SoundPathSpecifier("/Audio/CultSounds/fart.ogg");
|
||||
@@ -21,12 +24,17 @@ public sealed partial class CultistComponent : Component
|
||||
[AutoNetworkedField]
|
||||
public List<NetEntity?> SelectedEmpowers = new();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 RitesBloodAmount = FixedPoint2.Zero;
|
||||
|
||||
public static string SummonCultDaggerAction = "InstantActionSummonCultDagger";
|
||||
|
||||
public static string BloodRitesAction = "InstantActionBloodRites";
|
||||
|
||||
public static string EmpPulseAction = "InstantActionEmpPulse";
|
||||
|
||||
public static string ConcealPresenceAction = "InstantActionConcealPresence";
|
||||
|
||||
public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction";
|
||||
|
||||
public static string CultTeleportAction = "ActionCultTeleport";
|
||||
@@ -40,6 +48,22 @@ public sealed partial class CultistComponent : Component
|
||||
public static List<string> CultistActions = new()
|
||||
{
|
||||
SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction,
|
||||
CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, CultShadowShacklesAction
|
||||
CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction
|
||||
};
|
||||
|
||||
[DataField("bloodRites", customTypeSerializer: typeof(PrototypeIdListSerializer<CultistFactoryProductionPrototype>))]
|
||||
public List<string> BloodRites = new ()
|
||||
{
|
||||
"FactoryCultBloodSpear",
|
||||
"FactoryCultBloodBarrage"
|
||||
};
|
||||
|
||||
[ViewVariables, NonSerialized]
|
||||
public Entity<BloodSpearComponent>? BloodSpear;
|
||||
|
||||
[ViewVariables, NonSerialized]
|
||||
public EntityUid? BloodSpearActionEntity;
|
||||
|
||||
[ViewVariables, NonSerialized]
|
||||
public Entity<MindComponent>? OriginalMind;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Cult.Components;
|
||||
|
||||
[Virtual, RegisterComponent, NetworkedComponent]
|
||||
public partial class ShowCultHudComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public sealed class CultistFactoryProductionPrototype : IPrototype
|
||||
|
||||
[DataField("name", required: true)]
|
||||
public string Name = default!;
|
||||
|
||||
[DataField]
|
||||
public int BloodCost;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
80
Content.Shared/_White/Cult/Systems/BloodSpearSystem.cs
Normal file
80
Content.Shared/_White/Cult/Systems/BloodSpearSystem.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared._White.Cult.Systems;
|
||||
|
||||
public sealed class BloodSpearSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BloodSpearComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<BloodSpearComponent, GotEquippedHandEvent>(OnEquip);
|
||||
SubscribeLocalEvent<BloodSpearComponent, ThrowDoHitEvent>(OnThrowDoHit);
|
||||
}
|
||||
|
||||
private void OnThrowDoHit(Entity<BloodSpearComponent> ent, ref ThrowDoHitEvent args)
|
||||
{
|
||||
if (!TryComp(args.Target, out StatusEffectsComponent? status))
|
||||
return;
|
||||
|
||||
if(!_stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(6), true, status))
|
||||
return;
|
||||
|
||||
if (_net.IsClient)
|
||||
return;
|
||||
|
||||
_audio.PlayPvs(ent.Comp.ShatterSound, Transform(ent).Coordinates);
|
||||
QueueDel(ent);
|
||||
}
|
||||
|
||||
private void OnEquip(Entity<BloodSpearComponent> ent, ref GotEquippedHandEvent args)
|
||||
{
|
||||
if (!TryComp(args.User, out CultistComponent? cultist))
|
||||
return;
|
||||
|
||||
Entity<CultistComponent> user = (args.User, cultist);
|
||||
|
||||
if (cultist.BloodSpear == ent && ent.Comp.User == user)
|
||||
return;
|
||||
|
||||
if (ent.Comp.User != null)
|
||||
DetachSpearFromUser(ent.Comp.User.Value);
|
||||
DetachSpearFromUser(user);
|
||||
AttachSpearToUser(user, ent);
|
||||
}
|
||||
|
||||
public void DetachSpearFromUser(Entity<CultistComponent> user)
|
||||
{
|
||||
_actionsSystem.RemoveAction(user, user.Comp.BloodSpearActionEntity);
|
||||
user.Comp.BloodSpearActionEntity = null;
|
||||
if (user.Comp.BloodSpear != null)
|
||||
user.Comp.BloodSpear.Value.Comp.User = null;
|
||||
user.Comp.BloodSpear = null;
|
||||
}
|
||||
|
||||
public void AttachSpearToUser(Entity<CultistComponent> user, Entity<BloodSpearComponent> spear)
|
||||
{
|
||||
_actionsSystem.AddAction(user, ref user.Comp.BloodSpearActionEntity, spear.Comp.Action);
|
||||
user.Comp.BloodSpear = spear;
|
||||
spear.Comp.User = user;
|
||||
}
|
||||
|
||||
private void OnRemove(Entity<BloodSpearComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
if (ent.Comp.User != null)
|
||||
DetachSpearFromUser(ent.Comp.User.Value);
|
||||
}
|
||||
}
|
||||
91
Content.Shared/_White/Cult/Systems/BoltBarrageSystem.cs
Normal file
91
Content.Shared/_White/Cult/Systems/BoltBarrageSystem.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._White.Cult.Systems;
|
||||
|
||||
public sealed class BoltBarrageSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BoltBarrageComponent, AttemptShootEvent>(OnShootAttempt);
|
||||
SubscribeLocalEvent<BoltBarrageComponent, GunShotEvent>(OnGunShot);
|
||||
SubscribeLocalEvent<BoltBarrageComponent, DroppedEvent>(OnDrop);
|
||||
SubscribeLocalEvent<BoltBarrageComponent, UnequippedHandEvent>(OnUnequipHand);
|
||||
SubscribeLocalEvent<BoltBarrageComponent, ContainerGettingRemovedAttemptEvent>(OnRemoveAttempt);
|
||||
SubscribeLocalEvent<BoltBarrageComponent, OnEmptyGunShotEvent>(OnEmptyShot);
|
||||
}
|
||||
|
||||
private void OnUnequipHand(Entity<BoltBarrageComponent> ent, ref UnequippedHandEvent args)
|
||||
{
|
||||
if (_net.IsServer && ent.Comp.Unremoveable)
|
||||
QueueDel(ent);
|
||||
}
|
||||
|
||||
private void OnRemoveAttempt(Entity<BoltBarrageComponent> ent, ref ContainerGettingRemovedAttemptEvent args)
|
||||
{
|
||||
if (!_timing.ApplyingState && ent.Comp.Unremoveable)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnEmptyShot(Entity<BoltBarrageComponent> ent, ref OnEmptyGunShotEvent args)
|
||||
{
|
||||
if (_net.IsServer)
|
||||
QueueDel(ent);
|
||||
}
|
||||
|
||||
private void OnDrop(Entity<BoltBarrageComponent> ent, ref DroppedEvent args)
|
||||
{
|
||||
if (_net.IsServer)
|
||||
QueueDel(ent);
|
||||
}
|
||||
|
||||
private void OnGunShot(Entity<BoltBarrageComponent> ent, ref GunShotEvent args)
|
||||
{
|
||||
if (!TryComp(args.User, out HandsComponent? hands))
|
||||
return;
|
||||
|
||||
foreach (var hand in _hands.EnumerateHands(args.User, hands))
|
||||
{
|
||||
if (!hand.IsEmpty)
|
||||
continue;
|
||||
|
||||
ent.Comp.Unremoveable = false;
|
||||
_hands.SetActiveHand(args.User, hand, hands);
|
||||
_hands.TryPickup(args.User, ent, hand, false, false, hands);
|
||||
ent.Comp.Unremoveable = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShootAttempt(Entity<BoltBarrageComponent> ent, ref AttemptShootEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.User) && !HasComp<GhostComponent>(args.User))
|
||||
{
|
||||
args.Cancelled = true;
|
||||
args.Message = Loc.GetString("bolt-barrage-component-not-cultist");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hands.EnumerateHands(args.User).Any(hand => hand.IsEmpty))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
args.Message = Loc.GetString("bolt-barrage-component-no-empty-hand");
|
||||
}
|
||||
}
|
||||
38
Content.Shared/_White/Cult/Systems/ConcealableSystem.cs
Normal file
38
Content.Shared/_White/Cult/Systems/ConcealableSystem.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Content.Shared._White.Cult.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction.Events;
|
||||
|
||||
namespace Content.Shared._White.Cult.Systems;
|
||||
|
||||
public sealed class ConcealableSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ConcealableComponent, ExamineAttemptEvent>(OnExamine);
|
||||
SubscribeLocalEvent<ConcealableComponent, InteractionAttemptEvent>(OnInteract);
|
||||
}
|
||||
|
||||
private void OnInteract(Entity<ConcealableComponent> ent, ref InteractionAttemptEvent args)
|
||||
{
|
||||
if (ent.Comp is {Concealed: true, ExaminableWhileConcealed: false})
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<ConcealableComponent> ent, ref ExamineAttemptEvent args)
|
||||
{
|
||||
if (ent.Comp is {Concealed: true, ExaminableWhileConcealed: false})
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ConcealEvent : EntityEventArgs
|
||||
{
|
||||
public bool Conceal;
|
||||
|
||||
public ConcealEvent(bool conceal)
|
||||
{
|
||||
Conceal = conceal;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,15 @@ public sealed class CultItemSystem : EntitySystem
|
||||
_popupSystem.PopupClient(Loc.GetString("cult-item-component-pickup-fail", ("name", Name(uid))), uid, args.User);
|
||||
}
|
||||
|
||||
public bool CanThrow(EntityUid player, EntityUid throwEnt)
|
||||
{
|
||||
if (!HasComp<CultItemComponent>(throwEnt) || CanUse(player))
|
||||
return true;
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("cult-item-component-throw-fail"), player, player);
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CanUse(EntityUid? uid)
|
||||
{
|
||||
return HasComp<CultistComponent>(uid) || HasComp<GhostComponent>(uid);
|
||||
|
||||
15
Content.Shared/_White/Cult/UI/ApocalypseRuneDrawMessage.cs
Normal file
15
Content.Shared/_White/Cult/UI/ApocalypseRuneDrawMessage.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._White.Cult.UI;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ApocalypseRuneDrawMessage : EuiMessageBase
|
||||
{
|
||||
public readonly bool Accepted;
|
||||
|
||||
public ApocalypseRuneDrawMessage(bool accepted)
|
||||
{
|
||||
Accepted = accepted;
|
||||
}
|
||||
}
|
||||
9
Content.Shared/_White/Cult/UI/BloodRitesUi.cs
Normal file
9
Content.Shared/_White/Cult/UI/BloodRitesUi.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._White.Cult.UI;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BloodRitesUi
|
||||
{
|
||||
Key
|
||||
}
|
||||
@@ -12,10 +12,10 @@ public enum ListViewSelectorUiKey
|
||||
[Serializable, NetSerializable]
|
||||
public class ListViewBUIState : BoundUserInterfaceState
|
||||
{
|
||||
public List<string> Items { get; set; }
|
||||
public List<EntProtoId> Items { get; set; }
|
||||
public bool IsUsingPrototypes { get; set; }
|
||||
|
||||
public ListViewBUIState(List<string> items, bool isUsingPrototypes)
|
||||
public ListViewBUIState(List<EntProtoId> items, bool isUsingPrototypes)
|
||||
{
|
||||
Items = items;
|
||||
IsUsingPrototypes = isUsingPrototypes;
|
||||
|
||||
10
Content.Shared/_White/MagGloves/KeepItemsOnFallComponent.cs
Normal file
10
Content.Shared/_White/MagGloves/KeepItemsOnFallComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to prevent entity loose it's items on fall.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class KeepItemsOnFallComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This is used as a marker for advanced magnetic gloves.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentState]
|
||||
public sealed partial class MagneticGlovesAdvancedComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? ToggleActionEntity;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId ToggleAction = "ActionToggleMagneticGlovesAdvanced";
|
||||
}
|
||||
31
Content.Shared/_White/MagGloves/MagneticGlovesComponent.cs
Normal file
31
Content.Shared/_White/MagGloves/MagneticGlovesComponent.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for...
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentState]
|
||||
public sealed partial class MagneticGlovesComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? ToggleActionEntity;
|
||||
|
||||
[DataField("action")]
|
||||
public EntProtoId ToggleAction = "ActionToggleMagneticGloves";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public TimeSpan GlovesReadyAt = TimeSpan.Zero;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public TimeSpan GlovesLastActivation = TimeSpan.Zero;
|
||||
|
||||
[DataField("glovesCooldown")]
|
||||
public TimeSpan GlovesCooldown = TimeSpan.FromSeconds(60);
|
||||
|
||||
[DataField("glovesActiveTime")]
|
||||
public TimeSpan GlovesActiveTime = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
10
Content.Shared/_White/MagGloves/PreventDisarmComponent.cs
Normal file
10
Content.Shared/_White/MagGloves/PreventDisarmComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to prevent disarming when magnetic gloves are enabled.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PreventDisarmComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to block stripping when magnetic gloves are enabled.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PreventStrippingFromHandsAndGlovesComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
107
Content.Shared/_White/MagGloves/SharedMagneticGlovesSystem.cs
Normal file
107
Content.Shared/_White/MagGloves/SharedMagneticGlovesSystem.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._White.MagGloves;
|
||||
|
||||
/// <summary>
|
||||
/// This handles...
|
||||
/// </summary>
|
||||
public sealed class SharedMagneticGlovesSystem : EntitySystem
|
||||
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _sharedActions = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _sharedContainer = default!;
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<MagneticGlovesComponent, GetItemActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<MagneticGlovesComponent, ToggleMagneticGlovesEvent>(OnToggleGloves);
|
||||
}
|
||||
|
||||
public void OnGetActions(EntityUid uid, MagneticGlovesComponent component, GetItemActionsEvent args)
|
||||
{
|
||||
if (!args.InHands)
|
||||
{
|
||||
args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OnToggleGloves(EntityUid uid, MagneticGlovesComponent component, ToggleMagneticGlovesEvent args)
|
||||
{
|
||||
if (args.Handled || _net.IsClient)
|
||||
return;
|
||||
|
||||
_sharedContainer.TryGetContainingContainer(uid, out var container);
|
||||
|
||||
if (!component.Enabled)
|
||||
{
|
||||
if (component.GlovesReadyAt > _gameTiming.CurTime)
|
||||
{
|
||||
if (container != null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("maggloves-not-ready"), uid, container.Owner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If the gloves are not enabled, we want to activate them.
|
||||
component.Enabled = true;
|
||||
component.GlovesLastActivation = _gameTiming.CurTime;
|
||||
_sharedActions.SetToggled(component.ToggleActionEntity, component.Enabled);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("maggloves-activated"), uid, container.Owner);
|
||||
EnsureComp<KeepItemsOnFallComponent>(container.Owner);
|
||||
if (TryComp<MagneticGlovesAdvancedComponent>(uid, out var adv))
|
||||
{
|
||||
EnsureComp<PreventDisarmComponent>(container.Owner);
|
||||
EnsureComp<PreventStrippingFromHandsAndGlovesComponent>(container.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Enabled = false;
|
||||
_sharedActions.SetToggled(component.ToggleActionEntity, component.Enabled);
|
||||
component.GlovesReadyAt = _gameTiming.CurTime + component.GlovesCooldown;
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("maggloves-deactivated"), uid, container.Owner);
|
||||
RemComp<KeepItemsOnFallComponent>(container.Owner);
|
||||
if (TryComp<MagneticGlovesAdvancedComponent>(uid, out var adv))
|
||||
{
|
||||
RemComp<PreventDisarmComponent>(container.Owner);
|
||||
RemComp<PreventStrippingFromHandsAndGlovesComponent>(container.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance) &&
|
||||
TryComp<ItemComponent>(uid, out var item))
|
||||
{
|
||||
_item.SetHeldPrefix(uid, component.Enabled ? "on" : "off", false, item);
|
||||
_appearance.SetData(uid, ToggleVisuals.Toggled, component.Enabled, appearance);
|
||||
_clothing.SetEquippedPrefix(uid, component.Enabled ? "on" : null);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class ToggleMagneticGlovesEvent : InstantActionEvent {}
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Content.Shared._White.ShitSilo;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class BluespaceSiloComponent : Component
|
||||
public sealed partial class BluespaceStorageComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public sealed class WhiteCVars
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<string> RulesWiki =
|
||||
CVarDef.Create("white.wiki_rules", "https://wiki.ss14.su/%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0",
|
||||
CVarDef.Create("white.wiki_rules", "https://js.ss14.su/rules",
|
||||
CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/*
|
||||
@@ -197,7 +197,7 @@ public sealed class WhiteCVars
|
||||
CVarDef.Create("white.chat_size_separated", "0.6;0", CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
public static readonly CVarDef<string> DefaultChatSize =
|
||||
CVarDef.Create("white.chat_size_default", "300;500", CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
CVarDef.Create("white.chat_size_default", "300;-500", CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/*
|
||||
* OnlyInOhio
|
||||
|
||||
Reference in New Issue
Block a user