Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
5.6 KiB
TypeScript
158 lines
5.6 KiB
TypeScript
import { Vec3 } from 'cc';
|
||
import { GridType } from '../core/Define';
|
||
import { GameManager } from '../manager/GameManager';
|
||
import { forEachLevelEntityNode } from '../level/TileLayout';
|
||
import { entityWorldPositionForRole, worldToMoverCellForRole } from '../level/EntitySpawnPlacement';
|
||
import { PlayerController } from './PlayerController';
|
||
import { VehicleController } from './VehicleController';
|
||
|
||
/** 按逻辑格查找载具(gridTypes + 当前 committedCell) */
|
||
export function findVehicleAtCell(cell: Vec3): VehicleController | null {
|
||
const gm = GameManager.instance;
|
||
if (!gm) return null;
|
||
const fromGrid = gm.getGameObjectAtCell(cell)?.getComponent(VehicleController);
|
||
if (fromGrid) return fromGrid;
|
||
const level = gm.curLevel;
|
||
if (!level) return null;
|
||
let found: VehicleController | null = null;
|
||
forEachLevelEntityNode(level, (ch) => {
|
||
if (found || !ch.name.includes('Vehicle')) return;
|
||
const vc = ch.getComponent(VehicleController);
|
||
if (!vc || !vehicleMatchesCell(vc, cell)) return;
|
||
found = vc;
|
||
});
|
||
return found;
|
||
}
|
||
|
||
/** 双向绑定玩家与载具,并确保 Ride 格注册 */
|
||
export function linkRidePair(player: PlayerController, vehicle: VehicleController, cell: Vec3) {
|
||
const gm = GameManager.instance;
|
||
if (!gm) return;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
const deck = entityWorldPositionForRole(cell, config ?? undefined, theme, 'vehicle');
|
||
player.setRideVehicle(vehicle);
|
||
vehicle.setPlayer(player);
|
||
vehicle.setDirection(player.direction);
|
||
player.shareCommittedCell(cell);
|
||
vehicle.shareCommittedCell(cell);
|
||
vehicle.node.setPosition(deck);
|
||
gm.addObjAtCell(cell, GridType.Ride, vehicle.node);
|
||
player.setRideBaseFromVehicle(deck);
|
||
}
|
||
|
||
function playerLogicCell(player: PlayerController): Vec3 {
|
||
const committed = player.getCommittedCell();
|
||
if (committed) return committed.clone();
|
||
const gm = GameManager.instance!;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
return worldToMoverCellForRole(
|
||
player.node.position,
|
||
config ?? undefined,
|
||
theme,
|
||
'player',
|
||
);
|
||
}
|
||
|
||
function vehicleLogicCell(vehicle: VehicleController): Vec3 | null {
|
||
const committed = vehicle.getCommittedCell();
|
||
if (committed) return committed.clone();
|
||
const spawn = vehicle.getSpawnCell() ?? vehicle.getCommittedCell();
|
||
if (spawn) return spawn.clone();
|
||
const gm = GameManager.instance;
|
||
if (!gm) return null;
|
||
const config = gm.getCurLevel();
|
||
const theme = config?.theme ?? gm.uiStyle;
|
||
return worldToMoverCellForRole(vehicle.node.position, config ?? undefined, theme, 'vehicle');
|
||
}
|
||
|
||
function vehicleMatchesCell(vehicle: VehicleController, cell: Vec3): boolean {
|
||
const vCell = vehicleLogicCell(vehicle);
|
||
return !!vCell && vCell.x === cell.x && vCell.y === cell.y;
|
||
}
|
||
|
||
/** 玩家与载具占同一逻辑格时绑定(不依赖 Ride 格是否已注册) */
|
||
export function tryLinkPlayerVehicle(player: PlayerController): boolean {
|
||
const gm = GameManager.instance;
|
||
const level = gm?.curLevel;
|
||
if (!level) return false;
|
||
|
||
const pCell = playerLogicCell(player);
|
||
if (!pCell) return false;
|
||
|
||
const existing = player.getRideVehicle();
|
||
if (existing) {
|
||
const vCell = vehicleLogicCell(existing);
|
||
if (!vCell || vCell.x !== pCell.x || vCell.y !== pCell.y) {
|
||
existing.setPlayer(null);
|
||
player.setRideVehicle(null);
|
||
return false;
|
||
}
|
||
linkRidePair(player, existing, pCell);
|
||
return true;
|
||
}
|
||
|
||
let linked = false;
|
||
forEachLevelEntityNode(level, (ch) => {
|
||
if (linked || !ch.name.includes('Vehicle')) return;
|
||
const vc = ch.getComponent(VehicleController);
|
||
if (!vc || !vehicleMatchesCell(vc, pCell)) return;
|
||
linkRidePair(player, vc, pCell);
|
||
linked = true;
|
||
});
|
||
if (linked) return true;
|
||
|
||
const rideVc = findVehicleAtCell(pCell);
|
||
if (rideVc) {
|
||
linkRidePair(player, rideVc, pCell);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/** 载具侧:同格玩家自动设为骑手 */
|
||
export function tryLinkVehicleRider(vehicle: VehicleController): boolean {
|
||
const gm = GameManager.instance;
|
||
const level = gm?.curLevel;
|
||
if (!level) return false;
|
||
const vCell = vehicleLogicCell(vehicle);
|
||
if (!vCell) return false;
|
||
|
||
const rider = vehicle.getPlayer();
|
||
if (rider) {
|
||
const pCell = playerLogicCell(rider);
|
||
if (pCell && pCell.x === vCell.x && pCell.y === vCell.y) {
|
||
linkRidePair(rider, vehicle, vCell);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
let matched = false;
|
||
forEachLevelEntityNode(level, (ch) => {
|
||
if (matched || !ch.name.includes('Player')) return;
|
||
const pc = ch.getComponent(PlayerController);
|
||
if (!pc) return;
|
||
const pCell = playerLogicCell(pc);
|
||
if (!pCell || pCell.x !== vCell.x || pCell.y !== vCell.y) return;
|
||
linkRidePair(pc, vehicle, vCell);
|
||
matched = true;
|
||
});
|
||
return matched;
|
||
}
|
||
|
||
/** 移动中强制位置联动(Unity OnMoving 语义) */
|
||
export function syncRidePositions(driver: 'player' | 'vehicle', player: PlayerController, vehicle: VehicleController) {
|
||
if (driver === 'player') {
|
||
const p = player.node.position;
|
||
vehicle.node.setPosition(p.x, p.y, p.z);
|
||
} else {
|
||
player.setRideBaseFromVehicle(vehicle.node.position);
|
||
}
|
||
}
|
||
|
||
export function resolveRideBind(player: PlayerController, cell: Vec3 | null): VehicleController | null {
|
||
if (!cell) return null;
|
||
return findVehicleAtCell(cell);
|
||
}
|