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

Перенос Wieldable #235

Merged
merged 2 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Content.Shared/Hands/Components/HandHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Shared.Hands.EntitySystems;

namespace Content.Shared.Hands.Components;

Expand All @@ -20,6 +21,15 @@ public static class HandHelpers
/// </summary>
public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty);

/// <summary>
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
/// cache it instead of accessing this multiple times.
/// </summary>
public static int CountFreeableHands(this Entity<HandsComponent> component, SharedHandsSystem system)
{
return system.CountFreeableHands(component);
}

/// <summary>
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
/// it instead of accessing this multiple times.
Expand Down
13 changes: 12 additions & 1 deletion Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Input.Binding;
Expand Down Expand Up @@ -299,4 +298,16 @@ public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] ou

return hands.Hands.TryGetValue(handId, out hand);
}

public int CountFreeableHands(Entity<HandsComponent> hands)
{
var freeable = 0;
foreach (var hand in hands.Comp.Hands.Values)
{
if (hand.IsEmpty || CanDropHeld(hands, hand))
freeable++;
}

return freeable;
}
}
50 changes: 43 additions & 7 deletions Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Content.Shared.Interaction;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Popups;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
Expand All @@ -29,6 +30,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

[ValidatePrototypeId<EntityPrototype>]
private const string VirtualItem = "VirtualItem";
Expand Down Expand Up @@ -71,23 +73,53 @@ private void OnBeforeRangedInteract(Entity<VirtualItemComponent> ent, ref Before
}

#region Hands

/// <summary>
/// Spawns a virtual item in a empty hand
/// </summary>
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
/// <param name="user">The entity that we want to insert the virtual entity</param>
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
/// <param name="dropOthers">Whether or not to try and drop other items to make space</param>
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, bool dropOthers = false)
{
return TrySpawnVirtualItemInHand(blockingEnt, user, out _);
return TrySpawnVirtualItemInHand(blockingEnt, user, out _, dropOthers);
}

/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid)"/>
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,bool)"/>
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false)
{
if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand))
virtualItem = null;
if (!_handsSystem.TryGetEmptyHand(user, out var empty))
{
if (!dropOthers)
return false;

foreach (var hand in _handsSystem.EnumerateHands(user))
{
if (hand.HeldEntity is not { } held)
continue;

if (held == blockingEnt || HasComp<VirtualItemComponent>(held))
continue;

if (!_handsSystem.TryDrop(user, hand))
continue;

if (!TerminatingOrDeleted(held))
_popup.PopupClient(Loc.GetString("virtual-item-dropped-other", ("dropped", held)), user, user);

empty = hand;
break;
}
}

if (empty == null)
return false;

if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem))
return false;

_handsSystem.DoPickup(user, hand, virtualItem.Value);
_handsSystem.DoPickup(user, empty, virtualItem.Value);
return true;
}

Expand Down Expand Up @@ -119,7 +151,8 @@ public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
/// </summary>
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
/// <param name="user">The entity that we want to insert the virtual entity</param>
/// <param name="slot">The slot to which we will insert the virtual entity (could be the "shoes" slot, for example)</param>
/// <param name="slot">The slot to which we will insert the virtual entity (could be the "shoes" slot, for example)</para
/// <param name="force">Whether or not to force an equip</param>
public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false)
{
return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _);
Expand All @@ -141,6 +174,8 @@ public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user
/// if it is, delete it
/// </summary>
/// <param name="slotName">Set this param if you have the name of the slot, it avoids unnecessary queries</param>
/// <param name="user">The entity that we want to delete the virtual entity from</param>
/// <param name="matching">The entity that made the virtual entity</param>
public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null)
{
// Client can't currently predict deleting networked entities so we use this workaround, another
Expand Down Expand Up @@ -178,6 +213,7 @@ public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slo
/// </summary>
/// <param name="blockingEnt">The entity we will make a virtual entity copy of</param>
/// <param name="user">The entity that we want to insert the virtual entity</param>
/// <param name="virtualItem">The virtual item, if spawned</param>
public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
{
if (_netManager.IsClient)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ namespace Content.Shared.Weapons.Ranged.Components;
/// <summary>
/// Indicates that this gun requires wielding to be useable.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(WieldableSystem))]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(WieldableSystem))]
public sealed partial class GunRequiresWieldComponent : Component
{
[DataField, AutoNetworkedField]
public TimeSpan LastPopup;

}
[DataField, AutoNetworkedField]
public TimeSpan PopupCooldown = TimeSpan.FromSeconds(1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ public sealed partial class GunWieldBonusComponent : Component
/// </summary>
[DataField, AutoNetworkedField]
public Angle AngleIncrease = Angle.FromDegrees(0);

[DataField]
public LocId? WieldBonusExamineMessage = "gunwieldbonus-component-examine";
}
3 changes: 2 additions & 1 deletion Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared.Weapons.Ranged.Components;
namespace Content.Shared.Weapons.Ranged.Events;

/// <summary>
Expand All @@ -15,7 +16,7 @@ public record struct ShotAttemptedEvent
/// <summary>
/// The gun being shot.
/// </summary>
public EntityUid Used;
public Entity<GunComponent> Used;

public bool Cancelled { get; private set; }

Expand Down
2 changes: 1 addition & 1 deletion Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ private bool CanShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
var prevention = new ShotAttemptedEvent
{
User = user,
Used = gunUid
Used = (gunUid, gun)
};
RaiseLocalEvent(gunUid, ref prevention);
if (prevention.Cancelled)
Expand Down
53 changes: 44 additions & 9 deletions Content.Shared/Wieldable/WieldableSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
Expand All @@ -15,7 +16,7 @@
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Timing;

namespace Content.Shared.Wieldable;

Expand All @@ -29,6 +30,7 @@ public sealed class WieldableSystem : EntitySystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly UseDelaySystem _delay = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly IGameTiming _timing = default!;

public override void Initialize()
{
Expand All @@ -39,12 +41,14 @@ public override void Initialize()
SubscribeLocalEvent<WieldableComponent, GotUnequippedHandEvent>(OnItemLeaveHand);
SubscribeLocalEvent<WieldableComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
SubscribeLocalEvent<WieldableComponent, GetVerbsEvent<InteractionVerb>>(AddToggleWieldVerb);
SubscribeLocalEvent<WieldableComponent, HandDeselectedEvent>(OnDeselectWieldable);

SubscribeLocalEvent<MeleeRequiresWieldComponent, AttemptMeleeEvent>(OnMeleeAttempt);
SubscribeLocalEvent<GunRequiresWieldComponent, AttemptShootEvent>(OnShootAttempt);
SubscribeLocalEvent<GunRequiresWieldComponent, ShotAttemptedEvent>(OnShootAttempt);
SubscribeLocalEvent<GunWieldBonusComponent, ItemWieldedEvent>(OnGunWielded);
SubscribeLocalEvent<GunWieldBonusComponent, ItemUnwieldedEvent>(OnGunUnwielded);
SubscribeLocalEvent<GunWieldBonusComponent, GunRefreshModifiersEvent>(OnGunRefreshModifiers);
SubscribeLocalEvent<GunWieldBonusComponent, ExaminedEvent>(OnExamine);

SubscribeLocalEvent<IncreaseDamageOnWieldComponent, GetMeleeDamageEvent>(OnGetMeleeDamage);
}
Expand All @@ -59,16 +63,21 @@ private void OnMeleeAttempt(EntityUid uid, MeleeRequiresWieldComponent component
}
}

private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref AttemptShootEvent args)
private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref ShotAttemptedEvent args)
{
if (TryComp<WieldableComponent>(uid, out var wieldable) &&
!wieldable.Wielded)
{
args.Cancelled = true;
args.Cancel();

if (!HasComp<MeleeWeaponComponent>(uid) && !HasComp<MeleeRequiresWieldComponent>(uid))
var time = _timing.CurTime;
if (time > component.LastPopup + component.PopupCooldown &&
!HasComp<MeleeWeaponComponent>(uid) &&
!HasComp<MeleeRequiresWieldComponent>(uid))
{
args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
component.LastPopup = time;
var message = Loc.GetString("wieldable-component-requires", ("item", uid));
_popupSystem.PopupClient(message, args.Used, args.User);
}
}
}
Expand All @@ -83,6 +92,14 @@ private void OnGunWielded(EntityUid uid, GunWieldBonusComponent component, ref I
_gun.RefreshModifiers(uid);
}

