| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- using Content.Shared.ActionBlocker;
- using Content.Shared.Chat;
- using Content.Shared.CombatMode;
- using Content.Shared.Damage;
- 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.Interaction.Events;
- using Content.Shared.Mind;
- using Robust.Shared.Player;
- using Robust.Shared.Audio.Systems;
- 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 SharedExecutionSystem _execution = default!;
- [Dependency] private readonly SharedMeleeWeaponSystem _melee = default!;
- /// <inheritdoc/>
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<ExecutionComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionsVerbs);
- SubscribeLocalEvent<ExecutionComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
- SubscribeLocalEvent<ExecutionComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
- SubscribeLocalEvent<ExecutionComponent, ExecutionDoAfterEvent>(OnExecutionDoAfter);
- }
- 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;
- 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;
- if (attacker == victim)
- {
- ShowExecutionInternalPopup(comp.InternalSelfExecutionMessage, attacker, victim, weapon);
- ShowExecutionExternalPopup(comp.ExternalSelfExecutionMessage, attacker, victim, weapon);
- }
- else
- {
- ShowExecutionInternalPopup(comp.InternalMeleeExecutionMessage, attacker, victim, weapon);
- ShowExecutionExternalPopup(comp.ExternalMeleeExecutionMessage, attacker, victim, weapon);
- }
- var doAfter =
- new DoAfterArgs(EntityManager, attacker, comp.DoAfterDuration, new ExecutionDoAfterEvent(), weapon, target: victim, used: weapon)
- {
- BreakOnMove = true,
- BreakOnDamage = true,
- NeedHand = true
- };
- _doAfter.TryStartDoAfter(doAfter);
- }
- public bool CanBeExecuted(EntityUid victim, EntityUid attacker)
- {
- // 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;
- }
- private void OnSuicideByEnvironment(Entity<ExecutionComponent> entity, ref SuicideByEnvironmentEvent args)
- {
- if (!TryComp<MeleeWeaponComponent>(entity, out var melee))
- return;
- string? internalMsg = entity.Comp.CompleteInternalSelfExecutionMessage;
- string? externalMsg = entity.Comp.CompleteExternalSelfExecutionMessage;
- 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);
- args.Handled = true;
- }
- 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;
- if (!TryComp<MeleeWeaponComponent>(entity, out var meleeWeaponComp))
- return;
- var attacker = args.User;
- var victim = args.Target.Value;
- var weapon = args.Used.Value;
- if (!_execution.CanBeExecuted(victim, attacker))
- return;
- // This is needed so the melee system does not stop it.
- var prev = _combat.IsInCombatMode(attacker);
- _combat.SetInCombatMode(attacker, true);
- entity.Comp.Executing = true;
- var internalMsg = entity.Comp.CompleteInternalMeleeExecutionMessage;
- var externalMsg = entity.Comp.CompleteExternalMeleeExecutionMessage;
- if (attacker == victim)
- {
- var suicideEvent = new SuicideEvent(victim);
- RaiseLocalEvent(victim, suicideEvent);
- var suicideGhostEvent = new SuicideGhostEvent(victim);
- RaiseLocalEvent(victim, suicideGhostEvent);
- }
- else
- {
- _melee.AttemptLightAttack(attacker, weapon, meleeWeaponComp, victim);
- }
- _combat.SetInCombatMode(attacker, prev);
- entity.Comp.Executing = false;
- args.Handled = true;
- if (attacker != victim)
- {
- _execution.ShowExecutionInternalPopup(internalMsg, attacker, victim, entity);
- _execution.ShowExecutionExternalPopup(externalMsg, attacker, victim, entity);
- }
- }
- }
|