using System; using System.Diagnostics; using UnityEngine; using Ashwild.Audio; using Ashwild.Harvesting; using Ashwild.Interaction; using Ashwild.Inventory; namespace Ashwild.Player { public static class PlayerEvents { // ============================================================ // State (single source of truth — read by everyone) // ============================================================ public static bool IsDead { get; private set; } public static bool IsGrounded { get; private set; } = true; public static bool IsCrouching { get; private set; } public static bool IsSprinting { get; private set; } public static bool IsSliding { get; private set; } public static bool IsOnSlope { get; private set; } public static bool IsInventoryOpen { get; private set; } public static bool IsPaused { get; private set; } // True when a networked session is live (host or client). public static bool IsOnline { get; private set; } // True when this instance owns the server (host = server + local client). public static bool IsHost { get; private set; } // Shareable code of the current session (host SteamID64). Empty when offline. public static string SessionCode { get; private set; } = string.Empty; // The interactable the player is currently aiming at (null when none). public static IInteractable HoveredInteractable { get; private set; } // Single source of truth for "the player must not respond to input right now". // Used by locomotion, camera, tools and interaction so they all agree. public static bool InputLocked => IsDead || IsInventoryOpen || IsPaused; // ============================================================ // Input events // ============================================================ public static event Action MoveInput; public static event Action LookInput; public static event Action JumpPressed; public static event Action SprintHeld; public static event Action CrouchPressed; public static event Action AttackPressed; public static event Action InteractPressed; public static event Action DropPressed; public static event Action InventoryTogglePressed; public static event Action HotbarSlotPressed; public static event Action HotbarScroll; public static event Action CancelPressed; // UI "Cancel" (Escape) — back / pause / close // ============================================================ // Locomotion events // ============================================================ public static event Action GroundedChanged; // isGrounded, impactSpeed public static event Action SlopeChanged; public static event Action GroundMaterialChanged; public static event Action VelocityChanged; public static event Action YawChanged; public static event Action Jumped; public static event Action CrouchChanged; public static event Action SprintChanged; public static event Action CapsuleHeightChanged; public static event Action CameraHeightChanged; public static event Action LandingStunStarted; public static event Action LandingStunEnded; public static event Action SlideStarted; public static event Action SlideEnded; public static event Action MultiJumpPerformed; // ============================================================ // Stats events // ============================================================ public static event Action HealthChanged; public static event Action HungerChanged; public static event Action ThirstChanged; public static event Action Damaged; public static event Action FallDamageApplied; public static event Action StarvationTick; public static event Action DehydrationTick; // ============================================================ // Inventory events // ============================================================ public static event Action InventorySlotChanged; public static event Action SelectedHotbarSlotChanged; public static event Action ItemAdded; public static event Action ItemDropped; public static event Action ItemConsumed; public static event Action ItemPickedUp; public static event Action HeldItemChanged; public static event Action InventoryOpenChanged; public static event Action GamePausedChanged; // ============================================================ // Interaction events // ============================================================ public static event Action InteractableHoverChanged; // null = nothing hovered // ============================================================ // Tools events // ============================================================ public static event Action AttackSwung; public static event Action HarvestableHit; public static event Action EquipAnimComplete; public static event Action UnequipAnimComplete; // ============================================================ // Cooking events // ============================================================ public static event Action FoodPlacedToCook; // the raw item placed on a station public static event Action FoodCookedReady; // the cooked result is ready to take public static event Action FuelAdded; // the fuel item consumed to feed a station // ============================================================ // Audio events // ============================================================ public static event Action SurfaceChanged; public static event Action FootstepTriggered; // ============================================================ // Lifecycle events // ============================================================ public static event Action Died; public static event Action Respawned; public static event Action DeathChanged; // ============================================================ // Network / session events // ============================================================ public static event Action SessionStarting; // host/join requested, before transport is up public static event Action SessionStarted; // host is up — payload is the shareable code public static event Action SessionJoining; // local client connecting to a code public static event Action SessionJoined; // local client connected to a host public static event Action SessionStopped; // session fully torn down (offline again) public static event Action SessionError; // human-readable failure reason public static event Action RemotePlayerCountChanged; // number of *other* connected clients public static event Action LocalPlayerSpawned; // the local owned player NetworkObject is ready public static event Action InviteReceived; // inviter SteamID64, inviter name, connect code // ============================================================ // Raisers — state is updated automatically here so consumers // can rely on PlayerEvents.IsX matching the latest event. // ============================================================ public static void RaiseMoveInput(Vector2 v) { Log(nameof(MoveInput)); MoveInput?.Invoke(v); } public static void RaiseLookInput(Vector2 v) { LookInput?.Invoke(v); } public static void RaiseJumpPressed() { Log(nameof(JumpPressed)); JumpPressed?.Invoke(); } public static void RaiseSprintHeld(bool b) { Log(nameof(SprintHeld)); SprintHeld?.Invoke(b); } public static void RaiseCrouchPressed() { Log(nameof(CrouchPressed)); CrouchPressed?.Invoke(); } public static void RaiseAttackPressed() { Log(nameof(AttackPressed)); AttackPressed?.Invoke(); } public static void RaiseInteractPressed() { Log(nameof(InteractPressed)); InteractPressed?.Invoke(); } public static void RaiseDropPressed() { Log(nameof(DropPressed)); DropPressed?.Invoke(); } public static void RaiseInventoryTogglePressed() { Log(nameof(InventoryTogglePressed)); InventoryTogglePressed?.Invoke(); } public static void RaiseHotbarSlotPressed(int i) { Log(nameof(HotbarSlotPressed)); HotbarSlotPressed?.Invoke(i); } public static void RaiseHotbarScroll(float f) { HotbarScroll?.Invoke(f); } public static void RaiseCancelPressed() { Log(nameof(CancelPressed)); CancelPressed?.Invoke(); } public static void RaiseGroundedChanged(bool grounded, float impactSpeed) { IsGrounded = grounded; Log(nameof(GroundedChanged)); GroundedChanged?.Invoke(grounded, impactSpeed); } public static void RaiseSlopeChanged(bool onSlope, Vector3 normal) { IsOnSlope = onSlope; SlopeChanged?.Invoke(onSlope, normal); } public static void RaiseGroundMaterialChanged(PhysicsMaterial m) { GroundMaterialChanged?.Invoke(m); } public static void RaiseVelocityChanged(Vector3 v) { VelocityChanged?.Invoke(v); } public static void RaiseYawChanged(float y) { YawChanged?.Invoke(y); } public static void RaiseJumped() { Log(nameof(Jumped)); Jumped?.Invoke(); } public static void RaiseCrouchChanged(bool b) { IsCrouching = b; Log(nameof(CrouchChanged)); CrouchChanged?.Invoke(b); } public static void RaiseSprintChanged(bool b) { IsSprinting = b; Log(nameof(SprintChanged)); SprintChanged?.Invoke(b); } public static void RaiseCapsuleHeightChanged(float h, Vector3 c) { CapsuleHeightChanged?.Invoke(h, c); } public static void RaiseCameraHeightChanged(float h) { CameraHeightChanged?.Invoke(h); } public static void RaiseLandingStunStarted(float d) { Log(nameof(LandingStunStarted)); LandingStunStarted?.Invoke(d); } public static void RaiseLandingStunEnded() { Log(nameof(LandingStunEnded)); LandingStunEnded?.Invoke(); } public static void RaiseSlideStarted() { IsSliding = true; Log(nameof(SlideStarted)); SlideStarted?.Invoke(); } public static void RaiseSlideEnded() { IsSliding = false; Log(nameof(SlideEnded)); SlideEnded?.Invoke(); } public static void RaiseMultiJumpPerformed(int i) { Log(nameof(MultiJumpPerformed)); MultiJumpPerformed?.Invoke(i); } public static void RaiseHealthChanged(float c, float m) { HealthChanged?.Invoke(c, m); } public static void RaiseHungerChanged(float c, float m) { HungerChanged?.Invoke(c, m); } public static void RaiseThirstChanged(float c, float m) { ThirstChanged?.Invoke(c, m); } public static void RaiseDamaged(float a, DamageType t) { Log(nameof(Damaged)); Damaged?.Invoke(a, t); } public static void RaiseFallDamageApplied(float a) { Log(nameof(FallDamageApplied)); FallDamageApplied?.Invoke(a); } public static void RaiseStarvationTick(float a) { StarvationTick?.Invoke(a); } public static void RaiseDehydrationTick(float a) { DehydrationTick?.Invoke(a); } public static void RaiseInventorySlotChanged(int i) { InventorySlotChanged?.Invoke(i); } public static void RaiseSelectedHotbarSlotChanged(int i) { Log(nameof(SelectedHotbarSlotChanged)); SelectedHotbarSlotChanged?.Invoke(i); } public static void RaiseItemAdded(ItemData d, int q) { Log(nameof(ItemAdded)); ItemAdded?.Invoke(d, q); } public static void RaiseItemDropped(ItemData d, int q) { Log(nameof(ItemDropped)); ItemDropped?.Invoke(d, q); } public static void RaiseItemConsumed(ItemData d) { Log(nameof(ItemConsumed)); ItemConsumed?.Invoke(d); } public static void RaiseItemPickedUp(ItemData d, int q) { Log(nameof(ItemPickedUp)); ItemPickedUp?.Invoke(d, q); } public static void RaiseHeldItemChanged(ItemData d) { Log(nameof(HeldItemChanged)); HeldItemChanged?.Invoke(d); } public static void RaiseInventoryOpenChanged(bool b) { IsInventoryOpen = b; Log(nameof(InventoryOpenChanged)); InventoryOpenChanged?.Invoke(b); } public static void RaiseGamePaused(bool b) { IsPaused = b; Log(nameof(GamePausedChanged)); GamePausedChanged?.Invoke(b); } public static void RaiseInteractableHoverChanged(IInteractable target) { HoveredInteractable = target; Log(nameof(InteractableHoverChanged)); InteractableHoverChanged?.Invoke(target); } public static void RaiseAttackSwung() { Log(nameof(AttackSwung)); AttackSwung?.Invoke(); } public static void RaiseHarvestableHit(Harvestable h, float d) { Log(nameof(HarvestableHit)); HarvestableHit?.Invoke(h, d); } public static void RaiseEquipAnimComplete() { EquipAnimComplete?.Invoke(); } public static void RaiseUnequipAnimComplete() { UnequipAnimComplete?.Invoke(); } public static void RaiseFoodPlacedToCook(ItemData raw) { Log(nameof(FoodPlacedToCook)); FoodPlacedToCook?.Invoke(raw); } public static void RaiseFoodCookedReady(ItemData cooked) { Log(nameof(FoodCookedReady)); FoodCookedReady?.Invoke(cooked); } public static void RaiseFuelAdded(ItemData fuel) { Log(nameof(FuelAdded)); FuelAdded?.Invoke(fuel); } public static void RaiseSurfaceChanged(SurfaceSoundProfile p) { SurfaceChanged?.Invoke(p); } public static void RaiseFootstepTriggered(SurfaceSoundProfile p, float v) { FootstepTriggered?.Invoke(p, v); } public static void RaiseDied() { IsDead = true; Log(nameof(Died)); Died?.Invoke(); DeathChanged?.Invoke(true); } public static void RaiseRespawned() { IsDead = false; IsCrouching = false; IsSliding = false; IsSprinting = false; Log(nameof(Respawned)); Respawned?.Invoke(); DeathChanged?.Invoke(false); } public static void RaiseSessionStarting() { Log(nameof(SessionStarting)); SessionStarting?.Invoke(); } public static void RaiseSessionStarted(string code) { IsOnline = true; IsHost = true; SessionCode = code ?? string.Empty; Log(nameof(SessionStarted)); SessionStarted?.Invoke(SessionCode); } public static void RaiseSessionJoining(string code) { SessionCode = code ?? string.Empty; Log(nameof(SessionJoining)); SessionJoining?.Invoke(SessionCode); } public static void RaiseSessionJoined() { IsOnline = true; IsHost = false; Log(nameof(SessionJoined)); SessionJoined?.Invoke(); } public static void RaiseSessionStopped() { IsOnline = false; IsHost = false; SessionCode = string.Empty; ResetPlayerState(); Log(nameof(SessionStopped)); SessionStopped?.Invoke(); } // Clears the transient, player-local state so a brand-new session/player starts clean. // The static bus tracks the LOCAL player only; without this, dying or opening the // inventory and then leaving a session would leave stale flags set for the next one. private static void ResetPlayerState() { IsDead = false; IsGrounded = true; IsCrouching = false; IsSprinting = false; IsSliding = false; IsOnSlope = false; IsInventoryOpen = false; IsPaused = false; HoveredInteractable = null; } public static void RaiseSessionError(string reason) { Log(nameof(SessionError)); SessionError?.Invoke(reason); } public static void RaiseRemotePlayerCountChanged(int count) { Log(nameof(RemotePlayerCountChanged)); RemotePlayerCountChanged?.Invoke(count); } public static void RaiseLocalPlayerSpawned() { Log(nameof(LocalPlayerSpawned)); LocalPlayerSpawned?.Invoke(); } /// /// A friend invited the local player to their session (delivered by Steam). Carries the /// inviter's SteamID64, display name, and the connect code used to join their room. /// public static void RaiseInviteReceived(ulong inviterSteamId, string inviterName, string connectCode) { Log(nameof(InviteReceived)); InviteReceived?.Invoke(inviterSteamId, inviterName, connectCode); } [Conditional("PLAYER_EVENT_LOG")] private static void Log(string evtName) { UnityEngine.Debug.Log($"[PlayerEvents] {evtName}"); } } public enum DamageType { Generic, Fall, Starvation, Dehydration, Combat } }