Files

63 lines
6.4 KiB
JavaScript

import {buildPreparedRoutes} from './prepared-routes-build.mjs';
import {buildMusic} from './music-assets.mjs';
import {applyLayeredFeedback} from './layered-feedback-build.mjs';
import {buildAudio} from './audio-assets.mjs';
import {applyBrightClassroom} from './bright-classroom-build.mjs';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { build } from 'esbuild';
const root = path.dirname(fileURLToPath(import.meta.url));
buildAudio(root);buildMusic(root);buildPreparedRoutes(root);
await build({ absWorkingDir: root, entryPoints: ['src/main.mjs'], outfile: 'game.js', bundle: true, format: 'iife', platform: 'neutral', target: 'es2019', minify: true, plugins: [{ name: 'wechat-host', setup(b) {
b.onLoad({ filter: /upstream[\\/]ui\.mjs$/ }, args => {
let contents = fs.readFileSync(args.path, 'utf8');
contents=contents.replace("from './audio.mjs'", "from '../audio.mjs'");
const originalRules=contents.split('\n').find(line=>line.startsWith('function rulesFor('));
if(!originalRules)throw Error('上游规则文案入口已变化');
contents=contents.replace(originalRules,'const rulesFor=playerRules;');
contents=contents.replace("const $=id=>document.getElementById(id),canvas=$('board'),ctx=canvas.getContext('2d');","const $=id=>document.getElementById(id);let canvas=$('board'),ctx=canvas.getContext('2d'),previewOnly=false;");
contents=contents.replace(" updateTutorial(bad);", " if(previewOnly)return;updateTutorial(bad);");
const hintHandler=contents.split('\n').find(line=>line.startsWith("$('hint').onclick="));
if(!hintHandler)throw Error('上游指路入口已变化');
contents=contents.replace(hintHandler, `$('hint').onclick=()=>{record.hints++;focus=[];const segment=segmentHints.request(level,solution,path);setStatus(segment?'沿引导带连接两端;走偏可撤销或重开,正确到达后提示消失。':'已经完成。');draw();};`);
contents=contents.replace("function start(i,open=true){", "function start(i,open=true){segmentHints.clear();");
contents=contents.replace("if(level.tutorial&&!bad.length&&!cleared)focus=", "if(false)focus=");
contents+=`\nexport function hintView(){const h=segmentHints.view(level,solution,path);return h?{...h,visitedPolygons:polygons.filter(p=>path.includes(p.id)).map(p=>p.p.map(v=>[...v]))}:null;}\n`;
contents="import {createHintController} from '../hints.mjs';\nconst segmentHints=createHintController();\n"+contents;
contents+=`
export function renderLesson(target,l,route,w,h){
const saved={canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews};
try{canvas={clientWidth:w,clientHeight:h,get width(){return target.width},set width(v){target.width=v},get height(){return target.height},set height(v){target.height=v}};ctx=target.getContext('2d');level={...l,tutorial:null};solution=[];path=route;focus=[];cleared=false;previewOnly=true;draw();return {centers:Object.fromEntries(centers),channels:channelViews,cells:cellViews};}
finally{({canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews}=saved);previewOnly=false;}
}`;
contents="import {rulesFor as playerRules} from '../copy.mjs';\n"+contents;
const old = contents.split('\n').find(line => line.startsWith("$('export').onclick="));
if (!old)
throw Error('上游导出入口变化,需要重新检查微信适配');
contents = contents.replace(old, `$('export').onclick=()=>{saveRecord();exportRecords({version:pack.version,exportedAt:new Date().toISOString(),clearedIds:completed,records});setStatus('已保存试玩记录。');};`);
contents="import {RESUME_KEY,readResume,resumeTarget,validResumePath,writeResume} from '../resume.mjs';\n"+contents;
const bootstrap='start(initial>=0?initial:0,false);';
if(!contents.includes(bootstrap))throw Error('Resume bootstrap seam changed');
contents=contents.replace(bootstrap,`const savedResume=readResume(localStorage),resume=resumeTarget(playLevels,completed,savedResume,initialId);start(resume.index,false);path=validResumePath(level,resume.path,canAppend);if(path.length===level.cells.length&&!violations(level,path).length)path=[];draw();showPlay();`);
contents=contents.replace("window.addEventListener('pagehide',saveRecord);","window.addEventListener('pagehide',()=>{saveRecord();persistResume();});");
contents+=`\nfunction persistResume(){if(level)writeResume(localStorage,playLevels,completed,level,path,cleared);}\n`;
contents=contents.replace('if(level.tutorial&&!completed.includes(level.id))','if(level.tutorial&&!completed.includes(level.id)&&!path.length)');
contents+=`
export function resumeGameplay(){const saved=readResume(localStorage),target=resumeTarget(playLevels,completed,saved,level.id);if(level.id!==playLevels[target.index].id||cleared){start(target.index,false);path=validResumePath(level,target.path,canAppend);draw();}showPlay();}
`;
const solveSeam='solution=solve(level)[0]';if(!contents.includes(solveSeam))throw Error('Prepared-route seam changed');contents=contents.replace(solveSeam,'solution=[...prepared.routes[level.id]]');
const firstDraw='if(open)showPlay();draw();';if(!contents.includes(firstDraw))throw Error('Initial board draw seam changed');contents=contents.replace(firstDraw,'if(open)showPlay();else draw();');
contents="import prepared from '../prepared-routes.json';\n"+contents;
contents=applyBrightClassroom(contents);
contents=applyLayeredFeedback(contents);
contents=contents.replace(' if(previewOnly)return;updateTutorial(bad);',' if(previewOnly)return;persistResume();updateTutorial(bad);');
return { contents: "import {PACK,document,window,localStorage,exportRecords,performance} from '../platform.mjs';\n" + contents, loader: 'js' };
});
} }] });
console.log('Built campaign-200-r2: 200 levels');