Merge remote-tracking branch 'upstream/stable' into ed-30-04-2025-upstream-sync

# Conflicts:
#	Content.Client/Parallax/ParallaxControl.cs
#	Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs
#	Content.IntegrationTests/Tests/PostMapInitTest.cs
#	Content.Server/Chat/Managers/ChatManager.cs
#	Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs
#	Content.Server/Labels/Label/LabelSystem.cs
#	Content.Shared/Actions/SharedActionsSystem.cs
#	Content.Shared/Fluids/Components/EvaporationComponent.cs
#	Content.Shared/Labels/EntitySystems/SharedLabelSystem.cs
#	README.md
#	Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
#	Resources/Prototypes/Maps/Pools/deathmatch.yml
#	Resources/Prototypes/Maps/arenas.yml
This commit is contained in:
Ed
2025-04-30 20:31:50 +03:00
2053 changed files with 113995 additions and 38376 deletions

View File

@@ -2,6 +2,8 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions.Events;
using Content.Shared.Administration.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
@@ -10,22 +12,30 @@ using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Physics;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using ItemToggleMeleeWeaponComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleMeleeWeaponComponent;
@@ -33,20 +43,24 @@ namespace Content.Shared.Weapons.Melee;
public abstract class SharedMeleeWeaponSystem : EntitySystem
{
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
[Dependency] protected readonly SharedCombatModeSystem CombatMode = default!;
[Dependency] protected readonly DamageableSystem Damageable = default!;
[Dependency] protected readonly SharedInteractionSystem Interaction = default!;
[Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly MeleeSoundSystem _meleeSound = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
[Dependency] protected readonly DamageableSystem Damageable = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly MeleeSoundSystem _meleeSound = default!;
[Dependency] protected readonly MobStateSystem MobState = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] protected readonly SharedCombatModeSystem CombatMode = default!;
[Dependency] protected readonly SharedInteractionSystem Interaction = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
@@ -788,7 +802,25 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return highestDamageType;
}
protected virtual bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
private float CalculateDisarmChance(EntityUid disarmer, EntityUid disarmed, EntityUid? inTargetHand, CombatModeComponent disarmerComp)
{
if (HasComp<DisarmProneComponent>(disarmer))
return 1.0f;
if (HasComp<DisarmProneComponent>(disarmed))
return 0.0f;
var chance = disarmerComp.BaseDisarmFailChance;
if (inTargetHand != null && TryComp<DisarmMalusComponent>(inTargetHand, out var malus))
{
chance += malus.Malus;
}
return Math.Clamp(chance, 0f, 1f);
}
private bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
{
var target = GetEntity(ev.Target);
@@ -798,8 +830,110 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return false;
}
// Play a sound to give instant feedback; same with playing the animations
_meleeSound.PlaySwingSound(user, meleeUid, component);
if (MobState.IsIncapacitated(target.Value))
{
return false;
}
if (!TryComp<CombatModeComponent>(user, out var combatMode) ||
combatMode.CanDisarm != true)
{
return false;
}
// Need hands or to be able to be shoved over.
if (!TryComp<HandsComponent>(target, out var targetHandsComponent))
{
if (!TryComp<StatusEffectsComponent>(target, out var status) ||
!status.AllowedEffects.Contains("KnockedDown"))
{
// Notify disarmable
if (HasComp<MobStateComponent>(target.Value))
PopupSystem.PopupClient(Loc.GetString("disarm-action-disarmable", ("targetName", target.Value)), target.Value);
return false;
}
}
if (!InRange(user, target.Value, component.Range, session))
{
return false;
}
EntityUid? inTargetHand = null;
if (targetHandsComponent?.ActiveHand is { IsEmpty: false })
{
inTargetHand = targetHandsComponent.ActiveHand.HeldEntity!.Value;
}
var attemptEvent = new DisarmAttemptEvent(target.Value, user, inTargetHand);
if (inTargetHand != null)
{
RaiseLocalEvent(inTargetHand.Value, ref attemptEvent);
}
RaiseLocalEvent(target.Value, ref attemptEvent);
if (attemptEvent.Cancelled)
return false;
var chance = CalculateDisarmChance(user, target.Value, inTargetHand, combatMode);
// At this point we diverge
if (_netMan.IsClient)
{
// Play a sound to give instant feedback; same with playing the animations
_meleeSound.PlaySwingSound(user, meleeUid, component);
return true;
}
if (_random.Prob(chance))
{
return false;
}
var eventArgs = new DisarmedEvent(target.Value, user, 1 - chance);
RaiseLocalEvent(target.Value, ref eventArgs);
// Nothing handled it so abort.
if (!eventArgs.Handled)
{
return false;
}
Interaction.DoContactInteraction(user, target);
AdminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(user):user} used disarm on {ToPrettyString(target):target}");
AdminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(user):user} used disarm on {ToPrettyString(target):target}");
_audio.PlayPvs(combatMode.DisarmSuccessSound, target.Value, AudioParams.Default.WithVariation(0.025f).WithVolume(5f));
var targetEnt = Identity.Entity(target.Value, EntityManager);
var userEnt = Identity.Entity(user, EntityManager);
var msgOther = Loc.GetString(
eventArgs.PopupPrefix + "popup-message-other-clients",
("performerName", userEnt),
("targetName", targetEnt));
var msgUser = Loc.GetString(eventArgs.PopupPrefix + "popup-message-cursor", ("targetName", targetEnt));
var filterOther = Filter.PvsExcept(user, entityManager: EntityManager);
PopupSystem.PopupEntity(msgOther, user, filterOther, true);
PopupSystem.PopupEntity(msgUser, target.Value, user);
if (eventArgs.IsStunned)
{
PopupSystem.PopupEntity(Loc.GetString("stunned-component-disarm-success-others", ("source", userEnt), ("target", targetEnt)), targetEnt, Filter.PvsExcept(user), true, PopupType.LargeCaution);
PopupSystem.PopupCursor(Loc.GetString("stunned-component-disarm-success", ("target", targetEnt)), user, PopupType.Large);
AdminLogger.Add(LogType.DisarmedKnockdown, LogImpact.Medium, $"{ToPrettyString(user):user} knocked down {ToPrettyString(target):target}");
}
return true;
}