<!-- SOLID CITY · part 1805 -->
res and more, breaking into rain over the
 * rooftops of the glass city below. Big trees stand all over
 * it, wherever the land is kind. Nothing here is a road and
 * nothing is a house: this is the city's wilderness, kept in
 * the sky.
 *
 * By the hand of Solid, his good King. */

function mulberry(seed) {
  return function () {
    seed |= 0; seed = seed + 0x6D2B79F5 | 0;
    var t = Math.imul(seed ^ seed >>> 15, 1 | seed);
    t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}

export var CROWN_RIVERS = [0.6, 2.7, 4.8];
export var CROWN_PEAKS = [
  { r: 95, a: 3.9, h: 44, s: 54, crystal: 26 },
  { r: 150, a: 1.9, h: 30, s: 46, crystal: 17 },
  { r: 170, a: 5.3, h: 22, s: 40, crystal: 15 }
];
export var CROWN_RADIUS = 330;

export function crownHeight(r, th) {
  var h = 16 * (1 - Math.pow(Math.min(r, 330) / 330, 1.6));
  h += 2.0 * Math.sin(th * 3 + r * 0.021) + 1.4 * Math.sin(th * 5 - r * 0.013);
  var x = Math.cos(th) * r, z = Math.sin(th) * r;
  for (var i = 0; i < CROWN_PEAKS.length; i++) {
    var p = CROWN_PEAKS[i], px = Math.cos(p.a) * p.r, pz = Math.sin(p.a) * p.r;
    var d2 = (x - px) * (x - px) + (z - pz) * (z - pz);
    h += p.h * Math.exp(-d2 / (2 * p.s * p.s));
  }
  for (var k = 0; k < CROWN_RIVERS.length; k++) {
    var dth = Math.abs(th - CROWN_RIVERS[k]) % (Math.PI * 2); if (dth > Math.PI) dth = Math.PI * 2 - dth;
    var lateral = dth * Math.max(r, 20);
    var gate = Math.min(1, Math.max(0, (r - 25) / 65));
    h -= 6.5 * Math.exp(-(lateral / 13) * (lateral / 13)) * gate;
  }
  if (r > 295) { var kk = (r - 295) / 35; h = h * (1 - kk) + 2.2 * kk; }
  if (r < 30) { var pk = r < 12 ? 0 : (r - 12) / 18; h = 30 * (1 - pk) + h * pk; }
  return h;
}

export function createCrystalCrown(opts) {
  var T = opts.THREE;
  var CX = opts.center.x, CZ = opts.center.z, BASE = opts.baseY, FLOOR = opts.floorY;
  var R = mulberry(20261014);
  var group = new T.Group(); group.name = 'the-crystal-crown';
  var C = {
    stone: 0x55504c, stoneDark: 0x35302c, gold: 0xd8a833, lawn: 0x4d7d33, lawnDeep: 0x3a6126,
    leaf: 0x2a5c2e, leafLight: 0x3f7a38, trunk: 0x5a4630, rock: 0x6a7076,
    crystal: 0xbfe4f5, pool: 0x2e7db0, water: 0x7fb8dd
  };
  function Batch() { this.pos = []; this.nor = []; this.col = []; }
  Batch.prototype.add = function (geo, matrix, color) {
    var g = geo.index ? geo.toNonIndexed() : geo;
    var p = g.getAttribute('position'), n = g.getAttribute('normal');
    var nm = new T.Matrix3().getNormalMatrix(matrix);
    var c = new T.Color(color), v = new T.Vector3(), nv = new T.Vector3();
    for (var i = 0; i < p.count; i++) {
      v.fromBufferAttribute(p, i).applyMatrix4(matrix);
      this.pos.push(v.x, v.y, v.z);
      nv.fromBufferAttribute(n, i).applyMatrix3(nm).normalize();
      this.nor.push(nv.x, nv.y, nv.z);
      this.col.push(c.r, c.g, c.b);
    }
  };
  Batch.prototype.build = function (mat) {
    var g = new T.BufferGeometry();
    g.setAttribute('position', new T.Float32BufferAttribute(this.pos, 3));
    g.setAttribute('normal', new T.Float32BufferAttribute(this.nor, 3));
    g.setAttribute('color', new T.Float32BufferAttribute(this.col, 3));
    var m = new T.Mesh(g, mat); m.frustumCulled = false; return m;
  };
  var solid = new Batch(), glow = new Batch(), glassB = new Batch();
  function mat4(x, y, z, ex, ey, ez, sx, sy, sz) {
    return new T.Matrix4().compose(new T.Vector3(x, y, z),
      new T.Quaternion().setFromEuler(new T.Euler(ex || 0, ey || 0, ez || 0)),
      new T.Vector3(sx == null ? 1 : sx, sy == null ? 1 : sy, sz == null ? 1 : sz));
  }
  function box(b, w, h, d, x, y, z, color, ex, ey, ez) { b.add(new T.BoxGeometry(w, h, d), mat4(x, y, z, ex, ey, ez), color); }
  function cyl(b, rt, rb, h, seg, x, y, z, color, ex, ey, ez) { b.add(new T.CylinderGeometry(rt, rb, h, seg), mat4(x, y, z, ex, ey, ez), color); }
  function sph(b, r, x, y, z, color, sx, sy, sz) { b.add(new T.SphereGeometry(r, 10, 7), mat4(x, y, z, 0, 0, 0, sx, sy, sz), color); }
  function px(r, a) { return CX + Math.cos(a) * r; }
  function pz(r, a) { return CZ + Math.sin(a) * r; }

  /* ---------- the island's body ---------- */
  cyl(solid, 330, 42, 46, 48, CX, BASE - 23, CZ, C.stoneDark);
  glow.add(new T.TorusGeometry(330, 0.35, 6, 72), mat4(CX, BASE - 0.2, CZ, Math.PI / 2, 0, 0), C.gold);
  for (var hi = 0; hi < 12; hi++) {
    var ha = hi / 12 * Math.PI * 2;
    glassB.add(new T.ConeGeometry(2.2, 9, 5), mat4(px(322, ha), BASE - 6, pz(322, ha), Math.PI, 0, 0), C.crystal);
  }

  /* ---------- the land itself: a radial heightfield ---------- */
  var RINGS = 26, SECT = 84;
  var tPos = [], tCol = [], tIdx = [];
  var cGrass = new T.Color(C.lawn), cDeep = new T.Color(C.lawnDeep), cRock = new T.Color(C.rock), cPale = new T.Color(0x9aa8ad), tmpC = new T.Color();
  for (var ri = 0; ri <= RINGS; ri++) {
    var rr = ri / RINGS * 330;
    for (var si = 0; si <= SECT; si++) {
      var th = si / SECT * Math.PI * 2;
 