You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
15 KiB
414 lines
15 KiB
|
1 week ago
|
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<ITableObstacle>(TableNames.Obstacle, this.type);
|
||
|
|
// this.MechanismData = gg.data.table.getTableData<ITablePlaymode>(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
|
||
|
|
}
|
||
|
|
}
|