Complete Cocos Creator port with level bundles, themes, and tooling.

Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-16 15:30:58 +08:00
parent cba5105908
commit d393302388
6248 changed files with 17322729 additions and 11036 deletions

View File

@@ -0,0 +1,157 @@
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);
}