private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args)
{
if (!component.Wielded)
return;

TryUnwield(uid, component, args.User);
}

private void OnGunRefreshModifiers(Entity<GunWieldBonusComponent> bonus, ref GunRefreshModifiersEvent args)
{
if (TryComp(bonus, out WieldableComponent? wield) &&
Expand All @@ -95,6 +112,12 @@ private void OnGunRefreshModifiers(Entity<GunWieldBonusComponent> bonus, ref Gun
}
}

private void OnExamine(EntityUid uid, GunWieldBonusComponent component, ref ExaminedEvent args)
{
if (component.WieldBonusExamineMessage != null)
args.PushText(Loc.GetString(component.WieldBonusExamineMessage));
}

private void AddToggleWieldVerb(EntityUid uid, WieldableComponent component, GetVerbsEvent<InteractionVerb> args)
{
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
Expand Down Expand Up @@ -147,7 +170,7 @@ public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user
return false;
}

if (hands.CountFreeHands() < component.FreeHandsRequired)
if (_handsSystem.CountFreeableHands((user, hands)) < component.FreeHandsRequired)
{
if (!quiet)
{
Expand Down Expand Up @@ -187,10 +210,22 @@ public bool TryWield(EntityUid used, WieldableComponent component, EntityUid use

if (component.WieldSound != null)
_audioSystem.PlayPredicted(component.WieldSound, used, user);


var virtuals = new List<EntityUid>();
for (var i = 0; i < component.FreeHandsRequired; i++)
{
_virtualItemSystem.TrySpawnVirtualItemInHand(used, user);
if (_virtualItemSystem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true))
{
virtuals.Add(virtualItem.Value);
continue;
}

foreach (var existingVirtual in virtuals)
{
QueueDel(existingVirtual);
}

return false;
}

if (TryComp(used, out UseDelayComponent? useDelay)
Expand Down
1 change: 1 addition & 0 deletions Resources/Locale/en-US/virtual/virtual-item.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
virtual-item-dropped-other = You dropped {THE($dropped)}!
1 change: 1 addition & 0 deletions Resources/Locale/en-US/wieldable/wieldable-component.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ wieldable-component-not-in-hands = { CAPITALIZE(THE($item)) } isn't in your hand

wieldable-component-requires = { CAPITALIZE(THE($item))} must be wielded!

gunwieldbonus-component-examine = This weapon has improved accuracy when wielded.
1 change: 1 addition & 0 deletions Resources/Locale/ru-RU/virtual/virtual-item.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
virtual-item-dropped-other = Вы уронили {($dropped)}!
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# A basic inheritable template for a gun that is wieldable and has the standard inaccuracy.
- type: entity
id: BaseGunWieldable
abstract: true
components:
- type: Wieldable
- type: GunWieldBonus
minAngle: -20
maxAngle: -30
- type: Gun
minAngle: 21
maxAngle: 32
Loading