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;
}
}