import { _decorator, Vec3 } from 'cc'; import { Direction, GridType } from '../core/Define'; import { EventManager, EventType } from '../core/EventManager'; import { JsBridge } from '../bridge/JsBridge'; import { GameManager } from '../manager/GameManager'; import { scaledMoveSpeed, UNITY_VEHICLE_MOVE_SPEED } from '../core/GridConstants'; import { Movement } from '../gameplay/Movement'; import { PlayerController, ExternalDataList } from './PlayerController'; import { syncRidePositions } from './RideLink'; import { VisualAssets } from '../visual/VisualAssets'; const { ccclass } = _decorator; /** * 载具移动(对齐 Unity VehicleController) * - 仅能在空地移动(None↔None、Ride→None) * - 有骑手时每帧同步玩家;落格更新 Ride 注册 */ @ccclass('VehicleController') export class VehicleController extends Movement { private player: PlayerController | null = null; onLoad() { this.moverRole = 'vehicle'; this.moveSpeed = scaledMoveSpeed(UNITY_VEHICLE_MOVE_SPEED); this.moveState = 0; } /** 不调用 super.start(),避免 component.start 晚于 LevelInit 绑定后再次用 spawn 方向刷贴图 */ start() { this.syncCommittedCellFromPosition(); if (this.player) { this.syncFacingFromRider(); } this.setIcon(); } setPlayer(p: PlayerController | null) { this.player = p; if (!p) return; if (p.direction !== this.direction) { super.setDirection(p.direction); } this.setIcon(); } getPlayer(): PlayerController | null { return this.player; } setPosition(pos: Vec3) { this.node.setPosition(pos); } setName(_name: string) { /* 可挂 Label */ } override setDirection(dir: Direction) { super.setDirection(dir); this.setIcon(); if (Movement.callEach) return; Movement.callEach = true; this.player?.setDirection(dir); Movement.callEach = false; } private entityVisualOptions() { const gm = GameManager.instance; const levelTheme = gm?.getCurLevel()?.theme?.trim(); return { ...(gm?.getEntityVisualOptions() ?? {}), theme: levelTheme ?? gm?.uiStyle ?? 'default', }; } /** 载具四向贴图:有骑手时跟随骑手 direction,转向时 setIcon 直接换图 */ private iconDirection(): Direction { return this.player?.direction ?? this.direction; } setIcon() { const dir = this.iconDirection(); if (this.player && dir !== this.direction) { super.setDirection(dir); } VisualAssets.applyVehicleIconForDirection( this.node, dir, this.entityVisualOptions(), ); this.player?.syncRideMountVisual(); } /** 骑乘时逻辑朝向与骑手保持一致(Scratch 回传等) */ private syncFacingFromRider() { if (!this.player || this.player.direction === this.direction) return; super.setDirection(this.player.direction); } /** 主题 / 软重置后按当前逻辑朝向重刷贴图 */ refreshIcon() { this.syncFacingFromRider(); this.setIcon(); } override callJump() { /* 载具不可跳跃 */ } protected override syncRideAfterMoveStep() { if (this.player) syncRidePositions('vehicle', this.player, this); } protected onMoveNextSet(isJump: boolean) { if (Movement.callEach) return; Movement.callEach = true; if (this.player) { this.player.setTargetGridType(this.targetGridType); this.player.syncFromVehicle(this.targetGridType, isJump); } Movement.callEach = false; } protected onMoving() { if (this.player) syncRidePositions('vehicle', this.player, this); if (Movement.callEach) return; Movement.callEach = true; this.player?.onMoving(); Movement.callEach = false; } protected onMoveToTarget() { this.commitLandingCell(); const gm = GameManager.instance!; gm.removeObj(this.lastPosition); if (this.committedCell) { gm.addObjAtCell(this.committedCell, GridType.Ride, this.node); } else { gm.addObj(this.node.position, GridType.Ride, this.node); } if (!Movement.callEach && this.player) { Movement.callEach = true; this.player.setRideBaseFromVehicle(this.node.position); if (this.committedCell) this.player.shareCommittedCell(this.committedCell); this.player.syncMoveToTargetFromVehicle(); Movement.callEach = false; } if (this.step === 0) this.externalCall(); } protected onMoveFail(_isJump: boolean) { this.externalCall(); EventManager.dispatch(EventType.InputEnd, GameManager.instance!.gameState); } callVehicleInfo() { this.externalCall(); } externalCall() { const gm = GameManager.instance!; const list: ExternalDataList = { direction: this.direction, externalDatas: [] }; const sample = this.getGridSamplePosition(); const self = gm.worldToCell(sample); list.externalDatas.push({ position: { x: self.x, y: self.y, z: 0 }, gridType: this.curGrid, direction: 'self', }); for (let d = Direction.North; d <= Direction.West; d++) { const wp = gm.nextGridPosition(sample, d); const cell = gm.worldToCell(wp); list.externalDatas.push({ position: { x: cell.x, y: cell.y, z: 0 }, gridType: gm.calculateNextGridType(sample, d), direction: gm.getRelativePosition(this.direction, d), }); } JsBridge.call('processVehicleData', JSON.stringify(list)); } }