<!-- CODERS100 · part 21 -->
e:true});
canvas.addEventListener('touchend', function(){ touching=false; }, {passive:true});

/* ===== STATUS ===== */
var statusEl = document.getElementById('status'), sT=null;
function say(m){
  statusEl.textContent = m; statusEl.classList.add('show');
  if(sT) clearTimeout(sT);
  sT = setTimeout(function(){ statusEl.classList.remove('show'); }, 3000);
}

/* ===== THE OS ===== */
var osEl = document.getElementById('os');
var osToggle = document.getElementById('os-toggle');
var osOpen = false;

osToggle.onclick = function(){ osOpen=!osOpen; osEl.classList.toggle('open', osOpen);; };
function osButton(emoji, title, fn){
  var b = document.createElement('button');
  b.innerHTML = emoji; b.title = title;
  b.onclick = function(){ fn(); osOpen=false; osEl.classList.remove('open');; };
  osEl.appendChild(b);
  return b;
}

/* ===== DISTRICTS ===== */
var districts = {};
function registerDistrict(n, l, fn){ districts[n] = {label:l, build:fn, loaded:false}; }
function activateDistrict(n){
  var d = districts[n];
  if(!d) return say('unknown');
  if(d.loaded) return say(d.label + ' already stands');
  try { d.build(); d.loaded=true; say(d.label + ' rises'); }
  catch(e){ say('stumbled'); }
}
var dOrder=[], dIdx=0;
function nextDistrict(){
  for(var i=0;i<dOrder.length;i++){
    var n=dOrder[(dIdx+i)%dOrder.length];
    if(districts[n] && !districts[n].loaded){
      activateDistrict(n); dIdx=(dIdx+i+1)%dOrder.length; return;
    }
  }
  say('all districts stand');
}

/* ================================================================
   THE CODES — organized by category, proper
   ================================================================ */

/* ---- SPECIAL: the 12 ---- */
var lastBam = null;

function doBam(args){
  var shape = args[0] || 'box';
  var size = parseFloat(args[1]) || 10;
  var count = 1, row = 0;

  /* bam box 10 x5 — batch */
  for(var i=0;i<args.length;i++){
    if(args[i][0]==='x') count = parseInt(args[i].slice(1)) || 1;
    if(args[i]==='line') row = parseInt(args[i+1]) || 0;
  }

  lastBam = {shape:shape, size:size, count:count, row:row};

  for(var c=0;c<count;c++){
    var mesh;
    if(shape==='box') mesh = new THREE.Mesh(new THREE.BoxGeometry(size,size,size), goldMat);
    else if(shape==='wall'){
      var len = row || 5;
      mesh = new THREE.Mesh(new THREE.BoxGeometry(size*len, size, 1), goldMat);
    }
    else mesh = new THREE.Mesh(new THREE.BoxGeometry(size,size,size), goldMat);

    var ox = row ? c * size * 1.2 : c * size * 1.5;
    mesh.position.set(kingPos.x + ox, groundY + size/2, kingPos.z);
    window.applyMinimum(mesh);
    scene.add(mesh);
  }
  say(shape + ' stands' + (count>1 ? ' x'+count : ''));
}


/* New body — spawns a fresh body the king can wear */
function spawnNewBody(){
  var g = new THREE.Group();
  var gold = new THREE.MeshStandardMaterial({color: 0xd8a920, roughness: 0.4, metalness: 0.7});
  var dark = new THREE.MeshStandardMaterial({color: 0x1a1a1a, roughness: 0.6});
  /* Head */
  var head = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), gold);
  head.position.y = 2.2;
  g.add(head);
  /* Torso */
  var torso = new THREE.Mesh(new THREE.BoxGeometry(0.8, 1.0, 0.4), dark);
  torso.position.y = 1.4;
  g.add(torso);
  /* Arms */
  var armL = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.9, 0.25), gold);
  armL.position.set(-0.55, 1.4, 0);
  g.add(armL);
  var armR = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.9, 0.25), gold);
  armR.position.set(0.55, 1.4, 0);
  g.add(armR);
  /* Legs */
  var legL = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.9, 0.3), dark);
  legL.position.set(-0.2, 0.45, 0);
  g.add(legL);
  var legR = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.9, 0.3), dark);
  legR.position.set(0.2, 0.45, 0);
  g.add(legR);
  /* Place at king position and track as player body */
  g.position.set(kingPos.x, groundY, kingPos.z);
  g.userData.legL = legL;
  g.userData.legR = legR;
  g.userData.armL = armL;
  g.userData.armR = armR;
  scene.add(g);
  playerBody = g;
  say('new body stands beside you');
}


/* FUTURISTIC SOUND SYSTEM — Web Audio API, synthesized, no files, short+fast */
var AudioSys = {
  ctx: null,
  enabled: true,
  init: function(){
    if(!this.ctx){
      try { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); }
      catch(e){ this.enabled = false; }
    }
    if(this.ctx && this.ctx.state === 'suspended') this.ctx.resume();
  },
  /* Core: play a futuristic blip — freq sweep, short envelope */
  blip: function(f0, f1, dur, type, vol){
    if(!this.enabled) return;
    this.init();
    if(!this.ctx) return;
    var t = this.ctx.currentTime;
    var o = this.ctx.createOscillator();
    var g = this.ctx.createGain();
    o.type = type || 'sine';
    o.frequency.setValueAtTime(f0, t);
    o.frequency.exponentialRampToValueAtTime(Math.max(f1, 1), t + dur);
    g.gain.setValueAtTime(vol || 0.15, t);
    g.gain.exponentialRampToValueAtTime(0.001, t + dur);
    o.connect(g); g.conne