import { _decorator, Component, Vec3 } from 'cc'; import { Direction, GameState, GridType, MoveState, MoverRole, addDirection, getMoveCondition, lookupMove, } from '../core/Define'; import { GameManager } from '../manager/GameManager'; const { ccclass, property } = _decorator; @ccclass('Movement') export class Movement extends Component { @property moveSpeed = 4; direction: Direction = Direction.North; moveState: MoveState = MoveState.Idle; moverRole: MoverRole = 'player'; protected targetPosition = new Vec3(); protected targetGridType: GridType = GridType.None; protected lastPosition = new Vec3(); protected step = 0; private moveWait = false; private queue: Promise = Promise.resolve(); static callEach = false; get curGrid(): GridType { return GameManager.instance!.calculateGridType(this.node.position); } get nextGrid(): GridType { return GameManager.instance!.calculateNextGridType(this.node.position, this.direction); } get lastGrid(): GridType { return GameManager.instance!.calculateLastGridType(this.node.position, this.direction); } get isFront(): boolean { return this.direction === Direction.South || this.direction === Direction.East; } start() { this.setDirection(this.direction); } update(dt: number) { if (this.moveState !== MoveState.Moving) return; const pos = this.node.position; const next = new Vec3(); Vec3.moveTowards(next, pos, this.targetPosition, this.moveSpeed * dt); this.node.setPosition(next); this.onMoving(); if (Vec3.distance(next, this.targetPosition) < 0.01) { this.node.setPosition(this.targetPosition); this.moveState = MoveState.Idle; this.moveWait = false; this.onMoveToTarget(); } } setDirection(dir: Direction) { this.direction = dir; } protected onMoving() {} protected onMoveToTarget() {} protected onMoveNextSet(_isJump: boolean) {} protected onMoveFail(_isJump: boolean) {} protected playMoveAnim() {} private moveNextCheck(isJump: boolean, toFront: boolean): number { const gm = GameManager.instance!; const dir = toFront ? this.direction : addDirection(this.direction, 2); const targetTmp = gm.nextGridPosition(this.node.position, dir); const targetType = gm.calculateGridType(targetTmp); const table = getMoveCondition(gm.multMode); const nextG = toFront ? this.nextGrid : this.lastGrid; const v = lookupMove(table, this.moverRole, this.curGrid, nextG, isJump); if (v !== undefined) { if (v === 1) { if (gm.multMode) { const nextCell = gm.worldToCell(targetTmp); for (const n of ['PlayerA1', 'PlayerA2', 'PlayerA3', 'PlayerB1', 'PlayerB2', 'PlayerB3']) { const p = gm.findNodeByName(n); if (p) { const c = gm.worldToCell(p.worldPosition); if (c.x === nextCell.x && c.y === nextCell.y) return 0; } } if (nextG === GridType.Ride) { const ride = gm.getGameObject(targetTmp); const expect = this.node.name.replace('Player', 'Vehicle'); if (ride && ride.name !== expect) return 0; } } this.targetPosition.set(targetTmp); this.targetGridType = targetType; } return v; } return gm.multMode ? 0 : -1; } private enqueue(fn: () => Promise) { this.queue = this.queue.then(fn).catch((e) => console.error(e)); } private waitUntil(cond: () => boolean): Promise { return new Promise((resolve) => { const tick = () => { if (cond()) resolve(); else requestAnimationFrame(tick); }; tick(); }); } private async moveCoroutine(n: number, isJump: boolean) { await this.waitUntil(() => !this.moveWait); const toFront = n > 0; this.step = Math.abs(n); const gm = GameManager.instance!; while (this.step > 0 && gm.gameState === GameState.Run) { this.step--; const r = this.moveNextCheck(isJump, toFront); if (r === -1) { this.onMoveFail(isJump); return; } if (r === 1) { this.moveWait = true; this.lastPosition.set(this.node.position); this.moveState = MoveState.Moving; this.onMoveNextSet(isJump); await this.waitUntil(() => !this.moveWait); } else { this.playMoveAnim(); this.moveState = MoveState.Moving; } } } private async rotateCoroutine(n: number) { await this.waitUntil(() => !this.moveWait); this.moveWait = true; this.setDirection(addDirection(this.direction, n)); this.moveWait = false; this.onMoveToTarget(); } protected jsCallCheck(n: number): boolean { const gm = GameManager.instance!; const name = this.node.name; if (name === 'Player' || name === 'Vehicle') return gm.jsCallCheck(n); if (gm.multMode) return gm.jsCallCheckMultMode(n, name); return false; } callMove(n: number) { if (!this.jsCallCheck(n)) return; this.enqueue(() => this.moveCoroutine(n, false)); } callRotateLeft(n: number) { if (!this.jsCallCheck(n)) return; this.enqueue(() => this.rotateCoroutine(-n)); } callRotateRight(n: number) { if (!this.jsCallCheck(n)) return; this.enqueue(() => this.rotateCoroutine(n)); } callJump() { if (this.moverRole !== 'player') return; if (!this.jsCallCheck(1)) return; this.enqueue(() => this.moveCoroutine(1, true)); } }