Re-organize all projects (#4166)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Weapons.Melee.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class MeleeLungeComponent : Component
|
||||
{
|
||||
public override string Name => "MeleeLunge";
|
||||
|
||||
private const float ResetTime = 0.3f;
|
||||
private const float BaseOffset = 0.25f;
|
||||
|
||||
private Angle _angle;
|
||||
private float _time;
|
||||
|
||||
public void SetData(Angle angle)
|
||||
{
|
||||
_angle = angle;
|
||||
_time = 0;
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
_time += frameTime;
|
||||
|
||||
var offset = Vector2.Zero;
|
||||
var deleteSelf = false;
|
||||
|
||||
if (_time > ResetTime)
|
||||
{
|
||||
deleteSelf = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = _angle.RotateVec((0, -BaseOffset));
|
||||
offset *= (ResetTime - _time) / ResetTime;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ISpriteComponent? spriteComponent))
|
||||
{
|
||||
spriteComponent.Offset = offset;
|
||||
}
|
||||
|
||||
if (deleteSelf)
|
||||
{
|
||||
Owner.RemoveComponent<MeleeLungeComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Weapons.Melee.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class MeleeWeaponArcAnimationComponent : Component
|
||||
{
|
||||
public override string Name => "MeleeWeaponArcAnimation";
|
||||
|
||||
private MeleeWeaponAnimationPrototype? _meleeWeaponAnimation;
|
||||
|
||||
private float _timer;
|
||||
private SpriteComponent? _sprite;
|
||||
private Angle _baseAngle;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_sprite = Owner.GetComponent<SpriteComponent>();
|
||||
}
|
||||
|
||||
public void SetData(MeleeWeaponAnimationPrototype prototype, Angle baseAngle, IEntity attacker, bool followAttacker = true)
|
||||
{
|
||||
_meleeWeaponAnimation = prototype;
|
||||
_sprite?.AddLayer(new RSI.StateId(prototype.State));
|
||||
_baseAngle = baseAngle;
|
||||
if(followAttacker)
|
||||
Owner.Transform.AttachParent(attacker);
|
||||
}
|
||||
|
||||
internal void Update(float frameTime)
|
||||
{
|
||||
if (_meleeWeaponAnimation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timer += frameTime;
|
||||
|
||||
var (r, g, b, a) =
|
||||
Vector4.Clamp(_meleeWeaponAnimation.Color + _meleeWeaponAnimation.ColorDelta * _timer, Vector4.Zero, Vector4.One);
|
||||
|
||||
if (_sprite != null)
|
||||
{
|
||||
_sprite.Color = new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
switch (_meleeWeaponAnimation.ArcType)
|
||||
{
|
||||
case WeaponArcType.Slash:
|
||||
var angle = Angle.FromDegrees(_meleeWeaponAnimation.Width)/2;
|
||||
Owner.Transform.WorldRotation =
|
||||
_baseAngle + Angle.Lerp(-angle, angle, (float) (_timer / _meleeWeaponAnimation.Length.TotalSeconds));
|
||||
break;
|
||||
|
||||
case WeaponArcType.Poke:
|
||||
Owner.Transform.WorldRotation = _baseAngle;
|
||||
|
||||
if (_sprite != null)
|
||||
{
|
||||
_sprite.Offset -= (0, _meleeWeaponAnimation.Speed * frameTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (_meleeWeaponAnimation.Length.TotalSeconds <= _timer)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Content.Client/Weapons/Melee/MeleeLungeSystem.cs
Normal file
20
Content.Client/Weapons/Melee/MeleeLungeSystem.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Client.Weapons.Melee.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Weapons.Melee
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MeleeLungeSystem : EntitySystem
|
||||
{
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
foreach (var meleeLungeComponent in EntityManager.ComponentManager.EntityQuery<MeleeLungeComponent>(true))
|
||||
{
|
||||
meleeLungeComponent.Update(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Content.Client/Weapons/Melee/MeleeWeaponSystem.cs
Normal file
128
Content.Client/Weapons/Melee/MeleeWeaponSystem.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using Content.Client.Weapons.Melee.Components;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using static Content.Shared.Weapons.Melee.MeleeWeaponSystemMessages;
|
||||
|
||||
namespace Content.Client.Weapons.Melee
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeNetworkEvent<PlayMeleeWeaponAnimationMessage>(PlayWeaponArc);
|
||||
SubscribeNetworkEvent<PlayLungeAnimationMessage>(PlayLunge);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
foreach (var arcAnimationComponent in EntityManager.ComponentManager.EntityQuery<MeleeWeaponArcAnimationComponent>(true))
|
||||
{
|
||||
arcAnimationComponent.Update(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayWeaponArc(PlayMeleeWeaponAnimationMessage msg)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(msg.ArcPrototype, out MeleeWeaponAnimationPrototype? weaponArc))
|
||||
{
|
||||
Logger.Error("Tried to play unknown weapon arc prototype '{0}'", msg.ArcPrototype);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetEntity(msg.Attacker, out var attacker))
|
||||
{
|
||||
// FIXME: This should never happen.
|
||||
Logger.Error($"Tried to play a weapon arc {msg.ArcPrototype}, but the attacker does not exist. attacker={msg.Attacker}, source={msg.Source}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!attacker.Deleted)
|
||||
{
|
||||
var lunge = attacker.EnsureComponent<MeleeLungeComponent>();
|
||||
lunge.SetData(msg.Angle);
|
||||
|
||||
var entity = EntityManager.SpawnEntity(weaponArc.Prototype, attacker.Transform.Coordinates);
|
||||
entity.Transform.LocalRotation = msg.Angle;
|
||||
|
||||
var weaponArcAnimation = entity.GetComponent<MeleeWeaponArcAnimationComponent>();
|
||||
weaponArcAnimation.SetData(weaponArc, msg.Angle, attacker, msg.ArcFollowAttacker);
|
||||
|
||||
// Due to ISpriteComponent limitations, weapons that don't use an RSI won't have this effect.
|
||||
if (EntityManager.TryGetEntity(msg.Source, out var source) &&
|
||||
msg.TextureEffect &&
|
||||
source.TryGetComponent(out ISpriteComponent? sourceSprite) &&
|
||||
sourceSprite.BaseRSI?.Path != null)
|
||||
{
|
||||
var sys = Get<EffectSystem>();
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var effect = new EffectSystemMessage
|
||||
{
|
||||
EffectSprite = sourceSprite.BaseRSI.Path.ToString(),
|
||||
RsiState = sourceSprite.LayerGetState(0).Name,
|
||||
Coordinates = attacker.Transform.Coordinates,
|
||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 125), 1.0f),
|
||||
ColorDelta = Vector4.Multiply(new Vector4(0, 0, 0, -10), 1.0f),
|
||||
Velocity = msg.Angle.ToWorldVec(),
|
||||
Acceleration = msg.Angle.ToWorldVec() * 5f,
|
||||
Born = curTime,
|
||||
DeathTime = curTime.Add(TimeSpan.FromMilliseconds(300f)),
|
||||
};
|
||||
sys.CreateEffect(effect);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var uid in msg.Hits)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(uid, out var hitEntity) || hitEntity.Deleted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hitEntity.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var originalColor = sprite.Color;
|
||||
var newColor = Color.Red * originalColor;
|
||||
sprite.Color = newColor;
|
||||
|
||||
hitEntity.SpawnTimer(100, () =>
|
||||
{
|
||||
// Only reset back to the original color if something else didn't change the color in the mean time.
|
||||
if (sprite.Color == newColor)
|
||||
{
|
||||
sprite.Color = originalColor;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayLunge(PlayLungeAnimationMessage msg)
|
||||
{
|
||||
if (EntityManager.TryGetEntity(msg.Source, out var entity))
|
||||
{
|
||||
entity.EnsureComponent<MeleeLungeComponent>().SetData(msg.Angle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: This should never happen.
|
||||
Logger.Error($"Tried to play a lunge animation, but the entity \"{msg.Source}\" does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.NetIDs;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClientBatteryBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
public override string Name => "BatteryBarrel";
|
||||
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
|
||||
|
||||
private StatusControl? _statusControl;
|
||||
|
||||
/// <summary>
|
||||
/// Count of bullets in the magazine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if no magazine is inserted.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
public (int count, int max)? MagazineCount { get; private set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not BatteryBarrelComponentState cast)
|
||||
return;
|
||||
|
||||
MagazineCount = cast.Magazine;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
_statusControl = new StatusControl(this);
|
||||
_statusControl.Update();
|
||||
return _statusControl;
|
||||
}
|
||||
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
if (_statusControl == control)
|
||||
{
|
||||
_statusControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly ClientBatteryBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsList;
|
||||
private readonly Label _noBatteryLabel;
|
||||
private readonly Label _ammoCount;
|
||||
|
||||
public StatusControl(ClientBatteryBarrelComponent parent)
|
||||
{
|
||||
MinHeight = 15;
|
||||
_parent = parent;
|
||||
HorizontalExpand = true;
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_bulletsList = new HBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 4
|
||||
}),
|
||||
(_noBatteryLabel = new Label
|
||||
{
|
||||
Text = "No Battery!",
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control() { MinSize = (5,0) },
|
||||
(_ammoCount = new Label
|
||||
{
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus},
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
}),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_bulletsList.RemoveAllChildren();
|
||||
|
||||
if (_parent.MagazineCount == null)
|
||||
{
|
||||
_noBatteryLabel.Visible = true;
|
||||
_ammoCount.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var (count, capacity) = _parent.MagazineCount.Value;
|
||||
|
||||
_noBatteryLabel.Visible = false;
|
||||
_ammoCount.Visible = true;
|
||||
|
||||
_ammoCount.Text = $"x{count:00}";
|
||||
capacity = Math.Min(capacity, 8);
|
||||
FillBulletRow(_bulletsList, count, capacity);
|
||||
}
|
||||
|
||||
private static void FillBulletRow(Control container, int count, int capacity)
|
||||
{
|
||||
var colorGone = Color.FromHex("#000000");
|
||||
var color = Color.FromHex("#E00000");
|
||||
|
||||
// Draw the empty ones
|
||||
for (var i = count; i < capacity; i++)
|
||||
{
|
||||
container.AddChild(new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = colorGone,
|
||||
},
|
||||
MinSize = (10, 15),
|
||||
});
|
||||
}
|
||||
|
||||
// Draw the full ones, but limit the count to the capacity
|
||||
count = Math.Min(count, capacity);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
container.AddChild(new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = color,
|
||||
},
|
||||
MinSize = (10, 15),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.NetIDs;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClientBoltActionBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
public override string Name => "BoltActionBarrel";
|
||||
public override uint? NetID => ContentNetIDs.BOLTACTION_BARREL;
|
||||
|
||||
private StatusControl? _statusControl;
|
||||
|
||||
/// <summary>
|
||||
/// chambered is true when a bullet is chambered
|
||||
/// spent is true when the chambered bullet is spent
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public (bool chambered, bool spent) Chamber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of bullets in the magazine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if no magazine is inserted.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
public (int count, int max)? MagazineCount { get; private set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not BoltActionBarrelComponentState cast)
|
||||
return;
|
||||
|
||||
Chamber = cast.Chamber;
|
||||
MagazineCount = cast.Magazine;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
_statusControl = new StatusControl(this);
|
||||
_statusControl.Update();
|
||||
return _statusControl;
|
||||
}
|
||||
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
if (_statusControl == control)
|
||||
{
|
||||
_statusControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly ClientBoltActionBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsListTop;
|
||||
private readonly HBoxContainer _bulletsListBottom;
|
||||
private readonly TextureRect _chamberedBullet;
|
||||
private readonly Label _noMagazineLabel;
|
||||
|
||||
public StatusControl(ClientBoltActionBarrelComponent parent)
|
||||
{
|
||||
MinHeight = 15;
|
||||
_parent = parent;
|
||||
HorizontalExpand = true;
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
AddChild(new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
(_bulletsListTop = new HBoxContainer {SeparationOverride = 0}),
|
||||
new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_bulletsListBottom = new HBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0
|
||||
}),
|
||||
(_noMagazineLabel = new Label
|
||||
{
|
||||
Text = "No Magazine!",
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
},
|
||||
(_chamberedBullet = new TextureRect
|
||||
{
|
||||
Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_chamberedBullet.ModulateSelfOverride =
|
||||
_parent.Chamber.chambered ?
|
||||
_parent.Chamber.spent ? Color.Red : Color.FromHex("#d7df60")
|
||||
: Color.Black;
|
||||
|
||||
_bulletsListTop.RemoveAllChildren();
|
||||
_bulletsListBottom.RemoveAllChildren();
|
||||
|
||||
if (_parent.MagazineCount == null)
|
||||
{
|
||||
_noMagazineLabel.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var (count, capacity) = _parent.MagazineCount.Value;
|
||||
|
||||
_noMagazineLabel.Visible = false;
|
||||
|
||||
string texturePath;
|
||||
if (capacity <= 20)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
|
||||
}
|
||||
else if (capacity <= 30)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png";
|
||||
}
|
||||
|
||||
var texture = StaticIoC.ResC.GetTexture(texturePath);
|
||||
|
||||
const int tinyMaxRow = 60;
|
||||
|
||||
if (capacity > tinyMaxRow)
|
||||
{
|
||||
FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture);
|
||||
FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillBulletRow(_bulletsListBottom, count, capacity, texture);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
|
||||
{
|
||||
var colorA = Color.FromHex("#b68f0e");
|
||||
var colorB = Color.FromHex("#d7df60");
|
||||
var colorGoneA = Color.FromHex("#000000");
|
||||
var colorGoneB = Color.FromHex("#222222");
|
||||
|
||||
var altColor = false;
|
||||
|
||||
for (var i = count; i < capacity; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorGoneA : colorGoneB
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorA : colorB
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
using System;
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.NetIDs;
|
||||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClientMagazineBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
private static readonly Animation AlarmAnimationSmg = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(1.4),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackControlProperty
|
||||
{
|
||||
// These timings match the SMG audio file.
|
||||
Property = nameof(Label.FontColorOverride),
|
||||
InterpolationMode = AnimationInterpolationMode.Previous,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.1f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.3f),
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.2f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.3f),
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.2f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.3f),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly Animation AlarmAnimationLmg = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(0.75),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackControlProperty
|
||||
{
|
||||
// These timings match the SMG audio file.
|
||||
Property = nameof(Label.FontColorOverride),
|
||||
InterpolationMode = AnimationInterpolationMode.Previous,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.0f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.15f),
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.15f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.15f),
|
||||
new AnimationTrackProperty.KeyFrame(Color.Red, 0.15f),
|
||||
new AnimationTrackProperty.KeyFrame(null!, 0.15f),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public override string Name => "MagazineBarrel";
|
||||
public override uint? NetID => ContentNetIDs.MAGAZINE_BARREL;
|
||||
|
||||
private StatusControl? _statusControl;
|
||||
|
||||
/// <summary>
|
||||
/// True if a bullet is chambered.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Chambered { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of bullets in the magazine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if no magazine is inserted.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
public (int count, int max)? MagazineCount { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lmg_alarm_animation")] private bool _isLmgAlarmAnimation = default;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not MagazineBarrelComponentState cast)
|
||||
return;
|
||||
|
||||
Chambered = cast.Chambered;
|
||||
MagazineCount = cast.Magazine;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
|
||||
case MagazineAutoEjectMessage _:
|
||||
_statusControl?.PlayAlarmAnimation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
_statusControl = new StatusControl(this);
|
||||
_statusControl.Update();
|
||||
return _statusControl;
|
||||
}
|
||||
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
if (_statusControl == control)
|
||||
{
|
||||
_statusControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly ClientMagazineBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsList;
|
||||
private readonly TextureRect _chamberedBullet;
|
||||
private readonly Label _noMagazineLabel;
|
||||
private readonly Label _ammoCount;
|
||||
|
||||
public StatusControl(ClientMagazineBarrelComponent parent)
|
||||
{
|
||||
MinHeight = 15;
|
||||
_parent = parent;
|
||||
HorizontalExpand = true;
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_chamberedBullet = new TextureRect
|
||||
{
|
||||
Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png"),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
}),
|
||||
new Control() { MinSize = (5,0) },
|
||||
new Control
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_bulletsList = new HBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0
|
||||
}),
|
||||
(_noMagazineLabel = new Label
|
||||
{
|
||||
Text = "No Magazine!",
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control() { MinSize = (5,0) },
|
||||
(_ammoCount = new Label
|
||||
{
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus},
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
}),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_chamberedBullet.ModulateSelfOverride =
|
||||
_parent.Chambered ? Color.FromHex("#d7df60") : Color.Black;
|
||||
|
||||
_bulletsList.RemoveAllChildren();
|
||||
|
||||
if (_parent.MagazineCount == null)
|
||||
{
|
||||
_noMagazineLabel.Visible = true;
|
||||
_ammoCount.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var (count, capacity) = _parent.MagazineCount.Value;
|
||||
|
||||
_noMagazineLabel.Visible = false;
|
||||
_ammoCount.Visible = true;
|
||||
|
||||
var texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
|
||||
var texture = StaticIoC.ResC.GetTexture(texturePath);
|
||||
|
||||
_ammoCount.Text = $"x{count:00}";
|
||||
capacity = Math.Min(capacity, 20);
|
||||
FillBulletRow(_bulletsList, count, capacity, texture);
|
||||
}
|
||||
|
||||
private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
|
||||
{
|
||||
var colorA = Color.FromHex("#b68f0e");
|
||||
var colorB = Color.FromHex("#d7df60");
|
||||
var colorGoneA = Color.FromHex("#000000");
|
||||
var colorGoneB = Color.FromHex("#222222");
|
||||
|
||||
var altColor = false;
|
||||
|
||||
// Draw the empty ones
|
||||
for (var i = count; i < capacity; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorGoneA : colorGoneB,
|
||||
Stretch = TextureRect.StretchMode.KeepCentered
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
|
||||
// Draw the full ones, but limit the count to the capacity
|
||||
count = Math.Min(count, capacity);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorA : colorB,
|
||||
Stretch = TextureRect.StretchMode.KeepCentered
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayAlarmAnimation()
|
||||
{
|
||||
var animation = _parent._isLmgAlarmAnimation ? AlarmAnimationLmg : AlarmAnimationSmg;
|
||||
_noMagazineLabel.PlayAnimation(animation, "alarm");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.NetIDs;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClientPumpBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
public override string Name => "PumpBarrel";
|
||||
public override uint? NetID => ContentNetIDs.PUMP_BARREL;
|
||||
|
||||
private StatusControl? _statusControl;
|
||||
|
||||
/// <summary>
|
||||
/// chambered is true when a bullet is chambered
|
||||
/// spent is true when the chambered bullet is spent
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public (bool chambered, bool spent) Chamber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of bullets in the magazine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if no magazine is inserted.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
public (int count, int max)? MagazineCount { get; private set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not PumpBarrelComponentState cast)
|
||||
return;
|
||||
|
||||
Chamber = cast.Chamber;
|
||||
MagazineCount = cast.Magazine;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
_statusControl = new StatusControl(this);
|
||||
_statusControl.Update();
|
||||
return _statusControl;
|
||||
}
|
||||
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
if (_statusControl == control)
|
||||
{
|
||||
_statusControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly ClientPumpBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsListTop;
|
||||
private readonly HBoxContainer _bulletsListBottom;
|
||||
private readonly TextureRect _chamberedBullet;
|
||||
private readonly Label _noMagazineLabel;
|
||||
|
||||
public StatusControl(ClientPumpBarrelComponent parent)
|
||||
{
|
||||
MinHeight = 15;
|
||||
_parent = parent;
|
||||
HorizontalExpand = true;
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
AddChild(new VBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
(_bulletsListTop = new HBoxContainer {SeparationOverride = 0}),
|
||||
new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
(_bulletsListBottom = new HBoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0
|
||||
}),
|
||||
(_noMagazineLabel = new Label
|
||||
{
|
||||
Text = "No Magazine!",
|
||||
StyleClasses = {StyleNano.StyleClassItemStatus}
|
||||
})
|
||||
}
|
||||
},
|
||||
(_chamberedBullet = new TextureRect
|
||||
{
|
||||
Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_chamberedBullet.ModulateSelfOverride =
|
||||
_parent.Chamber.chambered ?
|
||||
_parent.Chamber.spent ? Color.Red : Color.FromHex("#d7df60")
|
||||
: Color.Black;
|
||||
|
||||
_bulletsListTop.RemoveAllChildren();
|
||||
_bulletsListBottom.RemoveAllChildren();
|
||||
|
||||
if (_parent.MagazineCount == null)
|
||||
{
|
||||
_noMagazineLabel.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var (count, capacity) = _parent.MagazineCount.Value;
|
||||
|
||||
_noMagazineLabel.Visible = false;
|
||||
|
||||
string texturePath;
|
||||
if (capacity <= 20)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
|
||||
}
|
||||
else if (capacity <= 30)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png";
|
||||
}
|
||||
|
||||
var texture = StaticIoC.ResC.GetTexture(texturePath);
|
||||
|
||||
const int tinyMaxRow = 60;
|
||||
|
||||
if (capacity > tinyMaxRow)
|
||||
{
|
||||
FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture);
|
||||
FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillBulletRow(_bulletsListBottom, count, capacity, texture);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
|
||||
{
|
||||
var colorA = Color.FromHex("#b68f0e");
|
||||
var colorB = Color.FromHex("#d7df60");
|
||||
var colorGoneA = Color.FromHex("#000000");
|
||||
var colorGoneB = Color.FromHex("#222222");
|
||||
|
||||
var altColor = false;
|
||||
|
||||
for (var i = count; i < capacity; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorGoneA : colorGoneB
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
container.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
ModulateSelfOverride = altColor ? colorA : colorB
|
||||
});
|
||||
|
||||
altColor ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Items.Components;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.NetIDs;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClientRevolverBarrelComponent : Component, IItemStatus
|
||||
{
|
||||
public override string Name => "RevolverBarrel";
|
||||
public override uint? NetID => ContentNetIDs.REVOLVER_BARREL;
|
||||
|
||||
private StatusControl? _statusControl;
|
||||
|
||||
/// <summary>
|
||||
/// A array that lists the bullet states
|
||||
/// true means a spent bullet
|
||||
/// false means a "shootable" bullet
|
||||
/// null means no bullet
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool?[] Bullets { get; private set; } = new bool?[0];
|
||||
|
||||
[ViewVariables]
|
||||
public int CurrentSlot { get; private set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not RevolverBarrelComponentState cast)
|
||||
return;
|
||||
|
||||
CurrentSlot = cast.CurrentSlot;
|
||||
Bullets = cast.Bullets;
|
||||
_statusControl?.Update();
|
||||
}
|
||||
|
||||
public Control MakeControl()
|
||||
{
|
||||
_statusControl = new StatusControl(this);
|
||||
_statusControl.Update();
|
||||
return _statusControl;
|
||||
}
|
||||
|
||||
public void DestroyControl(Control control)
|
||||
{
|
||||
if (_statusControl == control)
|
||||
{
|
||||
_statusControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
{
|
||||
private readonly ClientRevolverBarrelComponent _parent;
|
||||
private readonly HBoxContainer _bulletsList;
|
||||
|
||||
public StatusControl(ClientRevolverBarrelComponent parent)
|
||||
{
|
||||
MinHeight = 15;
|
||||
_parent = parent;
|
||||
HorizontalExpand = true;
|
||||
VerticalAlignment = VAlignment.Center;
|
||||
AddChild((_bulletsList = new HBoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SeparationOverride = 0
|
||||
}));
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_bulletsList.RemoveAllChildren();
|
||||
|
||||
var capacity = _parent.Bullets.Length;
|
||||
|
||||
string texturePath;
|
||||
if (capacity <= 20)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
|
||||
}
|
||||
else if (capacity <= 30)
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png";
|
||||
}
|
||||
|
||||
var texture = StaticIoC.ResC.GetTexture(texturePath);
|
||||
var spentTexture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/empty.png");
|
||||
|
||||
FillBulletRow(_bulletsList, texture, spentTexture);
|
||||
}
|
||||
|
||||
private void FillBulletRow(Control container, Texture texture, Texture emptyTexture)
|
||||
{
|
||||
var colorA = Color.FromHex("#b68f0e");
|
||||
var colorB = Color.FromHex("#d7df60");
|
||||
var colorSpentA = Color.FromHex("#b50e25");
|
||||
var colorSpentB = Color.FromHex("#d3745f");
|
||||
var colorGoneA = Color.FromHex("#000000");
|
||||
var colorGoneB = Color.FromHex("#222222");
|
||||
|
||||
var altColor = false;
|
||||
var scale = 1.3f;
|
||||
|
||||
for (var i = 0; i < _parent.Bullets.Length; i++)
|
||||
{
|
||||
var bulletSpent = _parent.Bullets[i];
|
||||
// Add a outline
|
||||
var box = new Control()
|
||||
{
|
||||
MinSize = texture.Size * scale,
|
||||
};
|
||||
if (i == _parent.CurrentSlot)
|
||||
{
|
||||
box.AddChild(new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
TextureScale = (scale, scale),
|
||||
ModulateSelfOverride = Color.Green,
|
||||
});
|
||||
}
|
||||
Color color;
|
||||
Texture bulletTexture = texture;
|
||||
|
||||
if (bulletSpent.HasValue)
|
||||
{
|
||||
if (bulletSpent.Value)
|
||||
{
|
||||
color = altColor ? colorSpentA : colorSpentB;
|
||||
bulletTexture = emptyTexture;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = altColor ? colorA : colorB;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = altColor ? colorGoneA : colorGoneB;
|
||||
}
|
||||
|
||||
box.AddChild(new TextureRect
|
||||
{
|
||||
Stretch = TextureRect.StretchMode.KeepCentered,
|
||||
Texture = bulletTexture,
|
||||
ModulateSelfOverride = color,
|
||||
});
|
||||
altColor ^= true;
|
||||
container.AddChild(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BarrelBoltVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, "bolt-open");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(BarrelBoltVisuals.BoltOpen, out bool boltOpen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (boltOpen)
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, "bolt-open");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Bolt, "bolt-closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MagVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private bool _magLoaded;
|
||||
[DataField("magState")]
|
||||
private string? _magState;
|
||||
[DataField("steps")]
|
||||
private int _magSteps;
|
||||
[DataField("zeroVisible")]
|
||||
private bool _zeroVisible;
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Mag, $"{_magState}-{_magSteps-1}");
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.MagUnshaded, $"{_magState}-unshaded-{_magSteps-1}");
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
// tl;dr
|
||||
// 1.If no mag then hide it OR
|
||||
// 2. If step 0 isn't visible then hide it (mag or unshaded)
|
||||
// 3. Otherwise just do mag / unshaded as is
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
component.TryGetData(MagazineBarrelVisuals.MagLoaded, out _magLoaded);
|
||||
|
||||
if (_magLoaded)
|
||||
{
|
||||
if (!component.TryGetData(AmmoVisuals.AmmoMax, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(AmmoVisuals.AmmoCount, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity, _magSteps);
|
||||
|
||||
if (step == 0 && !_zeroVisible)
|
||||
{
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, true);
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.Mag, $"{_magState}-{step}");
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, true);
|
||||
sprite.LayerSetState(RangedBarrelVisualLayers.MagUnshaded, $"{_magState}-unshaded-{step}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.Mag, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.Mag, false);
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(RangedBarrelVisualLayers.MagUnshaded, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(RangedBarrelVisualLayers.MagUnshaded, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Weapons.Ranged.Barrels.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Barrels.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class SpentAmmoVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(AmmoVisuals.Spent, out bool spent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.LayerSetState(AmmoVisualLayers.Base, spent ? "spent" : "base");
|
||||
}
|
||||
}
|
||||
|
||||
public enum AmmoVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
}
|
||||
}
|
||||
39
Content.Client/Weapons/Ranged/ClientRangedWeaponComponent.cs
Normal file
39
Content.Client/Weapons/Ranged/ClientRangedWeaponComponent.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged
|
||||
{
|
||||
// Yeah I put it all in the same enum, don't judge me
|
||||
public enum RangedBarrelVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
BaseUnshaded,
|
||||
Bolt,
|
||||
Mag,
|
||||
MagUnshaded,
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class ClientRangedWeaponComponent : SharedRangedWeaponComponent
|
||||
{
|
||||
public FireRateSelector FireRateSelector { get; private set; } = FireRateSelector.Safety;
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (curState is not RangedWeaponComponentState rangedState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FireRateSelector = rangedState.FireRateSelector;
|
||||
}
|
||||
|
||||
public void SyncFirePos(GridId targetGrid, Vector2 targetPosition)
|
||||
{
|
||||
SendNetworkMessage(new FirePosComponentMessage(targetGrid, targetPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Content.Client/Weapons/Ranged/RangedWeaponSystem.cs
Normal file
107
Content.Client/Weapons/Ranged/RangedWeaponSystem.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using Content.Client.CombatMode;
|
||||
using Content.Client.Hands;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class RangedWeaponSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private InputSystem _inputSystem = default!;
|
||||
private CombatModeSystem _combatModeSystem = default!;
|
||||
private bool _blocked;
|
||||
private int _shotCounter;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
_inputSystem = Get<InputSystem>();
|
||||
_combatModeSystem = Get<CombatModeSystem>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
|
||||
if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down)
|
||||
{
|
||||
_shotCounter = 0;
|
||||
_blocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (entity == null || !entity.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var held = hands.ActiveHand;
|
||||
if (held == null || !held.TryGetComponent(out ClientRangedWeaponComponent? weapon))
|
||||
{
|
||||
_blocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (weapon.FireRateSelector)
|
||||
{
|
||||
case FireRateSelector.Safety:
|
||||
_blocked = true;
|
||||
return;
|
||||
case FireRateSelector.Single:
|
||||
if (_shotCounter >= 1)
|
||||
{
|
||||
_blocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case FireRateSelector.Automatic:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (_blocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var worldPos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition);
|
||||
|
||||
if (!_mapManager.TryFindGridAt(worldPos, out var grid))
|
||||
{
|
||||
weapon.SyncFirePos(GridId.Invalid, worldPos.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
weapon.SyncFirePos(grid.Index, grid.MapToGrid(worldPos).Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user