upd
This commit is contained in:
22
Code/Inventory/AttachmentSlotResolver.cs
Normal file
22
Code/Inventory/AttachmentSlotResolver.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public class AttachmentSlotResolver
|
||||
{
|
||||
private readonly Func<string, GameObject> _attachmentGetter;
|
||||
|
||||
public AttachmentSlotResolver( Func<string, GameObject> attachmentGetter )
|
||||
{
|
||||
_attachmentGetter = attachmentGetter;
|
||||
}
|
||||
|
||||
public GameObject GetSlotObject( Inventar.InventorySlot slot )
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
Inventar.InventorySlot.LeftHand => _attachmentGetter.Invoke( "hold_L" ),
|
||||
Inventar.InventorySlot.RightHand => _attachmentGetter.Invoke( "hold_R" ),
|
||||
Inventar.InventorySlot.Body => _attachmentGetter.Invoke( "forward_reference_modelspace" ),
|
||||
_ => _attachmentGetter.Invoke( "forward_reference_modelspace" )
|
||||
};
|
||||
}
|
||||
}
|
||||
69
Code/Inventory/Inventar.cs
Normal file
69
Code/Inventory/Inventar.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public class Inventar
|
||||
{
|
||||
[Flags]
|
||||
public enum InventorySlot
|
||||
{
|
||||
None = 0,
|
||||
LeftHand = 1 << 0, // 1
|
||||
RightHand = 1 << 1, // 2
|
||||
Head = 1 << 2, // 4
|
||||
Body = 1 << 3, // 8
|
||||
Hands = 1 << 4, // 16
|
||||
Bottom = 1 << 5, // 32
|
||||
Feet = 1 << 6 // 64
|
||||
}
|
||||
|
||||
public List<InventoryItem> Items { get; private set; } = new();
|
||||
public static bool IsInventoryOpen = false;
|
||||
|
||||
// public Dictionary<InventorySlot, EquippedItem> EquippedItems { get; private set; } = new();
|
||||
public Dictionary<InventorySlot, InventoryItem> EquippedItems { get; private set; } = new();
|
||||
|
||||
public event Action OnChanged;
|
||||
public event Action<InventoryItem> OnEquipped;
|
||||
public event Action<InventoryItem> OnUnEquipped;
|
||||
|
||||
// public class EquippedItem
|
||||
// {
|
||||
// public InventoryItem Item { get; set; }
|
||||
// public GameObject SpawnedObject { get; set; }
|
||||
// }
|
||||
|
||||
public void AddItem( InventoryItem item )
|
||||
{
|
||||
Items.Add( item );
|
||||
OnChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void RemoveItem( InventoryItem item )
|
||||
{
|
||||
UnEquipItem( item );
|
||||
Items.Remove( item );
|
||||
OnChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void EquipItem( InventoryItem item )
|
||||
{
|
||||
if ( EquippedItems.ContainsValue( item ) )
|
||||
{
|
||||
UnEquipItem( item );
|
||||
}
|
||||
else
|
||||
{
|
||||
EquippedItems.Add( item.Definition.Slot, item );
|
||||
OnEquipped?.Invoke( item );
|
||||
}
|
||||
}
|
||||
|
||||
public void UnEquipItem( InventoryItem item )
|
||||
{
|
||||
foreach ( var kvp in EquippedItems.Where( kvp => kvp.Value == item ).ToList() )
|
||||
{
|
||||
EquippedItems.Remove( kvp.Key );
|
||||
}
|
||||
|
||||
OnUnEquipped?.Invoke( item );
|
||||
}
|
||||
}
|
||||
15
Code/Inventory/InventoryItem.cs
Normal file
15
Code/Inventory/InventoryItem.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace Sasalka;
|
||||
|
||||
public class InventoryItem : Component
|
||||
{
|
||||
public InventoryItemDefinition Definition { get; set; }
|
||||
|
||||
public int Count { get; set; } = 1;
|
||||
public int MaxCount { get; set; } = 1;
|
||||
|
||||
// public GameObject SpawnedObject { get; set; }
|
||||
}
|
||||
// public int Count { get; set; } = 1;
|
||||
// public int MaxCount { get; set; } = 1;
|
||||
9
Code/Inventory/InventoryItemCountable.cs
Normal file
9
Code/Inventory/InventoryItemCountable.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace Sasalka;
|
||||
|
||||
public class InventoryItemCountable : InventoryItem
|
||||
{
|
||||
// public int Count { get; set; } = 1;
|
||||
// public int MaxCount { get; set; } = 1;
|
||||
}
|
||||
30
Code/Inventory/InventoryItemDefinition.cs
Normal file
30
Code/Inventory/InventoryItemDefinition.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Sandbox.Citizen;
|
||||
|
||||
namespace Sasalka;
|
||||
|
||||
[GameResource( "Inventory Item Definition", "inv", "", Category = "Sasalka", Icon = "inventory_2" )]
|
||||
public class InventoryItemDefinition : GameResource
|
||||
{
|
||||
public Inventar.InventorySlot Slot { get; set; }
|
||||
|
||||
public CitizenAnimationHelper.HoldTypes HoldType { get; set; } = CitizenAnimationHelper.HoldTypes.None;
|
||||
|
||||
public Texture ImageTexture { get; set; }
|
||||
|
||||
public string ImageUrl { get; set; }
|
||||
public string ClothUrl { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
[ResourceType( "prefab" )] public GameObject Prefab { get; set; }
|
||||
|
||||
[InlineEditor, Space] public WeaponDefinition WeaponDefinition { get; set; }
|
||||
}
|
||||
|
||||
public struct WeaponDefinition
|
||||
{
|
||||
// public CitizenAnimationHelper.Hand Hand { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
public Rotation Rotation { get; set; }
|
||||
}
|
||||
43
Code/Inventory/Ui/Inventory.razor
Normal file
43
Code/Inventory/Ui/Inventory.razor
Normal file
@@ -0,0 +1,43 @@
|
||||
@using Sasalka
|
||||
@inherits PanelComponent
|
||||
@namespace Sasalka.Ui
|
||||
|
||||
<root class="@( Inventar.IsInventoryOpen ? "" : "hidden" )">
|
||||
<div class="inventory-panel">
|
||||
@foreach ( var item in PlayerInventory.Items )
|
||||
{
|
||||
<Sasalka.Ui.InventoryItem Item="@item" OnItemClick="@( UseItem )"/>
|
||||
}
|
||||
</div>
|
||||
</root>
|
||||
|
||||
@code {
|
||||
Dedugan Player => Dedugan.Local;
|
||||
Inventar PlayerInventory => Player?.Inventory;
|
||||
|
||||
|
||||
void UseItem( Sasalka.InventoryItem item )
|
||||
{
|
||||
Player?.Inventory?.EquipItem( item );
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if ( Input.Pressed( "Score" ) )
|
||||
{
|
||||
Inventar.IsInventoryOpen = !Inventar.IsInventoryOpen;
|
||||
}
|
||||
}
|
||||
|
||||
protected override int BuildHash()
|
||||
{
|
||||
if ( !Inventar.IsInventoryOpen || PlayerInventory == null )
|
||||
return -1;
|
||||
|
||||
var hash = new HashCode();
|
||||
hash.Add( Inventar.IsInventoryOpen );
|
||||
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
34
Code/Inventory/Ui/Inventory.razor.scss
Normal file
34
Code/Inventory/Ui/Inventory.razor.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
Inventory {
|
||||
background: linear-gradient(135deg, #0a1a2b 0%, #08111f 100%);
|
||||
border: 3px solid #2a3d54;
|
||||
border-radius: 14px;
|
||||
font-family: 'Orbitron', 'Poppins', sans-serif;
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
height: 96vh;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
pointer-events: all;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.inventory-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
38
Code/Inventory/Ui/InventoryItem.razor
Normal file
38
Code/Inventory/Ui/InventoryItem.razor
Normal file
@@ -0,0 +1,38 @@
|
||||
@using Sandbox.UI
|
||||
@using Sasalka
|
||||
@inherits Sandbox.UI.Panel
|
||||
@namespace Sasalka.Ui
|
||||
|
||||
<root class="inventory-item @( Equipped ? "equipped" : "" )" @onclick="@(() => OnItemClick?.Invoke( Item ))">
|
||||
<input type="checkbox" class="equipped-checkbox" checked="@Equipped" disabled/>
|
||||
@if ( Item.Definition.ImageTexture.IsValid() )
|
||||
{
|
||||
<img src="@Item.Definition.ImageTexture.ResourcePath" alt="@Item.Definition.Name"/>
|
||||
}
|
||||
else if ( Item.Definition.ImageUrl.Length > 0 )
|
||||
{
|
||||
<img src="@Item.Definition.ImageUrl" alt="@Item.Definition.Name">
|
||||
}
|
||||
|
||||
<div class="inventory-item__name">@Item?.Definition.Name</div>
|
||||
|
||||
<div class="inventory-item__count">@Item?.Count / @Item?.MaxCount</div>
|
||||
</root>
|
||||
|
||||
@code {
|
||||
public Sasalka.InventoryItem Item { get; set; }
|
||||
public Action<Sasalka.InventoryItem> OnItemClick { get; set; }
|
||||
public bool Equipped { get; set; }
|
||||
|
||||
protected override int BuildHash()
|
||||
{
|
||||
base.BuildHash();
|
||||
|
||||
var hash = new HashCode();
|
||||
|
||||
hash.Add( Item.Count );
|
||||
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
41
Code/Inventory/Ui/InventoryItem.razor.scss
Normal file
41
Code/Inventory/Ui/InventoryItem.razor.scss
Normal file
@@ -0,0 +1,41 @@
|
||||
InventoryItem {
|
||||
width: 100%;
|
||||
//height: 64px;
|
||||
background: #2a3d53;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
border: 1px solid #666;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
border-radius: 12px;
|
||||
padding: 12px 24px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&.name {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&.equipped {
|
||||
border: 2px solid #4caf50;
|
||||
background: #2e3e2e;
|
||||
}
|
||||
|
||||
.equipped-checkbox {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
pointer-events: none;
|
||||
accent-color: #4caf50;
|
||||
}
|
||||
}
|
||||
39
Code/Inventory/Usable/AmmoUseableBase.cs
Normal file
39
Code/Inventory/Usable/AmmoUseableBase.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public abstract class AmmoUseableBase : UseableBase
|
||||
{
|
||||
protected InventoryItem AmmoItem => FindAmmoItem();
|
||||
|
||||
private InventoryItem FindAmmoItem()
|
||||
{
|
||||
// var ammoDefinition = new InventoryItemDefinition();
|
||||
|
||||
return Dedugan.Local.Inventory.Items.FirstOrDefault( i => i.Definition.Name == "Pistol Ammo" );
|
||||
}
|
||||
|
||||
public override bool CanUse()
|
||||
{
|
||||
var ammo = AmmoItem;
|
||||
return base.CanUse() && ammo != null && ammo.Count > 0;
|
||||
}
|
||||
|
||||
public override void Use()
|
||||
{
|
||||
if ( !CanUse() )
|
||||
return;
|
||||
|
||||
OnUse();
|
||||
|
||||
var ammo = AmmoItem;
|
||||
if ( ammo != null )
|
||||
{
|
||||
ammo.Count--;
|
||||
Log.Info( $"[AmmoUseableBase] Ammo left: {ammo.Count}" );
|
||||
|
||||
if ( ammo.Count <= 0 )
|
||||
{
|
||||
Dedugan.Local.Inventory.RemoveItem( ammo );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Code/Inventory/Usable/IUseContext.cs
Normal file
6
Code/Inventory/Usable/IUseContext.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public interface IUseContext
|
||||
{
|
||||
public IEnumerable<IUseable> GetUsables();
|
||||
}
|
||||
8
Code/Inventory/Usable/IUseable.cs
Normal file
8
Code/Inventory/Usable/IUseable.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public interface IUseable
|
||||
{
|
||||
public void Use();
|
||||
bool CanUse();
|
||||
float Cooldown { get; }
|
||||
}
|
||||
15
Code/Inventory/Usable/UseSystem.cs
Normal file
15
Code/Inventory/Usable/UseSystem.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Sasalka;
|
||||
|
||||
public static class UseSystem
|
||||
{
|
||||
public static void TryUse( IUseContext context )
|
||||
{
|
||||
foreach ( var useable in context.GetUsables() )
|
||||
{
|
||||
if ( useable.CanUse() )
|
||||
{
|
||||
useable.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Code/Inventory/Usable/UseableBase.cs
Normal file
31
Code/Inventory/Usable/UseableBase.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace Sasalka;
|
||||
|
||||
public abstract class UseableBase : Component, IUseable
|
||||
{
|
||||
[Property] public float Cooldown { get; set; } = 0.5f;
|
||||
|
||||
private TimeSince _timeSinceUsed;
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
_timeSinceUsed = Cooldown;
|
||||
}
|
||||
|
||||
public virtual bool CanUse()
|
||||
{
|
||||
return _timeSinceUsed >= Cooldown && !Inventar.IsInventoryOpen;
|
||||
}
|
||||
|
||||
public virtual void Use()
|
||||
{
|
||||
if ( !CanUse() )
|
||||
return;
|
||||
|
||||
OnUse();
|
||||
_timeSinceUsed = 0;
|
||||
}
|
||||
|
||||
protected abstract void OnUse();
|
||||
}
|
||||
@@ -73,6 +73,7 @@ public sealed class NetworkManager : Component, Component.INetworkListener
|
||||
/// </summary>
|
||||
public void OnDisconnected( Connection channel )
|
||||
{
|
||||
Dedugan.InternalPlayers.Remove( Dedugan.GetByID( channel.Id ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed partial class Dedugan
|
||||
Camera.LocalRotation.Backward * MathF.Min( 0f, EyeAngles.pitch ) * 0.8f +
|
||||
Camera.LocalRotation.Right * -anotherPivot;
|
||||
|
||||
if ( AnimationHelper.HoldType == CitizenAnimationHelper.HoldTypes.Pistol )
|
||||
if ( InAds )
|
||||
{
|
||||
pivotOffset = CameraPivot.LocalRotation.Backward * CamOffsetX * 0.5f + CameraPivot.LocalRotation.Up * 8f;
|
||||
}
|
||||
|
||||
206
Code/Player/Dedugan.Inventory.cs
Normal file
206
Code/Player/Dedugan.Inventory.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using Sandbox.Citizen;
|
||||
using Sandbox.Weapons;
|
||||
using Sasalka;
|
||||
|
||||
public sealed partial class Dedugan : Component
|
||||
{
|
||||
[Property, InlineEditor] public Inventar Inventory { get; private set; } = new();
|
||||
|
||||
private Dictionary<Inventar.InventorySlot, (GameObject obj, IUseable useable)> _useableCache = new();
|
||||
[Sync] private bool InAds { get; set; } = false;
|
||||
private AttachmentSlotResolver _resolver;
|
||||
|
||||
void InventoryStart()
|
||||
{
|
||||
if ( !Network.IsOwner ) return;
|
||||
|
||||
_resolver = new AttachmentSlotResolver( Renderer.GetAttachmentObject );
|
||||
|
||||
Inventory.AddItem( new InventoryItem
|
||||
{
|
||||
Definition = ResourceLibrary.Get<InventoryItemDefinition>( "Items/Pijama.inv" )
|
||||
} );
|
||||
|
||||
Inventory.AddItem( new InventoryItem
|
||||
{
|
||||
Definition = ResourceLibrary.Get<InventoryItemDefinition>( "Items/pistol.inv" )
|
||||
} );
|
||||
|
||||
var ammo = new InventoryItem
|
||||
{
|
||||
Definition = ResourceLibrary.Get<InventoryItemDefinition>( "Items/pistol_ammo.inv" )
|
||||
};
|
||||
ammo.Count = 30;
|
||||
ammo.MaxCount = 130;
|
||||
|
||||
Inventory.AddItem( ammo );
|
||||
|
||||
Inventory.OnEquipped += OnItemEquipped;
|
||||
Inventory.OnUnEquipped += OnItemUnEquipped;
|
||||
}
|
||||
|
||||
private void OnItemEquipped( InventoryItem item )
|
||||
{
|
||||
var go = item.Definition.Prefab.Clone();
|
||||
|
||||
AnimationHelper.HoldType = item.Definition.HoldType;
|
||||
|
||||
// switch ( item.Definition.Slot )
|
||||
// {
|
||||
// case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
|
||||
// go.Parent = Renderer.GetAttachmentObject( "hold_R" );
|
||||
// AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Both;
|
||||
// break;
|
||||
// case Inventar.InventorySlot.RightHand:
|
||||
// go.Parent = Renderer.GetAttachmentObject( "hold_R" );
|
||||
// AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Right;
|
||||
// break;
|
||||
// case Inventar.InventorySlot.LeftHand:
|
||||
// go.Parent = Renderer.GetAttachmentObject( "hold_L" );
|
||||
// AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Left;
|
||||
// break;
|
||||
// default:
|
||||
// go.Parent = Renderer.GetAttachmentObject( "forward_reference_modelspace" );
|
||||
// break;
|
||||
// }
|
||||
switch ( item.Definition.Slot )
|
||||
{
|
||||
case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
|
||||
go.Parent = Renderer.GetAttachmentObject( "hold_R" );
|
||||
break;
|
||||
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 = item.Definition.WeaponDefinition.Position;
|
||||
go.LocalRotation = item.Definition.WeaponDefinition.Rotation;
|
||||
|
||||
go.NetworkSpawn();
|
||||
|
||||
var hand = item.Definition.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.HoldType = item.Definition.HoldType;
|
||||
AnimationHelper.Handedness = hand;
|
||||
|
||||
RpcSetHoldAnimation( item.Definition.HoldType, hand );
|
||||
|
||||
InAds = true;
|
||||
// item.SpawnedObject = go;
|
||||
}
|
||||
|
||||
private void OnItemUnEquipped( InventoryItem item )
|
||||
{
|
||||
switch ( item.Definition.Slot )
|
||||
{
|
||||
case Inventar.InventorySlot.LeftHand | Inventar.InventorySlot.RightHand:
|
||||
case Inventar.InventorySlot.RightHand:
|
||||
case Inventar.InventorySlot.LeftHand:
|
||||
var attachmentName = !item.Definition.Slot.HasFlag( Inventar.InventorySlot.RightHand )
|
||||
? "hold_L"
|
||||
: "hold_R";
|
||||
|
||||
Renderer.GetAttachmentObject( attachmentName ).Children.ForEach( child => child.Destroy() );
|
||||
// AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Both;
|
||||
// AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.None;
|
||||
|
||||
RpcSetHoldAnimation( CitizenAnimationHelper.HoldTypes.None, CitizenAnimationHelper.Hand.Both );
|
||||
break;
|
||||
default:
|
||||
Renderer.GetAttachmentObject( "forward_reference_modelspace" ).Children
|
||||
.ForEach( child => child.Destroy() );
|
||||
break;
|
||||
}
|
||||
|
||||
// item.SpawnedObject = null;
|
||||
item.Destroy();
|
||||
|
||||
InAds = false;
|
||||
}
|
||||
|
||||
[Rpc.Broadcast]
|
||||
public void RpcSetHoldAnimation( CitizenAnimationHelper.HoldTypes HoldType, CitizenAnimationHelper.Hand hand )
|
||||
{
|
||||
AnimationHelper.HoldType = HoldType;
|
||||
AnimationHelper.Handedness = hand;
|
||||
}
|
||||
|
||||
|
||||
// AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.None;
|
||||
// AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Both;
|
||||
|
||||
|
||||
void InventoryUpdate()
|
||||
{
|
||||
if ( !Network.IsOwner ) return;
|
||||
|
||||
// InAds = Input.Down( "Attack2" );
|
||||
|
||||
if ( Input.Pressed( "Attack1" ) )
|
||||
{
|
||||
UseSystem.TryUse( this );
|
||||
Attack();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IUseable>
|
||||
GetUsables() //Допустим, у джетпака слот Body. Просто дописываешь в GetUsables() Inventar.InventorySlot.Body:
|
||||
{
|
||||
foreach ( var slot in new[] { Inventar.InventorySlot.LeftHand, Inventar.InventorySlot.RightHand } )
|
||||
{
|
||||
if ( !Inventory.EquippedItems.TryGetValue( slot, out var item ) )
|
||||
continue;
|
||||
|
||||
var holder = _resolver.GetSlotObject( slot );
|
||||
var heldObject = holder?.Children.FirstOrDefault();
|
||||
|
||||
if ( heldObject == null )
|
||||
continue;
|
||||
|
||||
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 ( useable != null )
|
||||
yield return useable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[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();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
@@ -1,12 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox;
|
||||
|
||||
partial class Dedugan
|
||||
{
|
||||
[Property] public List<string> WorkshopItems { get; set; }
|
||||
[Property] public ClothingContainer CurrentClothing { get; set; }
|
||||
public static IReadOnlyList<Dedugan> All => InternalPlayers;
|
||||
public static List<Dedugan> InternalPlayers = new List<Dedugan>();
|
||||
|
||||
public List<ClothingContainer.ClothingEntry> Clothings { get; set; }
|
||||
public static Dedugan Local { get; set; }
|
||||
|
||||
private Guid _guid;
|
||||
@@ -47,4 +52,87 @@ partial class Dedugan
|
||||
public static Dedugan GetByID( Guid id )
|
||||
=> InternalPlayers.FirstOrDefault( x => x.ConnectionID == id );
|
||||
|
||||
public void OnNetworkSpawn( Connection owner )
|
||||
{
|
||||
CurrentClothing = ClothingContainer.CreateFromJson( owner.GetUserData( "avatar" ) );
|
||||
|
||||
var allowedCategories = new[]
|
||||
{
|
||||
Clothing.ClothingCategory.Skin, Clothing.ClothingCategory.Facial, Clothing.ClothingCategory.Eyes,
|
||||
Clothing.ClothingCategory.Eyebrows, Clothing.ClothingCategory.Eyelashes,
|
||||
Clothing.ClothingCategory.MakeupLips, Clothing.ClothingCategory.MakeupEyeshadow,
|
||||
Clothing.ClothingCategory.MakeupEyeliner, Clothing.ClothingCategory.MakeupHighlighter,
|
||||
Clothing.ClothingCategory.MakeupBlush, Clothing.ClothingCategory.MakeupSpecial,
|
||||
Clothing.ClothingCategory.ComplexionFreckles, Clothing.ClothingCategory.ComplexionScars,
|
||||
Clothing.ClothingCategory.ComplexionAcne, Clothing.ClothingCategory.FacialHairMustache,
|
||||
Clothing.ClothingCategory.FacialHairBeard, Clothing.ClothingCategory.FacialHairStubble,
|
||||
Clothing.ClothingCategory.FacialHairSideburns, Clothing.ClothingCategory.FacialHairGoatee,
|
||||
Clothing.ClothingCategory.PierceNose, Clothing.ClothingCategory.PierceEyebrow,
|
||||
Clothing.ClothingCategory.PierceSpecial, Clothing.ClothingCategory.Hair,
|
||||
Clothing.ClothingCategory.HairShort, Clothing.ClothingCategory.HairMedium,
|
||||
Clothing.ClothingCategory.HairLong, Clothing.ClothingCategory.HairUpdo,
|
||||
Clothing.ClothingCategory.HairSpecial,
|
||||
};
|
||||
|
||||
CurrentClothing.Clothing.RemoveAll( entry => !allowedCategories.Contains( entry.Clothing.Category ) );
|
||||
CurrentClothing.Apply( Renderer );
|
||||
|
||||
WearWorkshop( WorkshopItems );
|
||||
}
|
||||
|
||||
CancellationTokenSource _cts;
|
||||
|
||||
public async void WearWorkshop( List<string> workshopItems )
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
// var clothing = new ClothingContainer();
|
||||
// clothing.AddRange( Clothings );
|
||||
|
||||
if ( workshopItems != null && workshopItems.Count > 0 )
|
||||
{
|
||||
var tasks = workshopItems.Select( x => InstallWorkshopClothing( x, token ) );
|
||||
|
||||
foreach ( var task in tasks )
|
||||
{
|
||||
var c = await task;
|
||||
|
||||
if ( c is null )
|
||||
continue;
|
||||
|
||||
CurrentClothing.Add( c );
|
||||
}
|
||||
}
|
||||
|
||||
CurrentClothing.Normalize();
|
||||
CurrentClothing.Apply( Renderer );
|
||||
Renderer.PostAnimationUpdate();
|
||||
|
||||
// foreach ( var clothing in CurrentClothing.Clothing )
|
||||
// {
|
||||
// Log.Info( clothing.Clothing.Title );
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
async Task<Clothing> InstallWorkshopClothing( string ident, CancellationToken ct )
|
||||
{
|
||||
if ( string.IsNullOrEmpty( ident ) ) return default;
|
||||
|
||||
var package = await Package.FetchAsync( ident, false );
|
||||
if ( package is null ) return default;
|
||||
if ( package.TypeName != "clothing" ) return default;
|
||||
if ( ct.IsCancellationRequested ) return default;
|
||||
|
||||
var primaryAsset = package.GetMeta<string>( "PrimaryAsset" );
|
||||
if ( string.IsNullOrWhiteSpace( primaryAsset ) ) return default;
|
||||
|
||||
var fs = await package.MountAsync();
|
||||
if ( fs is null ) return default;
|
||||
if ( ct.IsCancellationRequested ) return default;
|
||||
|
||||
// try to load it
|
||||
return ResourceLibrary.Get<Clothing>( primaryAsset );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
using Sandbox.Citizen;
|
||||
using Sandbox.Weapons;
|
||||
|
||||
public sealed partial class Dedugan
|
||||
{
|
||||
[Property] public GameObject Gun { get; set; }
|
||||
[Sync] private bool InAds { get; set; } = false;
|
||||
|
||||
private Weapon _weapon { get; set; }
|
||||
|
||||
void WeaponStart()
|
||||
{
|
||||
_weapon = Gun.Components.Get<Weapon>(FindMode.EverythingInSelfAndDescendants );
|
||||
|
||||
Log.Info( $"_weapon: {_weapon}, Network.IsOwner: {Network.IsOwner}" );
|
||||
}
|
||||
|
||||
[Rpc.Broadcast]
|
||||
void Attack()
|
||||
{
|
||||
Renderer.Set( "b_attack", true );
|
||||
}
|
||||
|
||||
void WeaponUpdate()
|
||||
{
|
||||
if ( InAds )
|
||||
{
|
||||
AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Right;
|
||||
AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.Pistol;
|
||||
|
||||
Gun.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationHelper.Handedness = CitizenAnimationHelper.Hand.Both;
|
||||
AnimationHelper.HoldType = CitizenAnimationHelper.HoldTypes.None;
|
||||
|
||||
Gun.Enabled = false;
|
||||
}
|
||||
|
||||
if ( !Network.IsOwner ) return;
|
||||
|
||||
InAds = Input.Down( "Attack2" );
|
||||
|
||||
if ( Input.Pressed( "Attack1" ) && InAds )
|
||||
{
|
||||
_weapon.Attack();
|
||||
Attack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using Sandbox;
|
||||
using Sandbox.Citizen;
|
||||
using Sasalka;
|
||||
using ShrimpleCharacterController;
|
||||
|
||||
public sealed partial class Dedugan : Component
|
||||
public sealed partial class Dedugan : Component, IUseContext, Component.INetworkSpawn
|
||||
{
|
||||
[RequireComponent] public ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
|
||||
[RequireComponent] public CitizenAnimationHelper AnimationHelper { get; set; }
|
||||
// [Property] public SkinnedModelRenderer Anim {get; set;}
|
||||
|
||||
public SkinnedModelRenderer Renderer { get; set; }
|
||||
[Property] public SkinnedModelRenderer Renderer { get; set; }
|
||||
[Property] public GameObject Camera { get; set; }
|
||||
[Property] public GameObject CameraPivot { get; set; }
|
||||
[Property] public GameObject InventoryUI { get; set; }
|
||||
|
||||
[Property] [Range( 1f, 200f, 1f )] public float CamOffsetX { get; set; }
|
||||
[Property] [Range( 50f, 1200f, 10f )] public float WalkSpeed { get; set; } = 100f;
|
||||
@@ -38,21 +38,23 @@ public sealed partial class Dedugan : Component
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
WeaponStart();
|
||||
InventoryStart();
|
||||
|
||||
RagdollController = Components.Get<RagdollController>();
|
||||
Renderer = Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
|
||||
// Renderer = Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
|
||||
|
||||
var cameraComponent = Camera.GetComponent<CameraComponent>();
|
||||
var listener = Components.Get<AudioListener>(true);
|
||||
var listener = Components.Get<AudioListener>( true );
|
||||
cameraComponent.Enabled = false;
|
||||
InventoryUI.Enabled = false;
|
||||
listener.Enabled = false;
|
||||
|
||||
if ( !Network.IsOwner ) return;
|
||||
|
||||
|
||||
cameraComponent.Enabled = true;
|
||||
listener.Enabled = true;
|
||||
|
||||
InventoryUI.Enabled = true;
|
||||
|
||||
if ( Renderer is null )
|
||||
return;
|
||||
|
||||
@@ -62,7 +64,7 @@ public sealed partial class Dedugan : Component
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
UpdateCustomAnimations();
|
||||
WeaponUpdate();
|
||||
InventoryUpdate();
|
||||
if ( Network.IsOwner )
|
||||
{
|
||||
EyeAngles += Input.AnalogLook;
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Sandbox;
|
||||
|
||||
public class PlayerDresser : Component, Component.INetworkSpawn
|
||||
{
|
||||
[Property]
|
||||
public SkinnedModelRenderer BodyRenderer { get; set; }
|
||||
[Property] public SkinnedModelRenderer BodyRenderer { get; set; }
|
||||
|
||||
[Property] public ClothingContainer currentClothing { get; set; }
|
||||
|
||||
// public void OnNetworkSpawn( Connection owner )
|
||||
// {
|
||||
// Log.Info( $"Hello {owner.Name}" );
|
||||
// var clothing = new ClothingContainer();
|
||||
// clothing.Deserialize( owner.GetUserData( "avatar" ) );
|
||||
// clothing.Apply( BodyRenderer );
|
||||
// }
|
||||
|
||||
public void OnNetworkSpawn( Connection owner )
|
||||
{
|
||||
Log.Info( $"Hello {owner.Name}" );
|
||||
var clothing = ClothingContainer.CreateFromJson( owner.GetUserData( "avatar" ) );
|
||||
clothing.Height = 1;
|
||||
clothing.Apply( BodyRenderer );
|
||||
currentClothing = ClothingContainer.CreateFromJson( owner.GetUserData( "avatar" ) );
|
||||
|
||||
var allowedCategories = new[]
|
||||
{
|
||||
Clothing.ClothingCategory.Skin, Clothing.ClothingCategory.Facial, Clothing.ClothingCategory.Eyes,
|
||||
Clothing.ClothingCategory.Eyebrows, Clothing.ClothingCategory.Eyelashes,
|
||||
Clothing.ClothingCategory.MakeupLips, Clothing.ClothingCategory.MakeupEyeshadow,
|
||||
Clothing.ClothingCategory.MakeupEyeliner, Clothing.ClothingCategory.MakeupHighlighter,
|
||||
Clothing.ClothingCategory.MakeupBlush, Clothing.ClothingCategory.MakeupSpecial,
|
||||
Clothing.ClothingCategory.ComplexionFreckles, Clothing.ClothingCategory.ComplexionScars,
|
||||
Clothing.ClothingCategory.ComplexionAcne, Clothing.ClothingCategory.FacialHairMustache,
|
||||
Clothing.ClothingCategory.FacialHairBeard, Clothing.ClothingCategory.FacialHairStubble,
|
||||
Clothing.ClothingCategory.FacialHairSideburns, Clothing.ClothingCategory.FacialHairGoatee,
|
||||
Clothing.ClothingCategory.PierceNose, Clothing.ClothingCategory.PierceEyebrow,
|
||||
Clothing.ClothingCategory.PierceSpecial, Clothing.ClothingCategory.Hair,
|
||||
Clothing.ClothingCategory.HairShort, Clothing.ClothingCategory.HairMedium,
|
||||
Clothing.ClothingCategory.HairLong, Clothing.ClothingCategory.HairUpdo,
|
||||
Clothing.ClothingCategory.HairSpecial,
|
||||
};
|
||||
|
||||
currentClothing.Clothing.RemoveAll( entry => !allowedCategories.Contains( entry.Clothing.Category ) );
|
||||
currentClothing.Apply( BodyRenderer );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,94 +2,105 @@
|
||||
@using Sandbox.UI;
|
||||
@inherits PanelComponent
|
||||
|
||||
<root class="@(Visible ? "" : "hidden")">
|
||||
@* <div class="decoration top-left"></div> *@
|
||||
@* <div class="decoration top-right"></div> *@
|
||||
@* <div class="decoration bottom-left"></div> *@
|
||||
@* <div class="decoration bottom-right"></div> *@
|
||||
|
||||
<label class="title">Players</label>
|
||||
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<label class="column nick">Player</label>
|
||||
<label class="column status">Status</label>
|
||||
<label class="column ping">Ping</label>
|
||||
</div>
|
||||
|
||||
<div class="player-list">
|
||||
@if (Dedugan.All is not null)
|
||||
{
|
||||
foreach (var ded in Dedugan.All)
|
||||
{
|
||||
<div class="player" onclick="@(() => OpenProfile(ded.Connection))">
|
||||
<div class="avatar">
|
||||
<img src="avatar:@ded.SteamID"/>
|
||||
</div>
|
||||
<label class="column nick">@ded.Connection.DisplayName</label>
|
||||
<label class="column status">@GetPlayerStatus(ded.Connection)</label>
|
||||
<label class="column ping">@ded.Connection.Ping</label>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<root class="@( Visible ? "" : "hidden" )">
|
||||
@* <div class="decoration top-left"></div> *@
|
||||
@* <div class="decoration top-right"></div> *@
|
||||
@* <div class="decoration bottom-left"></div> *@
|
||||
@* <div class="decoration bottom-right"></div> *@
|
||||
|
||||
<label class="title">Players</label>
|
||||
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<label class="column nick">Player</label>
|
||||
<label class="column status">Status</label>
|
||||
<label class="column ping">Ping</label>
|
||||
</div>
|
||||
|
||||
<div class="player-list">
|
||||
@if ( Dedugan.All is not null )
|
||||
{
|
||||
foreach ( var ded in Dedugan.All )
|
||||
{
|
||||
<div class="player" onclick="@( () => OpenProfile( ded.Connection ) )">
|
||||
<div class="avatar">
|
||||
<img src="avatar:@ded.SteamID"/>
|
||||
</div>
|
||||
<label class="column nick">@ded.Connection.DisplayName</label>
|
||||
<label class="column status">@GetPlayerStatus( ded.Connection )</label>
|
||||
<label class="column ping">@ded.Connection.Ping</label>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</root>
|
||||
|
||||
@code
|
||||
{
|
||||
public static bool Visible => Input.Down("Score");
|
||||
private NetworkManager _networkManager;
|
||||
|
||||
private NetworkManager NetworkManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_networkManager == null || !_networkManager.IsValid)
|
||||
{
|
||||
_networkManager = Scene.Directory.FindByName("Network Manager")
|
||||
.FirstOrDefault()?
|
||||
.GetComponent<NetworkManager>();
|
||||
}
|
||||
return _networkManager;
|
||||
}
|
||||
}
|
||||
public static bool Visible = false;
|
||||
private NetworkManager _networkManager;
|
||||
|
||||
protected override void OnEnabled()
|
||||
{
|
||||
// Кэшируем NetworkManager при включении компонента
|
||||
_networkManager = Scene.Directory.FindByName("Network Manager")
|
||||
.FirstOrDefault()?
|
||||
.GetComponent<NetworkManager>();
|
||||
}
|
||||
private NetworkManager NetworkManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _networkManager == null || !_networkManager.IsValid )
|
||||
{
|
||||
_networkManager = Scene.Directory.FindByName( "Network Manager" )
|
||||
.FirstOrDefault()?
|
||||
.GetComponent<NetworkManager>();
|
||||
}
|
||||
|
||||
private string GetPlayerStatus(Connection conn)
|
||||
{
|
||||
var playerObj = Dedugan.GetByID( conn.Id ).GameObject;
|
||||
return playerObj?.IsValid == true ? "In Game" : "Connecting";
|
||||
}
|
||||
return _networkManager;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenProfile(Connection connection)
|
||||
{
|
||||
Log.Info($"Opening profile: {connection.SteamId}");
|
||||
Game.Overlay.ShowPlayer(connection.SteamId);
|
||||
}
|
||||
|
||||
protected override int BuildHash()
|
||||
{
|
||||
if (!Visible || Dedugan.All == null)
|
||||
return -1;
|
||||
|
||||
var hash = new System.HashCode();
|
||||
hash.Add(Visible);
|
||||
|
||||
foreach (var ded in Dedugan.All)
|
||||
{
|
||||
hash.Add(ded.Id);
|
||||
hash.Add(ded.Connection.Ping);
|
||||
hash.Add(ded.Name);
|
||||
}
|
||||
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if ( Input.Pressed( "Score" ) )
|
||||
{
|
||||
Visible = !Visible;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnabled()
|
||||
{
|
||||
// Кэшируем NetworkManager при включении компонента
|
||||
_networkManager = Scene.Directory.FindByName( "Network Manager" )
|
||||
.FirstOrDefault()?
|
||||
.GetComponent<NetworkManager>();
|
||||
}
|
||||
|
||||
private string GetPlayerStatus( Connection conn )
|
||||
{
|
||||
var playerObj = Dedugan.GetByID( conn.Id ).GameObject;
|
||||
return playerObj?.IsValid == true ? "In Game" : "Connecting";
|
||||
}
|
||||
|
||||
private void OpenProfile( Connection connection )
|
||||
{
|
||||
Log.Info( $"Opening profile: {connection.SteamId}" );
|
||||
Game.Overlay.ShowPlayer( connection.SteamId );
|
||||
}
|
||||
|
||||
protected override int BuildHash()
|
||||
{
|
||||
base.BuildHash();
|
||||
|
||||
if ( !Visible || Dedugan.All == null )
|
||||
return -1;
|
||||
|
||||
var hash = new System.HashCode();
|
||||
hash.Add( Visible );
|
||||
|
||||
foreach ( var ded in Dedugan.All )
|
||||
{
|
||||
hash.Add( ded.Id );
|
||||
hash.Add( ded.Connection.Ping );
|
||||
hash.Add( ded.Name );
|
||||
}
|
||||
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,191 +1,191 @@
|
||||
Scoreboard {
|
||||
background: linear-gradient(135deg, #0a1a2b 0%, #08111f 100%);
|
||||
border: 3px solid #2a3d54;
|
||||
border-radius: 16px;
|
||||
//box-shadow:
|
||||
// 0 0 15px rgba(0, 150, 255, 0.2),
|
||||
// inset 0 0 10px rgba(0, 100, 200, 0.1);
|
||||
font-family: 'Orbitron', 'Poppins', sans-serif;
|
||||
background: linear-gradient(135deg, #0a1a2b 0%, #08111f 100%);
|
||||
border: 3px solid #2a3d54;
|
||||
border-radius: 14px;
|
||||
//box-shadow:
|
||||
// 0 0 15px rgba(0, 150, 255, 0.2),
|
||||
// inset 0 0 10px rgba(0, 100, 200, 0.1);
|
||||
font-family: 'Orbitron', 'Poppins', sans-serif;
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
height: 96vh;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.decoration {
|
||||
position: absolute;
|
||||
width: 60%;
|
||||
height: 70vh;
|
||||
top: 15vh;
|
||||
left: 20%;
|
||||
padding: 30px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 2px solid rgba(0, 180, 255, 0.25);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 0 8px rgba(100, 200, 255, 0.1);
|
||||
|
||||
&.top-left {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
border-radius: 12px 0 0 0;
|
||||
}
|
||||
|
||||
&.top-right {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
border-radius: 0 12px 0 0;
|
||||
}
|
||||
|
||||
&.bottom-left {
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-radius: 0 0 0 12px;
|
||||
}
|
||||
|
||||
&.bottom-right {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-radius: 0 0 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
color: #a0e0ff;
|
||||
text-align: center;
|
||||
//text-shadow:
|
||||
// 0 0 10px rgba(100, 200, 255, 0.7),
|
||||
// 0 0 20px rgba(80, 180, 255, 0.4);
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 100;
|
||||
background-color: rgba(10, 25, 40, 0.6);
|
||||
border-radius: 12px;
|
||||
border: 1px solid #253a50;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
//box-shadow: inset 0 0 20px rgba(0, 30, 60, 0.5);
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
padding: 15px 20px 15px 85px;
|
||||
background: linear-gradient(90deg, #0f2a42 0%, #0a1d30 100%);
|
||||
border-bottom: 2px solid #1e3a5c;
|
||||
text-shadow: 0 0 5px rgba(100, 200, 255, 0.5);
|
||||
|
||||
.decoration {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 2px solid rgba(0, 180, 255, 0.25);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 0 8px rgba(100, 200, 255, 0.1);
|
||||
|
||||
&.top-left {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
border-radius: 12px 0 0 0;
|
||||
}
|
||||
|
||||
&.top-right {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
border-radius: 0 12px 0 0;
|
||||
}
|
||||
|
||||
&.bottom-left {
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-radius: 0 0 0 12px;
|
||||
}
|
||||
|
||||
&.bottom-right {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-radius: 0 0 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 42px;
|
||||
color: #a0e0ff;
|
||||
text-align: center;
|
||||
//text-shadow:
|
||||
// 0 0 10px rgba(100, 200, 255, 0.7),
|
||||
// 0 0 20px rgba(80, 180, 255, 0.4);
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 15px;
|
||||
.column {
|
||||
font-size: 18px;
|
||||
color: #6eb4ff;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
|
||||
&.nick {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.status {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.ping {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.player-list {
|
||||
pointer-events: all;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
.player {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: rgba(10, 25, 40, 0.6);
|
||||
border-radius: 12px;
|
||||
border: 1px solid #253a50;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
//box-shadow: inset 0 0 20px rgba(0, 30, 60, 0.5);
|
||||
align-items: center;
|
||||
padding: 16px 0 16px 24px;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
//background: rgba(15, 30, 50, 0.4);
|
||||
border-bottom: 1px solid rgba(40, 80, 120, 0.2);
|
||||
cursor: pointer;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
padding: 15px 20px 15px 85px;
|
||||
background: linear-gradient(90deg, #0f2a42 0%, #0a1d30 100%);
|
||||
border-bottom: 2px solid #1e3a5c;
|
||||
text-shadow: 0 0 5px rgba(100, 200, 255, 0.5);
|
||||
|
||||
.column {
|
||||
font-size: 18px;
|
||||
color: #6eb4ff;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
|
||||
&.nick {
|
||||
flex: 3;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.status {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.ping {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
//background: rgba(25, 60, 90, 0.4);
|
||||
box-shadow: 0 0 15px rgba(0, 150, 255, 0.1);
|
||||
}
|
||||
|
||||
.player-list {
|
||||
pointer-events: all;
|
||||
flex-grow: 1;
|
||||
.avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-right: 20px;
|
||||
border: 2px solid #2a4a6b;
|
||||
box-shadow: 0 0 8px rgba(100, 180, 255, 0.2);
|
||||
background: #0c1a2a;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
.player {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 25px;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
//background: rgba(15, 30, 50, 0.4);
|
||||
border-bottom: 1px solid rgba(40, 80, 120, 0.2);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
//background: rgba(25, 60, 90, 0.4);
|
||||
box-shadow: 0 0 15px rgba(0, 150, 255, 0.1);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-right: 20px;
|
||||
border: 2px solid #2a4a6b;
|
||||
box-shadow: 0 0 8px rgba(100, 180, 255, 0.2);
|
||||
background: #0c1a2a;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
font-size: 17px;
|
||||
color: #c0e8ff;
|
||||
text-shadow: 0 0 5px rgba(100, 180, 255, 0.3);
|
||||
|
||||
&.nick {
|
||||
flex: 3;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.status {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
color: #6ecbff;
|
||||
}
|
||||
|
||||
&.ping {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #88d6ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
font-size: 17px;
|
||||
color: #c0e8ff;
|
||||
text-shadow: 0 0 5px rgba(100, 180, 255, 0.3);
|
||||
|
||||
&.nick {
|
||||
flex: 3;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.status {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
color: #6ecbff;
|
||||
}
|
||||
|
||||
&.ping {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #88d6ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
using Sasalka;
|
||||
|
||||
namespace Sandbox.Weapons;
|
||||
|
||||
public sealed class Weapon : Component
|
||||
public sealed class Weapon : AmmoUseableBase
|
||||
{
|
||||
[Property] public SkinnedModelRenderer GunRenderer { get; private set; }
|
||||
[Property] public GameObject BulletOut { get; private set; }
|
||||
[Property] public GameObject MuzzleLight { get; private set; }
|
||||
|
||||
[Property] public GameObject particlePrefab { get; set; }
|
||||
[Property] public GameObject bloodParticle { get; set; }
|
||||
|
||||
[Property] public DecalDefinition ImpactDecal { get; set; }
|
||||
[Property] public SoundEvent ImpactSound { get; set; }
|
||||
|
||||
private SoundPointComponent _sound;
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
_sound = GameObject.GetComponent<SoundPointComponent>( true );
|
||||
}
|
||||
|
||||
@@ -36,32 +33,30 @@ public sealed class Weapon : Component
|
||||
.UseHitboxes()
|
||||
.Run();
|
||||
|
||||
Log.Info( Dedugan.Local.GameObject );
|
||||
|
||||
if (tr.Hit && tr.Hitbox != null)
|
||||
if ( tr.Hit && tr.Hitbox != null )
|
||||
{
|
||||
var components = tr.GameObject.Components;
|
||||
var boneIndex = tr.Hitbox.Bone.Index;
|
||||
|
||||
|
||||
// Log.Info($"{tr.GameObject.Name} attacked");
|
||||
|
||||
var dedugan = components.Get<Dedugan>();
|
||||
var enemy = components.Get<Enemy>();
|
||||
|
||||
if (dedugan.IsValid() || enemy.IsValid())
|
||||
if ( dedugan.IsValid() || enemy.IsValid() )
|
||||
{
|
||||
CreateHitEffects(tr.EndPosition, tr.Normal, true);
|
||||
CreateHitEffects( tr.EndPosition, tr.Normal, true );
|
||||
}
|
||||
|
||||
if (dedugan.IsValid())
|
||||
if ( dedugan.IsValid() )
|
||||
{
|
||||
dedugan.ReportHit(dir, boneIndex);
|
||||
dedugan.ReportHit( dir, boneIndex );
|
||||
}
|
||||
|
||||
if (enemy.IsValid())
|
||||
|
||||
if ( enemy.IsValid() )
|
||||
{
|
||||
enemy.ReportHit(dir, boneIndex);
|
||||
Log.Info(boneIndex);
|
||||
enemy.ReportHit( dir, boneIndex );
|
||||
Log.Info( boneIndex );
|
||||
}
|
||||
}
|
||||
else if ( tr.Hitbox == null )
|
||||
@@ -100,4 +95,9 @@ public sealed class Weapon : Component
|
||||
await GameTask.DelaySeconds( 0.05f );
|
||||
MuzzleLight.Enabled = false;
|
||||
}
|
||||
|
||||
protected override void OnUse()
|
||||
{
|
||||
Attack();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user