This commit is contained in:
Oscar
2025-06-28 18:13:47 +03:00
parent 875d594038
commit 23a35fe3cd
23 changed files with 2579 additions and 607 deletions

View File

@@ -10,33 +10,17 @@ public sealed partial class Dedugan : Component
[Sync] private bool InAds { get; set; } = false;
private AttachmentSlotResolver _resolver;
// Автоматическая стрельба
private bool _isShooting = false;
private TimeSince _lastShootTime = 0f;
void InventoryStart()
{
if (!Network.IsOwner) return;
if ( !Network.IsOwner ) return;
// Создаем инвентарь как компонент
Inventory = GameObject.Components.GetOrCreate<Inventar>();
_resolver = new AttachmentSlotResolver(Renderer.GetAttachmentObject);
// Добавляем тестовые предметы (раскомментируйте для тестирования)
var clothingItem = new InventoryItem
{
Definition = ResourceLibrary.Get<ClothingItemDefinition>("Items/Cloth/cloth_pijama.clitem")
};
Inventory.AddItem(clothingItem);
var weaponItem = new InventoryItem
{
Definition = ResourceLibrary.Get<WeaponItemDefinition>("Items/pistol_test.weapon")
};
Inventory.AddItem(weaponItem);
var ammoItem = new InventoryItem
{
Definition = ResourceLibrary.Get<AmmoItemDefinition>("Items/pistol_ammo.inv")
};
ammoItem.Count = 30;
Inventory.AddItem(ammoItem);
_resolver = new AttachmentSlotResolver( Renderer.GetAttachmentObject );
Inventory.OnEquipped += OnItemEquipped;
Inventory.OnUnEquipped += OnItemUnEquipped;
@@ -44,29 +28,29 @@ public sealed partial class Dedugan : Component
Inventory.OnItemRemoved += OnItemRemoved;
}
private void OnItemEquipped(InventoryItem item)
private void OnItemEquipped( InventoryItem item )
{
// Очищаем кэши при экипировке предмета
_useableCache.Clear();
_resolver?.ClearCache();
if (item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid())
if ( item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid() )
{
var go = weaponDef.Prefab.Clone();
AnimationHelper.HoldType = weaponDef.HoldType;
switch (weaponDef.Slot)
switch ( weaponDef.Slot )
{
case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
case Inventar.InventorySlot.RightHand:
go.Parent = Renderer.GetAttachmentObject("hold_R");
go.Parent = Renderer.GetAttachmentObject( "hold_R" );
break;
case Inventar.InventorySlot.LeftHand:
go.Parent = Renderer.GetAttachmentObject("hold_L");
go.Parent = Renderer.GetAttachmentObject( "hold_L" );
break;
default:
go.Parent = Renderer.GetAttachmentObject("forward_reference_modelspace");
go.Parent = Renderer.GetAttachmentObject( "forward_reference_modelspace" );
break;
}
@@ -74,9 +58,11 @@ public sealed partial class Dedugan : Component
go.LocalRotation = weaponDef.WeaponDefinition.Rotation;
go.LocalScale = weaponDef.WeaponDefinition.Scale;
if (go.Components.TryGet<UseableBase>(out var useable))
// Получаем компонент оружия и вызываем экипировку
if ( go.Components.TryGet<BaseWeapon>( out var weapon ) )
{
useable.Equipped = true;
weapon.OnEquipped();
_useableCache[weaponDef.Slot] = (go, weapon);
}
go.NetworkSpawn();
@@ -90,65 +76,70 @@ public sealed partial class Dedugan : Component
};
AnimationHelper.Handedness = hand;
RpcSetHoldAnimation(weaponDef.HoldType, hand);
RpcSetHoldAnimation( weaponDef.HoldType, hand );
InAds = true;
}
else if (item?.Definition is ClothingItemDefinition clothingDef)
else if ( item?.Definition is ClothingItemDefinition clothingDef )
{
WearWorkshop(new List<string>() { clothingDef.ClothUrl });
WearWorkshop( new List<string>() { clothingDef.ClothUrl } );
}
}
private void OnItemUnEquipped(InventoryItem item)
private void OnItemUnEquipped( InventoryItem item )
{
// Очищаем кэши при снятии предмета
_useableCache.Clear();
_resolver?.ClearCache();
if (item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid())
if ( item?.Definition is WeaponItemDefinition weaponDef && weaponDef.Prefab.IsValid() )
{
switch (weaponDef.Slot)
// Вызываем 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)
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);
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());
Renderer.GetAttachmentObject( "forward_reference_modelspace" ).Children
.ForEach( child => child.Destroy() );
break;
}
InAds = false;
}
else if (item?.Definition is ClothingItemDefinition clothingDef)
else if ( item?.Definition is ClothingItemDefinition clothingDef )
{
StripByName(clothingDef.Description);
StripByName( clothingDef.Description );
}
}
private void OnItemAdded(InventoryItem item)
private void OnItemAdded( InventoryItem item )
{
// Очищаем кэши при добавлении предмета
_useableCache.Clear();
_resolver?.ClearCache();
// Кэш не очищаем при добавлении предметов, чтобы не нарушить работу оружия
}
private void OnItemRemoved(InventoryItem item)
private void OnItemRemoved( InventoryItem item )
{
// Очищаем кэши при удалении предмета
_useableCache.Clear();
_resolver?.ClearCache();
// Кэш не очищаем при удалении предметов, чтобы не нарушить работу оружия
}
[Rpc.Broadcast]
public void RpcSetHoldAnimation(CitizenAnimationHelper.HoldTypes HoldType, CitizenAnimationHelper.Hand hand)
public void RpcSetHoldAnimation( CitizenAnimationHelper.HoldTypes HoldType, CitizenAnimationHelper.Hand hand )
{
AnimationHelper.HoldType = HoldType;
AnimationHelper.Handedness = hand;
@@ -156,59 +147,158 @@ public sealed partial class Dedugan : Component
void InventoryUpdate()
{
if (!Network.IsOwner) return;
if ( !Network.IsOwner ) return;
if (Input.Pressed("Attack1"))
// Обработка автоматической стрельбы
HandleAutomaticShooting();
// Обработка перезарядки
if ( Input.Pressed( "Reload" ) )
{
if (UseSystem.TryUse(this))
{
Attack();
}
TryReloadWeapon();
}
}
public IEnumerable<IUseable> GetUsables()
/// <summary>
/// Обработка автоматической стрельбы
/// </summary>
private void HandleAutomaticShooting()
{
// Кэшируем слоты для избежания повторного создания массива
var slots = new[] { Inventar.InventorySlot.LeftHand, Inventar.InventorySlot.RightHand };
foreach (var slot in slots)
var weapon = GetEquippedWeapon();
if ( weapon == null ) return;
var weaponDef = weapon.GetWeaponDefinition();
if ( weaponDef == null ) return;
// Начало стрельбы
if ( Input.Pressed( "Attack1" ) )
{
if (!Inventory.EquippedItems.TryGetValue(slot, out var item))
{
continue;
}
var holder = _resolver.GetSlotObject(slot);
if (holder == null) continue;
var heldObject = holder.Children.FirstOrDefault();
if (heldObject == null)
{
continue;
}
_isShooting = true;
TryUseWeapon();
}
// Проверяем кэш
if (_useableCache.TryGetValue(slot, out var cached) && cached.obj == heldObject)
{
if (cached.useable != null)
yield return cached.useable;
}
else
{
var useable = heldObject.Components.Get<IUseable>();
_useableCache[slot] = (heldObject, useable);
// Продолжение стрельбы (только если оружие автоматическое)
if ( Input.Down( "Attack1" ) && _isShooting && weaponDef.IsAutomatic )
{
TryUseWeapon();
}
if (useable != null)
yield return useable;
// Остановка стрельбы
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();
}
}
/// <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);
Renderer.Set( "b_attack", true );
}
}

View File

@@ -3,6 +3,8 @@ using Sandbox;
using Sandbox.Citizen;
using Sasalka;
using ShrimpleCharacterController;
using System.Collections.Generic;
using System.Linq;
public sealed partial class Dedugan : Component, IUseContext, Component.INetworkSpawn
{
@@ -121,4 +123,14 @@ public sealed partial class Dedugan : Component, IUseContext, Component.INetwork
{
DrawDebugGizmos();
}
/// <summary>
/// Реализация интерфейса IUseContext
/// Возвращает список используемых предметов
/// </summary>
public IEnumerable<IUseable> GetUsables()
{
// Возвращаем пустой список, так как теперь оружие управляется через новую систему
return Enumerable.Empty<IUseable>();
}
}