1094 lines
42 KiB
C#
1094 lines
42 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using asap.core;
|
|
using cfg;
|
|
using DG.Tweening;
|
|
using game;
|
|
using Game;
|
|
using GameCore;
|
|
using UniRx;
|
|
using UnityEngine;
|
|
using UnityEngine.AddressableAssets;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace EventMowForTreasure
|
|
{
|
|
public class EventMowForTreasureAct : AGameAct
|
|
{
|
|
protected override void OnDestroy()
|
|
{
|
|
}
|
|
|
|
public override async Task<bool> StartAsync()
|
|
{
|
|
UITypes.EventMowForTreasurePanel.SetType(Data.EventMain.UIPanel);
|
|
GContext.OnEvent<RewardPanelClose>().Subscribe(OnRewardPanelClose).AddTo(this);
|
|
await InitAct();
|
|
await UIManager.Instance.ShowUILoad(UITypes.EventMowForTreasurePanel);
|
|
Manager.TryShowGuide();
|
|
return await base.StartAsync();
|
|
}
|
|
|
|
public override async Task StopAsync()
|
|
{
|
|
foreach (var cell in _slots.Values)
|
|
if (cell != null)
|
|
Destroy(cell.gameObject);
|
|
_slots?.Clear();
|
|
foreach (var prefab in _obstaclePrefabDir.Values)
|
|
if (prefab != null)
|
|
Addressables.Release(prefab);
|
|
StopFinalRewardSound();
|
|
ReleaseTool();
|
|
_obstaclePrefabDir?.Clear();
|
|
_heightLightObstacleCells?.Clear();
|
|
_disposable?.Dispose();
|
|
await base.StopAsync();
|
|
}
|
|
|
|
private async Task InitAct()
|
|
{
|
|
await InitStageItemDir();
|
|
await InitToolGameObjs();
|
|
InitObstacleSlots();
|
|
InitCharacter();
|
|
CheckFinalRewardStatus();
|
|
}
|
|
|
|
#region event
|
|
|
|
private async void OnRewardPanelClose(RewardPanelClose e)
|
|
{
|
|
try
|
|
{
|
|
if (!_slots.TryGetValue(FinalRewardCoordinate, out var cell) || !cell.GetData().IsBroke) return;
|
|
var newActID = Manager.GetCurrentStageAct();
|
|
if (newActID == Id)
|
|
await ReloadCurrentAct();
|
|
else
|
|
{
|
|
UIManager.Instance.DestroyUI(UITypes.EventMowForTreasurePanel);
|
|
GContext.Publish(new UnloadActToNextAct { actId = Manager.GetCurrentStageAct() });
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"jsd {ex}");
|
|
}
|
|
}
|
|
|
|
private async Task ReloadCurrentAct()
|
|
{
|
|
await UIManager.Instance.ShowUI(UITypes.CloudTransitionPanel);
|
|
foreach (var slotCell in _slots.Values)
|
|
{
|
|
if (!slotCell || !slotCell.name.Contains("(Clone)"))
|
|
continue;
|
|
Destroy(slotCell.gameObject);
|
|
}
|
|
|
|
_disposable?.Dispose();
|
|
InitObstacleSlots();
|
|
InitCharacter();
|
|
CheckFinalRewardStatus();
|
|
GContext.Publish(new EndTransition());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GamePrefab
|
|
|
|
private readonly Dictionary<int, GameObject> _obstaclePrefabDir = new();
|
|
|
|
/// <summary>
|
|
/// id instantiate
|
|
/// </summary>
|
|
private readonly Dictionary<int, EventMowForTreasureToolCell> _toolCells = new();
|
|
|
|
#endregion
|
|
|
|
#region data
|
|
|
|
private int _rowAmount;
|
|
private int _columnAmount;
|
|
private string _currentCharacterStateName;
|
|
private IDisposable _disposable;
|
|
private Vector2Int _currentCoordinate = -Vector2Int.one;
|
|
private Vector2Int StartCoordinate => -Vector2Int.one;
|
|
private Vector2Int FinalRewardCoordinate => new(_columnAmount, _rowAmount);
|
|
[Inject] public EventMowForTreasureManager Manager { get; set; }
|
|
|
|
private EventMowForTreasureData Data => Manager.Data;
|
|
|
|
private List<EventMowForTreasureObstacleCell> _heightLightObstacleCells = new();
|
|
|
|
#endregion
|
|
|
|
#region GameObj
|
|
|
|
private Transform _npcTrans;
|
|
private GameObject _grid;
|
|
private EventMowForTreasureObstacleCell _startCell;
|
|
private EventMowForTreasureObstacleCell _finalReward;
|
|
private readonly Dictionary<Vector2Int, EventMowForTreasureObstacleCell> _slots = new();
|
|
|
|
#endregion
|
|
|
|
#region character
|
|
#region SerializeField
|
|
|
|
[Header("character")]
|
|
[SerializeField] [Tooltip("角色走一个格子的时间")]
|
|
private float characterMoveTimeOneGrid = 0.35f;
|
|
|
|
[SerializeField] [Tooltip("角色从当前格子转向下一个格子的时间")]
|
|
private float rotateTime = 0.35f;
|
|
|
|
[SerializeField] [Tooltip("角色跑一个格子的时间")]
|
|
private float characterRunMoveTimeOneGrid = 0.35f;
|
|
[SerializeField] [Tooltip("跑步状态下角色从当前格子转向下一个格子的时间")]
|
|
private float characterRunRotateTime = 0.35f;
|
|
[SerializeField] [Tooltip("大于等于多少格子开始跑步")]
|
|
private int characterStartRun = 5;
|
|
|
|
[SerializeField] private float switchSpIdleTime = 0.35f;
|
|
[SerializeField] private float switchSpIdleProbability = 0.35f;
|
|
private const string IdleParamName = "_idle";
|
|
private const string SpIdleParamName = "_spIdle";
|
|
private const string RunParamName = "_run";
|
|
private const string WalkParamName = "_walk";
|
|
private const string WorkParamName = "_work";
|
|
|
|
#endregion
|
|
private void InitCharacter()
|
|
{
|
|
if (!_npcTrans)
|
|
_npcTrans = gameObject.FindChildGameObject("p_npc").transform;
|
|
_npcTrans.transform.position = _startCell.transform.position;
|
|
_disposable = Observable.Interval(TimeSpan.FromSeconds(switchSpIdleTime)).Subscribe(RandomSwitchSpIdle)
|
|
.AddTo(this);
|
|
_currentCharacterStateName = IdleParamName;
|
|
}
|
|
|
|
private void RandomSwitchSpIdle(long _)
|
|
{
|
|
var animator = _npcTrans.GetComponentInChildren<Animator>();
|
|
if (!animator.GetCurrentAnimatorStateInfo(0).IsName("Idle01")) return;
|
|
if (Random.value > switchSpIdleProbability) return;
|
|
SwitchCharacterAniState(SpIdleParamName, animator);
|
|
}
|
|
|
|
private void SwitchCharacterAniState(string stateName, Animator animator = null)
|
|
{
|
|
if (!animator)
|
|
animator = _npcTrans.GetComponentInChildren<Animator>();
|
|
if (!animator) return;
|
|
if(animator.GetBool(stateName)) return;
|
|
animator.SetBool(_currentCharacterStateName, false);
|
|
animator.SetBool(stateName, true);
|
|
_currentCharacterStateName = stateName;
|
|
}
|
|
|
|
private async Task CharacterPathFinding(List<Vector2Int> path, EventMowForTreasureObstacleData endData
|
|
,CancellationToken cancellationToken)
|
|
{
|
|
var animator = _npcTrans.GetComponentInChildren<Animator>();
|
|
var isWalk = path.Count < characterStartRun;
|
|
var walkCount=0;
|
|
foreach (var coordinate in path)
|
|
{
|
|
if (!_slots.TryGetValue(coordinate, out var cell))
|
|
continue;
|
|
if (coordinate == _currentCoordinate)
|
|
continue;
|
|
var isEndCoordinate = coordinate == endData.Coordinate;
|
|
var cellData = cell.GetData();
|
|
if (cellData.IsCollectionReward && !cellData.IsBroke)
|
|
{
|
|
await ShowCharacterCollect(cell,isWalk);
|
|
isWalk=path.Count-walkCount<characterStartRun;
|
|
if(isEndCoordinate)break;
|
|
}
|
|
if (isEndCoordinate && !endData.IsBroke)
|
|
break;
|
|
|
|
if (animator.GetCurrentAnimatorStateInfo(0).IsName("Work")||animator.GetBool(WorkParamName)) return;
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
await ShowCharacterWalk(cell, animator, coordinate,cancellationToken,isWalk);
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
walkCount++;
|
|
}
|
|
}
|
|
|
|
private async Task ShowCharacterCollect(EventMowForTreasureObstacleCell cell,bool isWalk)
|
|
{
|
|
var direction = GetCharacterDirection(cell);
|
|
SetCharacterDirectionVector(direction,isWalk);
|
|
SwitchCharacterAniState(IdleParamName);
|
|
await CollectCollectionCell(cell);
|
|
}
|
|
|
|
private async Task ShowCharacterWalk(EventMowForTreasureObstacleCell cell, Animator animator,
|
|
Vector2Int coordinate,CancellationToken cancellationToken,bool isWalk)
|
|
{
|
|
var direction = GetCharacterDirection(cell);
|
|
SetCharacterDirectionVector(direction,isWalk);
|
|
var aniName = isWalk ? WalkParamName : RunParamName;
|
|
SwitchCharacterAniState(aniName, animator);
|
|
var oneMoveTime = isWalk ? characterMoveTimeOneGrid : characterRunMoveTimeOneGrid;
|
|
var moveTime = direction.magnitude / Vector2.up.magnitude * oneMoveTime;
|
|
|
|
var tcs = new TaskCompletionSource<bool>();
|
|
var tween = _npcTrans.DOMove(cell.transform.position, moveTime).SetEase(Ease.Linear);
|
|
tween.OnComplete(() => tcs.TrySetResult(true));
|
|
|
|
await using var registration = cancellationToken.Register(() =>
|
|
{
|
|
tween.Kill();
|
|
tcs.TrySetCanceled(cancellationToken);
|
|
SwitchCharacterAniState(IdleParamName, animator);
|
|
});
|
|
|
|
await tcs.Task;
|
|
if (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
_currentCoordinate = coordinate;
|
|
SwitchCharacterAniState(IdleParamName, animator);
|
|
}
|
|
}
|
|
|
|
private void ShowCharacterWork(EventMowForTreasureObstacleCell cell, Animator animator = null)
|
|
{
|
|
var direction = GetCharacterDirection(cell);
|
|
SetCharacterDirectionVector(direction);
|
|
SwitchCharacterAniState(WorkParamName, animator);
|
|
}
|
|
|
|
private void SetCharacterDirectionVector(Vector3 direction,bool isWalk=true)
|
|
{
|
|
direction.y = 0f;
|
|
if (direction.magnitude < 0.01f) return;
|
|
direction.Normalize();
|
|
|
|
var targetYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
|
|
var time = isWalk ? rotateTime : characterRunRotateTime;
|
|
_npcTrans.DORotate(new Vector3(0f, targetYRotation, 0f), time);
|
|
}
|
|
|
|
private Vector3 GetCharacterDirection(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
var targetPos = cell.transform.position;
|
|
var currentPos = _npcTrans.transform.position;
|
|
var direction = targetPos - currentPos;
|
|
return direction;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region StageSlots
|
|
|
|
private async Task InitStageItemDir()
|
|
{
|
|
foreach (var item in Manager.GetCurrentStageItemDir())
|
|
{
|
|
var config = Manager.Tables.TbEventMowForTreasureStageItem.GetOrDefault(item.Value);
|
|
if (config == null)
|
|
continue;
|
|
if (string.IsNullOrEmpty(config.Prefab))
|
|
continue;
|
|
if (_obstaclePrefabDir.ContainsKey(config.ID))
|
|
continue;
|
|
var obj = await Addressables.LoadAssetAsync<GameObject>(config.Prefab).Task;
|
|
_obstaclePrefabDir.Add(config.ID, obj);
|
|
}
|
|
}
|
|
|
|
private void InitObstacleSlots()
|
|
{
|
|
InitGridGameObject();
|
|
CalculateGridDimensions();
|
|
Manager.InitStageAllObstacle(StartCoordinate, FinalRewardCoordinate);
|
|
_slots?.Clear();
|
|
for (var i = 0; i < _rowAmount; i++)
|
|
{
|
|
var row = _grid.transform.GetChild(i);
|
|
for (var j = 0; j < _columnAmount; j++) CreateObstacleCell(row, j, i);
|
|
}
|
|
|
|
//finalReward
|
|
_slots?.Add(FinalRewardCoordinate, _finalReward);
|
|
_finalReward.SetData(Data.EndObstacle, OnGridClicked);
|
|
//start
|
|
_slots?.Add(StartCoordinate, _startCell);
|
|
_startCell.SetData(Data.StartObstacle, null);
|
|
|
|
_currentCoordinate = StartCoordinate;
|
|
}
|
|
|
|
private void CreateObstacleCell(Transform row, int j, int i)
|
|
{
|
|
var slot = row.GetChild(j);
|
|
EventMowForTreasureObstacleCell cell;
|
|
EventMowForTreasureObstacleData data;
|
|
var coordinate = new Vector2Int(j, i);
|
|
if (i == 0 || i == _rowAmount - 1)
|
|
{
|
|
cell = slot.gameObject.GetOrAddComponent<EventMowForTreasureObstacleCell>();
|
|
data = new EventMowForTreasureObstacleData
|
|
{
|
|
Coordinate = coordinate,
|
|
IsBroke = true,
|
|
Type = EEventMowForTreasureObstacleType.None
|
|
};
|
|
cell.SetData(data, OnGridClicked);
|
|
_slots?.Add(coordinate, cell);
|
|
return;
|
|
}
|
|
|
|
data = Manager.GetObstacleData(coordinate, _columnAmount);
|
|
if (data == null)
|
|
{
|
|
Debug.LogError($"jsd data is null coordinate {coordinate}");
|
|
return;
|
|
}
|
|
|
|
data.Coordinate = coordinate;
|
|
var targetType = data.Type;
|
|
if (targetType == EEventMowForTreasureObstacleType.SpecialReward)
|
|
{
|
|
targetType = EEventMowForTreasureObstacleType.CommonObstacle;
|
|
}
|
|
|
|
|
|
var obstaclePrefab = GetObstaclePrefabByType(targetType);
|
|
if (!obstaclePrefab)
|
|
{
|
|
Debug.LogError($"jsd obstaclePrefab is null type {data.Type}");
|
|
return;
|
|
}
|
|
|
|
var newObj = Instantiate(obstaclePrefab, slot.position, Quaternion.identity, _grid.transform);
|
|
newObj.transform.SetParent(slot);
|
|
cell = newObj.GetComponent<EventMowForTreasureObstacleCell>();
|
|
cell.SetData(data, OnGridClicked);
|
|
if (newObj) newObj.SetActive(true);
|
|
_slots?.Add(coordinate, cell);
|
|
}
|
|
|
|
private void CalculateGridDimensions()
|
|
{
|
|
_rowAmount = _grid.transform.childCount;
|
|
_columnAmount = _grid.transform.GetChild(0).childCount;
|
|
}
|
|
|
|
private void InitGridGameObject()
|
|
{
|
|
if (!_grid)
|
|
_grid = gameObject.FindChildGameObject("grid");
|
|
if (!_finalReward)
|
|
_finalReward = gameObject.FindChildGameObject("p_final_reward").GetComponentInChildren<EventMowForTreasureObstacleCell>()
|
|
.GetComponent<EventMowForTreasureObstacleCell>();
|
|
if (!_startCell)
|
|
_startCell = gameObject.FindChildGameObject("p_start")
|
|
.GetComponent<EventMowForTreasureObstacleCell>();
|
|
}
|
|
|
|
private GameObject GetObstaclePrefabByType(EEventMowForTreasureObstacleType type)
|
|
{
|
|
var stageItemDir = Manager.GetCurrentStageItemDir();
|
|
if (!stageItemDir.TryGetValue((int)type, out var obstacleId))
|
|
return null;
|
|
return _obstaclePrefabDir.GetValueOrDefault(obstacleId);
|
|
}
|
|
|
|
private List<EventMowForTreasureObstacleCell> GetBreakObstacleCellByTool (EventMowForTreasureTool toolConfig,
|
|
EventMowForTreasureObstacleCell hitObj,out bool isAllBroke)
|
|
{
|
|
isAllBroke = true;
|
|
List<EventMowForTreasureObstacleCell> cells = new();
|
|
if (!IsValidToolAndObstacle(toolConfig, hitObj, out var hitCoordinate))
|
|
return cells;
|
|
if (toolConfig.IsSuperBreak)
|
|
GetSuperToolCanBreakCell(hitCoordinate,ref cells,out isAllBroke);
|
|
else
|
|
GetRegularToolCanBreakCell(toolConfig, hitCoordinate,ref cells,out isAllBroke);
|
|
return cells;
|
|
}
|
|
private bool IsValidToolAndObstacle(EventMowForTreasureTool toolConfig,EventMowForTreasureObstacleCell hitObj,
|
|
out Vector2Int coordinate)
|
|
{
|
|
coordinate = default;
|
|
|
|
if (toolConfig == null || hitObj == null)
|
|
return false;
|
|
var toolType = Manager.GetToolTypeByConfig(toolConfig);
|
|
|
|
var data = hitObj.GetData();
|
|
coordinate = data.Coordinate;
|
|
if (data.Type is EEventMowForTreasureObstacleType.None
|
|
or EEventMowForTreasureObstacleType.FinalReward)
|
|
return false;
|
|
switch (toolType)
|
|
{
|
|
case EEventMowForTreasureToolType.SimpleTool:
|
|
case EEventMowForTreasureToolType.CommonTool:
|
|
if (!IsValidTool(hitObj))
|
|
return false;
|
|
break;
|
|
}
|
|
return _slots.ContainsKey(coordinate);
|
|
}
|
|
private bool IsValidTool(EventMowForTreasureObstacleCell hitObj)
|
|
{
|
|
return !hitObj.GetData().IsBroke;
|
|
}
|
|
|
|
private void GetSuperToolCanBreakCell(Vector2Int hitCoordinate,ref List<EventMowForTreasureObstacleCell> cells,out bool isAllBroke)
|
|
{
|
|
isAllBroke = true;
|
|
for (int i = 0; i < _rowAmount; i++)
|
|
{
|
|
if (!_slots.TryGetValue(new Vector2Int(hitCoordinate.x, i), out var cell)) continue;
|
|
if(cell.GetData().Type==EEventMowForTreasureObstacleType.None) continue;
|
|
if (isAllBroke)
|
|
isAllBroke = cell.GetData().IsBroke;
|
|
cells.Add(cell);
|
|
}
|
|
}
|
|
private void GetRegularToolCanBreakCell(EventMowForTreasureTool toolConfig, Vector2Int hitCoordinate,
|
|
ref List<EventMowForTreasureObstacleCell> cells,out bool isAllBroke)
|
|
{
|
|
isAllBroke = true;
|
|
for (var i = 0; i < toolConfig.BreakCount; i++)
|
|
{
|
|
var targetCoordinate = new Vector2Int(hitCoordinate.x, hitCoordinate.y + i);
|
|
if (!_slots.TryGetValue(targetCoordinate, out var cell))
|
|
continue;
|
|
if (isAllBroke)
|
|
isAllBroke = cell.GetData().IsBroke;
|
|
|
|
if (!Manager.IsDefaultTool(toolConfig.ID) && !IsCanBreakObstacle(toolConfig, cell))
|
|
{
|
|
cells.Add(cell);
|
|
break;
|
|
}
|
|
cells.Add(cell);
|
|
}
|
|
}
|
|
public void ShowSelectedObstacleByTool(EventMowForTreasureTool toolConfig,
|
|
EventMowForTreasureObstacleCell hitObj)
|
|
{
|
|
if (!IsValidToolAndObstacle(toolConfig, hitObj, out _))
|
|
return;
|
|
_heightLightObstacleCells= GetBreakObstacleCellByTool(toolConfig, hitObj,out _);
|
|
if (toolConfig.IsSuperBreak)
|
|
ProcessSuperToolSelection(_heightLightObstacleCells);
|
|
else
|
|
ProcessRegularToolSelection(toolConfig, _heightLightObstacleCells);
|
|
}
|
|
private void ProcessSuperToolSelection(List<EventMowForTreasureObstacleCell> cells)
|
|
{
|
|
foreach (var cell in cells)
|
|
cell.ShowSelected(true);
|
|
}
|
|
private void ProcessRegularToolSelection(EventMowForTreasureTool toolConfig,List<EventMowForTreasureObstacleCell> cells)
|
|
{
|
|
var isDefault= Manager.IsDefaultTool(toolConfig.ID);
|
|
foreach (var cell in cells)
|
|
{
|
|
if (isDefault)
|
|
{
|
|
var isHasPath= TryGetPath(_currentCoordinate, cell.GetData().Coordinate, out _);
|
|
cell.ShowSelected(false, !isHasPath);
|
|
}
|
|
else
|
|
cell.ShowSelected();
|
|
}
|
|
}
|
|
|
|
public void RevertChangedObstacle()
|
|
{
|
|
foreach (var changedCell in _heightLightObstacleCells) changedCell.Show();
|
|
_heightLightObstacleCells.Clear();
|
|
}
|
|
|
|
public async void BreakObstacle(EventMowForTreasureTool toolConfig, EventMowForTreasureObstacleCell hitObj)
|
|
{
|
|
try
|
|
{
|
|
if (toolConfig == null || hitObj == null)
|
|
return;
|
|
if (!Manager.GetCanUseTool(toolConfig.ID))
|
|
return;
|
|
var type = hitObj.GetData().Type;
|
|
if(type==EEventMowForTreasureObstacleType.None)
|
|
return;
|
|
var cells = GetBreakObstacleCellByTool(toolConfig, hitObj,out var isAllBroke);
|
|
if (cells == null || cells.Count == 0 ||isAllBroke)
|
|
return;
|
|
if (!IsCanBreakObstacle(toolConfig, cells[0]))
|
|
{
|
|
ToastPanel.Show(LocalizationMgr.GetText("EventMowForTreasurePanel_7"));
|
|
return;
|
|
}
|
|
|
|
Manager.ConsumeTool(toolConfig.ID);
|
|
|
|
GContext.Publish(new EventMowForTreasureObstacleBreakingEvent()); // 通知panel打开mask
|
|
var changedCells= ProcessObstacleBreakingInOnePass(toolConfig, cells,out var cantBreakCell,out var brokeCount);
|
|
var isKnife = changedCells.Any(c =>
|
|
c.GetData().Type == EEventMowForTreasureObstacleType.SpecialReward);
|
|
Manager.AggItem(toolConfig.ID,isKnife,brokeCount);
|
|
|
|
var isDefault = Manager.IsDefaultTool(toolConfig.ID);
|
|
await ShowBrokeEffect(toolConfig, isDefault,changedCells, cantBreakCell);
|
|
GContext.Publish(new EventMowForTreasureObstacleBreakingEvent()); //通知panel关闭mask
|
|
CheckFinalRewardStatus();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
private List<EventMowForTreasureObstacleCell> ProcessObstacleBreakingInOnePass(EventMowForTreasureTool toolConfig,
|
|
List<EventMowForTreasureObstacleCell> cells,out EventMowForTreasureObstacleCell cantBreakObstacleCell,
|
|
out int brokeCount)
|
|
{
|
|
cantBreakObstacleCell = null;
|
|
brokeCount = 0;
|
|
int specialRewardBrokeCount = 0;
|
|
List<EventMowForTreasureObstacleCell> changedCells = new();
|
|
foreach (var cell in cells)
|
|
{
|
|
if (!IsCanBreakObstacle(toolConfig, cell))
|
|
{
|
|
cantBreakObstacleCell = cell;
|
|
break;
|
|
}
|
|
changedCells.Add(cell);
|
|
if (cell.GetData().IsBroke)
|
|
continue;
|
|
brokeCount++;
|
|
Manager.ProcessSingleCellBreak(cell,GetSpecialRewardBreakItems, ref specialRewardBrokeCount);
|
|
}
|
|
brokeCount += specialRewardBrokeCount;
|
|
return changedCells;
|
|
}
|
|
|
|
private async Task ShowBrokeEffect(EventMowForTreasureTool toolCell, bool isDefault,List<EventMowForTreasureObstacleCell> cells,EventMowForTreasureObstacleCell cantBreakCell)
|
|
{
|
|
if (cells.Count == 0) return;
|
|
|
|
await ShowToolMove(toolCell,isDefault,cells,cantBreakCell);
|
|
|
|
foreach (var cell in cells)
|
|
await cell.PlayBreakEffect(!isDefault, GetBreakCallBackEffect( cell));
|
|
}
|
|
|
|
private Action GetBreakCallBackEffect( EventMowForTreasureObstacleCell cell)
|
|
{
|
|
var data = cell.GetData();
|
|
if (data.IsSpecialReward)
|
|
return () => ShowSpecialRewardBreakEffect(cell);
|
|
|
|
if (data.IsCollectionReward)
|
|
return () =>
|
|
{
|
|
var collectionData = Manager.GetCollectionDataByType((int)cell.GetData().Type);
|
|
if (collectionData==null) return;
|
|
ShowCollectCollectionEffect(cell, collectionData);
|
|
};
|
|
|
|
return ()=>ShowItemDrop(cell);
|
|
}
|
|
|
|
private void ShowItemDrop(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
var data = cell.GetData();
|
|
if (data.ItemData==null) return;
|
|
GContext.Publish(new EventMowForTreasureObstacleBrokeEvent
|
|
{
|
|
Position = UIManager.Instance.WorldToScreen(cell.transform.position),
|
|
ItemData = data.ItemData
|
|
});
|
|
data.ItemData = null;
|
|
}
|
|
|
|
private bool IsCanBreakObstacle(EventMowForTreasureTool toolConfig, EventMowForTreasureObstacleCell cell)
|
|
{
|
|
var data = cell.GetData();
|
|
var type = (int)data.Type;
|
|
if (!toolConfig.IsCanBreakWithTool(type))
|
|
return false;
|
|
return !Manager.IsDefaultTool(toolConfig.ID) || TryGetPath(_currentCoordinate, data.Coordinate, out _);
|
|
}
|
|
|
|
private CancellationTokenSource _clickProcessingToken;
|
|
private EventMowForTreasureObstacleCell _lastClickedCell;
|
|
private bool _isWorking;
|
|
private async void OnGridClicked(EventMowForTreasureObstacleCell clickedCell)
|
|
{
|
|
try
|
|
{
|
|
if(_isWorking)return;
|
|
if (_lastClickedCell == clickedCell&& _clickProcessingToken != null) return;
|
|
var animator = _npcTrans.GetComponentInChildren<Animator>();
|
|
if (animator.GetCurrentAnimatorStateInfo(0).IsName("Work")||animator.GetBool(WorkParamName)) return;
|
|
|
|
var cellData = clickedCell.GetData();
|
|
var clickedCoordinate = clickedCell.GetData().Coordinate;
|
|
if (_currentCoordinate == clickedCoordinate
|
|
|| !CheckIsPathFindingWithType(clickedCell, cellData)
|
|
|| !TryGetPath(_currentCoordinate, clickedCoordinate, out var path))
|
|
{
|
|
CancelPathFinding();
|
|
return;
|
|
}
|
|
|
|
CancelPathFinding();
|
|
|
|
_clickProcessingToken = new CancellationTokenSource();
|
|
_lastClickedCell = clickedCell;
|
|
await CharacterPathFinding(path, cellData, _clickProcessingToken.Token);
|
|
_isWorking = true;
|
|
if (_clickProcessingToken.Token.IsCancellationRequested) return;
|
|
ShowClickEffectWithType(clickedCell, cellData);
|
|
_lastClickedCell = null;
|
|
_isWorking = false;
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
//不做操作
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"jsd {e}");
|
|
}
|
|
}
|
|
|
|
private void CancelPathFinding()
|
|
{
|
|
if (_clickProcessingToken is not { IsCancellationRequested: false }) return;
|
|
_clickProcessingToken.Cancel();
|
|
_clickProcessingToken.Dispose();
|
|
}
|
|
|
|
private bool CheckIsPathFindingWithType(EventMowForTreasureObstacleCell clickedCell,
|
|
EventMowForTreasureObstacleData cellData)
|
|
{
|
|
switch (cellData.Type)
|
|
{
|
|
case EEventMowForTreasureObstacleType.CommonObstacle:
|
|
case EEventMowForTreasureObstacleType.SpecialReward:
|
|
return ClickCommonObstacleCell(clickedCell);
|
|
case EEventMowForTreasureObstacleType.HeightObstacle:
|
|
return ClickSpecialObstacleCell(clickedCell);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
private bool ClickCommonObstacleCell(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
if (cell.GetData().IsBroke) return true;
|
|
if (Manager.GetCanUseDefaultTool()) return true;
|
|
Manager.ShowFestPackPanel();
|
|
return false;
|
|
}
|
|
private void ShowClickEffectWithType(EventMowForTreasureObstacleCell clickedCell,
|
|
EventMowForTreasureObstacleData cellData)
|
|
{
|
|
switch (cellData.Type)
|
|
{
|
|
case EEventMowForTreasureObstacleType.CommonObstacle:
|
|
case EEventMowForTreasureObstacleType.SpecialReward:
|
|
ShowClickCommonObstacleCell(clickedCell);
|
|
break;
|
|
case EEventMowForTreasureObstacleType.FinalReward:
|
|
ShowClickFinalRewardCell(clickedCell);
|
|
break;
|
|
}
|
|
}
|
|
private void ShowClickCommonObstacleCell(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
if (cell.GetData().IsBroke) return;
|
|
ShowCharacterWork(cell);
|
|
BreakObstacle(Manager.GetDefaultToolConfig(), cell);
|
|
}
|
|
private void ShowClickFinalRewardCell(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
if (cell.GetData().IsBroke) return;
|
|
cell.GetData().IsBroke = true;
|
|
Manager.SetCompleteStageAndSendReward();
|
|
}
|
|
|
|
public Vector2 GetFinalRewardPosition()
|
|
{
|
|
return UIManager.Instance.WorldToScreen(_finalReward.transform.position);
|
|
}
|
|
|
|
public Vector2 GetCanObstacleCellPosition(out bool isFinalReward )
|
|
{
|
|
isFinalReward = false;
|
|
//优先级 南瓜>最终奖励>草(优先玩家所在的那一列)
|
|
var position=Vector3.zero;
|
|
if (Data?.ObstacleList == null) return position;
|
|
foreach (var data in Data.ObstacleList.Where(data => CheckIsCanBreak(data)))
|
|
{
|
|
if(!_slots.TryGetValue(data.Coordinate,out var cell))continue;
|
|
if (data.IsCollectionReward)
|
|
return UIManager.Instance.WorldToScreen(cell.transform.position);
|
|
if(position==Vector3.zero)
|
|
position=UIManager.Instance.WorldToScreen(cell.transform.position);
|
|
}
|
|
|
|
if (TryGetPath(_currentCoordinate, FinalRewardCoordinate, out _))
|
|
{
|
|
isFinalReward = true;
|
|
return UIManager.Instance.WorldToScreen(_finalReward.transform.position);
|
|
}
|
|
|
|
|
|
for (int i = 0; i < _rowAmount; i++)
|
|
{
|
|
Debug.Log($"jsd {i} _rowAmount {_rowAmount}");
|
|
var coordinate = new Vector2Int(_currentCoordinate.x, i);
|
|
if(!_slots.TryGetValue(coordinate,out var cell))continue;
|
|
if (!CheckIsCanBreak(cell.GetData())) continue;
|
|
return UIManager.Instance.WorldToScreen(cell.transform.position);
|
|
}
|
|
|
|
return position;
|
|
}
|
|
public Vector2 GetObstacleCellPositionByCoordinate(Vector2Int coordinate)
|
|
{
|
|
if(!_slots.TryGetValue(coordinate,out var cell))return Vector2.zero;
|
|
return UIManager.Instance.WorldToScreen(cell.transform.position);
|
|
}
|
|
private bool CheckIsCanBreak(EventMowForTreasureObstacleData data)
|
|
{
|
|
if(data.IsBroke)return false;
|
|
if(data.Type==EEventMowForTreasureObstacleType.HeightObstacle) return false;
|
|
return TryGetPath(_currentCoordinate,data.Coordinate,out _);
|
|
}
|
|
|
|
#region FinalReward
|
|
|
|
private EventUISound _truckRewardSound;
|
|
private void CheckFinalRewardStatus()
|
|
{
|
|
if (_finalReward is not EventMowForTreasureObstacleCell_FinalReward finalReward)
|
|
{
|
|
StopFinalRewardSound();
|
|
return;
|
|
}
|
|
var isCanClaim= TryGetPath(_currentCoordinate,FinalRewardCoordinate,out _);
|
|
finalReward.SetEffectActive(isCanClaim);
|
|
if (isCanClaim)
|
|
{
|
|
if(_truckRewardSound!=null)return;
|
|
_truckRewardSound = new EventUISound("audio_ui_mowfortreasure_npc_truck_reward");
|
|
GContext.Publish(_truckRewardSound);
|
|
}
|
|
else
|
|
{
|
|
StopFinalRewardSound();
|
|
}
|
|
}
|
|
|
|
private void StopFinalRewardSound()
|
|
{
|
|
if(_truckRewardSound!=null)
|
|
GContext.Publish(new FadeOutEvent(_truckRewardSound.Sound));
|
|
_truckRewardSound = null;
|
|
}
|
|
|
|
#endregion
|
|
#region Collection
|
|
|
|
private async Task CollectCollectionCell(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
var cellData = cell.GetData();
|
|
if (cellData.IsBroke)
|
|
return;
|
|
if (!Manager.TryCollectCollection(cellData, out var collectionData)) return;
|
|
await cell.PlayBreakEffect(false,()=> ShowCollectCollectionEffect(cell, collectionData),true);
|
|
GContext.Publish(new EventUISound("audio_ui_mowfortreasure_pumpkin_get"));
|
|
}
|
|
|
|
private async void ShowCollectCollectionEffect(EventMowForTreasureObstacleCell cell,
|
|
EventMowForTreasureCollectionData collectionData)
|
|
{
|
|
try
|
|
{
|
|
if(!cell) return;
|
|
if(cell is not EventMowForTreasureObstacleCell_CollecitonReward collectionCell) return;
|
|
var position = collectionCell.GetNormalPositon();
|
|
var screenPosition = UIManager.Instance.WorldToScreen(position);
|
|
cell.Show();
|
|
|
|
collectionData.Position = screenPosition;
|
|
collectionData.IsPlayReward = true;
|
|
GContext.Publish(collectionData);
|
|
if (collectionData.PlayRewardFly != null)
|
|
await collectionData.PlayRewardFly;
|
|
collectionData.IsObtainReward =
|
|
Manager.IsCanObtainCollectionReward(collectionData, collectionData.Amount);
|
|
collectionData.IsRefresh = true;
|
|
GContext.Publish(collectionData);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
#region specialReward
|
|
private bool ClickSpecialObstacleCell(EventMowForTreasureObstacleCell cell)
|
|
{
|
|
if (cell.GetData().IsBroke) return true;
|
|
ToastPanel.Show(LocalizationMgr.GetText("EventMowForTreasurePanel_7"));
|
|
return false;
|
|
}
|
|
private async void ShowSpecialRewardBreakEffect(EventMowForTreasureObstacleCell commonCell)
|
|
{
|
|
try
|
|
{
|
|
var prefab= GetObstaclePrefabByType(EEventMowForTreasureObstacleType.SpecialReward);
|
|
var obj =Instantiate(prefab, commonCell.transform.parent.position, Quaternion.identity, _grid.transform);
|
|
var specialRewardCell = obj.GetComponent<EventMowForTreasureObstacleCell_Landmine>();
|
|
specialRewardCell.transform.SetParent(commonCell.transform.parent);
|
|
specialRewardCell.SetData(commonCell.GetData(),OnGridClicked);
|
|
if(specialRewardCell==null) return;
|
|
await Awaiters.Seconds(specialRewardCell.GetSpecialCellAniDelayTime());
|
|
var cells = GetSpecialRewardBreakItems(specialRewardCell);
|
|
foreach (var cell in cells)
|
|
_ = cell.PlayBreakEffect(specialRewardCell,GetBreakCallBackEffect(cell) );
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
private readonly Vector2Int[] _directions2 =
|
|
{
|
|
Vector2Int.one, //右上
|
|
new(1, -1) ,//右下
|
|
-Vector2Int.one, //左下
|
|
new(-1, 1) //左上
|
|
};
|
|
|
|
private List<EventMowForTreasureObstacleCell> GetSpecialRewardBreakItems(
|
|
EventMowForTreasureObstacleCell specialRewardCell)
|
|
{
|
|
var cells = new List<EventMowForTreasureObstacleCell>();
|
|
var coordinate = specialRewardCell.GetData().Coordinate;
|
|
foreach (var t in _directions)
|
|
{
|
|
if (!_slots.TryGetValue(coordinate + t, out var cell)) continue;
|
|
cells.Add(cell);
|
|
}
|
|
|
|
foreach (var t in _directions2)
|
|
{
|
|
if (!_slots.TryGetValue(coordinate + t, out var cell)) continue;
|
|
cells.Add(cell);
|
|
}
|
|
|
|
return cells;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ToolItem
|
|
|
|
private async Task InitToolGameObjs()
|
|
{
|
|
foreach (var toolID in Data.EventMain.ToolConfigList)
|
|
{
|
|
if (_toolCells.ContainsKey(toolID))
|
|
continue;
|
|
var config = Manager.Tables.TbEventMowForTreasureTool.GetOrDefault(toolID);
|
|
if (config == null)
|
|
continue;
|
|
|
|
var obj = await Addressables.InstantiateAsync(config.Prefab).Task;
|
|
obj.SetActive(false);
|
|
var toolCell = obj.GetComponentInChildren<EventMowForTreasureToolCell>();
|
|
_toolCells.Add(config.ID, toolCell);
|
|
}
|
|
}
|
|
|
|
private void ReleaseTool()
|
|
{
|
|
foreach (var toolObj in _toolCells.Values)
|
|
if (toolObj != null)
|
|
Addressables.ReleaseInstance(toolObj.gameObject);
|
|
_toolCells?.Clear();
|
|
}
|
|
|
|
public EventMowForTreasureToolCell GetToolCell(int toolId)
|
|
{
|
|
return _toolCells.GetValueOrDefault(toolId);
|
|
}
|
|
|
|
private EventMowForTreasureToolCell PlayToolOngoingEffect(EventMowForTreasureTool toolConfig, bool isDefault,Transform firstTransform,out float time)
|
|
{
|
|
time = 0;
|
|
var toolCell = GetToolCell(toolConfig.ID);
|
|
toolCell.PlayOngoingEffect(isDefault,firstTransform,out time);
|
|
return toolCell;
|
|
}
|
|
|
|
private async Task ShowToolMove(EventMowForTreasureTool toolConfig, bool isDefault,List<EventMowForTreasureObstacleCell> cells,EventMowForTreasureObstacleCell cantBreakCell)
|
|
{
|
|
if(cells is not { Count: > 0 })return;
|
|
|
|
var toolCell = PlayToolOngoingEffect(toolConfig, isDefault,cells[0].transform,out var time);
|
|
if (toolCell == null) return;
|
|
var totalTime = cells.Count * toolCell.GetMoveTime();
|
|
var endCell = cells[^1];
|
|
var endPosition = endCell.transform.position;
|
|
await Awaiters.Seconds(time);
|
|
toolCell.transform.DOMove(endPosition, totalTime).SetEase(Ease.Linear)
|
|
.OnComplete(() =>
|
|
{
|
|
if (cantBreakCell!=null)
|
|
toolCell.PlayToolDamageTimeLine(()=>toolCell.gameObject.SetActive(false));
|
|
else
|
|
toolCell.gameObject.SetActive(false);
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region A*
|
|
|
|
private bool IsPassable(Vector2Int pos)
|
|
{
|
|
if (pos.x < -1 || pos.x >= _columnAmount || pos.y < -1 || pos.y >= _rowAmount)
|
|
return false;
|
|
if (_slots.TryGetValue(pos, out var cell))
|
|
return cell.GetData().IsCollectionReward || cell.GetData().IsBroke;
|
|
return false;
|
|
}
|
|
|
|
private bool TryGetPath(Vector2Int start, Vector2Int end, out List<Vector2Int> path)
|
|
{
|
|
path = null;
|
|
if (!_slots.TryGetValue(start, out var cell))
|
|
return false;
|
|
var cellData = cell.GetData();
|
|
cellData.Reset();
|
|
path = FindPath(cellData, end);
|
|
return path != null;
|
|
}
|
|
|
|
private readonly Vector2Int[] _directions =
|
|
{
|
|
Vector2Int.up, // 上
|
|
Vector2Int.down, // 下
|
|
Vector2Int.left, // 左
|
|
Vector2Int.right // 右
|
|
};
|
|
|
|
private List<Vector2Int> GetNeighbors(Vector2Int coordinate, Vector2Int endCoordinate)
|
|
{
|
|
var neighbors = new List<Vector2Int>();
|
|
Vector2Int neighborPos;
|
|
foreach (var dir in _directions)
|
|
{
|
|
neighborPos = coordinate + dir;
|
|
if (IsPassable(neighborPos) || neighborPos == endCoordinate) neighbors.Add(neighborPos);
|
|
}
|
|
|
|
if (coordinate ==StartCoordinate)
|
|
for (var i = 0; i < _columnAmount; i++)
|
|
{
|
|
neighborPos = new Vector2Int(i, 0);
|
|
if (IsPassable(neighborPos) || neighborPos == endCoordinate) neighbors.Add(neighborPos);
|
|
}
|
|
else if (IsAtRewardPosition(coordinate) && endCoordinate == FinalRewardCoordinate)
|
|
neighbors.Add(FinalRewardCoordinate);
|
|
|
|
return neighbors;
|
|
}
|
|
|
|
private bool IsAtRewardPosition(Vector2Int coordinate)
|
|
{
|
|
return (coordinate.x == FinalRewardCoordinate.x / 2 || coordinate.x == FinalRewardCoordinate.x / 2 - 1)
|
|
&& coordinate.y == _rowAmount - 1;
|
|
}
|
|
|
|
private List<Vector2Int> FindPath(EventMowForTreasureObstacleData startNode, Vector2Int endCoordinate)
|
|
{
|
|
var start = startNode.Coordinate;
|
|
if (start == -Vector2Int.one && endCoordinate.y == 0)
|
|
return new List<Vector2Int> { endCoordinate };
|
|
if (!IsPassable(start)) return null;
|
|
|
|
if (start == endCoordinate) return new List<Vector2Int> { start };
|
|
|
|
var openSet = new EventMowForTreasurePriorityQueue<EventMowForTreasureObstacleData>();
|
|
var closedSet = new HashSet<Vector2Int>();
|
|
|
|
openSet.Enqueue(startNode);
|
|
|
|
while (openSet.Count > 0)
|
|
{
|
|
// 获取FCost最小的节点
|
|
var currentNode = openSet.Dequeue();
|
|
if (currentNode.Coordinate == endCoordinate) return ReconstructPath(currentNode);
|
|
|
|
closedSet.Add(currentNode.Coordinate);
|
|
|
|
foreach (var neighborPos in GetNeighbors(currentNode.Coordinate, endCoordinate))
|
|
{
|
|
if (closedSet.Contains(neighborPos))
|
|
continue;
|
|
|
|
var tentativeGCost = currentNode.GCost + Vector2Int.Distance(currentNode.Coordinate, neighborPos);
|
|
|
|
var neighborNode = _slots.GetValueOrDefault(neighborPos).GetData();
|
|
if (!(tentativeGCost < neighborNode.GCost) && openSet.Contains(neighborNode)) continue;
|
|
neighborNode.GCost = tentativeGCost;
|
|
neighborNode.HCost = Vector2Int.Distance(neighborPos, endCoordinate);
|
|
neighborNode.Parent = currentNode;
|
|
|
|
if (!openSet.Contains(neighborNode))
|
|
openSet.Enqueue(neighborNode);
|
|
else
|
|
openSet.UpdateItem(neighborNode);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private List<Vector2Int> ReconstructPath(EventMowForTreasureObstacleData endNode)
|
|
{
|
|
var path = new List<Vector2Int>();
|
|
var currentNode = endNode;
|
|
|
|
while (currentNode != null)
|
|
{
|
|
path.Add(currentNode.Coordinate);
|
|
currentNode = currentNode.Parent;
|
|
}
|
|
|
|
path.Reverse();
|
|
return path;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |