import { _decorator, CCBoolean, ccenum, CCString, Component, Enum, EventTouch, Input, Label, math, Node, Sprite, SpriteFrame, Vec2, Vec3 } from 'cc'; import { GBundle, GPath, UIID } from '../../game/ConfigRes'; import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad'; import { MapGrid } from './MapGrid'; import { GEvent } from 'db://assets/mx/module/event/GEvent'; import { TableNames } from '../../game/ConfigTableData'; import { EDITOR } from 'cc/env'; import { BattleType } from '../../manager/ChapterDataManager'; /** * 障碍物类型 */ export enum ObstacleType { /**无 */ 无 = 999999, /**静态障碍物 */ 静态障碍物 = 0, /**普通 */ 普通障碍物 = 1, /**炸弹 */ 炸弹 = 2, /**银币 */ 银币 = 3, /**减速 */ 减速 = 4, /**火炉 */ 火炉 = 5, /**吹风机 */ 吹风机 = 6, /**灯塔 */ 灯塔 = 7, /**藤蔓 */ 藤蔓 = 8, /**加速门 */ 加速门 = 9,//占用格子,可通行 /**血包 */ 血包 = 10, /**屏蔽 */ 屏蔽 = 11, } const { ccclass, property, executeInEditMode } = _decorator; @ccclass('MapObstacle') @executeInEditMode export class MapObstacle extends Component { /** 标记:本帧/本次操作里 tryEditAction 是否触发了“银币获取弹窗” */ private static _openedCoinPanelByEditAction: boolean = false; public static consumeOpenedCoinPanelByEditAction(): boolean { const v = MapObstacle._openedCoinPanelByEditAction; MapObstacle._openedCoinPanelByEditAction = false; return v; } @property({ type: Enum(ObstacleType) }) type: ObstacleType = ObstacleType.静态障碍物; @property({ type: SpriteFrame }) set img(value: SpriteFrame) { this._img = value; this.icon.spriteFrame = value; } get img(): SpriteFrame { return this._img } @property({ type: SpriteFrame }) private _img: SpriteFrame = null; icon: Sprite = null; /**所在地图格子对象 */ Grid: MapGrid = null; /**生成的障碍物节点(具体的机制逻辑所在节点) */ ObstacleNode: Node = null; /**障碍物数据 */ Data: ITableObstacle = null; /**机制数据 */ MechanismData: ITablePlaymode = null; /**铲除前的障碍物类型 */ oldType: ObstacleType = ObstacleType.无; /**进入编辑前格子状态快照(用于取消编辑完整回滚) */ oldIsCanWalk: boolean = true; oldIsCanPutObstacle: boolean = true; oldIsCanRemove: boolean = false; oldIsCanBeThunder: boolean = false; oldIsBroken: boolean = false; /**本次编辑已经放置该位置 */ private _isEditPlace: boolean = false; /**本次在本格放置障碍物时,从本格挤走的怪物及其放置前世界坐标(移除障碍时还原) */ private _displacedMonsters: { node: Node; worldPos: Vec3 }[] = []; protected onLoad(): void { this.icon = this.node.getChildByName('icon').getComponent(Sprite); this.Grid = this.getComponent(MapGrid); if (this.img) { this.icon.spriteFrame = this.img; } if (this.type == ObstacleType.静态障碍物 && this.Grid.isCanWalk) this.type = ObstacleType.无; } protected start(): void { if (EDITOR) return; this.init(); } protected onEnable(): void { this.node.on(Input.EventType.TOUCH_START, this.onTouchStart, this); this.node.on(Input.EventType.TOUCH_END, this.onTouchEnd, this); this.node.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this); GEvent.Ins.on(GEvent.EnterEdit, this.enterEdit, this); GEvent.Ins.on(GEvent.EndEdit, this.EndEdit, this); } protected onDisable(): void { this.node.off(Input.EventType.TOUCH_START, this.onTouchStart, this); this.node.off(Input.EventType.TOUCH_END, this.onTouchEnd, this); this.node.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this); GEvent.Ins.off(GEvent.EnterEdit, this.enterEdit, this); GEvent.Ins.off(GEvent.EndEdit, this.EndEdit, this); } private onTouchStart(event: EventTouch) { } // 触摸结束事件 private onTouchEnd(event: EventTouch) { this.tryEditAction(true); } /** 编辑态单格操作;单点/滑动均复用,保证规则一致 */ public tryEditAction(reportClick: boolean) { // console.log("触摸结束事件", this.Grid); if (!gg.game.CurentBattle.IsEdit) return; if (this.Grid.isCanPutObstacle && this.Grid.isCanWalk) { this.Grid.isCanWalk = false; gg.game.CurentBattle.Map.notifyMapWalkStateChanged(); let nothavePath = gg.game.CurentBattle.checkMonsterAndSpawnerReachEnd(this.Grid); this.Grid.isCanWalk = true; gg.game.CurentBattle.Map.notifyMapWalkStateChanged(); if (!nothavePath) { gg.ui.showToast(gg.lang.get("不可将路堵死")); return; } if (gg.game.CurentBattle.BattleType == BattleType.GachaMode) { if (gg.game.CurentBattle.GachaModeObstacleNum <= 0) { gg.ui.showToast(gg.lang.get("次数不够")); return; } } else { if (gg.game.CurentBattle.CurentCoinNum < gg.game.CurentBattle.CurEditObstacleConfig.price) { if (gg.game.CurentBattle.CurentVideoAddCoinTimes > 0) { MapObstacle._openedCoinPanelByEditAction = true; if (gg.ui.panelIsShow(UIID.BattleGetCoin)) return; gg.ui.openPanel(UIID.BattleGetCoin) } else { gg.ui.showToast(gg.lang.get("银币数不够")); } return; } } this.placeObstacle(); return; } else { if (this.Grid.isCanRemove) { if (gg.game.CurentBattle.BattleType == BattleType.GachaMode) { if (gg.game.CurentBattle.GachaModeShovelNum <= 0) { gg.ui.showToast(gg.lang.get("次数不够")); return; } else { gg.game.CurentBattle.GachaModeShovelNum--; GEvent.Ins.emit(GEvent.UpdateShovelNum) } } this.remove(); this.updateGrid(); } else { gg.ui.showToast(gg.lang.get("不可放置!")); } } } init() { this.node.getComponent(Sprite).spriteFrame = null; this.setObstacle(this.type, true); } /**放置障碍物 */ placeObstacle() { if (!this.Grid.isCanPutObstacle) { // gg.ui.showToast("不可放置!"); return } if (!gg.game.CurentBattle.CurEditObstacleConfig) return; this.Grid.isCanPutObstacle = false; this.Grid.isCanRemove = true; this.Grid.isCanBeThunder = true; this.type = gg.game.CurentBattle.CurEditObstacleConfig.id; this.Grid.isBroken = false; console.log(this.Grid.isBroken, '放置后状态为修复状态'); gg.audio.playEffect({ name: "放置障碍物", path: "sound/" }); this._isEditPlace = true; this.setObstacle(this.type); this.Grid.isCanWalk = false; gg.game.CurentBattle.Map.notifyMapWalkStateChanged(); if (gg.game.CurentBattle.GachaModeObstacleNum > 0) { gg.game.CurentBattle.GachaModeObstacleNum--; GEvent.Ins.emit(GEvent.UpdateObstacleNum) } else { gg.game.CurentBattle.CurentCoinNum -= this.Data.price; GEvent.Ins.emit(GEvent.UpdateCoin); } //放置后状态为修复状态 gg.game.CurentBattle.showWallLinePath(); GEvent.Ins.emit(GEvent.UpdateBestPlace); this._displacedMonsters.length = 0; const preMove = this.getMonsterNodesOnThisGrid(); for (let i = 0; i < preMove.length; i++) { const n = preMove[i]; this._displacedMonsters.push({ node: n, worldPos: n.worldPosition.clone() }); } this.moveMonster(); } /**雷击障碍物 */ thunderAtk() { this.remove(); } /**设置障碍物 */ setObstacle(type: ObstacleType, remove = false) { if (this.type == ObstacleType.静态障碍物) return; this.type = type; // if (type == ObstacleType.无) { // if (!remove) // this.remove(); // } else { // this.Data = gg.data.table.getTableData(TableNames.Obstacle, this.type); // this.MechanismData = gg.data.table.getTableData(TableNames.Playmode, this.Data.playMode); // this.Grid.isCanWalk = this.Data.canPass == 1; // if (this.type == ObstacleType.普通障碍物) { // let name = gg.game.CurentBattle.DefaultBarrier; // this.setIcon(name); // if (this.ObstacleNode) { // gg.res.putNode(this.ObstacleNode); // } // } else { // this.icon.node.active = false; // console.log(this.Grid.isBroken, 'isBroken状态为修复状态'); // this.ObstacleNode = gg.res.getNode('prefab/障碍物/', this.Data.res, GBundle.BattleMap); // this.ObstacleNode.parent = this.node; // this.ObstacleNode.setSiblingIndex(0); // } // } this.updateGrid(); } /**设置障碍物 */ setIcon(name: string) { if (!this.icon) { return; } this.icon.node.active = true; this.icon.node.getOrAddComponent(SpriteLoad).setSprite(GPath.ObstacleItem(name), GBundle.BattleMap); } /**移除障碍物 */ remove() { if (this._isEditPlace) { this._isEditPlace = false; gg.game.CurentBattle.CurentCoinNum += this.Data.price; GEvent.Ins.emit(GEvent.UpdateCoin); } if (this.icon) { this.icon.spriteFrame = null; //console.log("移除障碍物", this.Grid); this.icon.node.getComponent(SpriteLoad)?.clear(); } if (this.ObstacleNode) { gg.res.putNode(this.ObstacleNode); this.ObstacleNode = null; } this.type = ObstacleType.无; this.Grid.isCanWalk = true; gg.game.CurentBattle.Map.notifyMapWalkStateChanged(); this.Grid.isCanPutObstacle = true; this.Grid.isCanRemove = false; this.Grid.isCanBeThunder = false; this.Grid.isBroken = false; if (gg.game.CurentBattle.IsEdit){ gg.game.CurentBattle.showWallLinePath(); GEvent.Ins.emit(GEvent.UpdateBestPlace); } if (gg.game.CurentBattle.IsEdit && this._displacedMonsters.length > 0) { for (let i = 0; i < this._displacedMonsters.length; i++) { const it = this._displacedMonsters[i]; if (it.node && it.node.isValid) { it.node.setWorldPosition(it.worldPos); } } this._displacedMonsters.length = 0; } } enterEdit() { this.oldType = this.type; this.oldIsCanWalk = this.Grid.isCanWalk; this.oldIsCanPutObstacle = this.Grid.isCanPutObstacle; this.oldIsCanRemove = this.Grid.isCanRemove; this.oldIsCanBeThunder = this.Grid.isCanBeThunder; this.oldIsBroken = this.Grid.isBroken; this.updateGrid(); } EndEdit(isSave: boolean) { if (isSave) { this._displacedMonsters.length = 0; } if (!isSave) { // 仅在“本格本次编辑发生过变化”时回滚。 // 1) type 变化:表示放置/铲除后最终类型已不同 // 2) _isEditPlace=true:表示本次发生过放置扣费(即使最终 type 与 oldType 相同,也要退款) const needRollback = this.type != this.oldType || this._isEditPlace; if (needRollback) { // 先移除当前编辑态结果(若本次放置过会在 remove 内自动退款) this.remove(); // 再恢复到进入编辑前的障碍物类型与表现 this.setObstacle(this.oldType, true); } // 取消编辑时完整恢复进入编辑前的格子状态,避免 type 回滚后标志位残留 this.Grid.isCanWalk = this.oldIsCanWalk; this.Grid.isCanPutObstacle = this.oldIsCanPutObstacle; this.Grid.isCanRemove = this.oldIsCanRemove; this.Grid.isCanBeThunder = this.oldIsCanBeThunder; this.Grid.isBroken = this.oldIsBroken; this._displacedMonsters.length = 0; } this._isEditPlace = false; this.updateGrid(); } updateGrid() { this.getComponent(MapGrid).updateGrid(); } /**候选落脚格必须能寻路到任一终点 */ private canReachAnyEnd(escapeGrid: MapGrid): boolean { if (!escapeGrid || !escapeGrid.isCanWalk) return false; const map = gg.game.CurentBattle.Map; const endPosArr = map.mapTargetParent.children.map(x => x.worldPosition); return map.checkAllStartsCanReachAnyEnd([escapeGrid.node.worldPosition], endPosArr, false, this.Grid); } /** 与原先顺序一致:上、左、右、下(行列相邻四格),且目标格可达终点 */ moveMonster() { const map = gg.game.CurentBattle.Map; const neighbors: [number, number][] = [ [this.Grid.Row - 1, this.Grid.Col], [this.Grid.Row, this.Grid.Col - 1], [this.Grid.Row, this.Grid.Col + 1], [this.Grid.Row + 1, this.Grid.Col], ]; // 同一次放置里,所有怪物的落脚格规则一致,先计算一次目标格再批量移动 let targetGrid: MapGrid = null; for (let j = 0; j < neighbors.length; j++) { const [r, c] = neighbors[j]; const grid = map.getMapGrid(r, c); if (grid && grid.isCanWalk && this.canReachAnyEnd(grid)) { targetGrid = grid; break; } } if (!targetGrid) return; const wp = targetGrid.node.worldPosition; const arr = this.getMonsterNodesOnThisGrid(); for (let i = 0; i < arr.length; i++) { const element = arr[i]; element.setWorldPosition(wp.x, wp.y, element.worldPosition.z); } } /**获取当前格上的所有怪物节点 */ getMonsterNodesOnThisGrid() { let arr: Node[] = []; let allMonsters = gg.game.CurentBattle.MonsterSpawner.node.children; for (let i = 0; i < allMonsters.length; i++) { const element = allMonsters[i]; let g = gg.game.CurentBattle.Map.getMapGridByWorldPos(element.worldPosition); if (g == this.Grid) { arr.push(element); } } return arr } }