* First commit * Lockers * Lockers electric boogaloo * Crates and Lockers * Almost finishing the Textures folder * Updating texture paths. Reminder to fix: * Lockers * Windows * Vending Machines * APC * Catwalks * Bedsheets and Cloaks * Status effects * dont know what happened here * Commit before merge * re-organizing * Lockers broken * Commit before merge * Submodule * renaming * Fixing most issues * forgot these ones * Updating submodule * typo * Fixing some paths * fixing some paths * updating submodule * (hopefully) fixing the submodule
177 lines
6.0 KiB
C#
177 lines
6.0 KiB
C#
using System;
|
|
using System.Timers;
|
|
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Server.GameObjects.EntitySystems;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.GameObjects.EntitySystemMessages;
|
|
using Robust.Shared.GameObjects.Systems;
|
|
using Robust.Shared.Interfaces.GameObjects;
|
|
using Robust.Shared.Interfaces.Timing;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Serialization;
|
|
using Robust.Shared.Utility;
|
|
using Logger = Robust.Shared.Log.Logger;
|
|
using Timer = Robust.Shared.Timers.Timer;
|
|
|
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
|
|
{
|
|
/// <summary>
|
|
/// Allows this entity to be loaded into a ranged weapon (if the caliber matches)
|
|
/// Generally used for bullets but can be used for other things like bananas
|
|
/// </summary>
|
|
[RegisterComponent]
|
|
public class AmmoComponent : Component
|
|
{
|
|
public override string Name => "Ammo";
|
|
public BallisticCaliber Caliber => _caliber;
|
|
private BallisticCaliber _caliber;
|
|
public bool Spent
|
|
{
|
|
get
|
|
{
|
|
if (_ammoIsProjectile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return _spent;
|
|
}
|
|
}
|
|
private bool _spent;
|
|
|
|
/// <summary>
|
|
/// Used for anything without a case that fires itself
|
|
/// </summary>
|
|
private bool _ammoIsProjectile;
|
|
|
|
/// <summary>
|
|
/// Used for something that is deleted when the projectile is retrieved
|
|
/// </summary>
|
|
public bool Caseless => _caseless;
|
|
private bool _caseless;
|
|
// Rather than managing bullet / case state seemed easier to just have 2 toggles
|
|
// ammoIsProjectile being for a beanbag for example and caseless being for ClRifle rounds
|
|
|
|
/// <summary>
|
|
/// For shotguns where they might shoot multiple entities
|
|
/// </summary>
|
|
public int ProjectilesFired => _projectilesFired;
|
|
private int _projectilesFired;
|
|
private string _projectileId;
|
|
// How far apart each entity is if multiple are shot
|
|
public float EvenSpreadAngle => _evenSpreadAngle;
|
|
private float _evenSpreadAngle;
|
|
/// <summary>
|
|
/// How fast the shot entities travel
|
|
/// </summary>
|
|
public float Velocity => _velocity;
|
|
private float _velocity;
|
|
|
|
private string _muzzleFlashSprite;
|
|
|
|
public string SoundCollectionEject => _soundCollectionEject;
|
|
private string _soundCollectionEject;
|
|
|
|
public override void ExposeData(ObjectSerializer serializer)
|
|
{
|
|
base.ExposeData(serializer);
|
|
// For shotty of whatever as well
|
|
serializer.DataField(ref _projectileId, "projectile", null);
|
|
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
|
|
serializer.DataField(ref _projectilesFired, "projectilesFired", 1);
|
|
// Used for shotty to determine overall pellet spread
|
|
serializer.DataField(ref _evenSpreadAngle, "ammoSpread", 0);
|
|
serializer.DataField(ref _velocity, "ammoVelocity", 20.0f);
|
|
serializer.DataField(ref _ammoIsProjectile, "isProjectile", false);
|
|
serializer.DataField(ref _caseless, "caseless", false);
|
|
// Being both caseless and shooting yourself doesn't make sense
|
|
DebugTools.Assert(!(_ammoIsProjectile && _caseless));
|
|
serializer.DataField(ref _muzzleFlashSprite, "muzzleFlash", "Objects/Weapons/Guns/Projectiles/bullet_muzzle.png");
|
|
serializer.DataField(ref _soundCollectionEject, "soundCollectionEject", "CasingEject");
|
|
|
|
if (_projectilesFired < 1)
|
|
{
|
|
Logger.Error("Ammo can't have less than 1 projectile");
|
|
}
|
|
|
|
if (_evenSpreadAngle > 0 && _projectilesFired == 1)
|
|
{
|
|
Logger.Error("Can't have an even spread if only 1 projectile is fired");
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
public IEntity TakeBullet(GridCoordinates spawnAt)
|
|
{
|
|
if (_ammoIsProjectile)
|
|
{
|
|
return Owner;
|
|
}
|
|
|
|
if (_spent)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
_spent = true;
|
|
if (Owner.TryGetComponent(out AppearanceComponent appearanceComponent))
|
|
{
|
|
appearanceComponent.SetData(AmmoVisuals.Spent, true);
|
|
}
|
|
|
|
var entity = Owner.EntityManager.SpawnEntity(_projectileId, spawnAt);
|
|
DebugTools.AssertNotNull(entity);
|
|
return entity;
|
|
}
|
|
|
|
public void MuzzleFlash(GridCoordinates grid, Angle angle)
|
|
{
|
|
if (_muzzleFlashSprite == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var time = IoCManager.Resolve<IGameTiming>().CurTime;
|
|
var deathTime = time + TimeSpan.FromMilliseconds(200);
|
|
// Offset the sprite so it actually looks like it's coming from the gun
|
|
var offset = angle.ToVec().Normalized / 2;
|
|
|
|
var message = new EffectSystemMessage
|
|
{
|
|
EffectSprite = _muzzleFlashSprite,
|
|
Born = time,
|
|
DeathTime = deathTime,
|
|
Coordinates = grid.Translated(offset),
|
|
//Rotated from east facing
|
|
Rotation = (float) angle.Theta,
|
|
Color = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
|
|
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
|
Shaded = false
|
|
};
|
|
EntitySystem.Get<EffectSystem>().CreateParticle(message);
|
|
}
|
|
}
|
|
|
|
public enum BallisticCaliber
|
|
{
|
|
Unspecified = 0,
|
|
A357, // Placeholder?
|
|
ClRifle,
|
|
SRifle,
|
|
Pistol,
|
|
A35, // Placeholder?
|
|
LRifle,
|
|
Magnum,
|
|
AntiMaterial,
|
|
Shotgun,
|
|
Cap,
|
|
Rocket,
|
|
Dart, // Placeholder
|
|
Grenade,
|
|
Energy,
|
|
}
|
|
}
|