using System; using Swihoni.Components; using Swihoni.Sessions.Components; using Swihoni.Sessions.Items; using Swihoni.Sessions.Items.Modifiers; using UnityEngine; namespace Swihoni.Sessions.Player.Components { [Serializable, ClientTrusted] public class CameraComponent : ComponentBase { [Angle] public FloatProperty yaw; public FloatProperty pitch; public Vector3 GetForward() { float y = yaw * Mathf.Deg2Rad, p = pitch * Mathf.Deg2Rad; var forward = new Vector3(Mathf.Cos(p) * Mathf.Sin(y), -Mathf.Sin(p), Mathf.Cos(p) * Mathf.Cos(y)); forward.Normalize(); return forward; } public override string ToString() => $"Yaw: {yaw}, Pitch: {pitch}"; } [Serializable] public class MoveType : ByteProperty { public const byte Grounded = 0, Flying = 1; } [Serializable, ModeElement, OnlyServerTrusted] public class FrozenProperty : BoolProperty { } [Serializable, ModeElement, OnlyServerTrusted] public class SuffocatingProperty : BoolProperty { } [Serializable, ModeElement, ClientChecked] public class MoveComponent : ComponentBase { public MoveType type; [PredictionTolerance(0.02f), InterpolateRange(2.0f), NeverCompress] public VectorProperty position, velocity; [NeverCompress] public ByteProperty groundTick; [PredictionTolerance(0.02f), NeverCompress] public FloatProperty normalizedCrouch; [Cyclic(0.0f, 1.0f)] public FloatProperty normalizedMove; public static implicit operator Vector3(MoveComponent move) => move.position; public override string ToString() => $"Position: {position}, Velocity: {velocity}"; } [Serializable, OnlyServerTrusted] public class HealthProperty : ByteProperty { public bool IsAlive => !IsDead; public bool IsDead => Value == 0; public bool IsInactiveOrDead => WithoutValue || IsDead; public bool IsActiveAndAlive => WithValue && IsAlive; } [Serializable, ModeElement, OnlyServerTrusted] public class StatsComponent : ComponentBase { public ByteProperty kills, deaths; public UShortProperty damage, ping; } [Serializable, ModeElement, OnlyServerTrusted] public class RespawnTimerProperty : TimeUsProperty { public override string ToString() => $"Respawn timer: {base.ToString()}"; } [Serializable, ModeElement, OnlyServerTrusted] public class TeamProperty : ByteProperty { public TeamProperty(byte value) : base(value) { } public TeamProperty() { } } [Serializable, SingleTick] public class WantedTeamProperty : ByteProperty { } [Serializable, CustomInterpolation] public class ByteStatusComponent : ComponentBase { public ByteProperty id; public ElapsedUsProperty elapsedUs; /// /// Note: Interpolation must be explicitly called for this type. /// public void InterpolateFrom(ByteStatusComponent s1, ByteStatusComponent s2, float interpolation, Func getStatusDurationUs) { uint d1 = getStatusDurationUs(s1.id), e1 = s1.elapsedUs, e2 = s2.elapsedUs; if (d1 == uint.MaxValue) { id.Value = s2.id; elapsedUs.Value = UIntProperty.InterpolateUInt(s1.id == s2.id ? e1 : 0u, e2, interpolation); } else { if (s1.id != s2.id) e2 += d1; else if (e1 > e2) e2 += d1; uint interpolatedElapsedUs = UIntProperty.InterpolateUInt(e1, e2, interpolation); byte interpolatedId; if (interpolatedElapsedUs > d1) { interpolatedElapsedUs -= d1; interpolatedId = s2.id; } else interpolatedId = s1.id; id.Value = interpolatedId; elapsedUs.Value = interpolatedElapsedUs; } } public override string ToString() => $"ID: {id}, Elapsed: {elapsedUs}"; } [Serializable, CustomInterpolation] public class ItemComponent : ComponentBase { public ByteProperty id; public ByteStatusComponent status; public UShortProperty ammoInMag, ammoInReserve; private static ItemModifierBase _modifier; public bool NoAmmoLeft => ammoInMag == 0 && ammoInReserve == 0; // Embedded item components are only explicitly interpolated, since usually it only needs to be done on equipped item public void InterpolateFrom(ItemComponent i1, ItemComponent i2, float interpolation) { if (i1.id.WithoutValue || i2.id.WithoutValue) { this.SetTo(i2); return; } _modifier = ItemAssetLink.GetModifier(i1.id); ammoInMag.SetTo(i2.ammoInMag); ammoInReserve.SetTo(i2.ammoInReserve); id.Value = i2.id.Value; status.InterpolateFrom(i1.status, i2.status, interpolation, statusId => InventoryComponent.VisualDuration(_modifier.GetStatusModifierProperties(statusId))); } } public class ItemsArray : ArrayElement { public const int ItemsCount = 10; public ItemsArray() : base(ItemsCount) { } } [Serializable, ModeElement, ClientChecked, CustomInterpolation] public class InventoryComponent : ComponentBase { public ByteProperty equippedIndex, previousEquippedIndex; public ByteStatusComponent equipStatus, adsStatus; public ItemsArray items = new(); [ClientNonChecked] public TimeUsProperty tracerTimeUs; [ClientNonChecked] public VectorProperty tracerStart, tracerEnd; public ItemComponent EquippedItemComponent => this[equippedIndex]; public bool HasItemEquipped => equippedIndex.WithValue; public bool HasNoItemEquipped => !HasItemEquipped; public bool WithItemEquipped(out ItemComponent equippedItem) { if (HasItemEquipped) { equippedItem = EquippedItemComponent; return true; } equippedItem = default; return false; } private static ItemModifierBase _m1; public override void CustomInterpolateFrom(ComponentBase c1, ComponentBase c2, float interpolation) { InventoryComponent i1 = (InventoryComponent) c1, i2 = (InventoryComponent) c2; if (i1.HasNoItemEquipped || i2.HasNoItemEquipped || i1.equippedIndex != i2.equippedIndex) { this.SetTo(i1); return; } // TODO:feature handle when id of equipped weapon changes ItemModifierBase m1 = ItemAssetLink.GetModifier(i1.EquippedItemComponent.id); _m1 = m1; // Prevent closure allocation if (m1 is GunModifierBase) { adsStatus.InterpolateFrom(i1.adsStatus, i2.adsStatus, interpolation, aimStatusId => VisualDuration(((GunModifierBase) _m1).GetAdsStatusModifierProperties(aimStatusId))); } equipStatus.InterpolateFrom(i1.equipStatus, i2.equipStatus, interpolation, equipStatusId => VisualDuration(_m1.GetEquipStatusModifierProperties(equipStatusId))); equippedIndex.Value = i1.equippedIndex; for (var i = 0; i < i1.items.Length; i++) items[i].InterpolateFrom(i1.items[i], i2.items[i], interpolation); } public new ItemComponent this[int index] => items[index]; public static uint VisualDuration(ItemStatusModiferProperties m) => m.isPersistent ? uint.MaxValue : m.durationUs; } }