diff --git a/Content.Server/Backmen/Soul/GolemSystem.cs b/Content.Server/Backmen/Soul/GolemSystem.cs index fe0afd89278..616e4a25c8d 100644 --- a/Content.Server/Backmen/Soul/GolemSystem.cs +++ b/Content.Server/Backmen/Soul/GolemSystem.cs @@ -21,6 +21,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Server.Backmen.Soul; @@ -49,8 +50,8 @@ public override void Initialize() SubscribeLocalEvent(OnRemMind); SubscribeLocalEvent(OnSoulInit); - SubscribeLocalEvent(OnEntInserted); - SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnEntInserted); + SubscribeLocalEvent(OnEntRemoved); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDispelled); @@ -98,17 +99,17 @@ private void OnGetLaws(EntityUid uid, GolemComponent component, ref GetSiliconLa args.Handled = true; } - private void OnEntInserted(EntityUid uid, SoulCrystalComponent component, EntInsertedIntoContainerMessage args) + private void OnEntInserted(EntityUid uid, SoulCrystalComponent component, EntGotInsertedIntoContainerMessage args) { - if(!TryComp(args.Entity, out var golemComponent)) + if(args.Container.ID != CrystalSlot) return; RemCompDeferred(uid); } - private void OnEntRemoved(EntityUid uid, SoulCrystalComponent component, EntRemovedFromContainerMessage args) + private void OnEntRemoved(EntityUid uid, SoulCrystalComponent component, EntGotRemovedFromContainerMessage args) { - if(!TryComp(args.Entity, out var golemComponent)) + if(args.Container.ID != CrystalSlot) return; EnsureComp(uid); @@ -146,30 +147,35 @@ private void OnAfterInteract(EntityUid uid, SoulCrystalComponent component, Afte _userInterfaceSystem.SetUiState(args.Target.Value, GolemUiKey.Key, state); } - private void OnDispelled(EntityUid uid, GolemComponent component, DispelledEvent args) + public bool EjectSoul(Entity ent) { - _slotsSystem.SetLock(uid, CrystalSlot, false); - _slotsSystem.TryEject(uid, CrystalSlot, null, out var item); - _slotsSystem.SetLock(uid, CrystalSlot, true); - - if (item == null) - return; + _slotsSystem.SetLock(ent, CrystalSlot, false); + _slotsSystem.TryEject(ent, CrystalSlot, null, out var item); + _slotsSystem.SetLock(ent, CrystalSlot, true); - args.Handled = true; + if (item is not {Valid: true} soul) + return false; var direction = new Vector2(_robustRandom.Next(-30, 30), _robustRandom.Next(-30, 30)); - _throwing.TryThrow(item.Value, direction, _robustRandom.Next(1, 10)); + _throwing.TryThrow(soul, direction, _robustRandom.Next(1, 10)); - if (TryComp(uid, out var appearance)) - _appearance.SetData(uid, ToggleVisuals.Toggled, false, appearance); + if (TryComp(ent, out var appearance)) + _appearance.SetData(ent, ToggleVisuals.Toggled, false, appearance); - if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) - return; + _metaDataSystem.SetEntityName(ent, Loc.GetString("golem-base-name")); + _metaDataSystem.SetEntityDescription(ent, Loc.GetString("golem-base-desc")); + DirtyEntity(ent); - _metaDataSystem.SetEntityName(uid, Loc.GetString("golem-base-name")); - _metaDataSystem.SetEntityDescription(uid, Loc.GetString("golem-base-desc")); - _mindSystem.TransferTo(mindId, item); - DirtyEntity(uid); + if (!_mindSystem.TryGetMind(ent, out var mindId, out var mind)) + return true; + _mindSystem.TransferTo(mindId, soul); + + return true; + } + + private void OnDispelled(EntityUid uid, GolemComponent component, DispelledEvent args) + { + args.Handled = EjectSoul((uid, component)); } [ValidatePrototypeId] @@ -181,8 +187,7 @@ private void OnMobStateChanged(EntityUid uid, GolemComponent component, MobState return; QueueDel(uid); - var ev = new DispelledEvent(); - RaiseLocalEvent(uid, ev, false); + EjectSoul((uid, component)); Spawn(Ash, Transform(uid).Coordinates); _audioSystem.PlayPvs(component.DeathSound, uid); diff --git a/Content.Shared/Backmen/Standing/SharedLayingDownSystem.cs b/Content.Shared/Backmen/Standing/SharedLayingDownSystem.cs index 6bd34379ccf..e27157c57b5 100644 --- a/Content.Shared/Backmen/Standing/SharedLayingDownSystem.cs +++ b/Content.Shared/Backmen/Standing/SharedLayingDownSystem.cs @@ -1,4 +1,6 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Backmen.CCVar; +using Content.Shared.Backmen.Targeting; using Content.Shared.Body.Components; using Content.Shared.Buckle; using Content.Shared.Buckle.Components; @@ -12,17 +14,22 @@ using Content.Shared.Movement.Components; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Movement.Systems; +using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.Standing; using Content.Shared.Stunnable; using Content.Shared.Tag; using Content.Shared.Traits.Assorted; using Content.Shared.UserInterface; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; using Robust.Shared.Map.Components; using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Serialization; @@ -44,6 +51,7 @@ public abstract class SharedLayingDownSystem : EntitySystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly IConfigurationManager _config = default!; @@ -161,6 +169,9 @@ public void TryProcessAutoGetUp(Entity ent) if(_pulling.IsPulled(ent)) return; + if(!IsSafeStanUp(ent, out _)) + return; + var autoUp = !_playerManager.TryGetSessionByEntity(ent, out var player) || GetAutoGetUp(ent, session: player); @@ -189,7 +200,7 @@ private void ToggleStanding(ICommonSession? session) } else { - RaiseNetworkEvent(new ChangeLayingDownEvent()); + RaisePredictiveEvent(new ChangeLayingDownEvent()); } } @@ -224,7 +235,7 @@ private void OnChangeState(ChangeLayingDownEvent ev, EntitySessionEventArgs args return; //RaiseNetworkEvent(new CheckAutoGetUpEvent(GetNetEntity(uid))); - AutoGetUp((uid,layingDown)); + TryProcessAutoGetUp((uid,layingDown)); if (_standing.IsDown(uid, standing)) TryStandUp(uid, layingDown, standing); @@ -235,9 +246,11 @@ private void OnChangeState(ChangeLayingDownEvent ev, EntitySessionEventArgs args private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component, StandingUpDoAfterEvent args) { if (args.Handled || args.Cancelled || HasComp(uid) || - _mobState.IsIncapacitated(uid) || !_standing.Stand(uid)) + _mobState.IsIncapacitated(uid) || !IsSafeStanUp(uid, out _) || !_standing.Stand(uid)) { component.CurrentState = StandingState.Lying; + Dirty(uid,component); + return; } component.CurrentState = StandingState.Standing; @@ -265,6 +278,28 @@ private void OnParentChanged(EntityUid uid, LayingDownComponent component, EntPa _standing.Stand(uid, standingState); } + public bool IsSafeStanUp(EntityUid entity, [NotNullWhen(false)] out EntityUid? obj) + { + var xform = Transform(entity); + if (xform.GridUid != null) + { + foreach (var ent in _map.GetAnchoredEntities(xform.GridUid.Value, Comp(xform.GridUid.Value), xform.Coordinates)) + { + if (!_tag.HasTag(ent, "Structure") || !TryComp(ent, out var phys)) + continue; + + if(!phys.CanCollide|| (phys.CollisionMask & (int) CollisionGroup.MidImpassable) == 0x0) + continue; + + obj = ent; + return false; + } + } + obj = null; + return true; + } + + private static SoundSpecifier _bonkSound = new SoundCollectionSpecifier("TrayHit"); public bool TryStandUp(EntityUid uid, LayingDownComponent? layingDown = null, StandingStateComponent? standingState = null) { if (!Resolve(uid, ref standingState, false) || @@ -279,19 +314,18 @@ standingState.CurrentState is not StandingState.Lying || return false; } - var xform = Transform(uid); - if (xform.GridUid != null) + if (!IsSafeStanUp(uid, out var obj)) { - foreach (var obj in _map.GetAnchoredEntities(xform.GridUid.Value, Comp(xform.GridUid.Value), xform.Coordinates)) - { - if (!_tag.HasTag(obj, "Structure")) - continue; - - _popup.PopupEntity(Loc.GetString("laying-table-head-dmg",("obj", obj), ("self", uid)), obj, PopupType.MediumCaution); - _damageable.TryChangeDamage(uid, new DamageSpecifier(){DamageDict = {{"Blunt", 5}}}, ignoreResistances: true); - _stun.TryStun(uid, TimeSpan.FromSeconds(2), true); - return false; - } + _popup.PopupPredicted( + Loc.GetString("bonkable-success-message-user",("bonkable", obj.Value)), + Loc.GetString("bonkable-success-message-others",("bonkable", obj.Value), ("user", uid)), + obj.Value, + uid, + PopupType.MediumCaution); + _damageable.TryChangeDamage(uid, new DamageSpecifier(){DamageDict = {{"Blunt", 5}}}, ignoreResistances: true, targetPart: TargetBodyPart.Head); + _stun.TryStun(uid, TimeSpan.FromSeconds(2), true); + _audioSystem.PlayPredicted(_bonkSound, uid, obj.Value); + return false; } var args = new DoAfterArgs(EntityManager, uid, layingDown.StandingUpTime, new StandingUpDoAfterEvent(), uid) diff --git a/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs b/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs index cc3b3f6d5d9..1bbc2d8080b 100644 --- a/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs +++ b/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs @@ -1,6 +1,8 @@ using Content.Shared.Administration.Logs; +using Content.Shared.Backmen.Targeting; using Content.Shared.Damage.Components; using Content.Shared.Database; +using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Popups; @@ -58,8 +60,23 @@ private void OnHandInteract(Entity entity, ref Intera totalDamage = DamageSpecifier.ApplyModifierSet(totalDamage, protectiveEntity.Comp.DamageProtection); } } + // start-backmen: surgery - totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, origin: args.Target); + TargetBodyPart? targetPart = null; + var hands = CompOrNull(args.User); + if (hands is { ActiveHand: not null }) + { + targetPart = hands.ActiveHand.Location switch + { + HandLocation.Left => TargetBodyPart.LeftArm, + HandLocation.Right => TargetBodyPart.RightArm, + _ => null + }; + } + + // end-backmen: surgery + + totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, origin: args.Target, targetPart: targetPart); if (totalDamage != null && totalDamage.AnyPositive()) { diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index da93b8f203d..5723b27668d 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -26,6 +26,7 @@ public sealed class DamageableSystem : EntitySystem private EntityQuery _appearanceQuery; private EntityQuery _damageableQuery; private EntityQuery _mindContainerQuery; + private EntityQuery _targetingQuery; public override void Initialize() { @@ -38,6 +39,7 @@ public override void Initialize() _appearanceQuery = GetEntityQuery(); _damageableQuery = GetEntityQuery(); _mindContainerQuery = GetEntityQuery(); + _targetingQuery = GetEntityQuery(); } /// @@ -99,16 +101,21 @@ public void SetDamage(EntityUid uid, DamageableComponent damageable, DamageSpeci /// The damage changed event is used by other systems, such as damage thresholds. /// public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSpecifier? damageDelta = null, - bool interruptsDoAfters = true, EntityUid? origin = null, bool canSever = true, float partMultiplier = 1.00f) + bool interruptsDoAfters = true, EntityUid? origin = null, bool canSever = true, float partMultiplier = 1.00f, + TargetBodyPart? targetPart = null) { - TargetBodyPart? targetPart = null; + ; component.Damage.GetDamagePerGroup(_prototypeManager, component.DamagePerGroup); component.TotalDamage = component.Damage.GetTotal(); // If our target has a TargetingComponent, that means they will take limb damage // And if their attacker also has one, then we use that part. - if (TryComp(uid, out var target)) + if (_targetingQuery.TryComp(uid, out var target)) { - if (origin.HasValue && TryComp(origin.Value, out var targeter)) + if (targetPart != null) + { + // keep from args + } + else if (origin.HasValue && _targetingQuery.TryComp(origin.Value, out var targeter)) { targetPart = targeter.Target; } @@ -143,7 +150,7 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp /// public DamageSpecifier? TryChangeDamage(EntityUid? uid, DamageSpecifier damage, bool ignoreResistances = false, bool interruptsDoAfters = true, DamageableComponent? damageable = null, EntityUid? origin = null, - bool? canSever = true, float? partMultiplier = 1.00f) + bool? canSever = true, float? partMultiplier = 1.00f, TargetBodyPart? targetPart = null) { if (!uid.HasValue || !_damageableQuery.Resolve(uid.Value, ref damageable, false)) { @@ -156,7 +163,7 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp return damage; } - var before = new BeforeDamageChangedEvent(damage, origin); + var before = new BeforeDamageChangedEvent(damage, origin, targetPart); RaiseLocalEvent(uid.Value, ref before); if (before.Cancelled) @@ -172,7 +179,7 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp // use a local private field instead of creating a new dictionary here.. damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet); } - var ev = new DamageModifyEvent(damage, origin); + var ev = new DamageModifyEvent(damage, origin, targetPart); RaiseLocalEvent(uid.Value, ev); damage = ev.Damage; @@ -204,7 +211,7 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp } if (delta.DamageDict.Count > 0) - DamageChanged(uid.Value, damageable, delta, interruptsDoAfters, origin, canSever ?? true, partMultiplier ?? 1.00f); + DamageChanged(uid.Value, damageable, delta, interruptsDoAfters, origin, canSever ?? true, partMultiplier ?? 1.00f, targetPart); return delta; } @@ -322,7 +329,11 @@ private void DamageableHandleState(EntityUid uid, DamageableComponent component, /// Raised before damage is done, so stuff can cancel it if necessary. /// [ByRefEvent] - public record struct BeforeDamageChangedEvent(DamageSpecifier Damage, EntityUid? Origin = null, bool Cancelled = false); + public record struct BeforeDamageChangedEvent( + DamageSpecifier Damage, + EntityUid? Origin = null, + TargetBodyPart? targetPart = null, + bool Cancelled = false); /// /// Raised on an entity when damage is about to be dealt, @@ -339,12 +350,14 @@ public sealed class DamageModifyEvent : EntityEventArgs, IInventoryRelayEvent public readonly DamageSpecifier OriginalDamage; public DamageSpecifier Damage; public EntityUid? Origin; + public readonly TargetBodyPart? TargetPart; - public DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null) + public DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null, TargetBodyPart? targetPart = null) { OriginalDamage = damage; Damage = damage; Origin = origin; + TargetPart = targetPart; } } diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index 95abea63db0..bb83bd3caee 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -44,7 +44,7 @@ public override void Update(float frameTime) continue; } - foreach (var state in status.ActiveEffects) + foreach (var state in status.ActiveEffects.ToArray()) { if (curTime > state.Value.Cooldown.Item2) TryRemoveStatusEffect(uid, state.Key, status); diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index cc1c85f1d34..c2684ad47fe 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -143,7 +143,7 @@ private void OnKnockInit(EntityUid uid, KnockedDownComponent component, Componen // start-backmen: Laying System if (TryComp(uid, out var layingDownComponent)) { - _layingDown.AutoGetUp((uid, layingDownComponent)); + _layingDown.TryProcessAutoGetUp((uid, layingDownComponent)); _layingDown.TryLieDown(uid, layingDownComponent, null, DropHeldItemsBehavior.DropIfStanding); // Ataraxia EDIT } // end-backmen: Laying System diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 68f6979faaa..7f8cfdcebb7 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -174,6 +174,9 @@ save: false description: След жидкости components: + - type: Tag + tags: + - HideContextMenu - type: Clickable - type: FootstepModifier footstepSoundCollection: