Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #112 from FireNameFN/Executions
Browse files Browse the repository at this point in the history
Executions
  • Loading branch information
Zekins3366 authored May 3, 2024
2 parents 647f428 + 6c110ca commit 97b478d
Show file tree
Hide file tree
Showing 28 changed files with 633 additions and 53 deletions.
29 changes: 26 additions & 3 deletions Content.Server/Projectiles/ProjectileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,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 @@ -45,11 +49,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 @@ -68,9 +87,11 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St
if (!deleted)
{
_guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound);
_sharedCameraRecoil.KickCamera(target, direction);
}

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

if (component.PenetrationScore > 0 && _penetratableQuery.TryGetComponent(target, out var penetratable))
{
component.DamagedEntity = component.PenetrationScore < penetratable.StoppingPower;
Expand All @@ -87,5 +108,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;
}
}
150 changes: 145 additions & 5 deletions Content.Server/Weapons/Ranged/Systems/GunSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,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 Down Expand Up @@ -34,6 +35,7 @@ public sealed partial class GunSystem : SharedGunSystem
[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 @@ -63,14 +65,147 @@ 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;
}

projectileComponent.Weapon = gunUid;

_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 @@ -91,14 +226,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 @@ -110,6 +247,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 @@ -138,21 +276,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
9 changes: 9 additions & 0 deletions Content.Shared/Execution/DoafterEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

namespace Content.Shared.Execution;

[Serializable, NetSerializable]
public sealed partial class ExecutionDoAfterEvent : SimpleDoAfterEvent
{
}
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 97b478d

Please sign in to comment.