geometry-tower-defense/Assets/GameMain/Scripts/Entity/EntityLogic/CombatSelectInputService.cs

129 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
using GeometryTD.UI;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Tilemaps;
namespace GeometryTD.Entity
{
public sealed class CombatSelectInputService
{
public bool TryBuildUserData(
Tilemap tilemap,
Transform mapTransform,
IReadOnlyDictionary<Vector3Int, int> towerEntityIdByFoundationCell,
Func<Vector3Int, bool> isFoundationCell,
Func<int, bool> isTowerAtMaxLevel,
int upgradeCost,
int destroyGain,
out CombatSelectFormUserData userData)
{
userData = null;
if (tilemap == null || !TryGetPointerWorldPosition(tilemap, mapTransform, out Vector3 worldPosition,
out Vector2 screenPosition))
{
return false;
}
Vector3Int clickedCell = tilemap.WorldToCell(worldPosition);
CombatSelectClickObjectType clickObjectType = CombatSelectClickObjectType.None;
int towerEntityId = 0;
Vector2 resolvedScreenPosition = screenPosition;
if (towerEntityIdByFoundationCell != null &&
towerEntityIdByFoundationCell.TryGetValue(clickedCell, out int occupiedTowerEntityId))
{
clickObjectType = CombatSelectClickObjectType.Tower;
towerEntityId = occupiedTowerEntityId;
resolvedScreenPosition = BuildScreenPositionFromCell(tilemap, clickedCell, screenPosition);
}
else if (isFoundationCell != null && isFoundationCell(clickedCell))
{
clickObjectType = CombatSelectClickObjectType.Foundation;
resolvedScreenPosition = BuildScreenPositionFromCell(tilemap, clickedCell, screenPosition);
}
userData = new CombatSelectFormUserData
{
ClickObjectType = clickObjectType,
ScreenPosition = resolvedScreenPosition,
WorldPosition = worldPosition,
CellPosition = clickedCell,
TowerEntityId = towerEntityId,
IsTowerAtMaxLevel = towerEntityId != 0 && isTowerAtMaxLevel != null && isTowerAtMaxLevel(towerEntityId),
UpgradeCost = Mathf.Max(0, upgradeCost),
DestroyGain = Mathf.Max(0, destroyGain)
};
return true;
}
private static bool TryGetPointerWorldPosition(Tilemap tilemap, Transform mapTransform,
out Vector3 worldPosition,
out Vector2 screenPosition)
{
worldPosition = Vector3.zero;
screenPosition = Vector2.zero;
Camera mainCamera = GameEntry.Scene != null ? GameEntry.Scene.MainCamera : Camera.main;
if (mainCamera == null)
{
return false;
}
Mouse mouse = Mouse.current;
if (mouse == null)
{
return false;
}
Vector2 pointerPosition = mouse.position.ReadValue();
Ray ray = mainCamera.ScreenPointToRay(pointerPosition);
float mapPlaneZ = tilemap != null
? tilemap.transform.position.z
: (mapTransform != null ? mapTransform.position.z : 0f);
Vector3 planeNormal = mainCamera.transform.forward.sqrMagnitude > Mathf.Epsilon
? -mainCamera.transform.forward
: Vector3.forward;
Plane mapPlane = new Plane(planeNormal, new Vector3(0f, 0f, mapPlaneZ));
if (!mapPlane.Raycast(ray, out float enterDistance))
{
return false;
}
worldPosition = ray.GetPoint(enterDistance);
screenPosition = BuildScreenPosition(pointerPosition);
return true;
}
private static Vector2 BuildScreenPosition(Vector3 pointerScreenPosition)
{
return new Vector2(pointerScreenPosition.x, pointerScreenPosition.y);
}
private static Vector2 BuildScreenPositionFromCell(Tilemap tilemap, Vector3Int cellPosition,
Vector2 fallbackScreenPosition)
{
if (tilemap == null)
{
return fallbackScreenPosition;
}
Camera mainCamera = GameEntry.Scene != null ? GameEntry.Scene.MainCamera : Camera.main;
if (mainCamera == null)
{
return fallbackScreenPosition;
}
Vector3 cellCenterWorld = tilemap.GetCellCenterWorld(cellPosition);
Vector3 cellScreenPosition = mainCamera.WorldToScreenPoint(cellCenterWorld);
if (cellScreenPosition.z < 0f)
{
return fallbackScreenPosition;
}
return BuildScreenPosition(cellScreenPosition);
}
}
}