using UnityEngine; /// /// Handles interaction between players and chunk-based blocks. /// Converts raycast hits to block indices and manages damage/reveal requests. /// public static class ChunkInteractionHandler { /// /// Result of a chunk interaction query /// public struct ChunkHitResult { public bool hasHit; public MineableChunk chunk; public int blockIndex; public Vector3 hitPoint; public Vector3 blockWorldPosition; public BlockData blockData; public static ChunkHitResult None => new ChunkHitResult { hasHit = false }; } /// /// Try to get chunk and block info from a raycast hit /// public static ChunkHitResult GetChunkHit(RaycastHit hit) { // Try to get MineableChunk from hit collider MineableChunk chunk = hit.collider.GetComponentInParent(); if (chunk == null) { return ChunkHitResult.None; } // Convert hit point to block index // Push slightly into the surface using the normal to get the correct block Vector3 insidePoint = hit.point - hit.normal * 0.01f; int blockIndex = chunk.WorldPointToBlockIndex(insidePoint); BlockData blockData = chunk.GetBlock(blockIndex); // Skip if block is empty if (blockData.IsEmpty) { return ChunkHitResult.None; } return new ChunkHitResult { hasHit = true, chunk = chunk, blockIndex = blockIndex, hitPoint = hit.point, blockWorldPosition = chunk.GetBlockWorldPosition(blockIndex), blockData = blockData }; } /// /// Try to damage a block at a raycast hit point /// public static bool TryDamageAtPoint(RaycastHit hit, float damage) { ChunkHitResult result = GetChunkHit(hit); if (!result.hasHit) return false; // Request damage on server result.chunk.DamageBlockServerRpc(result.blockIndex, (byte)Mathf.Min(255, damage)); return true; } /// /// Try to damage a block directly on a known chunk /// public static bool TryDamageBlock(MineableChunk chunk, int blockIndex, float damage) { if (chunk == null) return false; BlockData block = chunk.GetBlock(blockIndex); if (block.IsEmpty) return false; chunk.DamageBlockServerRpc(blockIndex, (byte)Mathf.Min(255, damage)); return true; } /// /// Try to reveal a block at a raycast hit point /// public static bool TryRevealAtPoint(RaycastHit hit) { MineableChunk chunk = hit.collider.GetComponentInParent(); if (chunk == null) return false; int blockIndex = chunk.WorldPointToBlockIndex(hit.point - hit.normal * 0.1f); BlockData blockData = chunk.GetBlock(blockIndex); if (blockData.IsEmpty || blockData.IsDiscovered) return false; chunk.RevealBlockServerRpc(blockIndex); return true; } /// /// Perform a sphere cast to find a targetable block in a chunk /// public static ChunkHitResult SphereCastForBlock(Vector3 origin, Vector3 direction, float radius, float maxDistance, LayerMask chunkLayer) { if (Physics.SphereCast(origin, radius, direction, out RaycastHit hit, maxDistance, chunkLayer)) { return GetChunkHit(hit); } return ChunkHitResult.None; } /// /// Perform a raycast to find a targetable block in a chunk /// public static ChunkHitResult RaycastForBlock(Vector3 origin, Vector3 direction, float maxDistance, LayerMask chunkLayer) { if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, chunkLayer)) { return GetChunkHit(hit); } return ChunkHitResult.None; } }