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 StartAsync() { UITypes.EventMowForTreasurePanel.SetType(Data.EventMain.UIPanel); GContext.OnEvent().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 _obstaclePrefabDir = new(); /// /// id instantiate /// private readonly Dictionary _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 _heightLightObstacleCells = new(); #endregion #region GameObj private Transform _npcTrans; private GameObject _grid; private EventMowForTreasureObstacleCell _startCell; private EventMowForTreasureObstacleCell _finalReward; private readonly Dictionary _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(); 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(); if (!animator) return; if(animator.GetBool(stateName)) return; animator.SetBool(_currentCharacterStateName, false); animator.SetBool(stateName, true); _currentCharacterStateName = stateName; } private async Task CharacterPathFinding(List path, EventMowForTreasureObstacleData endData ,CancellationToken cancellationToken) { var animator = _npcTrans.GetComponentInChildren(); 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(); 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(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(); 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(); 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() .GetComponent(); if (!_startCell) _startCell = gameObject.FindChildGameObject("p_start") .GetComponent(); } 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 GetBreakObstacleCellByTool (EventMowForTreasureTool toolConfig, EventMowForTreasureObstacleCell hitObj,out bool isAllBroke) { isAllBroke = true; List 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 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 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 cells) { foreach (var cell in cells) cell.ShowSelected(true); } private void ProcessRegularToolSelection(EventMowForTreasureTool toolConfig,List 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 ProcessObstacleBreakingInOnePass(EventMowForTreasureTool toolConfig, List cells,out EventMowForTreasureObstacleCell cantBreakObstacleCell, out int brokeCount) { cantBreakObstacleCell = null; brokeCount = 0; int specialRewardBrokeCount = 0; List 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 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(); 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(); 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 GetSpecialRewardBreakItems( EventMowForTreasureObstacleCell specialRewardCell) { var cells = new List(); 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(); _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 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 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 GetNeighbors(Vector2Int coordinate, Vector2Int endCoordinate) { var neighbors = new List(); 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 FindPath(EventMowForTreasureObstacleData startNode, Vector2Int endCoordinate) { var start = startNode.Coordinate; if (start == -Vector2Int.one && endCoordinate.y == 0) return new List { endCoordinate }; if (!IsPassable(start)) return null; if (start == endCoordinate) return new List { start }; var openSet = new EventMowForTreasurePriorityQueue(); var closedSet = new HashSet(); 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 ReconstructPath(EventMowForTreasureObstacleData endNode) { var path = new List(); var currentNode = endNode; while (currentNode != null) { path.Add(currentNode.Coordinate); currentNode = currentNode.Parent; } path.Reverse(); return path; } #endregion } }