Files
ft/Client/Assets/Scripts/EventMowForTreasure/Act/EventMowForTreasureAct.cs
2026-06-29 21:18:33 +08:00

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