Files
OldThink/Content.Server/_White/Other/CritSystem/CritSystem.cs

166 lines
6.4 KiB
C#
Raw Normal View History

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;
using Content.Shared._White.Blocking;
2023-07-16 14:32:54 +06:00
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
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;
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!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
2023-07-16 14:32:54 +06:00
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CritComponent, ExaminedEvent>(OnExamine);
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);
}
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" +
"Кровавый абсорб: При каждом успешном критическом ударе, кинжал извлекает кровь из цели, восстанавливая здоровье владельцу пропорционально количеству высосанной крови.[/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;
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");
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
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)
{
2024-04-12 23:08:17 +09:00
if (!TryComp(target, out BloodstreamComponent? bloodstream))
continue;
2023-07-16 14:32:54 +06: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;
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));
}
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;
2024-04-12 23:08:17 +09:00
args.BonusDamage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"),
damage - args.BaseDamage.GetTotal());
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;
}
}