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

@@ -1,59 +1,75 @@
import { _decorator, Component, Vec3 } from 'cc';
import { GameManager } from '../manager/GameManager';
import { forEachLevelEntityNode } from '../level/TileLayout';
import { PlayerController } from './PlayerController';
const { ccclass } = _decorator;
/**
* 可拾取物(对齐 Unity PropController OnTriggerEnter2D
* 玩家进入道具格且靠近时拾取(由 PlayerController 判定时机)
*/
@ccclass('PropController')
export class PropController extends Component {
private collected = false;
private spawnCell: Vec3 | null = null;
setSpawnCell(cell: Vec3) {
this.spawnCell = cell.clone();
}
getSpawnCell(): Vec3 | null {
return this.spawnCell ? this.spawnCell.clone() : null;
}
matchesCell(cell: Vec3): boolean {
const propCell = this.getLogicCell();
return propCell.x === cell.x && propCell.y === cell.y;
}
private getLogicCell(): Vec3 {
if (this.spawnCell) return this.spawnCell;
const gm = GameManager.instance!;
return gm.worldToCell(this.node.position);
}
/** 玩家落格后尝试拾取(由 PlayerController 调用) */
static tryCollectAtCell(cell: Vec3, player: PlayerController) {
const gm = GameManager.instance;
const level = gm?.curLevel;
if (!level) return;
forEachLevelEntityNode(level, (ch) => {
const prop = ch.getComponent(PropController);
if (!prop || prop.collected || !prop.matchesCell(cell)) return;
prop.collect(player);
});
}
update() {
if (this.collected || !GameManager.instance) return;
const gm = GameManager.instance;
const players = gm.curLevel?.children.filter((c) => c.name.includes('Player')) ?? [];
const propCell = gm.worldToCell(this.node.worldPosition);
for (const p of players) {
const pc = p.getComponent(PlayerController);
if (!pc) continue;
const pcCell = gm.worldToCell(p.worldPosition);
if (pcCell.x !== propCell.x || pcCell.y !== propCell.y) continue;
this.onCollected(pc);
break;
}
const player = GameManager.instance.findNodeByName('Player');
const pc = player?.getComponent(PlayerController);
if (!pc?.canCollectProp(this)) return;
this.collect(pc);
}
collect(player: PlayerController) {
if (this.collected) return;
this.onCollected(player);
}
private onCollected(player: PlayerController) {
if (this.collected) return;
this.collected = true;
this.node.active = false;
const gm = GameManager.instance!;
player.addCoins();
gm.removeProp(this.node.worldPosition);
const remaining = (gm.curLevel?.children.filter((c) => c.name.includes('Prop') && c !== this.node).length ?? 0);
const propCell = this.getLogicCell();
gm.removePropAtCell(propCell);
this.node.removeFromParent();
this.node.destroy();
if (remaining === 0) {
if (gm.multMode) this.resolveMultWin(gm);
else player.externalCallResult(true);
}
}
private resolveMultWin(gm: GameManager) {
const sum = (names: string[]) =>
names.reduce((t, n) => t + (gm.findNodeByName(n)?.getComponent(PlayerController)?.coins ?? 0), 0);
const totalA = sum(['PlayerA1', 'PlayerA2', 'PlayerA3']);
const totalB = sum(['PlayerB1', 'PlayerB2', 'PlayerB3']);
const winA = totalA > totalB || (totalA === totalB && gm.stepA1Num + gm.stepA2Num + gm.stepA3Num <
gm.stepB1Num + gm.stepB2Num + gm.stepB3Num);
const set = (names: string[], win: boolean) => {
for (const n of names) gm.findNodeByName(n)?.getComponent(PlayerController)?.externalCallResult(win);
};
if (winA) {
set(['PlayerA1', 'PlayerA2', 'PlayerA3'], true);
set(['PlayerB1', 'PlayerB2', 'PlayerB3'], false);
} else {
set(['PlayerA1', 'PlayerA2', 'PlayerA3'], false);
set(['PlayerB1', 'PlayerB2', 'PlayerB3'], true);
if (gm.allPropsCollected()) {
player.externalCallResult(true);
}
}
}