This reverts commit 3c9c149b81.
# Conflicts:
# Content.Server/Projectiles/ProjectileSystem.cs
# Content.Server/Weapons/Ranged/Systems/GunSystem.cs
# Content.Shared/Projectiles/SharedProjectileSystem.cs
# Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml
This commit is contained in:
@@ -1,419 +0,0 @@
|
|||||||
using Content.Server.Interaction;
|
|
||||||
using Content.Server.Kitchen.Components;
|
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
|
||||||
using Content.Shared.ActionBlocker;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.DoAfter;
|
|
||||||
using Content.Shared.Execution;
|
|
||||||
using Content.Shared.Interaction.Components;
|
|
||||||
using Content.Shared.Mobs.Components;
|
|
||||||
using Content.Shared.Mobs.Systems;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Projectiles;
|
|
||||||
using Content.Shared.Verbs;
|
|
||||||
using Content.Shared.Weapons.Melee;
|
|
||||||
using Content.Shared.Weapons.Ranged;
|
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
|
||||||
using Content.Shared.Weapons.Ranged.Systems;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Execution;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verb for violently murdering cuffed creatures.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ExecutionSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
|
||||||
[Dependency] private readonly InteractionSystem _interactionSystem = default!;
|
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
|
||||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
|
||||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
|
||||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
|
||||||
|
|
||||||
private const float MeleeExecutionTimeModifier = 5.0f;
|
|
||||||
private const float GunExecutionTime = 6.0f;
|
|
||||||
private const float DamageModifier = 9.0f;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SharpComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionVerbsMelee);
|
|
||||||
SubscribeLocalEvent<GunComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionVerbsGun);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SharpComponent, ExecutionDoAfterEvent>(OnDoafterMelee);
|
|
||||||
SubscribeLocalEvent<GunComponent, ExecutionDoAfterEvent>(OnDoafterGun);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetInteractionVerbsMelee(
|
|
||||||
EntityUid uid,
|
|
||||||
SharpComponent component,
|
|
||||||
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 (!CanExecuteWithMelee(weapon, victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
UtilityVerb verb = new()
|
|
||||||
{
|
|
||||||
Act = () =>
|
|
||||||
{
|
|
||||||
TryStartMeleeExecutionDoafter(weapon, victim, attacker);
|
|
||||||
},
|
|
||||||
Impact = LogImpact.High,
|
|
||||||
Text = Loc.GetString("execution-verb-name"),
|
|
||||||
Message = Loc.GetString("execution-verb-message"),
|
|
||||||
};
|
|
||||||
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetInteractionVerbsGun(
|
|
||||||
EntityUid uid,
|
|
||||||
GunComponent component,
|
|
||||||
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 (!CanExecuteWithGun(weapon, victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
UtilityVerb verb = new()
|
|
||||||
{
|
|
||||||
Act = () =>
|
|
||||||
{
|
|
||||||
TryStartGunExecutionDoafter(weapon, victim, attacker);
|
|
||||||
},
|
|
||||||
Impact = LogImpact.High,
|
|
||||||
Text = Loc.GetString("execution-verb-name"),
|
|
||||||
Message = Loc.GetString("execution-verb-message"),
|
|
||||||
};
|
|
||||||
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanExecuteWithAny(EntityUid victim, EntityUid attacker)
|
|
||||||
{
|
|
||||||
// No point executing someone if they can't take damage
|
|
||||||
if (!TryComp<DamageableComponent>(victim, out _))
|
|
||||||
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 (_mobStateSystem.IsDead(victim, mobState))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// You must be able to attack people to execute
|
|
||||||
if (!_actionBlockerSystem.CanAttack(attacker, victim))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// The victim must be incapacitated to be executed
|
|
||||||
if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// All checks passed
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanExecuteWithMelee(EntityUid weapon, EntityUid victim, EntityUid user)
|
|
||||||
{
|
|
||||||
if (!CanExecuteWithAny(victim, user)) return false;
|
|
||||||
|
|
||||||
// We must be able to actually hurt people with the weapon
|
|
||||||
if (!TryComp<MeleeWeaponComponent>(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanExecuteWithGun(EntityUid weapon, EntityUid victim, EntityUid user)
|
|
||||||
{
|
|
||||||
if (!CanExecuteWithAny(victim, user)) return false;
|
|
||||||
|
|
||||||
// We must be able to actually fire the gun
|
|
||||||
if (!TryComp<GunComponent>(weapon, out var gun) && _gunSystem.CanShoot(gun!))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryStartMeleeExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker)
|
|
||||||
{
|
|
||||||
if (!CanExecuteWithMelee(weapon, victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var executionTime = (1.0f / Comp<MeleeWeaponComponent>(weapon).AttackRate) * MeleeExecutionTimeModifier;
|
|
||||||
|
|
||||||
if (attacker == victim)
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("suicide-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("suicide-popup-melee-initial-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("execution-popup-melee-initial-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("execution-popup-melee-initial-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
|
|
||||||
var doAfter = new DoAfterArgs(EntityManager, attacker, executionTime, new ExecutionDoAfterEvent(), weapon,
|
|
||||||
target: victim, used: weapon)
|
|
||||||
{
|
|
||||||
BreakOnMove = true,
|
|
||||||
BreakOnDamage = true,
|
|
||||||
NeedHand = true
|
|
||||||
};
|
|
||||||
|
|
||||||
_doAfterSystem.TryStartDoAfter(doAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryStartGunExecutionDoafter(EntityUid weapon, EntityUid victim, EntityUid attacker)
|
|
||||||
{
|
|
||||||
if (!CanExecuteWithGun(weapon, victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (attacker == victim)
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("suicide-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("suicide-popup-gun-initial-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("execution-popup-gun-initial-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("execution-popup-gun-initial-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
|
|
||||||
var doAfter = new DoAfterArgs(EntityManager, attacker, GunExecutionTime, new ExecutionDoAfterEvent(), weapon,
|
|
||||||
target: victim, used: weapon)
|
|
||||||
{
|
|
||||||
BreakOnDamage = true,
|
|
||||||
NeedHand = true
|
|
||||||
};
|
|
||||||
|
|
||||||
_doAfterSystem.TryStartDoAfter(doAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDoafterMelee(EntityUid uid, SharpComponent component, DoAfterEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var attacker = args.User;
|
|
||||||
var victim = args.Target!.Value;
|
|
||||||
var weapon = args.Used!.Value;
|
|
||||||
|
|
||||||
if (!CanExecuteWithMelee(weapon, victim, attacker)) return;
|
|
||||||
|
|
||||||
if (!TryComp<MeleeWeaponComponent>(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true);
|
|
||||||
_audioSystem.PlayEntity(melee.HitSound, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
|
||||||
|
|
||||||
if (attacker == victim)
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("suicide-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("suicide-popup-melee-complete-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("execution-popup-melee-complete-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("execution-popup-melee-complete-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This repeats a lot of the code of the serverside GunSystem, make it not do that
|
|
||||||
private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var attacker = args.User;
|
|
||||||
var weapon = args.Used!.Value;
|
|
||||||
var victim = args.Target!.Value;
|
|
||||||
|
|
||||||
if (!CanExecuteWithGun(weapon, victim, attacker)) return;
|
|
||||||
|
|
||||||
// Check if any systems want to block our shot
|
|
||||||
var prevention = new ShotAttemptedEvent
|
|
||||||
{
|
|
||||||
User = attacker,
|
|
||||||
Used = weapon
|
|
||||||
};
|
|
||||||
|
|
||||||
RaiseLocalEvent(weapon, ref prevention);
|
|
||||||
if (prevention.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
RaiseLocalEvent(attacker, ref prevention);
|
|
||||||
if (prevention.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Not sure what this is for but gunsystem uses it so ehhh
|
|
||||||
var attemptEv = new AttemptShootEvent(attacker, null);
|
|
||||||
RaiseLocalEvent(weapon, ref attemptEv);
|
|
||||||
|
|
||||||
if (attemptEv.Cancelled)
|
|
||||||
{
|
|
||||||
if (attemptEv.Message != null)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(attemptEv.Message, weapon, attacker);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take some ammunition for the shot (one bullet)
|
|
||||||
var fromCoordinates = Transform(attacker).Coordinates;
|
|
||||||
var ev = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, attacker);
|
|
||||||
RaiseLocalEvent(weapon, ev);
|
|
||||||
|
|
||||||
// Check if there's any ammo left
|
|
||||||
if (ev.Ammo.Count <= 0)
|
|
||||||
{
|
|
||||||
_audioSystem.PlayEntity(component.SoundEmpty, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
|
||||||
ShowExecutionPopup("execution-popup-gun-empty", Filter.Pvs(weapon), PopupType.Medium, attacker, victim,
|
|
||||||
weapon);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Information about the ammo like damage
|
|
||||||
DamageSpecifier damage = new DamageSpecifier();
|
|
||||||
|
|
||||||
// Get some information from IShootable
|
|
||||||
var ammoUid = ev.Ammo[0].Entity;
|
|
||||||
switch (ev.Ammo[0].Shootable)
|
|
||||||
{
|
|
||||||
case CartridgeAmmoComponent cartridge:
|
|
||||||
// Get the damage value
|
|
||||||
var prototype = _prototypeManager.Index<EntityPrototype>(cartridge.Prototype);
|
|
||||||
prototype.TryGetComponent<ProjectileComponent>(out var projectileA,
|
|
||||||
_componentFactory); // sloth forgive me
|
|
||||||
|
|
||||||
if (projectileA != null)
|
|
||||||
{
|
|
||||||
damage = projectileA.Damage * cartridge.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expend the cartridge
|
|
||||||
cartridge.Spent = true;
|
|
||||||
_appearanceSystem.SetData(ammoUid!.Value, AmmoVisuals.Spent, true);
|
|
||||||
Dirty(ammoUid.Value, cartridge);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AmmoComponent newAmmo:
|
|
||||||
TryComp<ProjectileComponent>(ammoUid, out var projectileB);
|
|
||||||
if (projectileB != null)
|
|
||||||
{
|
|
||||||
damage = projectileB.Damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
Del(ammoUid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HitscanPrototype hitscan:
|
|
||||||
damage = hitscan.Damage!;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clumsy people have a chance to shoot themselves
|
|
||||||
if (TryComp<ClumsyComponent>(attacker, out var clumsy) && component.ClumsyProof == false)
|
|
||||||
{
|
|
||||||
if (_interactionSystem.TryRollClumsy(attacker, 0.33333333f, clumsy))
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("execution-popup-gun-clumsy-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("execution-popup-gun-clumsy-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.MediumCaution, attacker, victim, weapon);
|
|
||||||
|
|
||||||
// You shoot yourself with the gun (no damage multiplier)
|
|
||||||
_damageableSystem.TryChangeDamage(attacker, damage, origin: attacker);
|
|
||||||
_audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, true, AudioParams.Default);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gun successfully fired, deal damage
|
|
||||||
_damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true);
|
|
||||||
_audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default);
|
|
||||||
|
|
||||||
// Popups
|
|
||||||
if (attacker != victim)
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("execution-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.Medium,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("execution-popup-gun-complete-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.LargeCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowExecutionPopup("suicide-popup-gun-complete-internal", Filter.Entities(attacker), PopupType.LargeCaution,
|
|
||||||
attacker, victim, weapon);
|
|
||||||
|
|
||||||
ShowExecutionPopup("suicide-popup-gun-complete-external", Filter.PvsExcept(attacker),
|
|
||||||
PopupType.LargeCaution, attacker, victim, weapon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowExecutionPopup(
|
|
||||||
string locString,
|
|
||||||
Filter filter,
|
|
||||||
PopupType type,
|
|
||||||
EntityUid attacker,
|
|
||||||
EntityUid victim,
|
|
||||||
EntityUid weapon)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString(
|
|
||||||
locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)),
|
|
||||||
attacker, filter, true, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared._White;
|
using Content.Shared._White;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
|
|
||||||
@@ -62,27 +61,12 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryHandleProjectile(target, (uid, component), args.OtherFixture))
|
|
||||||
{
|
|
||||||
var direction = args.OurBody.LinearVelocity.Normalized();
|
|
||||||
_sharedCameraRecoil.KickCamera(target, direction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to handle a projectile interacting with the target.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the target isn't deleted.</returns>
|
|
||||||
public bool TryHandleProjectile(EntityUid target, Entity<ProjectileComponent> projectile, Fixture? otherFixture)
|
|
||||||
{
|
|
||||||
var uid = projectile.Owner;
|
|
||||||
var component = projectile.Comp;
|
|
||||||
|
|
||||||
var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter);
|
var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter);
|
||||||
RaiseLocalEvent(uid, ref ev);
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
var otherName = ToPrettyString(target);
|
var otherName = ToPrettyString(target);
|
||||||
var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter);
|
var direction = args.OurBody.LinearVelocity.Normalized();
|
||||||
|
var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage * DamageModifier, component.IgnoreResistances, origin: component.Shooter);
|
||||||
var deleted = Deleted(target);
|
var deleted = Deleted(target);
|
||||||
|
|
||||||
if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter))
|
if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter))
|
||||||
@@ -100,13 +84,11 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
if (!deleted)
|
if (!deleted)
|
||||||
{
|
{
|
||||||
_guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound);
|
_guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound);
|
||||||
|
_sharedCameraRecoil.KickCamera(target, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.DamagedEntity = true;
|
component.DamagedEntity = true;
|
||||||
|
|
||||||
var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target, otherFixture);
|
|
||||||
RaiseLocalEvent(uid, ref afterProjectileHitEvent);
|
|
||||||
|
|
||||||
if (component.DeleteOnCollide)
|
if (component.DeleteOnCollide)
|
||||||
QueueDel(uid);
|
QueueDel(uid);
|
||||||
|
|
||||||
@@ -114,7 +96,5 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
{
|
{
|
||||||
RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager));
|
RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
return !deleted;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Numerics;
|
|||||||
using Content.Server.Cargo.Systems;
|
using Content.Server.Cargo.Systems;
|
||||||
using Content.Server.Interaction;
|
using Content.Server.Interaction;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Projectiles;
|
|
||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Server.Weapons.Ranged.Components;
|
using Content.Server.Weapons.Ranged.Components;
|
||||||
using Content.Server._White.Crossbow;
|
using Content.Server._White.Crossbow;
|
||||||
@@ -38,7 +37,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
[Dependency] private readonly InteractionSystem _interaction = default!;
|
[Dependency] private readonly InteractionSystem _interaction = default!;
|
||||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||||
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
||||||
[Dependency] private readonly ProjectileSystem _projectile = default!;
|
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly StaminaSystem _stamina = default!;
|
[Dependency] private readonly StaminaSystem _stamina = default!;
|
||||||
[Dependency] private readonly StunSystem _stun = default!;
|
[Dependency] private readonly StunSystem _stun = default!;
|
||||||
@@ -69,137 +67,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
args.Price += price * component.UnspawnedCount;
|
args.Price += price * component.UnspawnedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user)
|
|
||||||
{
|
|
||||||
var result = false;
|
|
||||||
|
|
||||||
// TODO: This is dogshit. I just want to get executions slightly better.
|
|
||||||
// Ideally you'd pull out cartridge + ammo to separate handling functions and re-use it here, then hitscan you need to bypass entirely.
|
|
||||||
// You should also make shooting into a struct of args given how many there are now.
|
|
||||||
var fromCoordinates = Transform(gunUid).Coordinates;
|
|
||||||
var toCoordinates = Transform(target).Coordinates;
|
|
||||||
|
|
||||||
var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem);
|
|
||||||
var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
|
||||||
var mapDirection = toMap - fromMap.Position;
|
|
||||||
var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle());
|
|
||||||
|
|
||||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
|
||||||
var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _)
|
|
||||||
? fromCoordinates.WithEntityId(gridUid, EntityManager)
|
|
||||||
: new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position);
|
|
||||||
|
|
||||||
// I must be high because this was getting tripped even when true.
|
|
||||||
// DebugTools.Assert(direction != Vector2.Zero);
|
|
||||||
var shotProjectiles = new List<EntityUid>(ammo.Count);
|
|
||||||
var cartridgeBullets = new List<EntityUid>();
|
|
||||||
|
|
||||||
foreach (var (ent, shootable) in ammo)
|
|
||||||
{
|
|
||||||
switch (shootable)
|
|
||||||
{
|
|
||||||
// Cartridge shoots something else
|
|
||||||
case CartridgeAmmoComponent cartridge:
|
|
||||||
if (!cartridge.Spent)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < cartridge.Count; i++)
|
|
||||||
{
|
|
||||||
var uid = Spawn(cartridge.Prototype, fromEnt);
|
|
||||||
cartridgeBullets.Add(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
RaiseLocalEvent(ent!.Value, new AmmoShotEvent()
|
|
||||||
{
|
|
||||||
FiredProjectiles = cartridgeBullets,
|
|
||||||
});
|
|
||||||
|
|
||||||
shotProjectiles.AddRange(cartridgeBullets);
|
|
||||||
cartridgeBullets.Clear();
|
|
||||||
SetCartridgeSpent(ent.Value, cartridge, true);
|
|
||||||
MuzzleFlash(gunUid, cartridge, user);
|
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
|
||||||
|
|
||||||
if (cartridge.DeleteOnSpawn)
|
|
||||||
Del(ent.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something like ballistic might want to leave it in the container still
|
|
||||||
if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value))
|
|
||||||
EjectCartridge(ent.Value, angle);
|
|
||||||
|
|
||||||
result = true;
|
|
||||||
Dirty(ent!.Value, cartridge);
|
|
||||||
break;
|
|
||||||
// Ammo shoots itself
|
|
||||||
case AmmoComponent newAmmo:
|
|
||||||
result = true;
|
|
||||||
shotProjectiles.Add(ent!.Value);
|
|
||||||
MuzzleFlash(gunUid, newAmmo, user);
|
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
|
||||||
break;
|
|
||||||
case HitscanPrototype hitscan:
|
|
||||||
result = true;
|
|
||||||
var hitEntity = target;
|
|
||||||
if (hitscan.StaminaDamage > 0f)
|
|
||||||
_stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user);
|
|
||||||
|
|
||||||
var dmg = hitscan.Damage;
|
|
||||||
|
|
||||||
var hitName = ToPrettyString(hitEntity);
|
|
||||||
if (dmg != null)
|
|
||||||
dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user);
|
|
||||||
|
|
||||||
// check null again, as TryChangeDamage returns modified damage values
|
|
||||||
if (dmg != null)
|
|
||||||
{
|
|
||||||
if (!Deleted(hitEntity))
|
|
||||||
{
|
|
||||||
if (dmg.Any())
|
|
||||||
{
|
|
||||||
_color.RaiseEffect(Color.Red, [hitEntity], Filter.Pvs(hitEntity, entityManager: EntityManager));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO get fallback position for playing hit sound.
|
|
||||||
PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logs.Add(LogType.HitScanHit,
|
|
||||||
$"{ToPrettyString(user):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage");
|
|
||||||
}
|
|
||||||
|
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var ammoUid in shotProjectiles)
|
|
||||||
{
|
|
||||||
// TODO: Handle this shit
|
|
||||||
if (!TryComp(ammoUid, out ProjectileComponent? projectileComponent))
|
|
||||||
{
|
|
||||||
QueueDel(ammoUid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_projectile.TryHandleProjectile(target, (ammoUid, projectileComponent), null);
|
|
||||||
// Even this deletion handling is mega sussy.
|
|
||||||
Del(ammoUid);
|
|
||||||
}
|
|
||||||
|
|
||||||
RaiseLocalEvent(gunUid, new AmmoShotEvent()
|
|
||||||
{
|
|
||||||
FiredProjectiles = shotProjectiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
|
public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
|
||||||
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
|
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
|
||||||
{
|
{
|
||||||
@@ -207,7 +74,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
|
|
||||||
// Try a clumsy roll
|
// Try a clumsy roll
|
||||||
// TODO: Who put this here
|
// TODO: Who put this here
|
||||||
if (TryComp<ClumsyComponent>(user, out var clumsy) && !gun.ClumsyProof)
|
if (TryComp<ClumsyComponent>(user, out var clumsy) && gun.ClumsyProof == false)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < ammo.Count; i++)
|
for (var i = 0; i < ammo.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -230,8 +97,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// As the above message wasn't obvious stop putting stuff here and use events
|
|
||||||
|
|
||||||
var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem);
|
var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem);
|
||||||
var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem);
|
||||||
var mapDirection = toMap - fromMap.Position;
|
var mapDirection = toMap - fromMap.Position;
|
||||||
@@ -258,7 +123,6 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
// I must be high because this was getting tripped even when true.
|
// I must be high because this was getting tripped even when true.
|
||||||
// DebugTools.Assert(direction != Vector2.Zero);
|
// DebugTools.Assert(direction != Vector2.Zero);
|
||||||
var shotProjectiles = new List<EntityUid>(ammo.Count);
|
var shotProjectiles = new List<EntityUid>(ammo.Count);
|
||||||
var cartridgeBullets = new List<EntityUid>();
|
|
||||||
|
|
||||||
foreach (var (ent, shootable) in ammo)
|
foreach (var (ent, shootable) in ammo)
|
||||||
{
|
{
|
||||||
@@ -287,23 +151,21 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
{
|
{
|
||||||
var uid = Spawn(cartridge.Prototype, fromEnt);
|
var uid = Spawn(cartridge.Prototype, fromEnt);
|
||||||
ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
|
ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
|
||||||
cartridgeBullets.Add(uid);
|
shotProjectiles.Add(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var uid = Spawn(cartridge.Prototype, fromEnt);
|
var uid = Spawn(cartridge.Prototype, fromEnt);
|
||||||
ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user);
|
ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user);
|
||||||
cartridgeBullets.Add(uid);
|
shotProjectiles.Add(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseLocalEvent(ent!.Value, new AmmoShotEvent()
|
RaiseLocalEvent(ent!.Value, new AmmoShotEvent()
|
||||||
{
|
{
|
||||||
FiredProjectiles = cartridgeBullets,
|
FiredProjectiles = shotProjectiles,
|
||||||
});
|
});
|
||||||
|
|
||||||
shotProjectiles.AddRange(cartridgeBullets);
|
|
||||||
cartridgeBullets.Clear();
|
|
||||||
SetCartridgeSpent(ent.Value, cartridge, true);
|
SetCartridgeSpent(ent.Value, cartridge, true);
|
||||||
MuzzleFlash(gunUid, cartridge, user);
|
MuzzleFlash(gunUid, cartridge, user);
|
||||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using Content.Shared.DoAfter;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Execution;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed partial class ExecutionDoAfterEvent : SimpleDoAfterEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using Robust.Shared.GameStates;
|
|
||||||
|
|
||||||
namespace Content.Shared.Execution;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Added to entities that can be used to execute another target.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
|
||||||
public sealed partial class ExecutionComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// How long the execution duration lasts.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public float DoAfterDuration = 5f;
|
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public float DamageModifier = 9f;
|
|
||||||
|
|
||||||
// Not networked because this is transient inside of a tick.
|
|
||||||
/// <summary>
|
|
||||||
/// True if it is currently executing for handlers.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public bool Executing = true;
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
using Content.Shared.Weapons.Ranged.Systems;
|
|
||||||
using Content.Shared.ActionBlocker;
|
|
||||||
using Content.Shared.CombatMode;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.DoAfter;
|
|
||||||
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;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
|
|
||||||
namespace Content.Shared.Execution;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verb for violently murdering cuffed creatures.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ExecutionSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
|
||||||
[Dependency] private readonly SharedGunSystem _gunSystem = default!;
|
|
||||||
[Dependency] private readonly SharedCombatModeSystem _combatSystem = default!;
|
|
||||||
[Dependency] private readonly SharedMeleeWeaponSystem _meleeSystem = default!;
|
|
||||||
|
|
||||||
// TODO: Still needs more cleaning up.
|
|
||||||
private const string DefaultInternalMeleeExecutionMessage = "execution-popup-melee-initial-internal";
|
|
||||||
private const string DefaultExternalMeleeExecutionMessage = "execution-popup-melee-initial-external";
|
|
||||||
private const string DefaultCompleteInternalMeleeExecutionMessage = "execution-popup-melee-complete-internal";
|
|
||||||
private const string DefaultCompleteExternalMeleeExecutionMessage = "execution-popup-melee-complete-external";
|
|
||||||
private const string DefaultInternalGunExecutionMessage = "execution-popup-gun-initial-internal";
|
|
||||||
private const string DefaultExternalGunExecutionMessage = "execution-popup-gun-initial-external";
|
|
||||||
private const string DefaultCompleteInternalGunExecutionMessage = "execution-popup-gun-complete-internal";
|
|
||||||
private const string DefaultCompleteExternalGunExecutionMessage = "execution-popup-gun-complete-external";
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<ExecutionComponent, GetVerbsEvent<UtilityVerb>>(OnGetInteractionsVerbs);
|
|
||||||
SubscribeLocalEvent<ExecutionComponent, ExecutionDoAfterEvent>(OnExecutionDoAfter);
|
|
||||||
SubscribeLocalEvent<ExecutionComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!CanExecuteWithAny(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 (!CanExecuteWithAny(victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: This should just be on the weapons as a single execution message.
|
|
||||||
var defaultExecutionInternal = DefaultInternalMeleeExecutionMessage;
|
|
||||||
var defaultExecutionExternal = DefaultExternalMeleeExecutionMessage;
|
|
||||||
|
|
||||||
if (HasComp<GunComponent>(weapon))
|
|
||||||
{
|
|
||||||
defaultExecutionExternal = DefaultInternalGunExecutionMessage;
|
|
||||||
defaultExecutionInternal = DefaultExternalGunExecutionMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
var internalMsg = defaultExecutionInternal;
|
|
||||||
var externalMsg = defaultExecutionExternal;
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
_doAfterSystem.TryStartDoAfter(doAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanExecuteWithAny(EntityUid victim, EntityUid attacker)
|
|
||||||
{
|
|
||||||
// Use suicide.
|
|
||||||
if (victim == attacker)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// No point executing someone if they can't take damage
|
|
||||||
if (!TryComp<DamageableComponent>(victim, out _))
|
|
||||||
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 (_mobStateSystem.IsDead(victim, mobState))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// You must be able to attack people to execute
|
|
||||||
if (!_actionBlockerSystem.CanAttack(attacker, victim))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// The victim must be incapacitated to be executed
|
|
||||||
if (victim != attacker && _actionBlockerSystem.CanInteract(victim, null))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// All checks passed
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutionDoAfter(EntityUid uid, ExecutionComponent component, ExecutionDoAfterEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled || args.Cancelled || args.Used == null || args.Target == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var attacker = args.User;
|
|
||||||
var victim = args.Target.Value;
|
|
||||||
var weapon = args.Used.Value;
|
|
||||||
|
|
||||||
if (!CanExecuteWithAny(victim, attacker))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// This is needed so the melee system does not stop it.
|
|
||||||
var prev = _combatSystem.IsInCombatMode(attacker);
|
|
||||||
_combatSystem.SetInCombatMode(attacker, true);
|
|
||||||
component.Executing = true;
|
|
||||||
string? internalMsg = null;
|
|
||||||
string? externalMsg = null;
|
|
||||||
|
|
||||||
if (TryComp(uid, out MeleeWeaponComponent? melee))
|
|
||||||
{
|
|
||||||
_meleeSystem.AttemptLightAttack(attacker, weapon, melee, victim);
|
|
||||||
internalMsg = DefaultCompleteInternalMeleeExecutionMessage;
|
|
||||||
externalMsg = DefaultCompleteExternalMeleeExecutionMessage;
|
|
||||||
}
|
|
||||||
else if (TryComp(uid, out GunComponent? gun))
|
|
||||||
{
|
|
||||||
// TODO: This should just be an event or something instead to get this.
|
|
||||||
// TODO: Handle clumsy.
|
|
||||||
if (!_gunSystem.AttemptDirectShoot(args.User, uid, args.Target.Value, gun))
|
|
||||||
{
|
|
||||||
internalMsg = null;
|
|
||||||
externalMsg = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
internalMsg = DefaultCompleteInternalGunExecutionMessage;
|
|
||||||
externalMsg = DefaultCompleteExternalGunExecutionMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_combatSystem.SetInCombatMode(attacker, prev);
|
|
||||||
component.Executing = false;
|
|
||||||
args.Handled = true;
|
|
||||||
|
|
||||||
if (internalMsg != null && externalMsg != null)
|
|
||||||
{
|
|
||||||
ShowExecutionInternalPopup(internalMsg, attacker, victim, uid);
|
|
||||||
ShowExecutionExternalPopup(externalMsg, attacker, victim, uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetMeleeDamage(EntityUid uid, ExecutionComponent comp, ref GetMeleeDamageEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<MeleeWeaponComponent>(uid, out var melee) ||
|
|
||||||
!TryComp<ExecutionComponent>(uid, out var execComp) ||
|
|
||||||
!execComp.Executing)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bonus = melee.Damage * execComp.DamageModifier - melee.Damage;
|
|
||||||
args.Damage += bonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowExecutionInternalPopup(
|
|
||||||
string locString,
|
|
||||||
EntityUid attacker,
|
|
||||||
EntityUid victim,
|
|
||||||
EntityUid weapon,
|
|
||||||
bool predict = true)
|
|
||||||
{
|
|
||||||
if (predict)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupClient(
|
|
||||||
Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)),
|
|
||||||
attacker,
|
|
||||||
attacker,
|
|
||||||
PopupType.Medium
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(
|
|
||||||
Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)),
|
|
||||||
attacker,
|
|
||||||
Filter.Entities(attacker),
|
|
||||||
true,
|
|
||||||
PopupType.Medium
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowExecutionExternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(
|
|
||||||
Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)),
|
|
||||||
attacker,
|
|
||||||
Filter.PvsExcept(attacker),
|
|
||||||
true,
|
|
||||||
PopupType.MediumCaution
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -308,9 +308,3 @@ public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, Projectile
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null);
|
public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, EntityUid? Shooter = null);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised after a projectile has dealt it's damage.
|
|
||||||
/// </summary>
|
|
||||||
[ByRefEvent]
|
|
||||||
public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture? Fixture);
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public record struct ShotAttemptedEvent
|
|||||||
|
|
||||||
public bool Cancelled { get; private set; }
|
public bool Cancelled { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Prevent the gun from shooting
|
/// Prevent the gun from shooting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
@@ -27,7 +27,7 @@ public record struct ShotAttemptedEvent
|
|||||||
Cancelled = true;
|
Cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Allow the gun to shoot again, only use if you know what you are doing
|
/// Allow the gun to shoot again, only use if you know what you are doing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Uncancel()
|
public void Uncancel()
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ using Content.Shared.Weapons.Melee;
|
|||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -142,7 +141,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
||||||
gun.Target = GetEntity(msg.Target);
|
gun.Target = GetEntity(msg.Target);
|
||||||
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
||||||
AttemptShootInternal(user.Value, ent, gun);
|
AttemptShoot(user.Value, ent, gun);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args)
|
private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args)
|
||||||
@@ -206,38 +205,13 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
Dirty(uid, gun);
|
Dirty(uid, gun);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to shoot the specified target directly.
|
|
||||||
/// This may bypass projectiles firing etc.
|
|
||||||
/// </summary>
|
|
||||||
public bool AttemptDirectShoot(EntityUid user, EntityUid gunUid, EntityUid target, GunComponent gun)
|
|
||||||
{
|
|
||||||
// Unique name so people don't think it's "shoot towards" and not "I will teleport a bullet into them".
|
|
||||||
gun.ShootCoordinates = Transform(target).Coordinates;
|
|
||||||
|
|
||||||
if (!TryTakeAmmo(user, gunUid, gun, out _, out _, out var args))
|
|
||||||
{
|
|
||||||
gun.ShootCoordinates = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = ShootDirect(gunUid, gun, target, args.Ammo, user: user);
|
|
||||||
gun.ShootCoordinates = null;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool ShootDirect(EntityUid gunUid, GunComponent gun, EntityUid target, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityUid user)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
|
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates)
|
public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates)
|
||||||
{
|
{
|
||||||
gun.ShootCoordinates = toCoordinates;
|
gun.ShootCoordinates = toCoordinates;
|
||||||
AttemptShootInternal(user, gunUid, gun);
|
AttemptShoot(user, gunUid, gun);
|
||||||
gun.ShotCounter = 0;
|
gun.ShotCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,35 +222,20 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
var coordinates = new EntityCoordinates(gunUid, new Vector2(0, -1));
|
var coordinates = new EntityCoordinates(gunUid, new Vector2(0, -1));
|
||||||
gun.ShootCoordinates = coordinates;
|
gun.ShootCoordinates = coordinates;
|
||||||
AttemptShootInternal(gunUid, gunUid, gun);
|
AttemptShoot(gunUid, gunUid, gun);
|
||||||
gun.ShotCounter = 0;
|
gun.ShotCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AttemptShootInternal(EntityUid user, EntityUid gunUid, GunComponent gun)
|
private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
|
||||||
{
|
|
||||||
if (!TryTakeAmmo(user, gunUid, gun, out var fromCoordinates, out var toCoordinates, out var args))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Shoot(gunUid, gun, args.Ammo, fromCoordinates, toCoordinates, out var userImpulse, user: user);
|
|
||||||
|
|
||||||
if (userImpulse && TryComp<PhysicsComponent>(user, out var userPhysics))
|
|
||||||
{
|
|
||||||
if (_gravity.IsWeightless(user, userPhysics))
|
|
||||||
CauseImpulse(fromCoordinates, toCoordinates, user, userPhysics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validates if a gun can currently shoot.
|
|
||||||
/// </summary>
|
|
||||||
[Pure]
|
|
||||||
private bool CanShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
|
|
||||||
{
|
{
|
||||||
if (gun.FireRateModified <= 0f ||
|
if (gun.FireRateModified <= 0f ||
|
||||||
!_actionBlockerSystem.CanAttack(user))
|
!_actionBlockerSystem.CanAttack(user))
|
||||||
{
|
return;
|
||||||
return false;
|
|
||||||
}
|
var toCoordinates = gun.ShootCoordinates;
|
||||||
|
|
||||||
|
if (toCoordinates == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var curTime = Timing.CurTime;
|
var curTime = Timing.CurTime;
|
||||||
|
|
||||||
@@ -288,42 +247,17 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
};
|
};
|
||||||
RaiseLocalEvent(gunUid, ref prevention);
|
RaiseLocalEvent(gunUid, ref prevention);
|
||||||
if (prevention.Cancelled)
|
if (prevention.Cancelled)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
RaiseLocalEvent(user, ref prevention);
|
RaiseLocalEvent(user, ref prevention);
|
||||||
if (prevention.Cancelled)
|
if (prevention.Cancelled)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
// Need to do this to play the clicking sound for empty automatic weapons
|
// Need to do this to play the clicking sound for empty automatic weapons
|
||||||
// but not play anything for burst fire.
|
// but not play anything for burst fire.
|
||||||
if (gun.NextFire > curTime)
|
if (gun.NextFire > curTime)
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to return ammo prepped for shooting if a gun is available to shoot.
|
|
||||||
/// </summary>
|
|
||||||
private bool TryTakeAmmo(
|
|
||||||
EntityUid user,
|
|
||||||
EntityUid gunUid, GunComponent gun,
|
|
||||||
out EntityCoordinates fromCoordinates,
|
|
||||||
out EntityCoordinates toCoordinates,
|
|
||||||
[NotNullWhen(true)] out TakeAmmoEvent? args)
|
|
||||||
{
|
|
||||||
toCoordinates = EntityCoordinates.Invalid;
|
|
||||||
fromCoordinates = EntityCoordinates.Invalid;
|
|
||||||
args = null;
|
|
||||||
|
|
||||||
if (!CanShoot(user, gunUid, gun))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (gun.ShootCoordinates == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
toCoordinates = gun.ShootCoordinates.Value;
|
|
||||||
var curTime = Timing.CurTime;
|
|
||||||
var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified);
|
var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified);
|
||||||
|
|
||||||
// First shot
|
// First shot
|
||||||
@@ -375,11 +309,10 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fromCoordinates = Transform(user).Coordinates;
|
var fromCoordinates = Transform(user).Coordinates;
|
||||||
|
|
||||||
// Remove ammo
|
// Remove ammo
|
||||||
var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user);
|
var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user);
|
||||||
|
|
||||||
@@ -414,18 +347,24 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
// May cause prediction issues? Needs more tweaking
|
// May cause prediction issues? Needs more tweaking
|
||||||
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
||||||
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
|
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent).
|
// Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent).
|
||||||
|
Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems);
|
||||||
var shotEv = new GunShotEvent(user, ev.Ammo);
|
var shotEv = new GunShotEvent(user, ev.Ammo);
|
||||||
RaiseLocalEvent(gunUid, ref shotEv);
|
RaiseLocalEvent(gunUid, ref shotEv);
|
||||||
|
|
||||||
args = ev;
|
if (userImpulse && TryComp<PhysicsComponent>(user, out var userPhysics))
|
||||||
return true;
|
{
|
||||||
|
if (_gravity.IsWeightless(user, userPhysics))
|
||||||
|
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(gunUid, gun);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Shoot(
|
public void Shoot(
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
execution-verb-name = Execute
|
|
||||||
execution-verb-message = Use your weapon to execute someone.
|
|
||||||
|
|
||||||
# All the below localisation strings have access to the following variables
|
|
||||||
# attacker (the person committing the execution)
|
|
||||||
# victim (the person being executed)
|
|
||||||
# weapon (the weapon used for the execution)
|
|
||||||
|
|
||||||
execution-popup-gun-initial-internal = You ready the muzzle of {THE($weapon)} against {$victim}'s head.
|
|
||||||
execution-popup-gun-initial-external = {$attacker} readies the muzzle of {THE($weapon)} against {$victim}'s head.
|
|
||||||
execution-popup-gun-complete-internal = You blast {$victim} in the head!
|
|
||||||
execution-popup-gun-complete-external = {$attacker} blasts {$victim} in the head!
|
|
||||||
execution-popup-gun-clumsy-internal = You miss {$victim}'s head and shoot your foot instead!
|
|
||||||
execution-popup-gun-clumsy-external = {$attacker} misses {$victim} and shoots {POSS-ADJ($attacker)} foot instead!
|
|
||||||
execution-popup-gun-empty = {CAPITALIZE(THE($weapon))} clicks.
|
|
||||||
|
|
||||||
execution-popup-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat.
|
|
||||||
execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against the throat of {$victim}.
|
|
||||||
execution-popup-melee-complete-internal = You slit the throat of {$victim}!
|
|
||||||
execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}!
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
butcherable-different-tool = You need a different tool to butcher { THE($target) }.
|
butcherable-different-tool = You are going to need a different tool to butcher { THE($target) }.
|
||||||
butcherable-knife-butchered-success = You butcher { THE($target) } with { THE($knife) }.
|
butcherable-knife-butchered-success = You butcher { THE($target) } with { THE($knife) }.
|
||||||
butcherable-need-knife = Use a sharp object to butcher { THE($target) }.
|
butcherable-need-knife = Use a sharp object to butcher { THE($target) }.
|
||||||
butcherable-not-in-container = { CAPITALIZE(THE($target)) } can't be in a container.
|
butcherable-not-in-container = { CAPITALIZE(THE($target)) } can't be in a container.
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
description: A small piece of crystal.
|
description: A small piece of crystal.
|
||||||
components:
|
components:
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- sprite: Objects/Materials/Shards/crystal.rsi
|
- sprite: Objects/Materials/Shards/crystal.rsi
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
description: It's a shard of some unknown material.
|
description: It's a shard of some unknown material.
|
||||||
components:
|
components:
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- sprite: Objects/Materials/Shards/shard.rsi
|
- sprite: Objects/Materials/Shards/shard.rsi
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
description: In Space Glasgow this is called a conversation starter.
|
description: In Space Glasgow this is called a conversation starter.
|
||||||
components:
|
components:
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: MeleeWeapon
|
- type: MeleeWeapon
|
||||||
attackRate: 1.5
|
attackRate: 1.5
|
||||||
damage:
|
damage:
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg
|
path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
# No chamber because HMG may want its own
|
# No chamber because HMG may want its own
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
|
|||||||
@@ -60,7 +60,6 @@
|
|||||||
price: 500
|
price: 500
|
||||||
- type: UseDelay
|
- type: UseDelay
|
||||||
delay: 1
|
delay: 1
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: L6 SAW
|
name: L6 SAW
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
containers:
|
containers:
|
||||||
ballistic-ammo: !type:Container
|
ballistic-ammo: !type:Container
|
||||||
ents: []
|
ents: []
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: china lake
|
name: china lake
|
||||||
|
|||||||
@@ -66,7 +66,6 @@
|
|||||||
- type: AmmoCounter
|
- type: AmmoCounter
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: viper
|
name: viper
|
||||||
@@ -201,7 +200,7 @@
|
|||||||
name: N1984
|
name: N1984
|
||||||
parent: BaseWeaponPistol
|
parent: BaseWeaponPistol
|
||||||
id: WeaponPistolN1984 # the spaces in description are for formatting.
|
id: WeaponPistolN1984 # the spaces in description are for formatting.
|
||||||
description: The sidearm of any self respecting officer. Comes in .45 magnum, the lord's caliber.
|
description: The sidearm of any self respecting officer. Comes in .45 magnum, the lord's caliber.
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
|
sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
gun_chamber: !type:ContainerSlot
|
gun_chamber: !type:ContainerSlot
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: AKMS
|
name: AKMS
|
||||||
|
|||||||
@@ -54,7 +54,6 @@
|
|||||||
gun_chamber: !type:ContainerSlot
|
gun_chamber: !type:ContainerSlot
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Atreides
|
name: Atreides
|
||||||
|
|||||||
@@ -43,7 +43,6 @@
|
|||||||
ents: []
|
ents: []
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Bulldog
|
name: Bulldog
|
||||||
@@ -100,7 +99,6 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: double-barreled shotgun
|
name: double-barreled shotgun
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
ents: []
|
ents: []
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Kardashev-Mosin
|
name: Kardashev-Mosin
|
||||||
|
|||||||
@@ -37,4 +37,3 @@
|
|||||||
slots:
|
slots:
|
||||||
- Belt
|
- Belt
|
||||||
- suitStorage
|
- suitStorage
|
||||||
- type: Execution
|
|
||||||
|
|||||||
@@ -107,7 +107,6 @@
|
|||||||
containers:
|
containers:
|
||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
ents: []
|
ents: []
|
||||||
- type: Execution
|
|
||||||
|
|
||||||
# shoots bullets instead of throwing them, no other changes
|
# shoots bullets instead of throwing them, no other changes
|
||||||
- type: entity
|
- type: entity
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
description: A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter.
|
description: A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter.
|
||||||
components:
|
components:
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Melee/armblade.rsi
|
sprite: Objects/Weapons/Melee/armblade.rsi
|
||||||
state: icon
|
state: icon
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
tags:
|
tags:
|
||||||
- FireAxe
|
- FireAxe
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Melee/fireaxe.rsi
|
sprite: Objects/Weapons/Melee/fireaxe.rsi
|
||||||
state: icon
|
state: icon
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
tags:
|
tags:
|
||||||
- Knife
|
- Knife
|
||||||
- type: Sharp
|
- type: Sharp
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: Utensil
|
- type: Utensil
|
||||||
types:
|
types:
|
||||||
- Knife
|
- Knife
|
||||||
|
|||||||
@@ -1,21 +1,10 @@
|
|||||||
- type: entity
|
|
||||||
name: Sword
|
|
||||||
parent: BaseItem
|
|
||||||
id: BaseSword
|
|
||||||
description: A sharp sword.
|
|
||||||
abstract: true
|
|
||||||
components:
|
|
||||||
- type: Sharp
|
|
||||||
- type: Execution
|
|
||||||
doAfterDuration: 4.0
|
|
||||||
- type: DisarmMalus
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: captain's sabre
|
name: captain's sabre
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: CaptainSabre
|
id: CaptainSabre
|
||||||
description: A ceremonial weapon belonging to the captain of the station.
|
description: A ceremonial weapon belonging to the captain of the station.
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Melee/captain_sabre.rsi
|
sprite: Objects/Weapons/Melee/captain_sabre.rsi
|
||||||
state: icon
|
state: icon
|
||||||
@@ -38,13 +27,15 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- CaptainSabre
|
- CaptainSabre
|
||||||
|
- type: DisarmMalus
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: katana
|
name: katana
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: Katana
|
id: Katana
|
||||||
description: Ancient craftwork made with not so ancient plasteel.
|
description: Ancient craftwork made with not so ancient plasteel.
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Katana
|
- Katana
|
||||||
@@ -67,6 +58,7 @@
|
|||||||
- Back
|
- Back
|
||||||
- Belt
|
- Belt
|
||||||
- SuitStorage
|
- SuitStorage
|
||||||
|
- type: DisarmMalus
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: energy katana
|
name: energy katana
|
||||||
@@ -102,10 +94,11 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: machete
|
name: machete
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: Machete
|
id: Machete
|
||||||
description: A large, vicious looking blade.
|
description: A large, vicious looking blade.
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Machete
|
- Machete
|
||||||
@@ -127,6 +120,7 @@
|
|||||||
slots:
|
slots:
|
||||||
- Belt
|
- Belt
|
||||||
- SuitStorage
|
- SuitStorage
|
||||||
|
- type: DisarmMalus
|
||||||
- type: Construction
|
- type: Construction
|
||||||
deconstructionTarget: null
|
deconstructionTarget: null
|
||||||
graph: SwordGraph
|
graph: SwordGraph
|
||||||
@@ -134,10 +128,11 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: claymore
|
name: claymore
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: Claymore
|
id: Claymore
|
||||||
description: An ancient war blade.
|
description: An ancient war blade.
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Melee/claymore.rsi
|
sprite: Objects/Weapons/Melee/claymore.rsi
|
||||||
state: icon
|
state: icon
|
||||||
@@ -157,6 +152,7 @@
|
|||||||
- back
|
- back
|
||||||
- belt
|
- belt
|
||||||
- suitStorage
|
- suitStorage
|
||||||
|
- type: DisarmMalus
|
||||||
- type: Construction
|
- type: Construction
|
||||||
deconstructionTarget: null
|
deconstructionTarget: null
|
||||||
graph: SwordGraph
|
graph: SwordGraph
|
||||||
@@ -164,10 +160,11 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: cutlass
|
name: cutlass
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: Cutlass
|
id: Cutlass
|
||||||
description: A wickedly curved blade, often seen in the hands of space pirates.
|
description: A wickedly curved blade, often seen in the hands of space pirates.
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Machete
|
- Machete
|
||||||
@@ -184,13 +181,15 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Large
|
size: Large
|
||||||
sprite: Objects/Weapons/Melee/cutlass.rsi
|
sprite: Objects/Weapons/Melee/cutlass.rsi
|
||||||
|
- type: DisarmMalus
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: The Throngler
|
name: The Throngler
|
||||||
parent: BaseSword
|
parent: BaseItem
|
||||||
id: Throngler
|
id: Throngler
|
||||||
description: Why would you make this?
|
description: Why would you make this?
|
||||||
components:
|
components:
|
||||||
|
- type: Sharp
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Weapons/Melee/Throngler2.rsi
|
sprite: Objects/Weapons/Melee/Throngler2.rsi
|
||||||
state: icon
|
state: icon
|
||||||
@@ -214,3 +213,4 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi
|
sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi
|
||||||
|
- type: DisarmMalus
|
||||||
|
|||||||
Reference in New Issue
Block a user