Files
2026-06-22 16:18:34 +02:00

353 lines
18 KiB
C#

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<Vector2> MoveInput;
public static event Action<Vector2> LookInput;
public static event Action JumpPressed;
public static event Action<bool> 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<int> HotbarSlotPressed;
public static event Action<float> HotbarScroll;
public static event Action CancelPressed; // UI "Cancel" (Escape) — back / pause / close
// ============================================================
// Locomotion events
// ============================================================
public static event Action<bool, float> GroundedChanged; // isGrounded, impactSpeed
public static event Action<bool, Vector3> SlopeChanged;
public static event Action<PhysicsMaterial> GroundMaterialChanged;
public static event Action<Vector3> VelocityChanged;
public static event Action<float> YawChanged;
public static event Action Jumped;
public static event Action<bool> CrouchChanged;
public static event Action<bool> SprintChanged;
public static event Action<float, Vector3> CapsuleHeightChanged;
public static event Action<float> CameraHeightChanged;
public static event Action<float> LandingStunStarted;
public static event Action LandingStunEnded;
public static event Action SlideStarted;
public static event Action SlideEnded;
public static event Action<int> MultiJumpPerformed;
// ============================================================
// Stats events
// ============================================================
public static event Action<float, float> HealthChanged;
public static event Action<float, float> HungerChanged;
public static event Action<float, float> ThirstChanged;
public static event Action<float, DamageType> Damaged;
public static event Action<float> FallDamageApplied;
public static event Action<float> StarvationTick;
public static event Action<float> DehydrationTick;
// ============================================================
// Inventory events
// ============================================================
public static event Action<int> InventorySlotChanged;
public static event Action<int> SelectedHotbarSlotChanged;
public static event Action<ItemData, int> ItemAdded;
public static event Action<ItemData, int> ItemDropped;
public static event Action<ItemData> ItemConsumed;
public static event Action<ItemData, int> ItemPickedUp;
public static event Action<ItemData> HeldItemChanged;
public static event Action<bool> InventoryOpenChanged;
public static event Action<bool> GamePausedChanged;
// ============================================================
// Interaction events
// ============================================================
public static event Action<IInteractable> InteractableHoverChanged; // null = nothing hovered
// ============================================================
// Tools events
// ============================================================
public static event Action AttackSwung;
public static event Action<Harvestable, float> HarvestableHit;
public static event Action EquipAnimComplete;
public static event Action UnequipAnimComplete;
// ============================================================
// Cooking events
// ============================================================
public static event Action<ItemData> FoodPlacedToCook; // the raw item placed on a station
public static event Action<ItemData> FoodCookedReady; // the cooked result is ready to take
public static event Action<ItemData> FuelAdded; // the fuel item consumed to feed a station
// ============================================================
// Audio events
// ============================================================
public static event Action<SurfaceSoundProfile> SurfaceChanged;
public static event Action<SurfaceSoundProfile, float> FootstepTriggered;
// ============================================================
// Lifecycle events
// ============================================================
public static event Action Died;
public static event Action Respawned;
public static event Action<bool> DeathChanged;
// ============================================================
// Network / session events
// ============================================================
public static event Action SessionStarting; // host/join requested, before transport is up
public static event Action<string> SessionStarted; // host is up — payload is the shareable code
public static event Action<string> 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<string> SessionError; // human-readable failure reason
public static event Action<int> RemotePlayerCountChanged; // number of *other* connected clients
public static event Action LocalPlayerSpawned; // the local owned player NetworkObject is ready
public static event Action<ulong, string, string> 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(); }
/// <summary>
/// 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.
/// </summary>
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 }
}