2024-04-12 23:08:17 +09:00
using Content.Server.Body.Components ;
2023-07-16 14:32:54 +06:00
using Content.Server.Body.Systems ;
using Content.Server.Popups ;
2024-08-02 11:50:26 +00:00
using Content.Shared._White.Blocking ;
2023-07-16 14:32:54 +06:00
using Content.Shared.Damage ;
using Content.Shared.Damage.Prototypes ;
2023-07-16 16:22:12 +06:00
using Content.Shared.Examine ;
2024-04-12 23:08:17 +09:00
using Content.Shared.Movement.Systems ;
2023-07-16 14:32:54 +06:00
using Content.Shared.Popups ;
using Content.Shared.Weapons.Melee.Events ;
using Robust.Shared.Prototypes ;
using Robust.Shared.Random ;
2024-01-28 18:18:54 +07:00
namespace Content.Server._White.Other.CritSystem ;
2023-07-16 14:32:54 +06:00
public sealed class CritSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default ! ;
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
[Dependency] private readonly PopupSystem _popup = default ! ;
[Dependency] private readonly BloodstreamSystem _bloodstream = default ! ;
2023-07-16 16:22:12 +06:00
[Dependency] private readonly DamageableSystem _damageableSystem = default ! ;
2023-07-16 14:32:54 +06:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2023-09-04 16:34:38 +06:00
2023-07-16 16:22:12 +06:00
SubscribeLocalEvent < CritComponent , ExaminedEvent > ( OnExamine ) ;
2024-08-02 11:50:26 +00:00
SubscribeLocalEvent < CritComponent , MeleeHitEvent > ( HandleHit , before : new [ ] { typeof ( MeleeBlockSystem ) } ) ;
2024-04-12 23:08:17 +09:00
SubscribeLocalEvent < CritComponent , GetMeleeAttackRateEvent > ( GetMeleeAttackRate ) ;
SubscribeLocalEvent < BloodLustComponent , RefreshMovementSpeedModifiersEvent > ( OnRefreshMoveSpeed ) ;
}
private void OnRefreshMoveSpeed ( Entity < BloodLustComponent > ent , ref RefreshMovementSpeedModifiersEvent args )
{
var modifier = GetBloodLustModifier ( ent ) ;
args . ModifySpeed ( GetBloodLustMultiplier ( ent . Comp . WalkModifier , modifier ) ,
GetBloodLustMultiplier ( ent . Comp . SprintModifier , modifier ) ) ;
}
private void GetMeleeAttackRate ( Entity < CritComponent > ent , ref GetMeleeAttackRateEvent args )
{
if ( ! ent . Comp . IsBloodDagger )
return ;
if ( ! TryComp ( args . User , out BloodLustComponent ? bloodLust ) )
return ;
args . Multipliers * = GetBloodLustMultiplier ( bloodLust . AttackRateModifier , GetBloodLustModifier ( args . User ) ) ;
}
private float GetBloodLustModifier ( EntityUid uid )
{
if ( ! TryComp ( uid , out BloodstreamComponent ? bloodstream ) | | bloodstream . MaxBleedAmount = = 0f )
return 1f ;
return Math . Clamp ( bloodstream . BleedAmount / bloodstream . MaxBleedAmount , 0f , 1f ) ;
}
private float GetBloodLustMultiplier ( float multiplier , float modifier )
{
return float . Lerp ( 1f , multiplier , modifier ) ;
2023-07-16 16:22:12 +06:00
}
private void OnExamine ( EntityUid uid , CritComponent component , ExaminedEvent args )
{
if ( component . IsBloodDagger )
{
args . PushMarkup (
2024-04-12 23:08:17 +09:00
"[color=red]Критическая жажда: Кинжал Жажды обладает смертоносной точностью. Е г о владелец имеет 50% шанс нанести критический урон, поражая врага в е г о самые уязвимые места.\n" +
"При ударе по с е б е кинжал наделит пользователя временным усилением скорости атаки и передвижения ценой обильного кровотечения.\n" +
2023-07-16 16:22:12 +06:00
"Кровавый а б с о р б : При каждом успешном критическом ударе, кинжал извлекает кровь из цели, восстанавливая здоровье владельцу пропорционально количеству высосанной крови.[/color]"
) ;
}
2023-07-16 14:32:54 +06:00
}
private void HandleHit ( EntityUid uid , CritComponent component , MeleeHitEvent args )
{
2024-04-12 23:08:17 +09:00
if ( args . HitEntities . Count = = 0 )
return ;
if ( args . HitEntities [ 0 ] = = args . User )
2023-07-16 14:32:54 +06:00
{
2024-04-12 23:08:17 +09:00
if ( ! component . IsBloodDagger )
return ;
if ( ! TryComp ( args . User , out BloodstreamComponent ? bloodstream ) )
2023-07-16 14:32:54 +06:00
return ;
2024-04-12 23:08:17 +09:00
EnsureComp < BloodLustComponent > ( args . User ) ;
_bloodstream . TryModifyBleedAmount ( args . User , bloodstream . MaxBleedAmount , bloodstream ) ;
return ;
}
if ( ! IsCriticalHit ( component ) )
return ;
var ohio = 0 ;
2024-04-23 22:20:16 +09:00
var absorbed = 0 ;
2024-04-12 23:08:17 +09:00
if ( component . IsBloodDagger )
{
var bruteGroup = _prototypeManager . Index < DamageGroupPrototype > ( "Brute" ) ;
var burnGroup = _prototypeManager . Index < DamageGroupPrototype > ( "Burn" ) ;
2024-04-23 22:20:16 +09:00
var airlossGroup = _prototypeManager . Index < DamageGroupPrototype > ( "Airloss" ) ;
2023-07-16 14:32:54 +06:00
2024-04-12 23:08:17 +09:00
ohio = _random . Next ( 1 , 21 ) ;
2023-07-16 14:32:54 +06:00
2024-04-23 22:20:16 +09:00
if ( args . Direction ! = null ) // Heavy attack
ohio = ( int ) MathF . Round ( ohio * 0.7f ) ;
2024-04-12 23:08:17 +09:00
foreach ( var target in args . HitEntities )
2023-07-16 16:22:12 +06:00
{
2024-04-12 23:08:17 +09:00
if ( ! TryComp ( target , out BloodstreamComponent ? bloodstream ) )
continue ;
2023-07-16 14:32:54 +06:00
2024-04-23 22:20:16 +09:00
var blood = bloodstream . BloodSolution ;
if ( blood = = null )
continue ;
var bloodLevel = blood . Value . Comp . Solution . Volume . Int ( ) ;
2024-04-12 23:08:17 +09:00
if ( ! _bloodstream . TryModifyBloodLevel ( target , - ohio , bloodstream , false ) )
continue ;
2023-07-16 16:22:12 +06:00
2024-04-23 22:20:16 +09:00
var toHeal = Math . Min ( ohio , bloodLevel ) ;
absorbed + = toHeal ;
_bloodstream . TryModifyBloodLevel ( args . User , toHeal ) ;
_damageableSystem . TryChangeDamage ( args . User , new DamageSpecifier ( bruteGroup , - toHeal ) ) ;
_damageableSystem . TryChangeDamage ( args . User , new DamageSpecifier ( burnGroup , - toHeal ) ) ;
_damageableSystem . TryChangeDamage ( args . User , new DamageSpecifier ( airlossGroup , - toHeal ) ) ;
2023-07-16 16:22:12 +06:00
}
2024-04-12 23:08:17 +09:00
}
2023-07-16 14:32:54 +06:00
2024-04-12 23:08:17 +09:00
var damage = args . BaseDamage . GetTotal ( ) * component . CritMultiplier + ohio ;
2023-07-16 16:22:12 +06:00
2024-04-12 23:08:17 +09:00
args . BonusDamage = new DamageSpecifier ( _prototypeManager . Index < DamageTypePrototype > ( "Slash" ) ,
damage - args . BaseDamage . GetTotal ( ) ) ;
2024-04-23 22:20:16 +09:00
var extra = component . IsBloodDagger ? $" Высосано крови: {absorbed}" : "" ;
_popup . PopupEntity ( $"Crit! {damage}" + extra , args . User , args . User , PopupType . MediumCaution ) ;
2023-07-16 14:32:54 +06:00
}
private bool IsCriticalHit ( CritComponent component )
{
var roll = _random . Next ( 1 , 101 ) ;
var critChance = component . CritChance ;
component . WorkingChance ? ? = component . CritChance ;
var isCritical = roll < = component . WorkingChance ;
if ( isCritical )
component . WorkingChance = critChance ;
else
component . WorkingChance + + ;
return isCritical ;
}
}