using Unity.BossRoom.Gameplay.GameplayObjects;
using UnityEngine;
using UnityEngine.AI;
namespace Unity.BossRoom.Gameplay.Actions
{
///
/// This class is the first step in AoE ability. It will update the initial input visuals' position and will be in charge
/// of tracking the user inputs. Once the ability
/// is confirmed and the mouse is clicked, it'll send the appropriate RPC to the server, triggering the AoE serer side gameplay logic.
/// The server side gameplay action will then trigger the client side resulting FX.
/// This action's flow is this: (Client) AoEActionInput --> (Server) AoEAction --> (Client) AoEActionFX
///
public class AoeActionInput : BaseActionInput
{
[SerializeField]
GameObject m_InRangeVisualization;
[SerializeField]
GameObject m_OutOfRangeVisualization;
Camera m_Camera;
//The general action system works on MouseDown events (to support Charged Actions), but that means that if we only wait for
//a mouse up event internally, we will fire as part of the same UI click that started the action input (meaning the user would
//have to drag her mouse from the button to the firing location). Tracking a mouse-down mouse-up cycle means that a user can
//click on the NavMesh separately from the mouse-click that engaged the action (which also makes the UI flow equivalent to the
//flow from hitting a number key).
bool m_ReceivedMouseDownEvent;
NavMeshHit m_NavMeshHit;
// plane that has normal pointing up on y, that is 0 distance units displaced from origin
// this is also the same height as the NavMesh baked in-game
static readonly Plane k_Plane = new Plane(Vector3.up, 0f);
void Start()
{
var radius = GameDataSource.Instance.GetActionPrototypeByID(m_ActionPrototypeID).Config.Radius;
transform.localScale = new Vector3(radius * 2, radius * 2, radius * 2);
m_Camera = Camera.main;
}
void Update()
{
if (PlaneRaycast(k_Plane, m_Camera.ScreenPointToRay(Input.mousePosition), out Vector3 pointOnPlane) &&
NavMesh.SamplePosition(pointOnPlane, out m_NavMeshHit, 2f, NavMesh.AllAreas))
{
transform.position = m_NavMeshHit.position;
}
float range = GameDataSource.Instance.GetActionPrototypeByID(m_ActionPrototypeID).Config.Range;
bool isInRange = (m_Origin - transform.position).sqrMagnitude <= range * range;
m_InRangeVisualization.SetActive(isInRange);
m_OutOfRangeVisualization.SetActive(!isInRange);
// wait for the player to click down and then release the mouse button before actually taking the input
if (Input.GetMouseButtonDown(0))
{
m_ReceivedMouseDownEvent = true;
}
if (Input.GetMouseButtonUp(0) && m_ReceivedMouseDownEvent)
{
if (isInRange)
{
var data = new ActionRequestData
{
Position = transform.position,
ActionID = m_ActionPrototypeID,
ShouldQueue = false,
TargetIds = null
};
m_SendInput(data);
}
Destroy(gameObject);
return;
}
}
///
/// Utility method to simulate a raycast to a given plane. Does not involve a Physics-based raycast.
///
/// Based on documented example here: https://docs.unity3d.com/ScriptReference/Plane.Raycast.html
///
///
///
///
/// true if intersection point lies inside NavMesh; false otherwise
static bool PlaneRaycast(Plane plane, Ray ray, out Vector3 pointOnPlane)
{
// validate that this ray intersects plane
if (plane.Raycast(ray, out var enter))
{
// get the point of intersection
pointOnPlane = ray.GetPoint(enter);
return true;
}
else
{
pointOnPlane = Vector3.zero;
return false;
}
}
}
}