Files
ss14-wega/Content.Shared/Execution/SharedExecutionSystem.cs
2026-01-07 04:16:10 +03:00

324 lines
12 KiB
C#

using Content.Shared.ActionBlocker;
using Content.Shared.Chat;
using Content.Shared.CombatMode;
using Content.Shared.Damage.Components;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components; // Corvax-Wega-Suicide
using Content.Shared.Weapons.Ranged.Systems; // Corvax-Wega-Suicide
using Content.Shared.Projectiles; // Corvax-Wega-Suicide
using Content.Shared.Interaction.Events;
using Robust.Shared.Player;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Timing; // Corvax-Wega-Suicide
namespace Content.Shared.Execution;
/// <summary>
/// Verb for violently murdering cuffed creatures.
/// </summary>
public sealed class SharedExecutionSystem : EntitySystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedSuicideSystem _suicide = default!;
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly SharedGunSystem _gun = default!; // Corvax-Wega-Suicide
[Dependency] private readonly SharedMeleeWeaponSystem _melee = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!; // Corvax-Wega-Suicide
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ExecutionComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionsVerbs);
SubscribeLocalEvent<ExecutionComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
SubscribeLocalEvent<ExecutionComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
SubscribeLocalEvent<ExecutionComponent, ExecutionDoAfterEvent>(OnExecutionDoAfter);
SubscribeLocalEvent<ProjectileComponent, ProjectileHitEvent>(OnProjectileHit); // Corvax-Wega-Suicide
}
private void OnGetInteractionsVerbs(EntityUid uid, ExecutionComponent comp, GetVerbsEvent<UtilityVerb> args)
{
if (args.Hands == null || args.Using == null || !args.CanAccess || !args.CanInteract)
return;
var attacker = args.User;
var weapon = args.Using.Value;
var victim = args.Target;
if (!CanBeExecuted(victim, attacker))
return;
// Corvax-Wega-Suicide-start
if (HasComp<GunComponent>(weapon) && !comp.CanGunExecute)
return;
if (HasComp<MeleeWeaponComponent>(weapon) && !comp.CanMeleeExecute)
return;
// Corvax-Wega-Suicide-end
UtilityVerb verb = new()
{
Act = () => TryStartExecutionDoAfter(weapon, victim, attacker, comp),
Impact = LogImpact.High,
Text = Loc.GetString("execution-verb-name"),
Message = Loc.GetString("execution-verb-message"),
};
args.Verbs.Add(verb);
}
private void TryStartExecutionDoAfter(EntityUid weapon, EntityUid victim, EntityUid attacker, ExecutionComponent comp)
{
if (!CanBeExecuted(victim, attacker))
return;
// Corvax-Wega-Suicide-Edit-start
string internalMsg;
string externalMsg;
if (attacker == victim)
{
if (HasComp<GunComponent>(weapon))
{
internalMsg = comp.InternalSelfGunExecutionMessage;
externalMsg = comp.ExternalSelfGunExecutionMessage;
}
else
{
internalMsg = comp.InternalSelfExecutionMessage;
externalMsg = comp.ExternalSelfExecutionMessage;
}
}
else if (HasComp<GunComponent>(weapon))
{
internalMsg = comp.InternalGunExecutionMessage;
externalMsg = comp.ExternalGunExecutionMessage;
}
else
{
internalMsg = comp.InternalMeleeExecutionMessage;
externalMsg = comp.ExternalMeleeExecutionMessage;
}
ShowExecutionInternalPopup(internalMsg, attacker, victim, weapon);
ShowExecutionExternalPopup(externalMsg, attacker, victim, weapon);
var doAfter = new DoAfterArgs(
EntityManager,
attacker,
comp.DoAfterDuration,
new ExecutionDoAfterEvent(),
weapon,
target: victim,
used: weapon)
{
BreakOnMove = true,
BreakOnDamage = true,
NeedHand = true
};
// Corvax-Wega-Suicide-Edit-end
_doAfter.TryStartDoAfter(doAfter);
}
public bool CanBeExecuted(EntityUid victim, EntityUid attacker)
{
// Corvax-Wega-Suicide-start
if (victim == attacker)
return true;
if ((_transform.GetWorldPosition(victim) - _transform.GetWorldPosition(attacker)).Length() > 0.8f)
return false;
// Corvax-Wega-Suicide-end
// No point executing someone if they can't take damage
if (!HasComp<DamageableComponent>(victim))
return false;
// You can't execute something that cannot die
if (!TryComp<MobStateComponent>(victim, out var mobState))
return false;
// You're not allowed to execute dead people (no fun allowed)
if (_mobState.IsDead(victim, mobState))
return false;
// You must be able to attack people to execute
if (!_actionBlocker.CanAttack(attacker, victim))
return false;
// The victim must be incapacitated to be executed
if (victim != attacker && _actionBlocker.CanInteract(victim, null))
return false;
// All checks passed
return true;
}
private void OnGetMeleeDamage(Entity<ExecutionComponent> entity, ref GetMeleeDamageEvent args)
{
if (!TryComp<MeleeWeaponComponent>(entity, out var melee) || !entity.Comp.Executing)
{
return;
}
var bonus = melee.Damage * entity.Comp.DamageMultiplier - melee.Damage;
args.Damage += bonus;
args.ResistanceBypass = true;
}
// Corvax-Wega-Suicide-start
private void OnProjectileHit(EntityUid entity, ProjectileComponent component, ref ProjectileHitEvent e)
{
if (!TryComp<ExecutionComponent>(component.Weapon, out var execution) || !execution.Executing)
return;
var bonus = component.Damage * execution.DamageMultiplier - component.Damage;
e.Damage += bonus;
}
// Corvax-Wega-Suicide-end
private void OnSuicideByEnvironment(Entity<ExecutionComponent> entity, ref SuicideByEnvironmentEvent args)
{
if (!TryComp<MeleeWeaponComponent>(entity, out var melee))
return;
string internalMsg = entity.Comp.CompleteInternalSelfExecutionMessage; // Corvax-Wega-Suicide
string externalMsg = entity.Comp.CompleteExternalSelfExecutionMessage; // Corvax-Wega-Suicide
if (!TryComp<DamageableComponent>(args.Victim, out var damageableComponent))
return;
ShowExecutionInternalPopup(internalMsg, args.Victim, args.Victim, entity, false);
ShowExecutionExternalPopup(externalMsg, args.Victim, args.Victim, entity);
_audio.PlayPredicted(melee.HitSound, args.Victim, args.Victim);
_suicide.ApplyLethalDamage((args.Victim, damageableComponent), melee.Damage * entity.Comp.DamageMultiplier); // Corvax-Wega-Suicide
args.Handled = true; // Corvax-Wega-Suicide
}
private void ShowExecutionInternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon, bool predict = true)
{
if (predict)
{
_popup.PopupClient(
Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)),
attacker,
attacker,
PopupType.MediumCaution
);
}
else
{
_popup.PopupEntity(
Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)),
attacker,
attacker,
PopupType.MediumCaution
);
}
}
private void ShowExecutionExternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon)
{
_popup.PopupEntity(
Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)),
attacker,
Filter.PvsExcept(attacker),
true,
PopupType.MediumCaution
);
}
private void OnExecutionDoAfter(Entity<ExecutionComponent> entity, ref ExecutionDoAfterEvent args)
{
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
return;
// Corvax-Wega-Suicide-Edit-start
var attacker = args.User;
var victim = args.Target.Value;
var weapon = args.Used.Value;
if (!CanBeExecuted(victim, attacker))
return;
entity.Comp.Executing = true;
TimeSpan time = TimeSpan.FromSeconds(0.1);
if (TryComp<GunComponent>(weapon, out var gun))
{
var prev = _combat.IsInCombatMode(attacker);
_combat.SetInCombatMode(attacker, true);
if (!_gun.AttemptDirectShoot(attacker, weapon, victim, gun))
{
_combat.SetInCombatMode(attacker, prev);
return;
}
_combat.SetInCombatMode(attacker, prev);
if (attacker == victim)
{
ShowExecutionInternalPopup(entity.Comp.CompleteInternalSelfGunExecutionMessage, attacker, victim, weapon);
ShowExecutionExternalPopup(entity.Comp.CompleteExternalSelfGunExecutionMessage, attacker, victim, weapon);
}
else
{
ShowExecutionInternalPopup(entity.Comp.CompleteInternalGunExecutionMessage, attacker, victim, weapon);
ShowExecutionExternalPopup(entity.Comp.CompleteExternalGunExecutionMessage, attacker, victim, weapon);
}
}
else if (TryComp<MeleeWeaponComponent>(weapon, out var melee))
{
var prev = _combat.IsInCombatMode(attacker);
_combat.SetInCombatMode(attacker, true);
_melee.AttemptLightAttack(attacker, weapon, melee, victim);
_combat.SetInCombatMode(attacker, prev);
if (attacker == victim)
{
ShowExecutionInternalPopup(entity.Comp.CompleteInternalSelfExecutionMessage, attacker, victim, weapon);
ShowExecutionExternalPopup(entity.Comp.CompleteExternalSelfExecutionMessage, attacker, victim, weapon);
}
else
{
ShowExecutionInternalPopup(entity.Comp.CompleteInternalMeleeExecutionMessage, attacker, victim, weapon);
ShowExecutionExternalPopup(entity.Comp.CompleteExternalMeleeExecutionMessage, attacker, victim, weapon);
}
time = TimeSpan.Zero;
}
if (attacker == victim)
{
var suicideEvent = new SuicideEvent(victim);
RaiseLocalEvent(victim, suicideEvent);
var suicideGhostEvent = new SuicideGhostEvent(victim);
RaiseLocalEvent(victim, suicideGhostEvent);
}
// Temporarily increased weapon damage.
Timer.Spawn(time, () => { entity.Comp.Executing = false; });
args.Handled = true;
// Corvax-Wega-Suicide-Edit-end
}
}