sasalka/Code/Player/Dedugan.Inventory.cs
2025-06-28 21:46:34 +03:00

323 lines
9.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Sandbox.Citizen;
using Sandbox.Weapons;
using Sasalka;
public sealed partial class Dedugan : Component
{
[Property] public Inventar Inventory { get; private set; }
private Dictionary<Inventar.InventorySlot, (GameObject obj, IUseable useable)> _useableCache = new();
[Sync] private bool InAds { get; set; } = false;
private AttachmentSlotResolver _resolver;
// Автоматическая стрельба
private bool _isShooting = false;
private TimeSince _lastShootTime = 0f;
void InventoryStart()
{
if ( !Network.IsOwner ) return;
// Создаем инвентарь как компонент
Inventory = GameObject.Components.GetOrCreate<Inventar>();
_resolver = new AttachmentSlotResolver( Renderer.GetAttachmentObject );
Inventory.OnEquipped += OnItemEquipped;
Inventory.OnUnEquipped += OnItemUnEquipped;
Inventory.OnItemAdded += OnItemAdded;
Inventory.OnItemRemoved += OnItemRemoved;
}
private void OnItemEquipped( InventoryItem item )
{
// Очищаем кэши при экипировке предмета
_useableCache.Clear();
_resolver?.ClearCache();
if ( item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid() )
{
var go = weaponDef.Prefab.Clone();
AnimationHelper.HoldType = weaponDef.HoldType;
switch ( weaponDef.Slot )
{
case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
case Inventar.InventorySlot.RightHand:
go.Parent = Renderer.GetAttachmentObject( "hold_R" );
break;
case Inventar.InventorySlot.LeftHand:
go.Parent = Renderer.GetAttachmentObject( "hold_L" );
break;
default:
go.Parent = Renderer.GetAttachmentObject( "forward_reference_modelspace" );
break;
}
go.LocalPosition = weaponDef.WeaponDefinition.Position;
go.LocalRotation = weaponDef.WeaponDefinition.Rotation;
go.LocalScale = weaponDef.WeaponDefinition.Scale;
// Получаем компонент оружия и вызываем экипировку
if ( go.Components.TryGet<BaseWeapon>( out var weapon ) )
{
// Передаем ссылку на InventoryItem в оружие
weapon.SetInventoryItem( item );
weapon.OnEquipped();
_useableCache[weaponDef.Slot] = (go, weapon);
}
go.NetworkSpawn();
var hand = weaponDef.Slot switch
{
Inventar.InventorySlot.LeftHand => CitizenAnimationHelper.Hand.Left,
Inventar.InventorySlot.RightHand => CitizenAnimationHelper.Hand.Right,
Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand => CitizenAnimationHelper.Hand.Both,
_ => CitizenAnimationHelper.Hand.Both
};
AnimationHelper.Handedness = hand;
RpcSetHoldAnimation( weaponDef.HoldType, hand );
InAds = true;
}
else if ( item?.Definition is ClothingItemDefinition clothingDef )
{
WearWorkshop( new List<string>() { clothingDef.ClothUrl } );
}
}
private void OnItemUnEquipped( InventoryItem item )
{
// Очищаем кэши при снятии предмета
_useableCache.Clear();
_resolver?.ClearCache();
if ( item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid() )
{
// Вызываем OnUnEquipped для оружия
if ( _useableCache.TryGetValue( weaponDef.Slot, out var cached ) )
{
if ( cached.useable is BaseWeapon weapon )
{
weapon.OnUnEquipped();
}
}
switch ( weaponDef.Slot )
{
case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
case Inventar.InventorySlot.RightHand:
case Inventar.InventorySlot.LeftHand:
var attachmentName = !weaponDef.Slot.HasFlag( Inventar.InventorySlot.RightHand )
? "hold_L"
: "hold_R";
Renderer.GetAttachmentObject( attachmentName ).Children.ForEach( child => child.Destroy() );
RpcSetHoldAnimation( CitizenAnimationHelper.HoldTypes.None, CitizenAnimationHelper.Hand.Both );
break;
default:
Renderer.GetAttachmentObject( "forward_reference_modelspace" ).Children
.ForEach( child => child.Destroy() );
break;
}
InAds = false;
}
else if ( item?.Definition is ClothingItemDefinition clothingDef )
{
StripByName( clothingDef.Description );
}
}
private void OnItemAdded( InventoryItem item )
{
// Кэш не очищаем при добавлении предметов, чтобы не нарушить работу оружия
}
private void OnItemRemoved( InventoryItem item )
{
// Кэш не очищаем при удалении предметов, чтобы не нарушить работу оружия
}
[Rpc.Broadcast]
public void RpcSetHoldAnimation( CitizenAnimationHelper.HoldTypes HoldType, CitizenAnimationHelper.Hand hand )
{
AnimationHelper.HoldType = HoldType;
AnimationHelper.Handedness = hand;
}
void InventoryUpdate()
{
if ( !Network.IsOwner ) return;
// Обработка автоматической стрельбы
HandleAutomaticShooting();
// Обработка перезарядки
if ( Input.Pressed( "Reload" ) )
{
TryReloadWeapon();
}
}
/// <summary>
/// Обработка автоматической стрельбы
/// </summary>
private void HandleAutomaticShooting()
{
var weapon = GetEquippedWeapon();
if ( weapon == null ) return;
var weaponDef = weapon.GetWeaponDefinition();
if ( weaponDef == null ) return;
// Начало стрельбы
if ( Input.Pressed( "Attack1" ) )
{
_isShooting = true;
TryUseWeapon();
}
// Продолжение стрельбы (только если оружие автоматическое)
if ( Input.Down( "Attack1" ) && _isShooting && weaponDef.IsAutomatic )
{
TryUseWeapon();
}
// Остановка стрельбы
if ( Input.Released( "Attack1" ) )
{
_isShooting = false;
}
}
/// <summary>
/// Попытка использования экипированного предмета
/// </summary>
private void TryUseWeapon()
{
var useable = GetEquippedUseable();
if ( useable != null && useable.CanUse() )
{
useable.Use();
Attack(); // Анимация атаки
}
}
/// <summary>
/// Попытка перезарядки экипированного оружия
/// </summary>
private void TryReloadWeapon()
{
var weapon = GetEquippedWeapon();
if ( weapon != null && !weapon.IsReloading )
{
weapon.StartReload();
// Анимация перезарядки персонажа (скорость уже установлена в weapon.StartReload())
// Дополнительно устанавливаем здесь на случай, если weapon.StartReload() не вызвался
var weaponDef = weapon.GetWeaponDefinition();
if ( weaponDef != null )
{
float reloadSpeed = weaponDef.ReloadTime > 0 ? 1f / weaponDef.ReloadTime : 1f;
Reload( reloadSpeed );
}
}
}
/// <summary>
/// Получить экипированный используемый предмет
/// </summary>
private IUseable GetEquippedUseable()
{
// Проверяем правую руку
if ( Inventory.EquippedItems.TryGetValue( Inventar.InventorySlot.RightHand, out var rightHandItem ) )
{
if ( _useableCache.TryGetValue( Inventar.InventorySlot.RightHand, out var cached ) &&
cached.useable != null )
{
return cached.useable;
}
}
// Проверяем левую руку
if ( Inventory.EquippedItems.TryGetValue( Inventar.InventorySlot.LeftHand, out var leftHandItem ) )
{
if ( _useableCache.TryGetValue( Inventar.InventorySlot.LeftHand, out var cached ) &&
cached.useable != null )
{
return cached.useable;
}
}
// Проверяем обе руки
if ( Inventory.EquippedItems.TryGetValue( Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand,
out var bothHandsItem ) )
{
if ( _useableCache.TryGetValue( Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand,
out var cached ) && cached.useable != null )
{
return cached.useable;
}
}
return null;
}
/// <summary>
/// Получить экипированное оружие
/// </summary>
private BaseWeapon GetEquippedWeapon()
{
var useable = GetEquippedUseable();
return useable as BaseWeapon;
}
/// <summary>
/// Получить информацию о патронах для UI
/// </summary>
public (int current, int max, int totalInInventory) GetAmmoInfo()
{
var weapon = GetEquippedWeapon();
if ( weapon == null )
{
return (0, 0, 0);
}
return weapon.GetAmmoInfo();
}
/// <summary>
/// Получить общее количество патронов в инвентаре (для UI)
/// </summary>
public int GetTotalInInventory()
{
var ammoInfo = GetAmmoInfo();
return ammoInfo.totalInInventory;
}
/// <summary>
/// Получить прогресс перезарядки
/// </summary>
public float GetReloadProgress()
{
var weapon = GetEquippedWeapon();
return weapon?.GetReloadProgress() ?? 1f;
}
[Rpc.Broadcast]
void Attack()
{
Renderer.Set( "b_attack", true );
}
[Rpc.Broadcast]
void Reload( float reloadSpeed )
{
Renderer.Set( "b_reload", true );
Renderer.Set( "speed_reload", reloadSpeed );
}
}