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

458 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using GeometryTD.CustomComponent;
using GeometryTD.CustomEvent;
using GameFramework.Event;
using GeometryTD.Map;
using GeometryTD.Definition;
using GeometryTD.Entity.EntityData;
using GeometryTD.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Tilemaps;
using UnityGameFramework.Runtime;
namespace GeometryTD.Entity
{
public class MapEntity : EntityBase
{
private const int DefaultTowerTypeId = 401;
private static readonly Spawner[] EmptySpawners = Array.Empty<Spawner>();
[SerializeField] private bool _enableCombatSelectInput = true;
[SerializeField] private int _towerTypeId = DefaultTowerTypeId;
[SerializeField] private int[] _buildTowerCosts = { 40, 60, 60, 80 };
[SerializeField] private int _upgradeCost = 50;
[SerializeField] private int _destroyGain = 40;
private MapDataRefs _mapDataRefs;
private MapData _mapData;
private MapTopologyService _mapTopologyService;
private CombatSelectFormUseCase _combatSelectFormUseCase;
private CombatSelectInputService _combatSelectInputService;
private TowerPlacementService _towerPlacementService;
private TowerSelectionPresenter _towerSelectionPresenter;
private CombatSelectUseCaseConfigurator _combatSelectUseCaseConfigurator;
private int _currentCoin;
private bool _isCoinEventSubscribed;
public IReadOnlyList<Vector3Int> PathCells => _mapTopologyService != null
? _mapTopologyService.PathCells
: Array.Empty<Vector3Int>();
public IReadOnlyList<Vector3Int> FoundationCells => _mapTopologyService != null
? _mapTopologyService.FoundationCells
: Array.Empty<Vector3Int>();
public Tilemap Tilemap => _mapDataRefs != null ? _mapDataRefs.Tilemap : null;
public Spawner[] Spawners => _mapDataRefs?.Spawners ?? EmptySpawners;
public House House => _mapDataRefs?.House;
public bool IsPathCell(Vector3Int cellPosition)
{
return _mapTopologyService != null && _mapTopologyService.IsPathCell(cellPosition);
}
public bool IsFoundationCell(Vector3Int cellPosition)
{
return _mapTopologyService != null && _mapTopologyService.IsFoundationCell(cellPosition);
}
public bool TryGetNearestPathCell(Vector3 worldPosition, out Vector3Int pathCell)
{
pathCell = default;
return _mapTopologyService != null && _mapTopologyService.TryGetNearestPathCell(Tilemap, worldPosition, out pathCell);
}
public Vector3 GetPathCellCenterWorld(Vector3Int pathCell)
{
return _mapTopologyService != null
? _mapTopologyService.GetPathCellCenterWorld(Tilemap, pathCell)
: Vector3.zero;
}
public bool TryGetDefaultPathCells(Spawner spawner, out IReadOnlyList<Vector3Int> pathCells)
{
pathCells = null;
return _mapTopologyService != null && _mapTopologyService.TryGetDefaultPathCells(spawner, out pathCells);
}
public bool TryFindPathCells(Spawner spawner, IReadOnlyCollection<Vector3Int> blockedCells,
List<Vector3Int> pathResult)
{
return _mapTopologyService != null && _mapTopologyService.TryFindPathCells(spawner, blockedCells, pathResult);
}
public bool TryFindPathWorldPoints(Spawner spawner, IReadOnlyCollection<Vector3Int> blockedCells,
List<Vector3> worldPathResult)
{
return _mapTopologyService != null &&
_mapTopologyService.TryFindPathWorldPoints(Tilemap, spawner, blockedCells, worldPathResult);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
_mapDataRefs = GetComponent<MapDataRefs>();
if (_mapDataRefs == null)
{
Log.Error("MapDataRefs is missing on map entity '{0}'.", name);
}
InitializeCombatSelectUseCase();
InitializeCombatSelectUseCaseConfigurator();
InitializeCombatSelectInputService();
InitializeMapTopologyService();
InitializeTowerPlacementService();
InitializeTowerSelectionPresenter();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
_mapData = userData as MapData;
if (_mapData == null)
{
Log.Warning("MapData is invalid for map entity '{0}'.", Id);
}
else
{
_currentCoin = Mathf.Max(0, _mapData.InitialCoin);
SubscribeCombatEvents();
}
RefreshTiles();
ConfigureCombatSelectUseCase();
HideCombatSelectForm();
}
protected override void OnHide(bool isShutdown, object userData)
{
UnsubscribeCombatEvents();
HideCombatSelectForm();
_towerPlacementService?.HideAndClearAllPlacedTowers();
ClearSelectionState();
ClearTowerTracking();
ClearMapTopology();
base.OnHide(isShutdown, userData);
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
HandleCombatSelectInput();
}
private void RefreshTiles()
{
ClearMapTopology();
ClearTowerTracking();
ClearSelectionState();
if (_mapDataRefs == null)
{
_mapDataRefs = GetComponent<MapDataRefs>();
if (_mapDataRefs == null)
{
Log.Error("MapDataRefs is missing on map entity '{0}'.", name);
return;
}
}
Tilemap tilemap = _mapDataRefs.Tilemap;
if (tilemap == null)
{
Log.Error("Tilemap reference is missing in MapDataRefs on '{0}'.", name);
return;
}
_mapTopologyService?.Refresh(tilemap, Spawners, House, name, _mapData != null ? _mapData.LevelId : 0);
}
private void ClearMapTopology()
{
_mapTopologyService?.Clear();
}
private void ClearTowerTracking()
{
_towerPlacementService?.ClearTracking();
}
private void ClearSelectionState()
{
_towerSelectionPresenter?.ClearSelectedObject();
}
private void InitializeCombatSelectUseCase()
{
if (_combatSelectFormUseCase == null)
{
_combatSelectFormUseCase = new CombatSelectFormUseCase();
}
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatSelectForm, _combatSelectFormUseCase);
}
private void InitializeCombatSelectUseCaseConfigurator()
{
if (_combatSelectUseCaseConfigurator == null)
{
_combatSelectUseCaseConfigurator = new CombatSelectUseCaseConfigurator();
}
}
private void InitializeTowerSelectionPresenter()
{
if (_towerSelectionPresenter == null)
{
_towerSelectionPresenter = new TowerSelectionPresenter();
}
}
private void InitializeTowerPlacementService()
{
if (_towerPlacementService == null)
{
_towerPlacementService = new TowerPlacementService();
}
}
private void InitializeCombatSelectInputService()
{
if (_combatSelectInputService == null)
{
_combatSelectInputService = new CombatSelectInputService();
}
}
private void InitializeMapTopologyService()
{
if (_mapTopologyService == null)
{
_mapTopologyService = new MapTopologyService();
}
}
private void ConfigureCombatSelectUseCase()
{
_combatSelectFormUseCase.SetCoinProvider(GetCurrentCoin);
_combatSelectUseCaseConfigurator?.Configure(
_combatSelectFormUseCase,
GetCurrentCoin,
buildIndex => () => TryBuildTower(buildIndex),
TryUpgradeTower,
_upgradeCost,
TryDestroyTower,
_destroyGain,
_buildTowerCosts,
GetCurrentBuildTowerCount(),
_mapData != null ? _mapData.InventorySnapshot : null,
_mapData != null ? _mapData.ParticipantTowerSnapshot : null);
}
private void HandleCombatSelectInput()
{
if (!_enableCombatSelectInput || !Input.GetMouseButtonDown(0))
{
return;
}
if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject())
{
return;
}
if (_combatSelectInputService == null ||
!_combatSelectInputService.TryBuildUserData(Tilemap, CachedTransform,
_towerPlacementService != null ? _towerPlacementService.TowerEntityIdByFoundationCell : null,
IsFoundationCell, IsTowerAtMaxLevel, _upgradeCost, _destroyGain,
out CombatSelectFormUserData userData))
{
userData = new CombatSelectFormUserData
{
ClickObjectType = CombatSelectClickObjectType.None
};
}
ApplySelectedObject(userData);
GameEntry.UIRouter.OpenUI(UIFormType.CombatSelectForm, userData);
}
private bool IsTowerAtMaxLevel(int towerEntityId)
{
return _towerPlacementService != null && _towerPlacementService.IsTowerAtMaxLevel(towerEntityId);
}
private void ApplySelectedObject(CombatSelectFormUserData userData)
{
_towerSelectionPresenter?.ApplySelectedObject(userData);
}
private bool TryBuildTower(int buildIndex)
{
if (_towerSelectionPresenter == null ||
_towerPlacementService == null ||
!_towerSelectionPresenter.TryGetSelectedFoundationCell(out Vector3Int selectedFoundationCell) ||
!IsFoundationCell(selectedFoundationCell))
{
return false;
}
if (_mapData == null || !_mapData.TryGetBuildTowerStats(buildIndex, out TowerStatsData buildTowerStats))
{
return false;
}
BuildTowerVisualInfo buildVisual = _combatSelectUseCaseConfigurator != null
? _combatSelectUseCaseConfigurator.GetBuildVisualInfo(buildIndex)
: BuildTowerVisualInfo.Default;
if (!_towerPlacementService.TryBuildTower(selectedFoundationCell, IsFoundationCell, buildIndex, _buildTowerCosts,
buildTowerStats, buildVisual.MuzzleColor, buildVisual.BearingColor, buildVisual.BaseColor, _towerTypeId, Tilemap, TryConsumeCoin, AddCoin, out int towerEntityId))
{
return false;
}
_towerSelectionPresenter.SelectTower(selectedFoundationCell, towerEntityId);
return true;
}
private bool TryUpgradeTower()
{
if (_towerSelectionPresenter == null ||
_towerPlacementService == null ||
!_towerSelectionPresenter.TryGetSelectedTower(_towerPlacementService.FoundationCellByTowerEntityId,
out int towerEntityId,
out Vector3Int foundationCell))
{
return false;
}
if (!_towerPlacementService.TryUpgradeTower(towerEntityId, _upgradeCost, _towerTypeId, Tilemap,
TryConsumeCoin, AddCoin, out int resultTowerEntityId, out foundationCell))
{
if (resultTowerEntityId != 0)
{
_towerSelectionPresenter.SelectTower(foundationCell, resultTowerEntityId);
}
else
{
_towerSelectionPresenter.ClearSelectedObject();
}
return false;
}
_towerSelectionPresenter.SelectTower(foundationCell, resultTowerEntityId);
return true;
}
private bool TryDestroyTower()
{
if (_towerSelectionPresenter == null ||
_towerPlacementService == null ||
!_towerSelectionPresenter.TryGetSelectedTower(_towerPlacementService.FoundationCellByTowerEntityId,
out int towerEntityId,
out Vector3Int foundationCell))
{
return false;
}
if (!_towerPlacementService.TryDestroyTower(towerEntityId, _destroyGain, AddCoin, out foundationCell))
{
return false;
}
_towerSelectionPresenter.ClearSelectedObject();
return true;
}
private void HideCombatSelectForm()
{
_combatSelectFormUseCase?.Hide();
GameEntry.UIRouter.CloseUI(UIFormType.CombatSelectForm);
}
private void SubscribeCombatEvents()
{
if (_isCoinEventSubscribed)
{
return;
}
GameEntry.Event.Subscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
_isCoinEventSubscribed = true;
}
private void UnsubscribeCombatEvents()
{
if (!_isCoinEventSubscribed)
{
return;
}
GameEntry.Event.Unsubscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
_isCoinEventSubscribed = false;
}
private void OnCombatCoinChanged(object sender, GameEventArgs e)
{
if (e is not CombatCoinChangedEventArgs args)
{
return;
}
_currentCoin = Mathf.Max(0, args.CurrentCoin);
}
private int GetCurrentBuildTowerCount()
{
if (_mapData == null)
{
return 0;
}
return Mathf.Clamp(_mapData.CurrentBuildTowerCount, 0, 4);
}
private bool TryConsumeCoin(int cost)
{
int requiredCoin = Mathf.Max(0, cost);
if (requiredCoin <= 0)
{
return true;
}
if (_mapData == null)
{
return false;
}
return _mapData.TryConsumeCoin(requiredCoin);
}
private int GetCurrentCoin()
{
return Mathf.Max(0, _currentCoin);
}
private void AddCoin(int coin)
{
int amount = Mathf.Max(0, coin);
if (amount <= 0)
{
return;
}
_mapData?.AddCoin(amount);
}
}
}