2021-03-05 07:23:09 +00:00
|
|
|
using System;
|
2020-06-22 05:47:15 +10:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2020-08-18 14:39:08 +02:00
|
|
|
using System.Threading.Tasks;
|
2021-06-09 22:19:39 +02:00
|
|
|
using Content.Server.Camera;
|
|
|
|
|
using Content.Server.Projectiles.Components;
|
|
|
|
|
using Content.Server.Weapon.Ranged.Ammunition.Components;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Content.Shared.Audio;
|
2021-06-09 22:19:39 +02:00
|
|
|
using Content.Shared.Damage.Components;
|
|
|
|
|
using Content.Shared.Examine;
|
|
|
|
|
using Content.Shared.Interaction;
|
|
|
|
|
using Content.Shared.Weapons.Ranged.Components;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Robust.Shared.Audio;
|
2020-09-13 14:23:52 +02:00
|
|
|
using Robust.Shared.GameObjects;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Robust.Shared.IoC;
|
2020-07-24 14:18:37 +02:00
|
|
|
using Robust.Shared.Localization;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Robust.Shared.Log;
|
|
|
|
|
using Robust.Shared.Map;
|
|
|
|
|
using Robust.Shared.Maths;
|
2021-02-11 01:13:03 -08:00
|
|
|
using Robust.Shared.Physics;
|
2021-03-08 04:09:59 +11:00
|
|
|
using Robust.Shared.Physics.Broadphase;
|
2021-03-16 15:50:20 +01:00
|
|
|
using Robust.Shared.Player;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Robust.Shared.Prototypes;
|
|
|
|
|
using Robust.Shared.Random;
|
|
|
|
|
using Robust.Shared.Serialization;
|
2021-03-05 01:08:38 +01:00
|
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
2021-02-11 01:13:03 -08:00
|
|
|
using Robust.Shared.Timing;
|
2020-06-22 05:47:15 +10:00
|
|
|
using Robust.Shared.Utility;
|
|
|
|
|
|
2021-06-09 22:19:39 +02:00
|
|
|
namespace Content.Server.Weapon.Ranged.Barrels.Components
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// All of the ranged weapon components inherit from this to share mechanics like shooting etc.
|
|
|
|
|
/// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.)
|
|
|
|
|
/// </summary>
|
2021-03-05 01:08:38 +01:00
|
|
|
public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IUse, IInteractUsing, IExamine, ISerializationHooks
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
// There's still some of py01 and PJB's work left over, especially in underlying shooting logic,
|
|
|
|
|
// it's just when I re-organised it changed me as the contributor
|
2020-08-24 14:10:28 +02:00
|
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
|
|
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
public override FireRateSelector FireRateSelector => _fireRateSelector;
|
2021-03-05 01:08:38 +01:00
|
|
|
|
|
|
|
|
[DataField("currentSelector")]
|
|
|
|
|
private FireRateSelector _fireRateSelector = FireRateSelector.Safety;
|
|
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
public override FireRateSelector AllRateSelectors => _fireRateSelector;
|
2021-03-05 01:08:38 +01:00
|
|
|
|
|
|
|
|
[DataField("allSelectors")]
|
2020-06-22 05:47:15 +10:00
|
|
|
private FireRateSelector _allRateSelectors;
|
2021-03-05 01:08:38 +01:00
|
|
|
|
2021-05-04 15:37:16 +02:00
|
|
|
[DataField("fireRate")]
|
2021-03-05 01:08:38 +01:00
|
|
|
public override float FireRate { get; } = 2f;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
// _lastFire is when we actually fired (so if we hold the button then recoil doesn't build up if we're not firing)
|
|
|
|
|
private TimeSpan _lastFire;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
public abstract IEntity? PeekAmmo();
|
|
|
|
|
public abstract IEntity? TakeProjectile(EntityCoordinates spawnAt);
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
// Recoil / spray control
|
2021-03-05 01:08:38 +01:00
|
|
|
[DataField("minAngle")]
|
|
|
|
|
private float _minAngleDegrees;
|
|
|
|
|
|
|
|
|
|
public Angle MinAngle { get; private set; }
|
|
|
|
|
|
|
|
|
|
[DataField("maxAngle")]
|
|
|
|
|
private float _maxAngleDegrees = 45;
|
|
|
|
|
|
|
|
|
|
public Angle MaxAngle { get; private set; }
|
|
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
private Angle _currentAngle = Angle.Zero;
|
2021-03-05 01:08:38 +01:00
|
|
|
|
|
|
|
|
[DataField("angleDecay")]
|
|
|
|
|
private float _angleDecayDegrees = 20;
|
|
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
/// <summary>
|
|
|
|
|
/// How slowly the angle's theta decays per second in radians
|
|
|
|
|
/// </summary>
|
2021-03-05 01:08:38 +01:00
|
|
|
public float AngleDecay { get; private set; }
|
|
|
|
|
|
|
|
|
|
[DataField("angleIncrease")]
|
|
|
|
|
private float? _angleIncreaseDegrees;
|
|
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
/// <summary>
|
|
|
|
|
/// How quickly the angle's theta builds for every shot fired in radians
|
|
|
|
|
/// </summary>
|
2021-03-05 01:08:38 +01:00
|
|
|
public float AngleIncrease { get; private set; }
|
|
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
// Multiplies the ammo spread to get the final spread of each pellet
|
2021-03-05 01:08:38 +01:00
|
|
|
[DataField("ammoSpreadRatio")]
|
|
|
|
|
public float SpreadRatio { get; private set; }
|
2020-06-22 05:47:15 +10:00
|
|
|
|
2021-05-04 15:37:16 +02:00
|
|
|
[DataField("canMuzzleFlash")]
|
2021-03-05 01:08:38 +01:00
|
|
|
public bool CanMuzzleFlash { get; } = true;
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
// Sounds
|
2021-05-04 15:37:16 +02:00
|
|
|
[DataField("soundGunshot")]
|
2021-03-16 15:50:20 +01:00
|
|
|
public string? SoundGunshot { get; set; }
|
2021-03-05 01:08:38 +01:00
|
|
|
|
2021-05-04 15:37:16 +02:00
|
|
|
[DataField("soundEmpty")]
|
2021-03-05 01:08:38 +01:00
|
|
|
public string SoundEmpty { get; } = "/Audio/Weapons/Guns/Empty/empty.ogg";
|
|
|
|
|
|
|
|
|
|
void ISerializationHooks.BeforeSerialization()
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
2021-03-05 01:08:38 +01:00
|
|
|
_minAngleDegrees = (float) (MinAngle.Degrees * 2);
|
|
|
|
|
_maxAngleDegrees = (float) (MaxAngle.Degrees * 2);
|
|
|
|
|
_angleIncreaseDegrees = MathF.Round(AngleIncrease / ((float) Math.PI / 180f), 2);
|
|
|
|
|
AngleDecay = MathF.Round(AngleDecay / ((float) Math.PI / 180f), 2);
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 01:08:38 +01:00
|
|
|
void ISerializationHooks.AfterDeserialization()
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
// This hard-to-read area's dealing with recoil
|
|
|
|
|
// Use degrees in yaml as it's easier to read compared to "0.0125f"
|
2021-03-05 01:08:38 +01:00
|
|
|
MinAngle = Angle.FromDegrees(_minAngleDegrees / 2f);
|
2020-07-23 01:46:09 +02:00
|
|
|
|
|
|
|
|
// Random doubles it as it's +/- so uhh we'll just half it here for readability
|
2021-03-05 01:08:38 +01:00
|
|
|
MaxAngle = Angle.FromDegrees(_maxAngleDegrees / 2f);
|
2020-07-23 01:46:09 +02:00
|
|
|
|
2021-03-05 01:08:38 +01:00
|
|
|
_angleIncreaseDegrees ??= 40 / FireRate;
|
|
|
|
|
AngleIncrease = _angleIncreaseDegrees.Value * (float) Math.PI / 180f;
|
2020-07-23 01:46:09 +02:00
|
|
|
|
2021-03-05 01:08:38 +01:00
|
|
|
AngleDecay = _angleDecayDegrees * (float) Math.PI / 180f;
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
// For simplicity we'll enforce it this way; ammo determines max spread
|
2021-03-05 01:08:38 +01:00
|
|
|
if (SpreadRatio > 1.0f)
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
Logger.Error("SpreadRatio must be <= 1.0f for guns");
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-19 19:41:26 -07:00
|
|
|
protected override void OnAdd()
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
base.OnAdd();
|
2020-08-24 13:39:00 +02:00
|
|
|
|
2020-12-04 13:26:54 +01:00
|
|
|
Owner.EnsureComponentWarn(out ServerRangedWeaponComponent rangedWeaponComponent);
|
2020-08-22 22:29:20 +02:00
|
|
|
|
|
|
|
|
rangedWeaponComponent.Barrel ??= this;
|
2020-08-11 16:44:15 -07:00
|
|
|
rangedWeaponComponent.FireHandler += Fire;
|
|
|
|
|
rangedWeaponComponent.WeaponCanFireHandler += WeaponCanFire;
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
2021-06-19 19:41:26 -07:00
|
|
|
protected override void OnRemove()
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
base.OnRemove();
|
2021-03-16 15:50:20 +01:00
|
|
|
if (Owner.TryGetComponent(out ServerRangedWeaponComponent? rangedWeaponComponent))
|
2020-06-25 01:31:04 +10:00
|
|
|
{
|
|
|
|
|
rangedWeaponComponent.Barrel = null;
|
|
|
|
|
rangedWeaponComponent.FireHandler -= Fire;
|
|
|
|
|
rangedWeaponComponent.WeaponCanFireHandler -= WeaponCanFire;
|
|
|
|
|
}
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Angle GetRecoilAngle(Angle direction)
|
|
|
|
|
{
|
|
|
|
|
var currentTime = _gameTiming.CurTime;
|
|
|
|
|
var timeSinceLastFire = (currentTime - _lastFire).TotalSeconds;
|
2021-03-05 01:08:38 +01:00
|
|
|
var newTheta = MathHelper.Clamp(_currentAngle.Theta + AngleIncrease - AngleDecay * timeSinceLastFire, MinAngle.Theta, MaxAngle.Theta);
|
2020-06-22 05:47:15 +10:00
|
|
|
_currentAngle = new Angle(newTheta);
|
|
|
|
|
|
|
|
|
|
var random = (_robustRandom.NextDouble() - 0.5) * 2;
|
|
|
|
|
var angle = Angle.FromDegrees(direction.Degrees + _currentAngle.Degrees * random);
|
|
|
|
|
return angle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public abstract bool UseEntity(UseEntityEventArgs eventArgs);
|
2020-08-18 14:39:08 +02:00
|
|
|
|
|
|
|
|
public abstract Task<bool> InteractUsing(InteractUsingEventArgs eventArgs);
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
public void ChangeFireSelector(FireRateSelector rateSelector)
|
|
|
|
|
{
|
|
|
|
|
if ((rateSelector & AllRateSelectors) != 0)
|
|
|
|
|
{
|
|
|
|
|
_fireRateSelector = rateSelector;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual bool WeaponCanFire()
|
|
|
|
|
{
|
|
|
|
|
// If the ServerRangedWeaponComponent gets re-done probably need to add the checks here
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 16:44:15 -07:00
|
|
|
/// <summary>
|
|
|
|
|
/// Fires a round of ammo out of the weapon.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="shooter">Entity that is operating the weapon, usually the player.</param>
|
|
|
|
|
/// <param name="targetPos">Target position on the map to shoot at.</param>
|
|
|
|
|
private void Fire(IEntity shooter, Vector2 targetPos)
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
if (ShotsLeft == 0)
|
|
|
|
|
{
|
2021-03-05 01:08:38 +01:00
|
|
|
if (SoundEmpty != null)
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
2021-03-05 01:08:38 +01:00
|
|
|
SoundSystem.Play(Filter.Broadcast(), SoundEmpty, Owner.Transform.Coordinates);
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ammo = PeekAmmo();
|
2020-10-17 00:44:22 +02:00
|
|
|
var projectile = TakeProjectile(shooter.Transform.Coordinates);
|
2020-06-22 05:47:15 +10:00
|
|
|
if (projectile == null)
|
|
|
|
|
{
|
2021-03-05 01:08:38 +01:00
|
|
|
SoundSystem.Play(Filter.Broadcast(), SoundEmpty, Owner.Transform.Coordinates);
|
2020-06-22 05:47:15 +10:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point firing is confirmed
|
2020-08-11 16:44:15 -07:00
|
|
|
var direction = (targetPos - shooter.Transform.WorldPosition).ToAngle();
|
2020-06-22 05:47:15 +10:00
|
|
|
var angle = GetRecoilAngle(direction);
|
|
|
|
|
// This should really be client-side but for now we'll just leave it here
|
2021-03-16 15:50:20 +01:00
|
|
|
if (shooter.TryGetComponent(out CameraRecoilComponent? recoilComponent))
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
recoilComponent.Kick(-angle.ToVec() * 0.15f);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-26 14:08:09 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
// This section probably needs tweaking so there can be caseless hitscan etc.
|
2021-03-16 15:50:20 +01:00
|
|
|
if (projectile.TryGetComponent(out HitscanComponent? hitscan))
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
|
|
|
|
FireHitscan(shooter, hitscan, angle);
|
|
|
|
|
}
|
2021-03-16 15:50:20 +01:00
|
|
|
else if (projectile.HasComponent<ProjectileComponent>() &&
|
|
|
|
|
ammo != null &&
|
|
|
|
|
ammo.TryGetComponent(out AmmoComponent? ammoComponent))
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
2020-12-15 07:38:41 -06:00
|
|
|
FireProjectiles(shooter, projectile, ammoComponent.ProjectilesFired, ammoComponent.EvenSpreadAngle, angle, ammoComponent.Velocity, ammo);
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
if (CanMuzzleFlash)
|
|
|
|
|
{
|
2020-08-29 20:47:03 +10:00
|
|
|
ammoComponent.MuzzleFlash(Owner, angle);
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ammoComponent.Caseless)
|
|
|
|
|
{
|
|
|
|
|
ammo.Delete();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Invalid types
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
if (!string.IsNullOrEmpty(SoundGunshot))
|
|
|
|
|
{
|
|
|
|
|
SoundSystem.Play(Filter.Broadcast(), SoundGunshot, Owner.Transform.Coordinates);
|
|
|
|
|
}
|
2020-06-22 05:47:15 +10:00
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
_lastFire = _gameTiming.CurTime;
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drops a single cartridge / shell
|
|
|
|
|
/// Made as a static function just because multiple places need it
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="entity"></param>
|
|
|
|
|
/// <param name="playSound"></param>
|
|
|
|
|
/// <param name="robustRandom"></param>
|
|
|
|
|
/// <param name="prototypeManager"></param>
|
|
|
|
|
/// <param name="ejectDirections"></param>
|
|
|
|
|
public static void EjectCasing(
|
2020-06-24 20:00:31 +02:00
|
|
|
IEntity entity,
|
2020-06-22 05:47:15 +10:00
|
|
|
bool playSound = true,
|
2021-03-16 15:50:20 +01:00
|
|
|
IRobustRandom? robustRandom = null,
|
|
|
|
|
IPrototypeManager? prototypeManager = null,
|
|
|
|
|
Direction[]? ejectDirections = null)
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
2021-03-16 15:50:20 +01:00
|
|
|
robustRandom ??= IoCManager.Resolve<IRobustRandom>();
|
|
|
|
|
ejectDirections ??= new[]
|
|
|
|
|
{Direction.East, Direction.North, Direction.NorthWest, Direction.South, Direction.SouthEast, Direction.West};
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2021-03-05 07:23:09 +00:00
|
|
|
const float ejectOffset = 1.8f;
|
2020-06-22 05:47:15 +10:00
|
|
|
var ammo = entity.GetComponent<AmmoComponent>();
|
2021-03-05 07:23:09 +00:00
|
|
|
var offsetPos = ((robustRandom.NextFloat() - 0.5f) * ejectOffset, (robustRandom.NextFloat() - 0.5f) * ejectOffset);
|
2020-09-06 16:11:53 +02:00
|
|
|
entity.Transform.Coordinates = entity.Transform.Coordinates.Offset(offsetPos);
|
2020-06-22 05:47:15 +10:00
|
|
|
entity.Transform.LocalRotation = robustRandom.Pick(ejectDirections).ToAngle();
|
|
|
|
|
|
|
|
|
|
if (ammo.SoundCollectionEject == null || !playSound)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
prototypeManager ??= IoCManager.Resolve<IPrototypeManager>();
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
var soundCollection = prototypeManager.Index<SoundCollectionPrototype>(ammo.SoundCollectionEject);
|
|
|
|
|
var randomFile = robustRandom.Pick(soundCollection.PickFiles);
|
2021-03-01 20:42:54 -08:00
|
|
|
SoundSystem.Play(Filter.Broadcast(), randomFile, entity.Transform.Coordinates, AudioParams.Default.WithVolume(-1));
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drops multiple cartridges / shells on the floor
|
|
|
|
|
/// Wraps EjectCasing to make it less toxic for bulk ejections
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="entities"></param>
|
|
|
|
|
public static void EjectCasings(IEnumerable<IEntity> entities)
|
|
|
|
|
{
|
|
|
|
|
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
|
|
|
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
2021-03-05 07:23:09 +00:00
|
|
|
var ejectDirections = new[] {Direction.East, Direction.North, Direction.NorthWest, Direction.South, Direction.SouthEast, Direction.West};
|
2020-06-22 05:47:15 +10:00
|
|
|
var soundPlayCount = 0;
|
|
|
|
|
var playSound = true;
|
|
|
|
|
|
|
|
|
|
foreach (var entity in entities)
|
|
|
|
|
{
|
|
|
|
|
EjectCasing(entity, playSound, robustRandom, prototypeManager, ejectDirections);
|
|
|
|
|
soundPlayCount++;
|
|
|
|
|
if (soundPlayCount > 3)
|
|
|
|
|
{
|
|
|
|
|
playSound = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Firing
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles firing one or many projectiles
|
|
|
|
|
/// </summary>
|
2020-12-15 07:38:41 -06:00
|
|
|
private void FireProjectiles(IEntity shooter, IEntity baseProjectile, int count, float evenSpreadAngle, Angle angle, float velocity, IEntity ammo)
|
2020-06-22 05:47:15 +10:00
|
|
|
{
|
2021-03-16 15:50:20 +01:00
|
|
|
List<Angle>? sprayAngleChange = null;
|
2020-06-22 05:47:15 +10:00
|
|
|
if (count > 1)
|
|
|
|
|
{
|
2021-03-05 01:08:38 +01:00
|
|
|
evenSpreadAngle *= SpreadRatio;
|
2020-06-22 05:47:15 +10:00
|
|
|
sprayAngleChange = Linspace(-evenSpreadAngle / 2, evenSpreadAngle / 2, count);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-15 07:38:41 -06:00
|
|
|
var firedProjectiles = new List<IEntity>();
|
2020-06-22 05:47:15 +10:00
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
IEntity projectile;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
projectile = baseProjectile;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
projectile =
|
2021-03-16 15:50:20 +01:00
|
|
|
Owner.EntityManager.SpawnEntity(baseProjectile.Prototype?.ID, baseProjectile.Transform.Coordinates);
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
2020-12-15 07:38:41 -06:00
|
|
|
firedProjectiles.Add(projectile);
|
2020-06-22 05:47:15 +10:00
|
|
|
|
|
|
|
|
Angle projectileAngle;
|
|
|
|
|
|
|
|
|
|
if (sprayAngleChange != null)
|
|
|
|
|
{
|
|
|
|
|
projectileAngle = angle + sprayAngleChange[i];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
projectileAngle = angle;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 04:09:59 +11:00
|
|
|
var physics = projectile.GetComponent<IPhysBody>();
|
|
|
|
|
physics.BodyStatus = BodyStatus.InAir;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
var projectileComponent = projectile.GetComponent<ProjectileComponent>();
|
|
|
|
|
projectileComponent.IgnoreEntity(shooter);
|
2020-07-23 18:33:37 +02:00
|
|
|
|
2021-03-19 19:32:22 +01:00
|
|
|
// FIXME: Work around issue where inserting and removing an entity from a container,
|
|
|
|
|
// then setting its linear velocity in the same tick resets velocity back to zero.
|
|
|
|
|
// See SharedBroadPhaseSystem.HandleContainerInsert()... It sets Awake to false, which causes this.
|
|
|
|
|
projectile.SpawnTimer(TimeSpan.FromMilliseconds(25), () =>
|
|
|
|
|
{
|
|
|
|
|
projectile
|
|
|
|
|
.GetComponent<IPhysBody>()
|
|
|
|
|
.LinearVelocity = projectileAngle.ToVec() * velocity;
|
|
|
|
|
});
|
|
|
|
|
|
2020-07-23 18:33:37 +02:00
|
|
|
|
2021-02-22 00:46:27 +01:00
|
|
|
projectile.Transform.LocalRotation = projectileAngle + MathHelper.PiOver2;
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
2020-12-15 07:38:41 -06:00
|
|
|
ammo.SendMessage(this, new BarrelFiredMessage(firedProjectiles));
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a list of numbers that form a set of equal intervals between the start and end value. Used to calculate shotgun spread angles.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<Angle> Linspace(double start, double end, int intervals)
|
|
|
|
|
{
|
|
|
|
|
DebugTools.Assert(intervals > 1);
|
2020-06-24 20:00:31 +02:00
|
|
|
|
2020-06-22 05:47:15 +10:00
|
|
|
var linspace = new List<Angle>(intervals);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i <= intervals - 1; i++)
|
|
|
|
|
{
|
|
|
|
|
linspace.Add(Angle.FromDegrees(start + (end - start) * i / (intervals - 1)));
|
|
|
|
|
}
|
|
|
|
|
return linspace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fires hitscan entities and then displays their effects
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void FireHitscan(IEntity shooter, HitscanComponent hitscan, Angle angle)
|
|
|
|
|
{
|
2020-10-12 23:15:41 +11:00
|
|
|
var ray = new CollisionRay(Owner.Transform.Coordinates.ToMapPos(Owner.EntityManager), angle.ToVec(), (int) hitscan.CollisionMask);
|
2021-03-08 04:09:59 +11:00
|
|
|
var physicsManager = EntitySystem.Get<SharedBroadPhaseSystem>();
|
2020-06-22 05:47:15 +10:00
|
|
|
var rayCastResults = physicsManager.IntersectRay(Owner.Transform.MapID, ray, hitscan.MaxLength, shooter, false).ToList();
|
|
|
|
|
|
|
|
|
|
if (rayCastResults.Count >= 1)
|
|
|
|
|
{
|
|
|
|
|
var result = rayCastResults[0];
|
2020-10-12 23:15:41 +11:00
|
|
|
var distance = result.Distance;
|
2020-06-22 05:47:15 +10:00
|
|
|
hitscan.FireEffects(shooter, distance, angle, result.HitEntity);
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
2020-06-22 05:47:15 +10:00
|
|
|
return;
|
2020-06-24 20:00:31 +02:00
|
|
|
|
Bodysystem and damagesystem rework (#1544)
* Things and stuff with grids, unfinished w/ code debug changes.
* Updated submodule and also lost some progress cause I fucked it up xd
* First unfinished draft of the BodySystem. Doesn't compile.
* More changes to make it compile, but still just a framework. Doesn't do anything at the moment.
* Many cleanup changes.
* Revert "Merge branch 'master' of https://github.com/GlassEclipse/space-station-14 into body_system"
This reverts commit ddd4aebbc76cf2a0b7b102f72b93d55a0816c88c, reversing
changes made to 12d0dd752706bdda8879393bd8191a1199a0c978.
* Commit human.yml
* Updated a lot of things to be more classy, more progress overall, etc. etc.
* Latest update with many changes
* Minor changes
* Fixed Travis build bug
* Adds first draft of Body Scanner console, apparently I also forgot to tie Mechanisms into body parts so now a heart just sits in the Torso like a good boy :)
* Commit rest of stuff
* Latest changes
* Latest changes again
* 14 naked cowboys
* Yay!
* Latest changes (probably doesnt compile)
* Surgery!!!!!!!!!~1116y
* Cleaned some stuff up
* More cleanup
* Refactoring of code. Basic surgery path now done.
* Removed readme, has been added to HackMD
* Fixes typo (and thus test errors)
* WIP changes, committing so I can pull latest master changes
* Still working on that god awful merge
* Latest changes
* Latest changes!!
* Beginning of refactor to BoundUserInterface
* Surgery!
* Latest changes - fixes pr change requests and random fixes
* oops
* Fixes bodypart recursion
* Beginning of work on revamping the damage system.
* More latest changes
* Latest changes
* Finished merge
* Commit before removing old healthcode
* Almost done with removing speciescomponent...
* It compiles!!!
* yahoo more work
* Fixes to make it work
* Merge conflict fixes
* Deleting species visualizer was a mistake
* IDE warnings are VERBOTEN
* makes the server not kill itself on startup, some cleanup (#1)
* Namespaces, comments and exception fixes
* Fix conveyor and conveyor switch serialization
SS14 in reactive when
* Move damage, acts and body to shared
Damage cleanup
Comment cleanup
* Rename SpeciesComponent to RotationComponent and cleanup
Damage cleanup
Comment cleanup
* Fix nullable warnings
* Address old reviews
Fix off welder suicide damage type, deathmatch and suspicion
* Fix new test fail with units being able to accept items when unpowered
* Remove RotationComponent, change references to IBodyManagerComponent
* Add a bloodstream to humans
* More cleanups
* Add body conduits, connections, connectors substances and valves
* Revert "Add body conduits, connections, connectors substances and valves"
This reverts commit 9ab0b50e6b15fe98852d7b0836c0cdbf4bd76d20.
* Implement the heart mechanism behavior with the circulatory network
* Added network property to mechanism behaviors
* Changed human organ sprites and added missing ones
* Fix tests
* Add individual body part sprite rendering
* Fix error where dropped mechanisms are not initialized
* Implement client/server body damage
* Make DamageContainer take care of raising events
* Reimplement medical scanner with the new body system
* Improve the medical scanner ui
* Merge conflict fixes
* Fix crash when colliding with something
* Fix microwave suicides and eyes sprite rendering
* Fix nullable reference error
* Fix up surgery client side
* Fix missing using from merge conflict
* Add breathing
*inhale
* Merge conflict fixes
* Fix accumulatedframetime being reset to 0 instead of decreased by the threshold
https://github.com/space-wizards/space-station-14/pull/1617
* Use and add to the new AtmosHelpers
* Fix feet
* Add proper coloring to dropped body parts
* Fix Urist's lungs being too strong
* Merge conflict fixes
* Merge conflict fixes
* Merge conflict fixes
Co-authored-by: GlassEclipse <tsymall5@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
2020-08-17 01:42:42 +02:00
|
|
|
damageable.ChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero), false, Owner);
|
2020-06-22 05:47:15 +10:00
|
|
|
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
|
|
|
|
//even numbers if halfway between two numbers, rather than rounding to nearest
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hitscan.FireEffects(shooter, hitscan.MaxLength, angle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2020-07-24 14:18:37 +02:00
|
|
|
|
|
|
|
|
public virtual void Examine(FormattedMessage message, bool inDetailsRange)
|
|
|
|
|
{
|
|
|
|
|
var fireRateMessage = Loc.GetString(FireRateSelector switch
|
|
|
|
|
{
|
2021-06-21 02:13:54 +02:00
|
|
|
FireRateSelector.Safety => "server-ranged-barrel-component-on-examine-fire-rate-safety-description",
|
|
|
|
|
FireRateSelector.Single => "server-ranged-barrel-component-on-examine-fire-rate-single-description",
|
|
|
|
|
FireRateSelector.Automatic => "server-ranged-barrel-component-on-examine-fire-rate-automatic-description",
|
2020-07-24 14:18:37 +02:00
|
|
|
_ => throw new IndexOutOfRangeException()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
message.AddText(fireRateMessage);
|
|
|
|
|
}
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|
2020-12-15 07:38:41 -06:00
|
|
|
|
|
|
|
|
public class BarrelFiredMessage : ComponentMessage
|
|
|
|
|
{
|
|
|
|
|
public readonly List<IEntity> FiredProjectiles;
|
|
|
|
|
|
|
|
|
|
public BarrelFiredMessage(List<IEntity> firedProjectiles)
|
|
|
|
|
{
|
|
|
|
|
FiredProjectiles = firedProjectiles;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-22 05:47:15 +10:00
|
|
|
}
|