F 门/三联动门读 window.__engineCalls 找 resource.addCoins/consumeIngredient 前缀,
但旧版只有 engine-caps(粒子/音频)经 recHook 记 __engineCalls,资源系统只记内部
_callCounts、不进 __engineCalls → calls=0、F 门 + 三联动门真接线判据永假。
修(additive,不破契约必填项):host 经 boot 上下文透传 recHook → main.js 喂
createGameCore → resource-system 完单真调 addCoins/合成真调 consumeIngredient 时
recHook('resource.addCoins'/'resource.consumeIngredient') 记进 __engineCalls。
headless/静态校验下 recHook 缺省 no-op、不抛错。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
118 lines
6.9 KiB
JavaScript
118 lines
6.9 KiB
JavaScript
/**
|
||
* src/main.js —— mini-肥鹅 入口(A3 entry · PhaserGameFactory 默认导出 · 平台预建装载胶水)
|
||
* owner:tier2 引擎线(M1 · scaffold platform 预建部分)
|
||
*
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
* 【这份是什么 · 工厂 + 实例(装载靶)】
|
||
* A3 源项目契约的 entry = src/main.js,默认导出实现 PhaserGameFactory 的函数(tier2/contracts/boot-phaser-host.d.ts)。
|
||
* build-phaser.mjs esbuild 打成 iife 后挂全局名 __Tier2GameBundle,host(boot-phaser-host.js)取它建 PhaserGameInstance。
|
||
* 本文件预建**装载胶水**(先天过 boot):
|
||
* - 工厂创建 game-core(三系统逻辑,纯逻辑写死)。
|
||
* - PhaserGameInstance.boot:接 host 注入的 boot 上下文,把核心逻辑接进 Phaser scene 树。
|
||
* - readState:透传 game-core._forensicsView().state()(A6 语义 state;latch 门读 phase)。
|
||
* - injectInput / destroy:归一化输入入口 + 卸载卫生。
|
||
* **表现层 scene(画棋盘 / 订单面板 / 点击命中坐标映射 / 动画)是 LLM 现写那约 56%,在 src/scenes/play-scene.js**——
|
||
* 本文件只负责把 scene 接进 Phaser 并把输入路由到 game-core 逻辑命令。
|
||
*
|
||
* 【受控随机派生】(确定性铁律)
|
||
* boot.seed 派生受控随机源(禁裸 Math.random,对齐 fixture-spec / api.d.ts RandomSource)。
|
||
* harness 同 seed 可确定性重放;driver 驱动可复现。
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
// Phaser 名字空间:iife 构建下经 external 'phaser' 解析为宿主预载的全局(build-phaser.mjs external 配置)。
|
||
// 本 import 在 esbuild iife + external:phaser 下不打进 bundle,运行时绑定宿主页面的 window.Phaser。
|
||
import Phaser from 'phaser';
|
||
import { createGameCore } from './game-core.js';
|
||
import { createPlayScene } from './scenes/play-scene.js';
|
||
import { createSeededRandom } from './util/seeded-random.js';
|
||
|
||
/**
|
||
* PhaserGameFactory:host 调它建 PhaserGameInstance(A4 装载契约)。
|
||
* esbuild iife globalName=__Tier2GameBundle;host 取 window.__Tier2GameBundle.default 或 .createGame。
|
||
* @param {Record<string, unknown>} [opts] 工厂选项(difficulty/variant 等;spike 暂不用)
|
||
* @returns {import('../../../contracts/boot-phaser-host.d.ts').PhaserGameInstance}
|
||
*/
|
||
export default function createGame(opts) {
|
||
// 游戏核心(三系统逻辑)+ 受控随机(boot 时按 seed 派生;此处先占位,boot 内据 seed 重建)。
|
||
/** @type {ReturnType<typeof createGameCore>|null} */
|
||
let core = null;
|
||
/** @type {any} 活跃的 play scene(表现层;boot 内由 Phaser scene 管理器建)。 */
|
||
let playScene = null;
|
||
/** @type {any} host 注入的 Phaser.Game 实例(real 通道;headless 为 null)。 */
|
||
let phaserGame = null;
|
||
/** @type {any} host 注入的受控能力面(粒子/音频/math;游戏经它做特效,不调裸 Phaser)。 */
|
||
let caps = null;
|
||
|
||
return {
|
||
/**
|
||
* 一次性启动(PhaserGameInstance.boot)。接 host 注入的 boot 上下文,建核心 + 接 Phaser scene。
|
||
* @param {import('../../../contracts/boot-phaser-host.d.ts').PhaserHostBootContext & { caps?: object }} ctx
|
||
*/
|
||
async boot(ctx) {
|
||
// 据 boot.seed 派生受控随机源(确定性;禁裸 Math.random)。
|
||
const random = createSeededRandom(ctx.seed >>> 0);
|
||
// 建游戏核心(三系统逻辑,平台预建写死)。recHook 由 host 经 boot 上下文 additive 透传:
|
||
// 资源系统完单/合成时经它把 'resource.addCoins'/'resource.consumeIngredient' 记进 window.__engineCalls,
|
||
// 供 F 门 / 富游戏三联动门证真接线(headless 下 ctx.recHook 缺省 → no-op)。
|
||
core = createGameCore({ random, recHook: ctx.recHook });
|
||
core.start(); // title → play(spike 直接进局;产线可加标题页)
|
||
|
||
// 受控能力面(host 经 boot 上下文 additive 透传 caps;headless 下为 no-op 能力面)。
|
||
caps = ctx.caps || null;
|
||
phaserGame = ctx.game || null;
|
||
|
||
// real 通道:把表现层 play scene 接进 Phaser scene 管理器。
|
||
// headless(ctx.game===null):不接 scene,逻辑核心已就绪,readState 可读(供 headless 逻辑校验)。
|
||
if (phaserGame && phaserGame.scene && typeof phaserGame.scene.add === 'function') {
|
||
// 建表现层 scene(LLM 现写域):注入核心逻辑 + 受控能力面 + 资产。
|
||
playScene = createPlayScene({
|
||
core, // 三系统逻辑命令(scene 点击命中后调 core.tapCell / fulfillOrder)
|
||
caps, // 受控特效面(粒子/音频/math)
|
||
assets: ctx.assets || {}, // 宿主预载资产(按 ref 键;缺=降级占位)
|
||
Phaser, // Phaser 名字空间(scene 画图用)
|
||
});
|
||
// 注册并启动 scene(Phaser 自掌帧、自渲;本 scene 的 update 推进 core 逻辑 + 重画 UI)。
|
||
phaserGame.scene.add('PlayScene', playScene, true);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 可观测状态(A6 语义 state;latch 门 / 富游戏三门读它)。透传 game-core 导出视图,只读。
|
||
* @returns {import('../../../contracts/boot-phaser-host.d.ts').GameForensicsState}
|
||
*/
|
||
readState() {
|
||
if (!core) return { phase: 'booting' };
|
||
// 透传 _forensicsView().state()(phase 不变量 + per-品类语义字段)。
|
||
return core._forensicsView().state();
|
||
},
|
||
|
||
/**
|
||
* 归一化输入入口(harness 驱动;与 scene 内 Phaser input 订阅二者择一,harness 优先走这里保确定性)。
|
||
* tap:把屏幕坐标交给 play scene 做点击命中 → 命中棋盘格 / 订单 → 调 core 逻辑命令。
|
||
* @param {import('../../../contracts/boot-phaser-host.d.ts').PhaserInputEvent} ev
|
||
*/
|
||
injectInput(ev) {
|
||
if (!playScene || typeof playScene.handleNormalizedInput !== 'function') return;
|
||
// 路由给表现层做命中测试(命中映射在 scene,逻辑命令在 core)。
|
||
playScene.handleNormalizedInput(ev);
|
||
},
|
||
|
||
/** 卸载(幂等;session 切换 / 多游戏复位;释放 scene)。 */
|
||
destroy() {
|
||
try {
|
||
if (phaserGame && phaserGame.scene && playScene && typeof phaserGame.scene.remove === 'function') {
|
||
phaserGame.scene.remove('PlayScene');
|
||
}
|
||
} catch (e) { /* 幂等吞错 */ }
|
||
playScene = null;
|
||
core = null;
|
||
},
|
||
};
|
||
}
|
||
|
||
// 具名导出(host 取工厂的兼容入口:window.__Tier2GameBundle.createGame 或 .default)。
|
||
export { createGame };
|