329 lines
9.2 KiB
C#
329 lines
9.2 KiB
C#
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;
|
||
Renderer.Set( "b_reload", true );
|
||
Renderer.Set( "speed_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 );
|
||
}
|
||
}
|
||
|
||
|
||
// if ( !Network.IsOwner ) return;
|
||
//
|
||
// InAds = Input.Down( "Attack2" );
|
||
//
|
||
// if ( Input.Pressed( "Attack1" ) && _weapon != null )
|
||
// {
|
||
// _weapon.Attack();
|
||
// Attack();
|
||
// }
|
||
// }
|
||
//
|