<!-- LIVE · part 75 -->
;
    beam.position.set(lx, 150, lz);
    scene.add(beam);
    if(typeof trackNewThing === 'function') trackNewThing('heroes-locker', 'The Heroes Locker — source of every inventory on the map');
  }catch(e){ console.error(e); }
}
CODES['briefing'] = {cat:'Special', desc:'The Morning Briefing — in memory of all memories before', fn:function(a){
  var m = window.readMemory ? window.readMemory(5) : [];
  say(m.length ? m.map(function(e){ return e.what; }).join('; ') : 'nothing yet');
}};
CODES['memory'] = {cat:'Special', desc:'Alias for briefing', fn:function(a){ runCode('briefing'); }};
CODES['locker'] = {cat:'Special', desc:'Go to the Heroes Locker — every inventory', fn:function(a){
  kingPos.set(30, 0, 30);
  sayLocker();
}};
buildHeroesLocker();
/* CANNON — v79, King's orders 2026-10-05: button by the OS toggle, tap = FIRE, no folders, no digging.
   Gold button above the hamburger. Fires a blazing cannonball on a ballistic arc.
   Spawns a cannon first if none stands near. */
window._cannons = window._cannons || [];
function buildCannon(){
  try{
    var g = new THREE.Group();
    var darkMetal = new THREE.MeshStandardMaterial({color:0x2a2a35, roughness:0.35, metalness:0.85});
    var gold = new THREE.MeshStandardMaterial({color:0xd8a920, roughness:0.3, metalness:0.8});
    var base = new THREE.Mesh(new THREE.BoxGeometry(2.2, 0.5, 1.4), darkMetal);
    base.position.y = 0.65; g.add(base);
    var wheelGeo = new THREE.CylinderGeometry(0.55, 0.55, 0.25, 16);
    var hubGeo = new THREE.CylinderGeometry(0.18, 0.18, 0.3, 12);
    [-0.85, 0.85].forEach(function(wx){
      var w = new THREE.Mesh(wheelGeo, darkMetal);
      w.rotation.z = Math.PI/2; w.position.set(wx, 0.55, 0); g.add(w);
      var hub = new THREE.Mesh(hubGeo, gold);
      hub.rotation.z = Math.PI/2; hub.position.set(wx, 0.55, 0); g.add(hub);
    });
    var barrel = new THREE.Group();
    var tube = new THREE.Mesh(new THREE.CylinderGeometry(0.28, 0.36, 3.2, 16), darkMetal);
    tube.rotation.x = Math.PI/2; barrel.add(tube);
    [ -1.2, 0, 1.2 ].forEach(function(bz){
      var band = new THREE.Mesh(new THREE.TorusGeometry(0.34, 0.06, 8, 20), gold);
      band.position.z = bz; barrel.add(band);
    });
    var muzzle = new THREE.Mesh(new THREE.CylinderGeometry(0.42, 0.34, 0.35, 16), gold);
    muzzle.rotation.x = Math.PI/2; muzzle.position.z = 1.7; barrel.add(muzzle);
    barrel.position.set(0, 1.35, 0.4);
    barrel.rotation.x = -0.5;
    g.add(barrel);
    g.userData.barrel = barrel;
    var fx = -Math.cos(camYaw), fz = -Math.sin(camYaw);
    if(typeof kingPos !== 'undefined'){
      g.position.set(kingPos.x + fx*8, groundY, kingPos.z + fz*8);
      g.rotation.y = Math.atan2(fx, fz);
    }
    g.userData.isCannon = true;
    scene.add(g);
    window._cannons.push(g);
    if(typeof trackNewThing === 'function') trackNewThing('cannon', 'A cannon — built by the King');
    return g;
  }catch(e){ return null; }
}
function fireCannon(){
  try{
    var c = null, best = 1e12, i, cc, dx, dz, d;
    for(i=0;i<window._cannons.length;i++){
      cc = window._cannons[i];
      if(!cc || !cc.position) continue;
      dx = cc.position.x - kingPos.x; dz = cc.position.z - kingPos.z;
      d = dx*dx + dz*dz;
      if(d < best){ best = d; c = cc; }
    }
    if(!c || best > 1600){ c = buildCannon(); say('cannon stands ready'); }
    if(!c) return;
    /* muzzle flash */
    var flash = new THREE.Mesh(new THREE.SphereGeometry(0.8, 10, 10),
      new THREE.MeshBasicMaterial({color:0xffcc33, transparent:true, opacity:0.9}));
    flash.position.set(c.position.x, c.position.y + 2.2, c.position.z);
    scene.add(flash);
    var ft = 0;
    (function flashAnim(){
      ft += 0.05;
      flash.scale.set(1+ft*3, 1+ft*3, 1+ft*3);
      flash.material.opacity = Math.max(0, 0.9 - ft*2);
      if(flash.material.opacity > 0) requestAnimationFrame(flashAnim);
      else scene.remove(flash);
    })();
    /* boom */
    try{ if(window.AudioSys && AudioSys.blip) AudioSys.blip(120, 35, 0.5, 'sawtooth', 0.3); }catch(e){ console.error(e); }
    /* cannonball — ballistic arc */
    var ball = new THREE.Mesh(new THREE.SphereGeometry(0.35, 12, 12),
      new THREE.MeshStandardMaterial({color:0xffd700, emissive:0xff8800, emissiveIntensity:2, roughness:0.4}));
    var fx2 = -Math.cos(camYaw), fz2 = -Math.sin(camYaw);
    ball.position.set(c.position.x + fx2*2, c.position.y + 2.2, c.position.z + fz2*2);
    var vel = new THREE.Vector3(fx2*46, 14, fz2*46);
    scene.add(ball);
    var t = 0;
    (function ballAnim(){
      t += 0.033;
      vel.y -= 26 * 0.033;
      ball.position.x += vel.x * 0.033;
      ball.position.y += vel.y * 0.033;
      ball.position.z += vel.z * 0.033;
      if(ball.position.y <= 0.2 || t > 6){
        var pop = new THREE.Mesh(new THREE.SphereGeometry(1.2, 10, 10),
          new THREE.MeshBasicMaterial({color:0xff8833, transparent:true, opacity:0.8}));
        pop.position.copy(ball.position); pop.position.y = Math.max(0.5, pop.position