Fancy guns. (#152)
@@ -77,6 +77,7 @@
|
|||||||
<Compile Include="GameObjects\Components\DamageableComponent.cs" />
|
<Compile Include="GameObjects\Components\DamageableComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Doors\AirlockVisualizer2D.cs" />
|
<Compile Include="GameObjects\Components\Doors\AirlockVisualizer2D.cs" />
|
||||||
<Compile Include="GameObjects\Components\HUD\Inventory\ClientInventoryComponent.cs" />
|
<Compile Include="GameObjects\Components\HUD\Inventory\ClientInventoryComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Mobs\CameraRecoilComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Mobs\ICharacterUI.cs" />
|
<Compile Include="GameObjects\Components\Mobs\ICharacterUI.cs" />
|
||||||
<Compile Include="GameObjects\Components\Mobs\SpeciesUI.cs" />
|
<Compile Include="GameObjects\Components\Mobs\SpeciesUI.cs" />
|
||||||
<Compile Include="GameObjects\Components\Clothing\ClothingComponent.cs" />
|
<Compile Include="GameObjects\Components\Clothing\ClothingComponent.cs" />
|
||||||
@@ -84,7 +85,10 @@
|
|||||||
<Compile Include="GameObjects\Components\Power\ApcBoundUserInterface.cs" />
|
<Compile Include="GameObjects\Components\Power\ApcBoundUserInterface.cs" />
|
||||||
<Compile Include="GameObjects\Components\Power\PowerCellVisualizer2D.cs" />
|
<Compile Include="GameObjects\Components\Power\PowerCellVisualizer2D.cs" />
|
||||||
<Compile Include="GameObjects\Components\Storage\ClientStorageComponent.cs" />
|
<Compile Include="GameObjects\Components\Storage\ClientStorageComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapons\Ranged\BallisticMagazineVisualizer2D.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapons\Ranged\BallisticMagazineWeaponVisualizer2D.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapons\Ranged\ClientRangedWeaponComponent.cs" />
|
<Compile Include="GameObjects\Components\Weapons\Ranged\ClientRangedWeaponComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\EntitySystems\CameraRecoilSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\ClientNotifySystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\ClientNotifySystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\RangedWeaponSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\RangedWeaponSystem.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
|
<Compile Include="GameObjects\EntitySystems\VerbSystem.cs" />
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ using SS14.Shared.Interfaces.GameObjects;
|
|||||||
using SS14.Shared.IoC;
|
using SS14.Shared.IoC;
|
||||||
using SS14.Shared.Prototypes;
|
using SS14.Shared.Prototypes;
|
||||||
using System;
|
using System;
|
||||||
|
using Content.Client.GameObjects.Components.Mobs;
|
||||||
using Content.Client.UserInterface;
|
using Content.Client.UserInterface;
|
||||||
using Content.Shared.GameObjects.Components.Markers;
|
using Content.Shared.GameObjects.Components.Markers;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using SS14.Client.Interfaces.UserInterface;
|
using SS14.Client.Interfaces.UserInterface;
|
||||||
using SS14.Shared.Log;
|
using SS14.Shared.Log;
|
||||||
|
|
||||||
@@ -100,6 +102,9 @@ namespace Content.Client
|
|||||||
|
|
||||||
factory.Register<SharedSpawnPointComponent>();
|
factory.Register<SharedSpawnPointComponent>();
|
||||||
|
|
||||||
|
factory.Register<CameraRecoilComponent>();
|
||||||
|
factory.RegisterReference<CameraRecoilComponent, SharedCameraRecoilComponent>();
|
||||||
|
|
||||||
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
|
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
|
||||||
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
|
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
|
||||||
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
|
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using SS14.Client.GameObjects;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.Network;
|
||||||
|
using SS14.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Mobs
|
||||||
|
{
|
||||||
|
public sealed class CameraRecoilComponent : SharedCameraRecoilComponent
|
||||||
|
{
|
||||||
|
// Maximum rate of magnitude restore towards 0 kick.
|
||||||
|
private const float RestoreRateMax = 1.5f;
|
||||||
|
|
||||||
|
// Minimum rate of magnitude restore towards 0 kick.
|
||||||
|
private const float RestoreRateMin = 0.5f;
|
||||||
|
|
||||||
|
// Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax
|
||||||
|
private const float RestoreRateRamp = 0.05f;
|
||||||
|
|
||||||
|
// The maximum magnitude of the kick applied to the camera at any point.
|
||||||
|
private const float KickMagnitudeMax = 0.25f;
|
||||||
|
|
||||||
|
private Vector2 _currentKick;
|
||||||
|
private float _lastKickTime;
|
||||||
|
|
||||||
|
private EyeComponent _eye;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_eye = Owner.GetComponent<EyeComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Kick(Vector2 recoil)
|
||||||
|
{
|
||||||
|
// Use really bad math to "dampen" kicks when we're already kicked.
|
||||||
|
var existing = _currentKick.Length;
|
||||||
|
var dampen = existing/KickMagnitudeMax;
|
||||||
|
_currentKick += recoil * (1-dampen);
|
||||||
|
if (_currentKick.Length > KickMagnitudeMax)
|
||||||
|
{
|
||||||
|
_currentKick = _currentKick.Normalized * KickMagnitudeMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastKickTime = 0;
|
||||||
|
_updateEye();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||||
|
{
|
||||||
|
base.HandleMessage(message, netChannel, component);
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case RecoilKickMessage msg:
|
||||||
|
Kick(msg.Recoil);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FrameUpdate(float frameTime)
|
||||||
|
{
|
||||||
|
var magnitude = _currentKick.Length;
|
||||||
|
if (magnitude <= 0.005f)
|
||||||
|
{
|
||||||
|
_currentKick = Vector2.Zero;
|
||||||
|
_updateEye();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continually restore camera to 0.
|
||||||
|
var normalized = _currentKick.Normalized;
|
||||||
|
var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
|
||||||
|
var restore = normalized * restoreRate * frameTime;
|
||||||
|
_currentKick -= restore;
|
||||||
|
_updateEye();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _updateEye()
|
||||||
|
{
|
||||||
|
_eye.Offset = _currentKick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using SS14.Client.GameObjects;
|
||||||
|
using SS14.Client.Interfaces.GameObjects.Components;
|
||||||
|
using SS14.Shared.Utility;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||||
|
{
|
||||||
|
public sealed class BallisticMagazineVisualizer2D : AppearanceVisualizer
|
||||||
|
{
|
||||||
|
private string _baseState;
|
||||||
|
private int _steps;
|
||||||
|
|
||||||
|
public override void LoadData(YamlMappingNode node)
|
||||||
|
{
|
||||||
|
base.LoadData(node);
|
||||||
|
|
||||||
|
_baseState = node.GetNode("base_state").AsString();
|
||||||
|
_steps = node.GetNode("steps").AsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
|
{
|
||||||
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
|
if (!component.TryGetData(BallisticMagazineVisuals.AmmoCapacity, out int capacity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!component.TryGetData(BallisticMagazineVisuals.AmmoLeft, out int current))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
|
||||||
|
|
||||||
|
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using SS14.Client.GameObjects;
|
||||||
|
using SS14.Client.Interfaces.GameObjects.Components;
|
||||||
|
using SS14.Shared.Utility;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||||
|
{
|
||||||
|
public sealed class BallisticMagazineWeaponVisualizer2D : AppearanceVisualizer
|
||||||
|
{
|
||||||
|
private string _baseState;
|
||||||
|
private int _steps;
|
||||||
|
|
||||||
|
public override void LoadData(YamlMappingNode node)
|
||||||
|
{
|
||||||
|
base.LoadData(node);
|
||||||
|
|
||||||
|
_baseState = node.GetNode("base_state").AsString();
|
||||||
|
_steps = node.GetNode("steps").AsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
|
{
|
||||||
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
|
component.TryGetData(BallisticMagazineWeaponVisuals.MagazineLoaded, out bool loaded);
|
||||||
|
|
||||||
|
if (loaded)
|
||||||
|
{
|
||||||
|
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoCapacity, out int capacity))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoLeft, out int current))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
|
||||||
|
|
||||||
|
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprite.LayerSetState(0, _baseState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Content.Client.GameObjects.Components.Mobs;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.GameObjects.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
public sealed class CameraRecoilSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
EntityQuery = new TypeEntityQuery(typeof(CameraRecoilComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FrameUpdate(float frameTime)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(frameTime);
|
||||||
|
|
||||||
|
foreach (var entity in RelevantEntities)
|
||||||
|
{
|
||||||
|
var recoil = entity.GetComponent<CameraRecoilComponent>();
|
||||||
|
recoil.FrameUpdate(frameTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
<Compile Include="GameObjects\Components\Items\Storage\ServerStorageComponent.cs" />
|
<Compile Include="GameObjects\Components\Items\Storage\ServerStorageComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Items\Storage\ItemComponent.cs" />
|
<Compile Include="GameObjects\Components\Items\Storage\ItemComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Markers\SpawnPointComponent.cs" />
|
<Compile Include="GameObjects\Components\Markers\SpawnPointComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Mobs\CameraRecoilComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Mobs\DamageStates.cs" />
|
<Compile Include="GameObjects\Components\Mobs\DamageStates.cs" />
|
||||||
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\DamageThresholdTemplates.cs" />
|
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\DamageThresholdTemplates.cs" />
|
||||||
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\HumanTemplate.cs" />
|
<Compile Include="GameObjects\Components\Mobs\DamageThresholdTemplates\HumanTemplate.cs" />
|
||||||
@@ -100,6 +101,10 @@
|
|||||||
<Compile Include="GameObjects\Components\Projectiles\ThrownItemComponent.cs" />
|
<Compile Include="GameObjects\Components\Projectiles\ThrownItemComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapon\Melee\MeleeWeaponComponent.cs" />
|
<Compile Include="GameObjects\Components\Weapon\Melee\MeleeWeaponComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapon\Ranged\Hitscan\HitscanWeaponComponent.cs" />
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Hitscan\HitscanWeaponComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\BallisticBulletComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\BallisticMagazineComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\BallisticMagazineWeaponComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\BallisticWeaponComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\ProjectileWeapon.cs" />
|
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\ProjectileWeapon.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapon\Ranged\RangedWeapon.cs" />
|
<Compile Include="GameObjects\Components\Weapon\Ranged\RangedWeapon.cs" />
|
||||||
<Compile Include="GameObjects\ContainerSlot.cs" />
|
<Compile Include="GameObjects\ContainerSlot.cs" />
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ using Content.Server.Interfaces;
|
|||||||
using Content.Server.Interfaces.GameTicking;
|
using Content.Server.Interfaces.GameTicking;
|
||||||
using Content.Shared.GameObjects.Components.Inventory;
|
using Content.Shared.GameObjects.Components.Inventory;
|
||||||
using Content.Shared.GameObjects.Components.Markers;
|
using Content.Shared.GameObjects.Components.Markers;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Content.Shared.Interfaces;
|
using Content.Shared.Interfaces;
|
||||||
using SS14.Server.Interfaces.ServerStatus;
|
using SS14.Server.Interfaces.ServerStatus;
|
||||||
using SS14.Shared.Timing;
|
using SS14.Shared.Timing;
|
||||||
@@ -98,7 +99,7 @@ namespace Content.Server
|
|||||||
|
|
||||||
factory.Register<HitscanWeaponComponent>();
|
factory.Register<HitscanWeaponComponent>();
|
||||||
factory.Register<RangedWeaponComponent>();
|
factory.Register<RangedWeaponComponent>();
|
||||||
factory.Register<ProjectileWeaponComponent>();
|
factory.Register<BallisticMagazineWeaponComponent>();
|
||||||
factory.Register<ProjectileComponent>();
|
factory.Register<ProjectileComponent>();
|
||||||
factory.Register<ThrownItemComponent>();
|
factory.Register<ThrownItemComponent>();
|
||||||
factory.Register<MeleeWeaponComponent>();
|
factory.Register<MeleeWeaponComponent>();
|
||||||
@@ -127,6 +128,12 @@ namespace Content.Server
|
|||||||
factory.Register<SpawnPointComponent>();
|
factory.Register<SpawnPointComponent>();
|
||||||
factory.RegisterReference<SpawnPointComponent, SharedSpawnPointComponent>();
|
factory.RegisterReference<SpawnPointComponent, SharedSpawnPointComponent>();
|
||||||
|
|
||||||
|
factory.Register<BallisticBulletComponent>();
|
||||||
|
factory.Register<BallisticMagazineComponent>();
|
||||||
|
|
||||||
|
factory.Register<CameraRecoilComponent>();
|
||||||
|
factory.RegisterReference<CameraRecoilComponent, SharedCameraRecoilComponent>();
|
||||||
|
|
||||||
IoCManager.Register<ISharedNotifyManager, ServerNotifyManager>();
|
IoCManager.Register<ISharedNotifyManager, ServerNotifyManager>();
|
||||||
IoCManager.Register<IServerNotifyManager, ServerNotifyManager>();
|
IoCManager.Register<IServerNotifyManager, ServerNotifyManager>();
|
||||||
IoCManager.Register<IGameTicker, GameTicker>();
|
IoCManager.Register<IGameTicker, GameTicker>();
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using SS14.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Mobs
|
||||||
|
{
|
||||||
|
public sealed class CameraRecoilComponent : SharedCameraRecoilComponent
|
||||||
|
{
|
||||||
|
public override void Kick(Vector2 recoil)
|
||||||
|
{
|
||||||
|
var msg = new RecoilKickMessage(recoil);
|
||||||
|
SendNetworkMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||||
|
{
|
||||||
|
public class BallisticBulletComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "BallisticBullet";
|
||||||
|
|
||||||
|
private BallisticCaliber _caliber;
|
||||||
|
private string _projectileType;
|
||||||
|
private bool _spent;
|
||||||
|
|
||||||
|
public string ProjectileType => _projectileType;
|
||||||
|
public BallisticCaliber Caliber => _caliber;
|
||||||
|
public bool Spent
|
||||||
|
{
|
||||||
|
get => _spent;
|
||||||
|
set => _spent = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
|
||||||
|
serializer.DataField(ref _projectileType, "projectile", null);
|
||||||
|
serializer.DataField(ref _spent, "spent", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||||
|
using SS14.Server.GameObjects;
|
||||||
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||||
|
{
|
||||||
|
public class BallisticMagazineComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "BallisticMagazine";
|
||||||
|
|
||||||
|
// Stack of loaded bullets.
|
||||||
|
private readonly Stack<IEntity> _loadedBullets = new Stack<IEntity>();
|
||||||
|
private string _fillType;
|
||||||
|
|
||||||
|
private Container _bulletContainer;
|
||||||
|
private AppearanceComponent _appearance;
|
||||||
|
|
||||||
|
private BallisticMagazineType _magazineType;
|
||||||
|
private BallisticCaliber _caliber;
|
||||||
|
private int _capacity;
|
||||||
|
|
||||||
|
public BallisticMagazineType MagazineType => _magazineType;
|
||||||
|
public BallisticCaliber Caliber => _caliber;
|
||||||
|
public int Capacity => _capacity;
|
||||||
|
|
||||||
|
public int CountLoaded => _loadedBullets.Count;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(ref _magazineType, "magazine", BallisticMagazineType.Unspecified);
|
||||||
|
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
|
||||||
|
serializer.DataField(ref _fillType, "fill", null);
|
||||||
|
serializer.DataField(ref _capacity, "capacity", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||||
|
|
||||||
|
_bulletContainer =
|
||||||
|
ContainerManagerComponent.Ensure<Container>("magazine_bullet_container", Owner, out var existed);
|
||||||
|
|
||||||
|
if (!existed && _fillType != null)
|
||||||
|
{
|
||||||
|
// Load up bullets from fill.
|
||||||
|
for (var i = 0; i < Capacity; i++)
|
||||||
|
{
|
||||||
|
var bullet = Owner.EntityManager.SpawnEntity(_fillType);
|
||||||
|
AddBullet(bullet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_appearance.SetData(BallisticMagazineVisuals.AmmoCapacity, Capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddBullet(IEntity bullet)
|
||||||
|
{
|
||||||
|
if (!bullet.TryGetComponent(out BallisticBulletComponent component))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("entity isn't a bullet.", nameof(bullet));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Caliber != Caliber)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("entity is of the wrong caliber.", nameof(bullet));
|
||||||
|
}
|
||||||
|
|
||||||
|
_bulletContainer.Insert(bullet);
|
||||||
|
_loadedBullets.Push(bullet);
|
||||||
|
_updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity TakeBullet()
|
||||||
|
{
|
||||||
|
if (_loadedBullets.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bullet = _loadedBullets.Pop();
|
||||||
|
_updateAppearance();
|
||||||
|
return bullet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _updateAppearance()
|
||||||
|
{
|
||||||
|
_appearance.SetData(BallisticMagazineVisuals.AmmoLeft, CountLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BallisticMagazineType
|
||||||
|
{
|
||||||
|
Unspecified = 0,
|
||||||
|
A12mm,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using SS14.Server.GameObjects;
|
||||||
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
|
using SS14.Server.GameObjects.EntitySystems;
|
||||||
|
using SS14.Shared.Audio;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.IoC;
|
||||||
|
using SS14.Shared.Maths;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
using SS14.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||||
|
{
|
||||||
|
public class BallisticMagazineWeaponComponent : BallisticWeaponComponent, IUse, IAttackby
|
||||||
|
{
|
||||||
|
public override string Name => "BallisticMagazineWeapon";
|
||||||
|
|
||||||
|
private string _defaultMagazine;
|
||||||
|
|
||||||
|
private ContainerSlot _magazineSlot;
|
||||||
|
private BallisticMagazineType _magazineType;
|
||||||
|
|
||||||
|
public BallisticMagazineType MagazineType => _magazineType;
|
||||||
|
private IEntity Magazine => _magazineSlot.ContainedEntity;
|
||||||
|
|
||||||
|
private Random _bulletDropRandom;
|
||||||
|
private string _magInSound;
|
||||||
|
private string _magOutSound;
|
||||||
|
private string _autoEjectSound;
|
||||||
|
private bool _autoEjectMagazine;
|
||||||
|
private AppearanceComponent _appearance;
|
||||||
|
|
||||||
|
private static readonly Direction[] _randomBulletDirs = {
|
||||||
|
Direction.North,
|
||||||
|
Direction.East,
|
||||||
|
Direction.South,
|
||||||
|
Direction.West
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override int ChamberCount => 1;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(ref _magazineType, "magazine", BallisticMagazineType.Unspecified);
|
||||||
|
serializer.DataField(ref _defaultMagazine, "default_magazine", null);
|
||||||
|
serializer.DataField(ref _autoEjectMagazine, "auto_eject_magazine", false);
|
||||||
|
serializer.DataField(ref _autoEjectSound, "sound_auto_eject", null);
|
||||||
|
serializer.DataField(ref _magInSound, "sound_magazine_in", null);
|
||||||
|
serializer.DataField(ref _magOutSound, "sound_magazine_out", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||||
|
|
||||||
|
_magazineSlot =
|
||||||
|
ContainerManagerComponent.Ensure<ContainerSlot>("ballistic_gun_magazine", Owner,
|
||||||
|
out var alreadyExisted);
|
||||||
|
|
||||||
|
_bulletDropRandom = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode());
|
||||||
|
|
||||||
|
if (!alreadyExisted && _defaultMagazine != null)
|
||||||
|
{
|
||||||
|
var magazine = Owner.EntityManager.SpawnEntity(_defaultMagazine);
|
||||||
|
InsertMagazine(magazine, false);
|
||||||
|
}
|
||||||
|
_updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool InsertMagazine(IEntity magazine, bool playSound=true)
|
||||||
|
{
|
||||||
|
if (!magazine.TryGetComponent(out BallisticMagazineComponent component))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Not a magazine", nameof(magazine));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.MagazineType != MagazineType)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Wrong magazine type", nameof(magazine));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Caliber != Caliber)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Wrong caliber", nameof(magazine));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_magazineSlot.Insert(magazine))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_magInSound != null)
|
||||||
|
{
|
||||||
|
var audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
|
||||||
|
audioSystem.Play(_magInSound, Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetChambered(0) == null)
|
||||||
|
{
|
||||||
|
// No bullet in chamber, load one from magazine.
|
||||||
|
var bullet = component.TakeBullet();
|
||||||
|
if (bullet != null)
|
||||||
|
{
|
||||||
|
LoadIntoChamber(0, bullet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAppearance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EjectMagazine(bool playSound=true)
|
||||||
|
{
|
||||||
|
var entity = Magazine;
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_magazineSlot.Remove(entity))
|
||||||
|
{
|
||||||
|
entity.Transform.GridPosition = Owner.Transform.GridPosition;
|
||||||
|
if (_magOutSound != null)
|
||||||
|
{
|
||||||
|
var audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
|
||||||
|
audioSystem.Play(_magOutSound, Owner);
|
||||||
|
}
|
||||||
|
_updateAppearance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAppearance();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CycleChamberedBullet(int chamber)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(chamber == 0);
|
||||||
|
|
||||||
|
// Eject chambered bullet.
|
||||||
|
var entity = RemoveFromChamber(chamber);
|
||||||
|
entity.Transform.GridPosition = Owner.Transform.GridPosition;
|
||||||
|
entity.Transform.LocalRotation = _bulletDropRandom.Pick(_randomBulletDirs).ToAngle();
|
||||||
|
var audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
|
||||||
|
var effect = $"/Audio/items/weapons/casingfall{_bulletDropRandom.Next(1, 4)}.ogg";
|
||||||
|
audioSystem.Play(effect, entity, AudioParams.Default.WithVolume(-3));
|
||||||
|
|
||||||
|
if (Magazine != null)
|
||||||
|
{
|
||||||
|
var magComponent = Magazine.GetComponent<BallisticMagazineComponent>();
|
||||||
|
var bullet = magComponent.TakeBullet();
|
||||||
|
if (bullet != null)
|
||||||
|
{
|
||||||
|
LoadIntoChamber(0, bullet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magComponent.CountLoaded == 0 && _autoEjectMagazine)
|
||||||
|
{
|
||||||
|
EjectMagazine();
|
||||||
|
if (_autoEjectSound != null)
|
||||||
|
{
|
||||||
|
audioSystem.Play(_autoEjectSound, Owner, AudioParams.Default.WithVolume(-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseEntity(IEntity user)
|
||||||
|
{
|
||||||
|
var ret = EjectMagazine();
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
Owner.PopupMessage(user, "Magazine ejected");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Owner.PopupMessage(user, "No magazine");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Attackby(IEntity user, IEntity attackwith)
|
||||||
|
{
|
||||||
|
if (!attackwith.TryGetComponent(out BallisticMagazineComponent component))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Magazine != null)
|
||||||
|
{
|
||||||
|
Owner.PopupMessage(user, "Already got a magazine.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.MagazineType != MagazineType || component.Caliber != Caliber)
|
||||||
|
{
|
||||||
|
Owner.PopupMessage(user, "Magazine doesn't fit.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InsertMagazine(attackwith);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _updateAppearance()
|
||||||
|
{
|
||||||
|
if (Magazine != null)
|
||||||
|
{
|
||||||
|
var comp = Magazine.GetComponent<BallisticMagazineComponent>();
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, comp.CountLoaded);
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.AmmoCapacity, comp.Capacity);
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.MagazineLoaded, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, 0);
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, 0);
|
||||||
|
_appearance.SetData(BallisticMagazineWeaponVisuals.MagazineLoaded, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Verb]
|
||||||
|
public sealed class EjectMagazineVerb : Verb<BallisticMagazineWeaponComponent>
|
||||||
|
{
|
||||||
|
protected override string GetText(IEntity user, BallisticMagazineWeaponComponent component)
|
||||||
|
{
|
||||||
|
return component.Magazine == null ? "Eject magazine (magazine missing)" : "Eject magazine";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsDisabled(IEntity user, BallisticMagazineWeaponComponent component)
|
||||||
|
{
|
||||||
|
return component.Magazine == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Activate(IEntity user, BallisticMagazineWeaponComponent component)
|
||||||
|
{
|
||||||
|
component.EjectMagazine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.GameObjects.Components.Interactable;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using SS14.Server.Chat;
|
||||||
|
using SS14.Server.GameObjects.Components.Container;
|
||||||
|
using SS14.Server.GameObjects.EntitySystems;
|
||||||
|
using SS14.Shared.Interfaces.GameObjects;
|
||||||
|
using SS14.Shared.IoC;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||||
|
{
|
||||||
|
public abstract class BallisticWeaponComponent : ProjectileWeaponComponent
|
||||||
|
{
|
||||||
|
private BallisticCaliber _caliber;
|
||||||
|
private Chamber[] _chambers;
|
||||||
|
|
||||||
|
public BallisticCaliber Caliber => _caliber;
|
||||||
|
protected abstract int ChamberCount { get; }
|
||||||
|
|
||||||
|
private string _soundGunEmpty;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
|
||||||
|
serializer.DataField(ref _soundGunEmpty, "sound_empty", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_chambers = new Chamber[ChamberCount];
|
||||||
|
for (var i = 0; i < _chambers.Length; i++)
|
||||||
|
{
|
||||||
|
var container = ContainerManagerComponent.Ensure<ContainerSlot>($"ballistics_chamber_{i}", Owner);
|
||||||
|
_chambers[i] = new Chamber(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity GetChambered(int chamber) => _chambers[chamber].Slot.ContainedEntity;
|
||||||
|
|
||||||
|
public bool LoadIntoChamber(int chamber, IEntity bullet)
|
||||||
|
{
|
||||||
|
if (!bullet.TryGetComponent(out BallisticBulletComponent component))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("entity isn't a bullet.", nameof(bullet));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Caliber != Caliber)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("entity is of the wrong caliber.", nameof(bullet));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetChambered(chamber) != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_chambers[chamber].Slot.Insert(bullet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override IEntity GetFiredProjectile()
|
||||||
|
{
|
||||||
|
void PlayEmpty()
|
||||||
|
{
|
||||||
|
if (_soundGunEmpty != null)
|
||||||
|
{
|
||||||
|
var audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
|
||||||
|
audioSystem.Play(_soundGunEmpty, Owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var chambered = GetChambered(0);
|
||||||
|
if (chambered != null)
|
||||||
|
{
|
||||||
|
var bullet = chambered.GetComponent<BallisticBulletComponent>();
|
||||||
|
if (bullet.Spent)
|
||||||
|
{
|
||||||
|
PlayEmpty();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectile = Owner.EntityManager.SpawnEntity(bullet.ProjectileType);
|
||||||
|
bullet.Spent = true;
|
||||||
|
|
||||||
|
CycleChamberedBullet(0);
|
||||||
|
|
||||||
|
// Load a new bullet into the chamber from magazine.
|
||||||
|
return projectile;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayEmpty();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void CycleChamberedBullet(int chamber)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEntity RemoveFromChamber(int chamber)
|
||||||
|
{
|
||||||
|
var c = _chambers[chamber];
|
||||||
|
var loaded = c.Slot.ContainedEntity;
|
||||||
|
if (loaded != null)
|
||||||
|
{
|
||||||
|
c.Slot.Remove(loaded);
|
||||||
|
}
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Chamber
|
||||||
|
{
|
||||||
|
public Chamber(ContainerSlot slot)
|
||||||
|
{
|
||||||
|
Slot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerSlot Slot { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Server.GameObjects.Components.Projectiles;
|
using Content.Server.GameObjects.Components.Projectiles;
|
||||||
using SS14.Server.GameObjects;
|
using SS14.Server.GameObjects;
|
||||||
using SS14.Server.GameObjects.EntitySystems;
|
using SS14.Server.GameObjects.EntitySystems;
|
||||||
@@ -15,12 +16,8 @@ using SS14.Shared.ViewVariables;
|
|||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||||
{
|
{
|
||||||
public class ProjectileWeaponComponent : Component
|
public abstract class ProjectileWeaponComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "ProjectileWeapon";
|
|
||||||
|
|
||||||
private string _ProjectilePrototype = "ProjectileBullet";
|
|
||||||
|
|
||||||
private float _velocity = 20f;
|
private float _velocity = 20f;
|
||||||
private float _spreadStdDev = 3;
|
private float _spreadStdDev = 3;
|
||||||
private bool _spread = true;
|
private bool _spread = true;
|
||||||
@@ -61,16 +58,27 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
|||||||
|
|
||||||
private void Fire(IEntity user, GridCoordinates clickLocation)
|
private void Fire(IEntity user, GridCoordinates clickLocation)
|
||||||
{
|
{
|
||||||
|
var projectile = GetFiredProjectile();
|
||||||
|
if (projectile == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var userPosition = user.Transform.GridPosition; //Remember world positions are ephemeral and can only be used instantaneously
|
var userPosition = user.Transform.GridPosition; //Remember world positions are ephemeral and can only be used instantaneously
|
||||||
var angle = new Angle(clickLocation.Position - userPosition.Position);
|
var angle = new Angle(clickLocation.Position - userPosition.Position);
|
||||||
|
|
||||||
|
if (user.TryGetComponent(out CameraRecoilComponent recoil))
|
||||||
|
{
|
||||||
|
var recoilVec = angle.ToVec() * -0.15f;
|
||||||
|
recoil.Kick(recoilVec);
|
||||||
|
}
|
||||||
|
|
||||||
if (Spread)
|
if (Spread)
|
||||||
{
|
{
|
||||||
angle += Angle.FromDegrees(_spreadRandom.NextGaussian(0, SpreadStdDev));
|
angle += Angle.FromDegrees(_spreadRandom.NextGaussian(0, SpreadStdDev));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Spawn the projectilePrototype
|
projectile.Transform.GridPosition = userPosition;
|
||||||
var projectile = IoCManager.Resolve<IServerEntityManager>().ForceSpawnEntityAt(_ProjectilePrototype, userPosition);
|
|
||||||
|
|
||||||
//Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character
|
//Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character
|
||||||
projectile.GetComponent<ProjectileComponent>().IgnoreEntity(user);
|
projectile.GetComponent<ProjectileComponent>().IgnoreEntity(user);
|
||||||
@@ -84,5 +92,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
|||||||
// Sound!
|
// Sound!
|
||||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().Play("/Audio/gunshot_c20.ogg", user);
|
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().Play("/Audio/gunshot_c20.ogg", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get a projectile for firing. If null, nothing will be fired.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract IEntity GetFiredProjectile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BallisticCaliber
|
||||||
|
{
|
||||||
|
Unspecified = 0,
|
||||||
|
A12mm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,12 @@
|
|||||||
<Compile Include="GameObjects\Components\Inventory\InventoryTemplates.cs" />
|
<Compile Include="GameObjects\Components\Inventory\InventoryTemplates.cs" />
|
||||||
<Compile Include="GameObjects\Components\Inventory\SharedInventoryComponent.cs" />
|
<Compile Include="GameObjects\Components\Inventory\SharedInventoryComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Markers\SharedSpawnPointComponent.cs" />
|
<Compile Include="GameObjects\Components\Markers\SharedSpawnPointComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Mobs\SharedCameraRecoilComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Power\PowerShared.cs" />
|
<Compile Include="GameObjects\Components\Power\PowerShared.cs" />
|
||||||
<Compile Include="GameObjects\Components\Power\SharedPowerCellComponent.cs" />
|
<Compile Include="GameObjects\Components\Power\SharedPowerCellComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Storage\SharedStorageComponent.cs" />
|
<Compile Include="GameObjects\Components\Storage\SharedStorageComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapons\Ranged\SharedBallisticMagazineComponent.cs" />
|
||||||
|
<Compile Include="GameObjects\Components\Weapons\Ranged\SharedBallisticMagazineWeaponComponent.cs" />
|
||||||
<Compile Include="GameObjects\Components\Weapons\Ranged\SharedRangedWeaponComponent.cs" />
|
<Compile Include="GameObjects\Components\Weapons\Ranged\SharedRangedWeaponComponent.cs" />
|
||||||
<Compile Include="GameObjects\ContentNetIDs.cs" />
|
<Compile Include="GameObjects\ContentNetIDs.cs" />
|
||||||
<Compile Include="GameObjects\EntitySystemMessages\VerbSystemMessages.cs" />
|
<Compile Include="GameObjects\EntitySystemMessages\VerbSystemMessages.cs" />
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using SS14.Shared.GameObjects;
|
||||||
|
using SS14.Shared.Maths;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Mobs
|
||||||
|
{
|
||||||
|
public abstract class SharedCameraRecoilComponent : Component
|
||||||
|
{
|
||||||
|
public sealed override string Name => "CameraRecoil";
|
||||||
|
|
||||||
|
public override uint? NetID => ContentNetIDs.CAMERA_RECOIL;
|
||||||
|
|
||||||
|
public abstract void Kick(Vector2 recoil);
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
protected class RecoilKickMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public readonly Vector2 Recoil;
|
||||||
|
|
||||||
|
public RecoilKickMessage(Vector2 recoil)
|
||||||
|
{
|
||||||
|
Directed = true;
|
||||||
|
Recoil = recoil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Weapons.Ranged
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BallisticMagazineVisuals
|
||||||
|
{
|
||||||
|
AmmoCapacity,
|
||||||
|
AmmoLeft,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using SS14.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Weapons.Ranged
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BallisticMagazineWeaponVisuals
|
||||||
|
{
|
||||||
|
MagazineLoaded,
|
||||||
|
AmmoCapacity,
|
||||||
|
AmmoLeft,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
public const uint INVENTORY = 1006;
|
public const uint INVENTORY = 1006;
|
||||||
public const uint POWER_DEBUG_TOOL = 1007;
|
public const uint POWER_DEBUG_TOOL = 1007;
|
||||||
public const uint CONSTRUCTOR = 1008;
|
public const uint CONSTRUCTOR = 1008;
|
||||||
public const uint RANGED_WEAPON = 1010;
|
|
||||||
public const uint SPECIES = 1009;
|
public const uint SPECIES = 1009;
|
||||||
|
public const uint RANGED_WEAPON = 1010;
|
||||||
|
public const uint CAMERA_RECOIL = 1011;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Resources/Audio/items/weapons/casingfall1.ogg
Normal file
BIN
Resources/Audio/items/weapons/casingfall2.ogg
Normal file
BIN
Resources/Audio/items/weapons/casingfall3.ogg
Normal file
BIN
Resources/Audio/items/weapons/gun_empty.ogg
Normal file
BIN
Resources/Audio/items/weapons/smg_empty_alarm.ogg
Normal file
BIN
Resources/Audio/items/weapons/smg_magin.ogg
Normal file
BIN
Resources/Audio/items/weapons/smg_magout.ogg
Normal file
7
Resources/Audio/items/weapons/sources.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
gun_empty.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/gun_empty.ogg
|
||||||
|
smg_magin.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/interact/smg_magin.ogg
|
||||||
|
smg_magout.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/interact/smg_magout.ogg
|
||||||
|
smg_empty_alarm.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/smg_empty_alarm.ogg
|
||||||
|
casingfall1.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall1.ogg
|
||||||
|
casingfall2.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall2.ogg
|
||||||
|
casingfall3.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall3.ogg
|
||||||
@@ -286,13 +286,13 @@ entities:
|
|||||||
pos: -2.015625,-3.859375
|
pos: -2.015625,-3.859375
|
||||||
rot: -1.570796 rad
|
rot: -1.570796 rad
|
||||||
type: Transform
|
type: Transform
|
||||||
- type: GUNITEM
|
- type: smg_c20r
|
||||||
components:
|
components:
|
||||||
- grid: 0
|
- grid: 0
|
||||||
pos: -2.890625,-4.484375
|
pos: -2.890625,-4.484375
|
||||||
rot: -1.570796 rad
|
rot: -1.570796 rad
|
||||||
type: Transform
|
type: Transform
|
||||||
- type: GUNITEM
|
- type: smg_c20r
|
||||||
components:
|
components:
|
||||||
- grid: 0
|
- grid: 0
|
||||||
pos: -1.984375,-4.484375
|
pos: -1.984375,-4.484375
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
zoom: 0.5, 0.5
|
||||||
|
|
||||||
|
- type: CameraRecoil
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: MobObserver
|
id: MobObserver
|
||||||
name: Observer
|
name: Observer
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
- type: entity
|
|
||||||
id: ProjectileBullet
|
|
||||||
name: ProjectileBullet
|
|
||||||
description: If you can see this you're dead!
|
|
||||||
components:
|
|
||||||
- type: Sprite
|
|
||||||
directional: false
|
|
||||||
texture: Objects/projectilebullet.png
|
|
||||||
#rotation: -180
|
|
||||||
|
|
||||||
- type: Icon
|
|
||||||
texture: Objects/projectilebullet.png
|
|
||||||
- type: BoundingBox
|
|
||||||
aabb: -0.2,-0.2,0.2,0.2
|
|
||||||
- type: Physics
|
|
||||||
edgeslide: false
|
|
||||||
- type: Projectile
|
|
||||||
- type: Collidable
|
|
||||||
hard: false
|
|
||||||
|
|||||||
@@ -19,28 +19,6 @@
|
|||||||
sprite: Objects/laser_retro.rsi
|
sprite: Objects/laser_retro.rsi
|
||||||
prefix: 100
|
prefix: 100
|
||||||
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
name: C-20r Sub Machine Gun
|
|
||||||
parent: BaseItem
|
|
||||||
id: GUNITEM
|
|
||||||
description: A rooty tooty point and shooty
|
|
||||||
components:
|
|
||||||
- type: Sprite
|
|
||||||
sprite: Objects/c20r.rsi
|
|
||||||
state: c20r-20
|
|
||||||
- type: Icon
|
|
||||||
sprite: Objects/c20r.rsi
|
|
||||||
state: c20r-20
|
|
||||||
- type: RangedWeapon
|
|
||||||
automatic: true
|
|
||||||
firerate: 8
|
|
||||||
- type: ProjectileWeapon
|
|
||||||
- type: Item
|
|
||||||
Size: 24
|
|
||||||
sprite: Objects/c20r.rsi
|
|
||||||
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Spear
|
name: Spear
|
||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
|
|||||||
50
Resources/Prototypes/Entities/weapons/ammunition.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
- type: entity
|
||||||
|
id: magazine_12mm
|
||||||
|
name: "12mm magazine"
|
||||||
|
parent: BaseItem
|
||||||
|
components:
|
||||||
|
- type: BallisticMagazine
|
||||||
|
caliber: A12mm
|
||||||
|
magazine: A12mm
|
||||||
|
capacity: 20
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/items/magazine_12mm.rsi
|
||||||
|
state: 12mm-0
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: Objects/items/magazine_12mm.rsi
|
||||||
|
state: 12mm-0
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: BallisticMagazineVisualizer2D
|
||||||
|
base_state: 12mm
|
||||||
|
steps: 11
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: magazine_12mm_filled
|
||||||
|
name: "12mm magazine"
|
||||||
|
parent: magazine_12mm
|
||||||
|
components:
|
||||||
|
- type: BallisticMagazine
|
||||||
|
fill: ammo_casing_12mm
|
||||||
|
- type: Icon
|
||||||
|
state: 12mm-10
|
||||||
|
- type: Sprite
|
||||||
|
state: 12mm-10
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ammo_casing_12mm
|
||||||
|
name: "12mm bullet"
|
||||||
|
parent: BaseItem
|
||||||
|
components:
|
||||||
|
- type: BallisticBullet
|
||||||
|
caliber: A12mm
|
||||||
|
projectile: ProjectileBullet
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/items/ammo_casing.rsi
|
||||||
|
state: s-casing
|
||||||
|
drawdepth: FloorObjects
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/items/ammo_casing.rsi
|
||||||
|
state: s-casing
|
||||||
|
|
||||||
35
Resources/Prototypes/Entities/weapons/guns.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
- type: entity
|
||||||
|
name: C-20r Sub Machine Gun
|
||||||
|
parent: BaseItem
|
||||||
|
id: smg_c20r
|
||||||
|
description: A rooty tooty point and shooty.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: Objects/c20r.rsi
|
||||||
|
state: c20r-5
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/c20r.rsi
|
||||||
|
state: c20r-5
|
||||||
|
- type: RangedWeapon
|
||||||
|
automatic: true
|
||||||
|
firerate: 8
|
||||||
|
- type: BallisticMagazineWeapon
|
||||||
|
caliber: A12mm
|
||||||
|
magazine: A12mm
|
||||||
|
default_magazine: magazine_12mm_filled
|
||||||
|
auto_eject_magazine: true
|
||||||
|
sound_auto_eject: /Audio/items/weapons/smg_empty_alarm.ogg
|
||||||
|
sound_magazine_in: /Audio/items/weapons/smg_magin.ogg
|
||||||
|
sound_magazine_out: /Audio/items/weapons/smg_magout.ogg
|
||||||
|
sound_empty: /Audio/items/weapons/gun_empty.ogg
|
||||||
|
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: BallisticMagazineWeaponVisualizer2D
|
||||||
|
base_state: c20r
|
||||||
|
steps: 6
|
||||||
|
|
||||||
|
- type: Item
|
||||||
|
Size: 24
|
||||||
|
sprite: Objects/c20r.rsi
|
||||||
19
Resources/Prototypes/Entities/weapons/projectiles.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
- type: entity
|
||||||
|
id: ProjectileBullet
|
||||||
|
name: ProjectileBullet
|
||||||
|
description: If you can see this you're dead!
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
directional: false
|
||||||
|
texture: Objects/projectilebullet.png
|
||||||
|
#rotation: -180
|
||||||
|
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/projectilebullet.png
|
||||||
|
- type: BoundingBox
|
||||||
|
aabb: -0.2,-0.2,0.2,0.2
|
||||||
|
- type: Physics
|
||||||
|
edgeslide: false
|
||||||
|
- type: Projectile
|
||||||
|
- type: Collidable
|
||||||
|
hard: false
|
||||||
BIN
Resources/Textures/Objects/c20r.rsi/c20r-1.png
Normal file
|
After Width: | Height: | Size: 509 B |
|
Before Width: | Height: | Size: 498 B |
|
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 498 B |
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
@@ -1 +1 @@
|
|||||||
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "c20r", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-0", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-12", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-16", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-20", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-4", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-8", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "c20r", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-0", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-3", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-4", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-5", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-1", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-2", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/0b3ab17dbad632ddf738b63900ef8df1926bba47/icons/obj/ammo.dmi", "states": [{"name": "s-casing", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
||||||
BIN
Resources/Textures/Objects/items/ammo_casing.rsi/s-casing.png
Normal file
|
After Width: | Height: | Size: 231 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-0.png
Normal file
|
After Width: | Height: | Size: 356 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-1.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-10.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-2.png
Normal file
|
After Width: | Height: | Size: 389 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-3.png
Normal file
|
After Width: | Height: | Size: 396 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-4.png
Normal file
|
After Width: | Height: | Size: 412 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-5.png
Normal file
|
After Width: | Height: | Size: 414 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-6.png
Normal file
|
After Width: | Height: | Size: 415 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-7.png
Normal file
|
After Width: | Height: | Size: 417 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-8.png
Normal file
|
After Width: | Height: | Size: 412 B |
BIN
Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-9.png
Normal file
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1 @@
|
|||||||
|
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/0b3ab17dbad632ddf738b63900ef8df1926bba47/icons/obj/ammo.dmi", "states": [{"name": "12mm-0", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-1", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-10", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-2", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-3", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-4", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-5", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-6", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-7", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-8", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-9", "directions": 1, "delays": [[1.0]]}]}
|
||||||