<!-- SAGA-STAGING · part 55 -->
tered (Hawaiian lava rock field)
  for(var j=0; j<20; j++){
    var rock = new THREE.Mesh(new THREE.DodecahedronGeometry(15+Math.random()*25, 0), lavaRock);
    var ra = Math.random()*Math.PI*2, rd = 700+Math.random()*400;
    rock.position.set(vx + Math.cos(ra)*rd, 15, vz + Math.sin(ra)*rd);
    rock.rotation.set(Math.random()*3, Math.random()*3, Math.random()*3);
    rock.castShadow = true;
    scene.add(rock);
  }
  if(typeof trackNewThing === 'function') trackNewThing('volcano', 'Volcano at LAT 100.0, LON 0.0 — Hawaiian lava rock');
})();


var sun = new THREE.DirectionalLight(0xffffff, 1.2);
sun.position.set(100, 200, 100);
scene.add(sun);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));

var groundY = 0;
var ground = new THREE.Mesh(
  new THREE.PlaneGeometry(20000, 20000),
  new THREE.MeshStandardMaterial({color: 0x000000, roughness: 0.9})
);
ground.rotation.x = -Math.PI/2;
ground.position.y = 0.13;
scene.add(ground);

var grid = new THREE.GridHelper(20000, 100, 0xff0000, 0xff0000);
grid.position.y = 0.14;
grid.material.opacity = 0.3;
grid.material.transparent = true;
scene.add(grid);

/* ===== MATERIALS ===== */
var goldMat = new THREE.MeshStandardMaterial({color: 0xffd700, emissive: 0xffd700, emissiveIntensity: 0.5, roughness: 0.3, metalness: 0.7});
var roadMat = new THREE.MeshStandardMaterial({color: 0x2a2a2a, roughness: 0.8});

/* ===== CAMERA ===== */
var camYaw = 0, camPitch = 0.5, camDist = 20;
var kingPos = new THREE.Vector3(0, 0, 0);
/* Load King's saved spawn — from our story */
try{
  var _ss = JSON.parse(localStorage.getItem('saga-staging-spawn'));
  if(_ss && isFinite(_ss.x) && isFinite(_ss.z)){
    kingPos.set(_ss.x, 0, _ss.z);
  }
}catch(e){ console.error(e); }
var playerBody = null;
/* Figure-8 head camera — smooth, attached to head, flows like games do */
var _camX = 0, _camY = 0, _camZ = 0, _camInit = false, _figT = 0;
function updateCamera(){
  if(window._freecam) return;
  /* Head position — camera connects here */
  var hx = kingPos.x, hy = kingPos.y + 4, hz = kingPos.z;
  /* Base orbit position */
  var bx = hx + Math.cos(camYaw) * Math.cos(camPitch) * camDist;
  var by = hy + Math.sin(camPitch) * camDist - 2;
  var bz = hz + Math.sin(camYaw) * Math.cos(camPitch) * camDist;
  /* Sway removed - was glitching. Stable follow. */
  /* Direct — camera goes where you point, no fighting */
  camera.position.set(bx, by, bz);
  camera.lookAt(hx, hy, hz);
}
/* ===== ONE LIGHTNING — v82, King's law 2026-10-05 ===== */
/* Twin lightnings are forbidden. One process (camera aim), one code. */
/* The right stick alone rules the camera. The canvas does not touch it. */

/* ===== 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(siz