Skip to content

Commit

Permalink
Cleanup ExecutionSystem (#24382)
Browse files Browse the repository at this point in the history
* Creat Execution Component and add to sharp items

* Kill Server ExecutionSystem. Create ExecutionSystem in shared. Create ActiveExecution Component.
Transferred the Execution system into shared. Heavily re-wrote the system in order to reduce duplication,
and remove gun code from the system.
The melee weapon modifier which was dependant on swing rate was removed.

The ActiveExecutionComponent was created in order to apply the damage modifier to the shot from a gun execution.
It is added just before the gun fires and removed after an attempt is made.

* Fix bugs

The execution completed text will now only show up if the gun fires.

The client also no longer crashes because I forgot to network the component.

* Remove clumsy text

* Make BaseSword abstract

* Add ExecutionComponent to every weapon

* Fix bug

* Remove execution comp from battery weapons

Currently the gun system does not have a way to alter hitscan damage like it does with projectiles.

* Cleanup

* Revert "Remove clumsy text"

This reverts commit a46da6448d5d179a4e936f9213d5622bedb58a16.

* Actually fix the ExecutionSystem

Everything about the shot goes through the gun system now.
The Damage multiplier is only applied when a projectile impacts the target so people that get in the way don't get hit
with 9 times damage for no reason.

In order to make suicides work I needed to create fake EntityCoordinates because the gun system and the projectile
system do not play well with a projectile that has the same start and end position.

* Make launchers able to execute

* Fix prediction bug

The OnAmmoShotEvent is only raised on the server.

* Readd ability for clowns to accidentally shoot themselves while executing

* Cleanup

* Reset melee cooldown to initial value

* Address reviews fix bug

Addressed reviews on overriding messages.
Now I actually mark doafters as handled.
Return normal cooldown to some meleeweapons I forgot on the previous commit.

* Address Reviews

Remove duplication

* Exorcise codebase

Remove evil null coercion that I was sure I removed a while ago

* Address reviews again

* Remove melee weapon attack logic and rely on the system. Remove gun and
melee checks.

* Make system functional again and cleanup

* Remove code I forgot to remove

* Cleanup

* stalled

* Selectively revert gun penetration

The collision layer check doesn't work and I don't have time to fix it.

* Fixes

---------

Co-authored-by: metalgearsloth <[email protected]>
  • Loading branch information
nikthechampiongr and metalgearsloth authored Feb 25, 2024
1 parent ec5edc3 commit bcbe2ec
Show file tree
Hide file tree
Showing 27 changed files with 565 additions and 477 deletions.
397 changes: 0 additions & 397 deletions Content.Server/Execution/ExecutionSystem.cs

This file was deleted.

28 changes: 24 additions & 4 deletions Content.Server/Projectiles/ProjectileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
{
// This is so entities that shouldn't get a collision are ignored.
if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard
|| component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
|| component.DamagedEntity || component is
{ Weapon: null, OnlyCollideWhenShot: true })
{
return;
}

var target = args.OtherEntity;

// it's here so this check is only done once before possible hit
var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false);
RaiseLocalEvent(target, ref attemptEv);
Expand All @@ -41,11 +45,26 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
return;
}

if (TryHandleProjectile(target, (uid, component)))
{
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)
{
var uid = projectile.Owner;
var component = projectile.Comp;

var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter);
RaiseLocalEvent(uid, ref ev);

var otherName = ToPrettyString(target);
var direction = args.OurBody.LinearVelocity.Normalized();
var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter);
var deleted = Deleted(target);

Expand All @@ -64,12 +83,11 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
if (!deleted)
{
_guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound);
_sharedCameraRecoil.KickCamera(target, direction);
}

component.DamagedEntity = true;

var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target, args.OtherFixture);
var afterProjectileHitEvent = new AfterProjectileHitEvent(component.Damage, target);
RaiseLocalEvent(uid, ref afterProjectileHitEvent);

if (component.DeleteOnCollide)
Expand All @@ -79,5 +97,7 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
{
RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager));
}

return !deleted;
}
}
149 changes: 143 additions & 6 deletions Content.Server/Weapons/Ranged/Systems/GunSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Content.Server.Cargo.Systems;
using Content.Server.Interaction;
using Content.Server.Power.EntitySystems;
using Content.Server.Projectiles;
using Content.Server.Stunnable;
using Content.Server.Weapons.Ranged.Components;
using Content.Shared.Damage;
Expand All @@ -29,13 +30,13 @@ namespace Content.Server.Weapons.Ranged.Systems;

public sealed partial class GunSystem : SharedGunSystem
{
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
[Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
[Dependency] private readonly ProjectileSystem _projectile = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StunSystem _stun = default!;
Expand Down Expand Up @@ -65,14 +66,145 @@ private void OnBallisticPrice(EntityUid uid, BallisticAmmoProviderComponent comp
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, new List<EntityUid>() { 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));
// 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,
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
{
userImpulse = true;

// Try a clumsy roll
// TODO: Who put this here
if (TryComp<ClumsyComponent>(user, out var clumsy) && gun.ClumsyProof == false)
if (TryComp<ClumsyComponent>(user, out var clumsy) && !gun.ClumsyProof)
{
for (var i = 0; i < ammo.Count; i++)
{
Expand All @@ -93,14 +225,16 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid?
}
}

// As the above message wasn't obvious stop putting stuff here and use events

var fromMap = fromCoordinates.ToMap(EntityManager, TransformSystem);
var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem);
var mapDirection = toMap - fromMap.Position;
var mapAngle = mapDirection.ToAngle();
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 var grid)
var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out _)
? fromCoordinates.WithEntityId(gridUid, EntityManager)
: new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position);

Expand All @@ -112,6 +246,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid?
// 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)
{
Expand Down Expand Up @@ -140,21 +275,23 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid?
{
var uid = Spawn(cartridge.Prototype, fromEnt);
ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
shotProjectiles.Add(uid);
cartridgeBullets.Add(uid);
}
}
else
{
var uid = Spawn(cartridge.Prototype, fromEnt);
ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user);
shotProjectiles.Add(uid);
cartridgeBullets.Add(uid);
}

RaiseLocalEvent(ent!.Value, new AmmoShotEvent()
{
FiredProjectiles = shotProjectiles,
FiredProjectiles = cartridgeBullets,
});

shotProjectiles.AddRange(cartridgeBullets);
cartridgeBullets.Clear();
SetCartridgeSpent(ent.Value, cartridge, true);
MuzzleFlash(gunUid, cartridge, user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Expand Down
26 changes: 26 additions & 0 deletions Content.Shared/Execution/ExecutionComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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;
}
Loading

0 comments on commit bcbe2ec

Please sign in to comment.