2022-02-26 18:24:08 +13:00
using Content.Server.Actions.Events ;
using Content.Server.Administration.Logs ;
2022-06-17 01:37:07 -04:00
using Content.Server.CombatMode.Disarm ;
2022-05-08 15:54:13 +08:00
using Content.Server.Hands.Components ;
2022-02-26 18:24:08 +13:00
using Content.Server.Popups ;
using Content.Server.Weapon.Melee ;
using Content.Shared.ActionBlocker ;
using Content.Shared.Audio ;
using Content.Shared.CombatMode ;
2022-06-17 01:37:07 -04:00
using Content.Shared.Damage ;
2022-02-26 18:24:08 +13:00
using Content.Shared.Database ;
2022-07-10 18:36:53 -07:00
using Content.Shared.IdentityManagement ;
2022-06-17 01:37:07 -04:00
using Content.Shared.Stunnable ;
2019-09-26 22:32:32 +02:00
using JetBrains.Annotations ;
2022-02-26 18:24:08 +13:00
using Robust.Shared.Audio ;
using Robust.Shared.Player ;
using Robust.Shared.Random ;
2022-06-17 01:37:07 -04:00
using Robust.Shared.Physics ;
2019-06-30 00:01:41 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.CombatMode
2019-06-30 00:01:41 +02:00
{
2019-09-26 22:32:32 +02:00
[UsedImplicitly]
2020-03-25 11:16:57 +01:00
public sealed class CombatModeSystem : SharedCombatModeSystem
2019-06-30 00:01:41 +02:00
{
2022-02-26 18:24:08 +13:00
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default ! ;
[Dependency] private readonly MeleeWeaponSystem _meleeWeaponSystem = default ! ;
[Dependency] private readonly PopupSystem _popupSystem = default ! ;
2022-05-28 23:41:17 -07:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2022-02-26 18:24:08 +13:00
[Dependency] private readonly IRobustRandom _random = default ! ;
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < SharedCombatModeComponent , DisarmActionEvent > ( OnEntityActionPerform ) ;
}
private void OnEntityActionPerform ( EntityUid uid , SharedCombatModeComponent component , DisarmActionEvent args )
{
if ( args . Handled )
return ;
if ( ! _actionBlockerSystem . CanAttack ( args . Performer ) )
return ;
2022-06-17 01:37:07 -04:00
if ( TryComp < HandsComponent > ( args . Performer , out var hands )
& & hands . ActiveHand ! = null
& & ! hands . ActiveHand . IsEmpty )
{
_popupSystem . PopupEntity ( Loc . GetString ( "disarm-action-free-hand" ) , args . Performer , Filter . Entities ( args . Performer ) ) ;
return ;
}
2022-05-08 15:54:13 +08:00
EntityUid ? inTargetHand = null ;
2022-06-17 01:37:07 -04:00
if ( TryComp < HandsComponent > ( args . Target , out HandsComponent ? targetHandsComponent )
2022-05-08 15:54:13 +08:00
& & targetHandsComponent . ActiveHand ! = null
& & ! targetHandsComponent . ActiveHand . IsEmpty )
{
inTargetHand = targetHandsComponent . ActiveHand . HeldEntity ! . Value ;
}
var attemptEvent = new DisarmAttemptEvent ( args . Target , args . Performer , inTargetHand ) ;
if ( inTargetHand ! = null )
{
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( inTargetHand . Value , attemptEvent , true ) ;
2022-05-08 15:54:13 +08:00
}
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( args . Target , attemptEvent , true ) ;
2022-02-26 18:24:08 +13:00
if ( attemptEvent . Cancelled )
return ;
var diff = Transform ( args . Target ) . MapPosition . Position - Transform ( args . Performer ) . MapPosition . Position ;
var angle = Angle . FromWorldVec ( diff ) ;
var filterAll = Filter . Pvs ( args . Performer ) ;
var filterOther = filterAll . RemoveWhereAttachedEntity ( e = > e = = args . Performer ) ;
args . Handled = true ;
2022-06-17 01:37:07 -04:00
var chance = CalculateDisarmChance ( args . Performer , args . Target , inTargetHand , component ) ;
if ( _random . Prob ( chance ) )
2022-02-26 18:24:08 +13:00
{
2022-06-12 19:45:47 -04:00
SoundSystem . Play ( component . DisarmFailSound . GetSound ( ) , Filter . Pvs ( args . Performer ) , args . Performer , AudioHelpers . WithVariation ( 0.025f ) ) ;
2022-02-26 18:24:08 +13:00
var msgOther = Loc . GetString (
"disarm-action-popup-message-other-clients" ,
2022-07-10 18:36:53 -07:00
( "performerName" , Identity . Entity ( args . Performer , EntityManager ) ) ,
( "targetName" , Identity . Entity ( args . Target , EntityManager ) ) ) ;
2022-02-26 18:24:08 +13:00
2022-07-10 18:36:53 -07:00
var msgUser = Loc . GetString ( "disarm-action-popup-message-cursor" , ( "targetName" , Identity . Entity ( args . Target , EntityManager ) ) ) ;
2022-02-26 18:24:08 +13:00
_popupSystem . PopupEntity ( msgOther , args . Performer , filterOther ) ;
_popupSystem . PopupEntity ( msgUser , args . Performer , Filter . Entities ( args . Performer ) ) ;
_meleeWeaponSystem . SendLunge ( angle , args . Performer ) ;
return ;
}
_meleeWeaponSystem . SendAnimation ( "disarm" , angle , args . Performer , args . Performer , new [ ] { args . Target } ) ;
2022-06-12 19:45:47 -04:00
SoundSystem . Play ( component . DisarmSuccessSound . GetSound ( ) , filterAll , args . Performer , AudioHelpers . WithVariation ( 0.025f ) ) ;
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . DisarmedAction , $"{ToPrettyString(args.Performer):user} used disarm on {ToPrettyString(args.Target):target}" ) ;
2022-02-26 18:24:08 +13:00
2022-06-17 01:37:07 -04:00
var eventArgs = new DisarmedEvent ( ) { Target = args . Target , Source = args . Performer , PushProbability = chance } ;
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( args . Target , eventArgs , true ) ;
2022-02-26 18:24:08 +13:00
}
2022-06-17 01:37:07 -04:00
private float CalculateDisarmChance ( EntityUid disarmer , EntityUid disarmed , EntityUid ? inTargetHand , SharedCombatModeComponent disarmerComp )
{
float healthMod = 0 ;
if ( TryComp < DamageableComponent > ( disarmer , out var disarmerDamage ) & & TryComp < DamageableComponent > ( disarmed , out var disarmedDamage ) )
{
// I wanted this to consider their mob state thresholds too but I'm not touching that shitcode after having a go at this.
healthMod = ( ( ( float ) disarmedDamage . TotalDamage - ( float ) disarmerDamage . TotalDamage ) / 200 ) ; // Ex. You have 0 damage, they have 90, you get a 45% chance increase
}
float massMod = 0 ;
if ( TryComp < PhysicsComponent > ( disarmer , out var disarmerPhysics ) & & TryComp < PhysicsComponent > ( disarmed , out var disarmedPhysics ) )
{
if ( disarmerPhysics . FixturesMass ! = 0 ) // yeah this will never happen but let's not kill the server if it does
massMod = ( ( ( disarmedPhysics . FixturesMass / disarmerPhysics . FixturesMass - 1 ) / 2 ) ) ; // Ex, you weigh 120, they weigh 70, you get a 29% bonus
}
float chance = ( disarmerComp . BaseDisarmFailChance - healthMod - massMod ) ;
if ( HasComp < SlowedDownComponent > ( disarmer ) ) // might need to revisit this part after stamina damage, right now this is basically "pre-stun"
chance + = 0.35f ;
if ( HasComp < SlowedDownComponent > ( disarmed ) )
chance - = 0.35f ;
if ( inTargetHand ! = null & & TryComp < DisarmMalusComponent > ( inTargetHand , out var malus ) )
{
chance + = malus . Malus ;
}
return Math . Clamp ( chance , 0f , 1f ) ;
}
2019-06-30 00:01:41 +02:00
}
}