Files

45 lines
3.8 KiB
JavaScript

// Checked adapter: upstream snapshots remain byte-for-byte unchanged.
export function applyBrightClassroom(source) {
const start=source.indexOf('function draw(){');
const end=source.indexOf('function roundedRect(',start);
if(start<0||end<0||source.indexOf('function draw(){',start+1)>=0)throw Error('Bright Classroom: upstream draw seam changed');
const original=source.slice(start,end);
const tail=original.slice(original.indexOf(' if(previewOnly)return;updateTutorial(bad);'));
if(!tail.startsWith(' if(previewOnly)return;'))throw Error('Bright Classroom: UI writeback seam changed');
const draw=`function draw(){
if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight;if(!width||!height)return;
const dpr=window.devicePixelRatio||1;
if(canvas.width!==Math.round(width*dpr))canvas.width=Math.round(width*dpr);
if(canvas.height!==Math.round(height*dpr))canvas.height=Math.round(height*dpr);
ctx.setTransform(dpr,0,0,dpr,0,0);
brightScene=renderBrightBoard(ctx,level,path,width,height,{preview:previewOnly,...brightHintOptions(Date.now()),pixelRatio:dpr});
({polygons,centers,unit}=brightScene);channelViews=brightScene.channels;cellViews=brightScene.views;
const bad=violations(level,path);
${tail}`;
source=source.slice(0,start)+draw+source.slice(end);
const checkpointCue="sound.play(cell.clue!==undefined&&cell.clue===path.length?'correct':'move',0,path.length)";
if(!source.includes(checkpointCue))throw Error('Checkpoint audio seam changed');
source=source.replace(checkpointCue,"if(path.length<level.cells.length)sound.play(cell.clue!==undefined&&cell.clue===path.length?(cell.clue>1?'checkpoint':'correct'):'move',0,path.length)");
if(!source.includes("sound.play('complete',.23)"))throw Error('Complete audio timing seam changed');
source=source.replace("sound.play('complete',.23)","sound.play('complete',0)");
const hint='visitedPolygons:polygons.filter(p=>path.includes(p.id)).map(p=>p.p.map(v=>[...v]))';
if(!source.includes(hint))throw Error('Bright Classroom: hint seam changed');
source=source.replace(hint,`${hint},...hintGeometry(brightScene,path,h.route)`);
// Preview rendering must restore every reference used by the live hint layer.
source=source.replace('const saved={canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews};','const saved={canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews,brightScene};');
source=source.replace('({canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews}=saved)','({canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews,brightScene}=saved)');
source=`import {renderBrightBoard,hintGeometry,drawBarriers} from '../bright-classroom/board-renderer.mjs';\nlet brightScene=null,brightHintKey='',brightHintStarted=0;\n`+source;
source+=`
export function refreshBrightBoard(now){
const options=brightHintOptions(now);if(!brightScene?.animating&&!options.hintTarget&&!brightScene?.hintLayers)return;
const dpr=window.devicePixelRatio||1;ctx.setTransform(dpr,0,0,dpr,0,0);
brightScene=renderBrightBoard(ctx,level,path,canvas.clientWidth,canvas.clientHeight,{now,...options,pixelRatio:dpr});
}
function brightHintOptions(now){const h=previewOnly?null:segmentHints.view(level,solution,path),key=h?level.id+':'+h.start+':'+h.end:'';if(key!==brightHintKey){brightHintKey=key;brightHintStarted=now;}return {hintTarget:h?.route.at(-1)||null,hintElapsed:now-brightHintStarted};}
export function resetBrightHint(){brightHintKey='';if(brightScene)brightScene.hintLayers=null;}
export function brightOverlay(target){if(brightScene)drawBarriers(target,brightScene,path);}
export function brightAnimating(){return Boolean(brightScene?.animating);}
`;
return source;
}