File size: 75,976 Bytes
94c4245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{
  "buildGorillaCharacter": "function buildGorillaCharacter(materials) {\n  const geos = getCharacterGeometry('gorilla');\n  const group = new THREE.Group();\n  const parts = {};\n  parts.body = addPart(group, geos.body, materials.primary, 0, 0.72, 0, 'body');\n  parts.chest = addPart(group, geos.chest, materials.secondary, 0, 0.67, 0.34, 'chest');\n  parts.head = addPart(group, geos.head, materials.primary, 0, 1.38, 0.08, 'head');\n  addPart(group, geos.brow, materials.primary, 0, 1.5, 0.28, 'brow');\n  addPart(group, geos.muzzle, materials.secondary, 0, 1.24, 0.34, 'muzzle');\n  addPart(group, geos.eye, materials.eye, -0.14, 1.33, 0.38, 'eye_l');\n  addPart(group, geos.eye, materials.eye, 0.14, 1.33, 0.38, 'eye_r');\n  parts.armL = addPart(group, geos.arm, materials.primary, -0.62, 0.48, 0.04, 'arm_l');\n  parts.armR = addPart(group, geos.arm, materials.primary, 0.62, 0.48, 0.04, 'arm_r');\n  parts.legL = addPart(group, geos.leg, materials.primary, -0.22, 0.18, 0, 'leg_l');\n  parts.legR = addPart(group, geos.leg, materials.primary, 0.22, 0.18, 0, 'leg_r');\n  group.userData.parts = parts;\n  return group;\n}",
  "buildPandaCharacter": "function buildPandaCharacter(materials) {\n  const geos = getCharacterGeometry('panda');\n  const group = new THREE.Group();\n  const parts = {};\n  parts.body = addPart(group, geos.body, materials.primary, 0, 0.72, 0, 'body');\n  addPart(group, geos.belly, materials.secondary, 0, 0.66, 0.34, 'belly');\n  parts.head = addPart(group, geos.head, materials.primary, 0, 1.36, 0.08, 'head');\n  addPart(group, geos.ear, materials.secondary, -0.26, 1.68, 0.02, 'ear_l');\n  addPart(group, geos.ear, materials.secondary, 0.26, 1.68, 0.02, 'ear_r');\n  addPart(group, geos.snout, materials.secondary, 0, 1.24, 0.34, 'snout');\n  addPart(group, geos.eye, materials.secondary, -0.16, 1.34, 0.36, 'eye_l');\n  addPart(group, geos.eye, materials.secondary, 0.16, 1.34, 0.36, 'eye_r');\n  parts.armL = addPart(group, geos.arm, materials.secondary, -0.52, 0.56, 0.02, 'arm_l');\n  parts.armR = addPart(group, geos.arm, materials.secondary, 0.52, 0.56, 0.02, 'arm_r');\n  parts.legL = addPart(group, geos.leg, materials.secondary, -0.2, 0.16, 0, 'leg_l');\n  parts.legR = addPart(group, geos.leg, materials.secondary, 0.2, 0.16, 0, 'leg_r');\n  parts.scarf = addPart(group, geos.scarf, materials.accent, 0, 1.02, 0.2, 'scarf');\n  parts.scarfTail = addPart(group, geos.scarfTail, materials.accent, 0.22, 0.86, 0.22, 'scarf_tail');\n  group.userData.parts = parts;\n  return group;\n}",
  "buildChameleonCharacter": "function buildChameleonCharacter(materials) {\n  const geos = getCharacterGeometry('chameleon');\n  const group = new THREE.Group();\n  const parts = {};\n  parts.body = addPart(group, geos.body, materials.primary, 0, 0.62, 0, 'body');\n  addPart(group, geos.belly, materials.secondary, 0, 0.58, 0.24, 'belly');\n  parts.head = addPart(group, geos.head, materials.primary, 0, 1.12, 0.14, 'head');\n  addPart(group, geos.crest, materials.accent, 0, 1.34, 0.06, 'crest');\n  addPart(group, geos.eye, materials.eye, -0.12, 1.18, 0.28, 'eye_l');\n  addPart(group, geos.eye, materials.eye, 0.12, 1.18, 0.28, 'eye_r');\n  parts.armL = addPart(group, geos.arm, materials.primary, -0.42, 0.54, 0.06, 'arm_l');\n  parts.armR = addPart(group, geos.arm, materials.primary, 0.42, 0.54, 0.06, 'arm_r');\n  parts.legL = addPart(group, geos.leg, materials.primary, -0.16, 0.2, 0.06, 'leg_l');\n  parts.legR = addPart(group, geos.leg, materials.primary, 0.16, 0.2, 0.06, 'leg_r');\n  parts.tailBase = addPart(group, geos.tailBase, materials.secondary, 0, 0.64, -0.34, 'tail_base');\n  parts.tailTip = addPart(group, geos.tailTip, materials.accent, 0, 0.64, -0.68, 'tail_tip');\n  group.userData.parts = parts;\n  return group;\n}",
  "buildBisonCharacter": "function buildBisonCharacter(materials) {\n  const geos = getCharacterGeometry('bison');\n  const group = new THREE.Group();\n  const parts = {};\n  parts.body = addPart(group, geos.body, materials.primary, 0, 0.72, 0, 'body');\n  parts.hump = addPart(group, geos.hump, materials.secondary, 0, 1.0, -0.1, 'hump');\n  parts.head = addPart(group, geos.head, materials.secondary, 0, 1.2, 0.26, 'head');\n  addPart(group, geos.muzzle, materials.accent, 0, 1.08, 0.48, 'muzzle');\n  addPart(group, geos.eye, materials.eye, -0.14, 1.22, 0.42, 'eye_l');\n  addPart(group, geos.eye, materials.eye, 0.14, 1.22, 0.42, 'eye_r');\n  addPart(group, geos.horn, materials.accent, -0.22, 1.42, 0.24, 'horn_l', { rotation: { x: 0, y: 0, z: 0.4 } });\n  addPart(group, geos.horn, materials.accent, 0.22, 1.42, 0.24, 'horn_r', { rotation: { x: 0, y: 0, z: -0.4 } });\n  parts.armL = addPart(group, geos.arm, materials.primary, -0.46, 0.48, 0.08, 'arm_l');\n  parts.armR = addPart(group, geos.arm, materials.primary, 0.46, 0.48, 0.08, 'arm_r');\n  parts.legL = addPart(group, geos.leg, materials.primary, -0.22, 0.2, 0.04, 'leg_l');\n  parts.legR = addPart(group, geos.leg, materials.primary, 0.22, 0.2, 0.04, 'leg_r');\n  parts.beard = addPart(group, geos.beard, materials.dark, 0, 0.92, 0.42, 'beard');\n  group.userData.parts = parts;\n  return group;\n}",
  "buildSongoku": "function buildSongoku() {\n  const group = new THREE.Group();\n\n  // Legs\n  const legGeo = new THREE.BoxGeometry(0.22, 0.42, 0.24);\n  const legMat = createMaterial(PALETTE.martial_blue);\n  const legL = new THREE.Mesh(legGeo, legMat);\n  legL.position.set(-0.18, 0.21, 0);\n  group.add(legL);\n  const legR = new THREE.Mesh(legGeo, legMat);\n  legR.position.set(0.18, 0.21, 0);\n  group.add(legR);\n\n  // Body (GI) \u2014 with procedural gi texture\n  const bodyGeo = new THREE.BoxGeometry(1.0, 0.88, 0.62);\n  const bodyMat = createMaterial(PALETTE.gi_orange);\n  const giTex = makeSongokuGiTexture();\n  bodyMat.map = giTex;\n  bodyMat.map.wrapS = THREE.RepeatWrapping;\n  bodyMat.map.wrapT = THREE.RepeatWrapping;\n  const body = new THREE.Mesh(bodyGeo, bodyMat);\n  body.position.set(0, 0.86, 0);\n  group.add(body);\n\n  // Arms\n  const armGeo = new THREE.BoxGeometry(0.16, 0.52, 0.18);\n  const armMat = createMaterial(PALETTE.gi_orange);\n  const armL = new THREE.Mesh(armGeo, armMat);\n  armL.position.set(-0.58, 0.92, 0);\n  group.add(armL);\n  const armR = new THREE.Mesh(armGeo, armMat);\n  armR.position.set(0.58, 0.92, 0);\n  group.add(armR);\n\n  // Wrists\n  const wristGeo = new THREE.BoxGeometry(0.18, 0.12, 0.20);\n  const wristMat = createMaterial(PALETTE.martial_blue);\n  const wristL = new THREE.Mesh(wristGeo, wristMat);\n  wristL.position.set(-0.58, 0.62, 0);\n  group.add(wristL);\n  const wristR = new THREE.Mesh(wristGeo, wristMat);\n  wristR.position.set(0.58, 0.62, 0);\n  group.add(wristR);\n\n  // Head\n  const headGeo = new THREE.BoxGeometry(0.68, 0.52, 0.56);\n  const headMat = createMaterial(PALETTE.skin_tan);\n  const head = new THREE.Mesh(headGeo, headMat);\n  head.position.set(0, 1.60, 0.04);\n  group.add(head);\n\n  // Hair main \u2014 with procedural hair texture\n  const hairMainGeo = new THREE.BoxGeometry(0.76, 0.34, 0.60);\n  const hairMat = createMaterial(PALETTE.dark_black);\n  const hairTex = makeSongokuHairTexture();\n  hairMat.map = hairTex;\n  hairMat.map.wrapS = THREE.RepeatWrapping;\n  hairMat.map.wrapT = THREE.RepeatWrapping;\n  const hairMain = new THREE.Mesh(hairMainGeo, hairMat);\n  hairMain.position.set(0, 1.97, -0.02);\n  group.add(hairMain);\n\n  // Hair spikes\n  const hairSpikeGeo = new THREE.BoxGeometry(0.22, 0.28, 0.18);\n  const hairSpikeL = new THREE.Mesh(hairSpikeGeo, hairMat);\n  hairSpikeL.position.set(-0.24, 2.24, -0.08);\n  group.add(hairSpikeL);\n  const hairSpikeR = new THREE.Mesh(hairSpikeGeo, hairMat);\n  hairSpikeR.position.set(0.24, 2.20, -0.04);\n  group.add(hairSpikeR);\n\n  // Eyes\n  const eyeGeo = new THREE.BoxGeometry(0.09, 0.09, 0.08);\n  const eyeMat = createMaterial(PALETTE.black);\n  const eyeL = new THREE.Mesh(eyeGeo, eyeMat);\n  eyeL.position.set(-0.14, 1.62, 0.28);\n  group.add(eyeL);\n  const eyeR = new THREE.Mesh(eyeGeo, eyeMat);\n  eyeR.position.set(0.14, 1.62, 0.28);\n  group.add(eyeR);\n\n  return group;\n}",
  "buildFallenLog": "function buildFallenLog() {\n  const group = new THREE.Group();\n\n  // Main body\n  const bodyGeo = new THREE.BoxGeometry(2.2, 0.48, 0.62);\n  const bodyMat = createMaterial(PALETTE.brown_dark);\n  const body = new THREE.Mesh(bodyGeo, bodyMat);\n  body.position.set(0, 0.24, 0);\n  group.add(body);\n\n  // End caps\n  const capGeo = new THREE.BoxGeometry(0.18, 0.40, 0.54);\n  const capMat = createMaterial(PALETTE.brown_darker);\n  const capL = new THREE.Mesh(capGeo, capMat);\n  capL.position.set(-1.01, 0.24, 0);\n  group.add(capL);\n  const capR = new THREE.Mesh(capGeo, capMat);\n  capR.position.set(1.01, 0.24, 0);\n  group.add(capR);\n\n  return group;\n}",
  "buildSaiyanPod": "function buildSaiyanPod() {\n  const group = new THREE.Group();\n\n  // Body lower\n  const bodyLowerGeo = new THREE.BoxGeometry(1.0, 0.52, 0.90);\n  const bodyMat = createMaterial(PALETTE.purple_dark);\n  const bodyLower = new THREE.Mesh(bodyLowerGeo, bodyMat);\n  bodyLower.position.set(0, 0.76, 0);\n  group.add(bodyLower);\n\n  // Body upper\n  const bodyUpperGeo = new THREE.BoxGeometry(0.82, 0.34, 0.74);\n  const bodyUpperMat = createMaterial(PALETTE.purple_mid);\n  const bodyUpper = new THREE.Mesh(bodyUpperGeo, bodyUpperMat);\n  bodyUpper.position.set(0, 1.19, 0);\n  group.add(bodyUpper);\n\n  // Fins\n  const finGeo = new THREE.BoxGeometry(0.14, 0.30, 0.42);\n  const finMat = createMaterial(PALETTE.purple_mid);\n  const finL = new THREE.Mesh(finGeo, finMat);\n  finL.position.set(-0.57, 0.88, 0);\n  group.add(finL);\n  const finR = new THREE.Mesh(finGeo, finMat);\n  finR.position.set(0.57, 0.88, 0);\n  group.add(finR);\n\n  // Window front\n  const windowGeo = new THREE.BoxGeometry(0.42, 0.24, 0.12);\n  const windowMat = createMaterial(PALETTE.threat_red_violet);\n  const window = new THREE.Mesh(windowGeo, windowMat);\n  window.position.set(0, 0.96, 0.51);\n  group.add(window);\n\n  return group;\n}",
  "buildDragonBall": "function buildDragonBall() {\n  const group = new THREE.Group();\n\n  // Orb bottom\n  const orbBottomGeo = new THREE.BoxGeometry(0.34, 0.12, 0.34);\n  const orbMat = createMaterial(PALETTE.gold_bright);\n  const orbBottom = new THREE.Mesh(orbBottomGeo, orbMat);\n  orbBottom.position.set(0, 0.06, 0);\n  group.add(orbBottom);\n\n  // Orb middle\n  const orbMiddleGeo = new THREE.BoxGeometry(0.42, 0.28, 0.42);\n  const orbMiddle = new THREE.Mesh(orbMiddleGeo, orbMat);\n  orbMiddle.position.set(0, 0.26, 0);\n  group.add(orbMiddle);\n\n  // Orb top\n  const orbTopGeo = new THREE.BoxGeometry(0.34, 0.12, 0.34);\n  const orbTop = new THREE.Mesh(orbTopGeo, orbMat);\n  orbTop.position.set(0, 0.46, 0);\n  group.add(orbTop);\n\n  // Star marking\n  const starGeo = new THREE.PlaneGeometry(0.12, 0.12);\n  const starMat = createMaterial(PALETTE.threat_red_violet);\n  const star = new THREE.Mesh(starGeo, starMat);\n  star.position.set(0, 0.26, 0.211);\n  star.rotation.z = Math.PI / 4;\n  group.add(star);\n\n  return group;\n}",
  "buildWastelandTile": "function buildWastelandTile() {\n  const group = new THREE.Group();\n\n  // Base tile\n  const baseTex = makeWastelandTileTexture();\n  const baseGeo = new THREE.BoxGeometry(1.0, 0.20, 1.0);\n  const baseMat = createMaterial(PALETTE.stone_tan);\n  baseMat.map = baseTex;\n  baseMat.map.wrapS = THREE.RepeatWrapping;\n  baseMat.map.wrapT = THREE.RepeatWrapping;\n  const base = new THREE.Mesh(baseGeo, baseMat);\n  base.position.set(0, 0.10, 0);\n  group.add(base);\n\n  // Highlight top slab\n  const highlightGeo = new THREE.BoxGeometry(0.96, 0.10, 0.96);\n  const highlightMat = createMaterial(PALETTE.warm_sand);\n  const highlight = new THREE.Mesh(highlightGeo, highlightMat);\n  highlight.position.set(0, 0.25, 0);\n  group.add(highlight);\n\n  return group;\n}",
  "buildTournamentWall": "function buildTournamentWall() {\n  const group = new THREE.Group();\n\n  // Wall body\n  const wallTex = makeTournamentWallTexture();\n  const wallGeo = new THREE.BoxGeometry(4.0, 1.60, 0.50);\n  const wallMat = createMaterial(PALETTE.warm_sand);\n  wallMat.map = wallTex;\n  wallMat.map.wrapS = THREE.RepeatWrapping;\n  wallMat.map.wrapT = THREE.RepeatWrapping;\n  const wall = new THREE.Mesh(wallGeo, wallMat);\n  wall.position.set(0, 0.80, -14.00);\n  group.add(wall);\n\n  // Roof cap\n  const roofGeo = new THREE.BoxGeometry(4.2, 0.14, 0.70);\n  const roofMat = createMaterial(PALETTE.brown_dark);\n  const roof = new THREE.Mesh(roofGeo, roofMat);\n  roof.position.set(0, 1.67, -14.00);\n  group.add(roof);\n\n  return group;\n}",
  "buildStoneMarker": "function buildStoneMarker() {\n  const group = new THREE.Group();\n\n  // Base\n  const baseTex = makeStoneMarkerTexture();\n  const baseGeo = new THREE.BoxGeometry(0.52, 0.34, 0.52);\n  const baseMat = createMaterial(PALETTE.stone_tan);\n  baseMat.map = baseTex;\n  baseMat.map.wrapS = THREE.RepeatWrapping;\n  baseMat.map.wrapT = THREE.RepeatWrapping;\n  const base = new THREE.Mesh(baseGeo, baseMat);\n  base.position.set(3.50, 0.17, 0);\n  group.add(base);\n\n  // Top\n  const topGeo = new THREE.BoxGeometry(0.34, 0.42, 0.34);\n  const topMat = createMaterial(PALETTE.brown_dark);\n  const top = new THREE.Mesh(topGeo, topMat);\n  top.position.set(3.50, 0.55, 0);\n  group.add(top);\n\n  return group;\n}",
  "buildKiOrb": "function buildKiOrb() {\n  const group = new THREE.Group();\n\n  // Core\n  const coreTex = makeKiOrbTexture();\n  const coreGeo = new THREE.BoxGeometry(0.28, 0.28, 0.28);\n  const coreMat = createMaterial(PALETTE.sky_blue);\n  coreMat.map = coreTex;\n  coreMat.map.wrapS = THREE.RepeatWrapping;\n  coreMat.map.wrapT = THREE.RepeatWrapping;\n  const core = new THREE.Mesh(coreGeo, coreMat);\n  core.position.set(0, 0.14, 0);\n  group.add(core);\n\n  // Glow shell\n  const glowGeo = new THREE.BoxGeometry(0.40, 0.40, 0.40);\n  const glowMat = createMaterial(PALETTE.martial_blue);\n  const glow = new THREE.Mesh(glowGeo, glowMat);\n  glow.position.set(0, 0.14, 0);\n  group.add(glow);\n\n  return group;\n}",
  "buildDistantButte": "function buildDistantButte() {\n  const group = new THREE.Group();\n\n  // Base\n  const baseTex = makeDistantButteTexture();\n  const baseGeo = new THREE.BoxGeometry(4.8, 1.6, 3.6);\n  const baseMat = createMaterial(PALETTE.brown_dark);\n  baseMat.map = baseTex;\n  baseMat.map.wrapS = THREE.RepeatWrapping;\n  baseMat.map.wrapT = THREE.RepeatWrapping;\n  const base = new THREE.Mesh(baseGeo, baseMat);\n  base.position.set(0, 0.80, -30.00);\n  group.add(base);\n\n  // Mid layer\n  const midGeo = new THREE.BoxGeometry(4.1, 1.2, 3.0);\n  const midMat = createMaterial(PALETTE.stone_tan);\n  const mid = new THREE.Mesh(midGeo, midMat);\n  mid.position.set(0.10, 2.20, -30.10);\n  group.add(mid);\n\n  // Top layer\n  const topGeo = new THREE.BoxGeometry(3.3, 0.9, 2.4);\n  const top = new THREE.Mesh(topGeo, midMat);\n  top.position.set(-0.06, 3.25, -29.95);\n  group.add(top);\n\n  // Sun cap\n  const capGeo = new THREE.BoxGeometry(2.7, 0.14, 1.9);\n  const capMat = createMaterial(PALETTE.warm_sand);\n  const cap = new THREE.Mesh(capGeo, capMat);\n  cap.position.set(-0.02, 3.77, -29.88);\n  group.add(cap);\n\n  return group;\n}",
  "buildShowcase": "function buildShowcase() {\n  // Hero on pedestal\n  if (gameState.hero) {\n    scene.remove(gameState.hero);\n  }\n  gameState.hero = buildSongoku();\n  gameState.hero.position.set(0, 1.8, 0);\n  scene.add(gameState.hero);\n\n  // Pedestal\n  const pedestalGeo = new THREE.BoxGeometry(1.8, 0.18, 1.8);\n  const pedestalMat = createMaterial(PALETTE.stone_tan);\n  const pedestal = new THREE.Mesh(pedestalGeo, pedestalMat);\n  pedestal.position.set(0, 0.09, 0);\n  pedestal.name = 'pedestal';\n  scene.add(pedestal);\n\n  // Set showcase camera\n  camera.position.copy(CAMERA_SHOWCASE.position);\n  camera.lookAt(CAMERA_SHOWCASE.lookAt);\n\n  // Reset game state\n  gameState.score = 0;\n  gameState.distance = 0;\n  gameState.scrollSpeed = SCROLL.initial;\n  gameState.elapsedTime = 0;\n  gameState.heroVelocityY = 0;\n  gameState.heroLane = 0;\n  gameState.obstacles = [];\n  gameState.collectibles = [];\n  gameState.spawnTimer = 0;\n\n  updateHUD();\n}",
  "buildRaticate": "function buildRaticate(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.70,0.50,0.80,'#c8a87a',0,0.25,0);p(0.58,0.28,0.30,'#c8a87a',0,0.42,-0.28);\n  p(0.48,0.28,0.58,'#f5f0e8',0,0.22,0.30);p(0.58,0.48,0.52,'#c8a87a',0,0.62,0.22);\n  p(0.40,0.18,0.26,'#c8a87a',0,0.52,0.48);\n  p(0.09,0.22,0.06,'#f8f8f8',-0.07,0.54,0.61);p(0.09,0.22,0.06,'#f8f8f8',0.07,0.54,0.61);\n  p(0.14,0.13,0.05,'#f8f8f8',-0.17,0.71,0.46);p(0.08,0.08,0.04,'#1a1a1a',-0.17,0.71,0.49);\n  p(0.14,0.13,0.05,'#f8f8f8',0.17,0.71,0.46);p(0.08,0.08,0.04,'#1a1a1a',0.17,0.71,0.49);\n  p(0.12,0.24,0.08,'#c8a87a',-0.26,0.87,0.18);p(0.08,0.16,0.05,'#e8c8b0',-0.26,0.87,0.21);\n  p(0.12,0.24,0.08,'#c8a87a',0.26,0.87,0.18);p(0.08,0.16,0.05,'#e8c8b0',0.26,0.87,0.21);\n  p(0.18,0.02,0.02,'#1a1a1a',-0.27,0.59,0.56);p(0.18,0.02,0.02,'#1a1a1a',-0.27,0.54,0.56);\n  p(0.18,0.02,0.02,'#1a1a1a',0.27,0.59,0.56);p(0.18,0.02,0.02,'#1a1a1a',0.27,0.54,0.56);\n  p(0.14,0.30,0.14,'#c8a87a',-0.32,0.08,0.24);p(0.14,0.30,0.14,'#c8a87a',0.32,0.08,0.24);\n  p(0.16,0.32,0.16,'#c8a87a',-0.30,0.08,-0.22);p(0.16,0.32,0.16,'#c8a87a',0.30,0.08,-0.22);\n  p(0.10,0.10,0.30,'#c8a87a',0,0.20,-0.55);p(0.08,0.08,0.22,'#c8a87a',0,0.30,-0.74);p(0.06,0.06,0.16,'#c8a87a',0,0.38,-0.88);\n  return g;\n}",
  "buildPersian": "function buildPersian(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.58,0.40,0.88,'#c8c8d4',0,0.20,0);p(0.38,0.24,0.68,'#e8e8f0',0,0.18,0.10);\n  p(0.28,0.22,0.26,'#c8c8d4',0,0.43,0.36);p(0.58,0.50,0.48,'#c8c8d4',0,0.68,0.28);\n  p(0.20,0.28,0.26,'#c8c8d4',-0.30,0.64,0.26);p(0.20,0.28,0.26,'#c8c8d4',0.30,0.64,0.26);\n  p(0.26,0.14,0.20,'#d8d8e4',0,0.60,0.50);p(0.12,0.12,0.06,'#e05050',0,0.75,0.52);\n  p(0.14,0.08,0.06,'#1a1a1a',-0.17,0.75,0.51);p(0.06,0.05,0.04,'#f8f8f8',-0.17,0.76,0.54);\n  p(0.14,0.08,0.06,'#1a1a1a',0.17,0.75,0.51);p(0.06,0.05,0.04,'#f8f8f8',0.17,0.76,0.54);\n  p(0.11,0.20,0.08,'#c8c8d4',-0.25,0.94,0.26);p(0.07,0.13,0.05,'#e8a8a8',-0.25,0.94,0.29);\n  p(0.11,0.20,0.08,'#c8c8d4',0.25,0.94,0.26);p(0.07,0.13,0.05,'#e8a8a8',0.25,0.94,0.29);\n  p(0.22,0.02,0.02,'#1a1a1a',-0.32,0.64,0.50);p(0.22,0.02,0.02,'#1a1a1a',-0.32,0.60,0.50);\n  p(0.22,0.02,0.02,'#1a1a1a',0.32,0.64,0.50);p(0.22,0.02,0.02,'#1a1a1a',0.32,0.60,0.50);\n  p(0.13,0.32,0.13,'#c8c8d4',-0.24,0.13,0.32);p(0.13,0.32,0.13,'#c8c8d4',0.24,0.13,0.32);\n  p(0.15,0.34,0.15,'#c8c8d4',-0.22,0.13,-0.28);p(0.15,0.34,0.15,'#c8c8d4',0.22,0.13,-0.28);\n  p(0.08,0.08,0.26,'#c8c8d4',0,0.18,-0.56);p(0.08,0.18,0.10,'#c8c8d4',0,0.28,-0.70);p(0.10,0.14,0.08,'#c8c8d4',0,0.36,-0.76);\n  return g;\n}",
  "buildTauros": "function buildTauros(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.88,0.56,0.96,'#8b6914',0,0.30,-0.08);p(0.76,0.42,0.28,'#8b6914',0,0.36,0.32);\n  p(0.44,0.34,0.34,'#8b6914',0,0.56,0.48);p(0.68,0.52,0.50,'#8b6914',0,0.80,0.46);\n  p(0.48,0.28,0.26,'#d0b860',0,0.70,0.72);p(0.22,0.06,0.06,'#c8c8c8',0,0.66,0.88);\n  p(0.09,0.08,0.05,'#1a1a1a',-0.13,0.70,0.86);p(0.09,0.08,0.05,'#1a1a1a',0.13,0.70,0.86);\n  p(0.13,0.13,0.06,'#1a1a1a',-0.29,0.92,0.68);p(0.13,0.13,0.06,'#1a1a1a',0.29,0.92,0.68);\n  p(0.14,0.12,0.12,'#d0b860',-0.32,1.06,0.42);p(0.08,0.08,0.26,'#d0b860',-0.36,1.14,0.24);\n  p(0.14,0.12,0.12,'#d0b860',0.32,1.06,0.42);p(0.08,0.08,0.26,'#d0b860',0.36,1.14,0.24);\n  p(0.20,0.28,0.20,'#8b6914',-0.33,0.14,0.30);p(0.20,0.10,0.22,'#1a1a1a',-0.33,-0.14,0.30);\n  p(0.20,0.28,0.20,'#8b6914',0.33,0.14,0.30);p(0.20,0.10,0.22,'#1a1a1a',0.33,-0.14,0.30);\n  p(0.22,0.30,0.22,'#8b6914',-0.31,0.14,-0.34);p(0.22,0.10,0.24,'#1a1a1a',-0.31,-0.14,-0.34);\n  p(0.22,0.30,0.22,'#8b6914',0.31,0.14,-0.34);p(0.22,0.10,0.24,'#1a1a1a',0.31,-0.14,-0.34);\n  p(0.06,0.06,0.34,'#8b6914',-0.11,0.23,-0.60);p(0.06,0.06,0.34,'#8b6914',0,0.28,-0.60);p(0.06,0.06,0.34,'#8b6914',0.11,0.23,-0.60);\n  p(0.20,0.16,0.08,'#1a1a1a',0,0.30,-0.80);\n  return g;\n}",
  "buildSnorlax": "function buildSnorlax(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.42,0.14,0.46,'#6080a0',-0.40,0.07,0.08);p(0.42,0.14,0.46,'#6080a0',0.40,0.07,0.08);\n  p(0.36,0.40,0.32,'#6080a0',-0.38,0.34,0.06);p(0.36,0.40,0.32,'#6080a0',0.38,0.34,0.06);\n  p(1.36,0.68,1.08,'#6080a0',0,0.72,0);p(1.16,0.56,0.94,'#6080a0',0,1.22,0);\n  p(0.88,0.84,0.28,'#e8d8b0',0,0.82,0.50);p(0.70,0.42,0.22,'#e8d8b0',0,1.30,0.44);\n  p(0.28,0.38,0.26,'#6080a0',-0.74,1.14,0.08);p(0.24,0.30,0.24,'#6080a0',-0.76,0.80,0.10);\n  p(0.30,0.22,0.28,'#6080a0',-0.78,0.62,0.08);p(0.10,0.10,0.14,'#6080a0',-0.86,0.57,0.14);p(0.10,0.10,0.14,'#6080a0',-0.70,0.57,0.14);\n  p(0.28,0.38,0.26,'#6080a0',0.74,1.14,0.08);p(0.24,0.30,0.24,'#6080a0',0.76,0.80,0.10);\n  p(0.30,0.22,0.28,'#6080a0',0.78,0.62,0.08);p(0.10,0.10,0.14,'#6080a0',0.86,0.57,0.14);p(0.10,0.10,0.14,'#6080a0',0.70,0.57,0.14);\n  p(0.54,0.22,0.50,'#6080a0',0,1.60,0.02);p(0.98,0.70,0.76,'#6080a0',0,1.94,0);\n  p(0.78,0.26,0.58,'#6080a0',0,2.26,0);\n  p(0.34,0.28,0.28,'#8098b0',-0.48,1.86,0.26);p(0.34,0.28,0.28,'#8098b0',0.48,1.86,0.26);\n  p(0.28,0.07,0.07,'#1a1a1a',-0.28,2.04,0.36);p(0.28,0.07,0.07,'#1a1a1a',0.28,2.04,0.36);\n  p(0.34,0.07,0.07,'#1a1a1a',0,1.86,0.38);\n  p(0.17,0.26,0.13,'#8098b0',-0.50,2.20,-0.02);p(0.17,0.26,0.13,'#8098b0',0.50,2.20,-0.02);\n  return g;\n}",
  "buildGraveler": "function buildGraveler(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(1.00,0.90,0.90,'#808080',0,0.46,0);p(0.84,0.28,0.74,'#808080',0,0.93,0);\n  p(0.22,0.60,0.60,'#606060',-0.56,0.46,0);p(0.22,0.60,0.60,'#606060',0.56,0.46,0);\n  p(0.18,0.14,0.16,'#404040',-0.42,0.78,-0.22);p(0.14,0.12,0.14,'#404040',-0.30,0.86,0.28);\n  p(0.18,0.14,0.16,'#404040',0.42,0.78,-0.22);p(0.14,0.12,0.14,'#404040',0.30,0.86,0.28);\n  p(0.18,0.16,0.06,'#f0f0f0',-0.22,0.64,0.44);p(0.10,0.10,0.05,'#e83030',-0.22,0.64,0.47);\n  p(0.18,0.16,0.06,'#f0f0f0',0.22,0.64,0.44);p(0.10,0.10,0.05,'#e83030',0.22,0.64,0.47);\n  p(0.22,0.08,0.06,'#404040',-0.22,0.74,0.44);p(0.22,0.08,0.06,'#404040',0.22,0.74,0.44);\n  p(0.20,0.14,0.18,'#808080',-0.62,0.70,0.16);p(0.22,0.18,0.20,'#404040',-0.88,0.52,0.22);\n  p(0.20,0.14,0.18,'#808080',0.62,0.70,0.16);p(0.22,0.18,0.20,'#404040',0.88,0.52,0.22);\n  p(0.20,0.12,0.18,'#808080',-0.58,0.28,0.18);p(0.22,0.16,0.20,'#404040',-0.84,0.10,0.24);\n  p(0.20,0.12,0.18,'#808080',0.58,0.28,0.18);p(0.22,0.16,0.20,'#404040',0.84,0.10,0.24);\n  p(0.26,0.22,0.28,'#808080',-0.28,0.08,0.04);p(0.26,0.22,0.28,'#808080',0.28,0.08,0.04);\n  return g;\n}",
  "buildOnix": "function buildOnix(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  const sc=[1.0,0.88,0.76,0.64,0.52],zs=[0,-1.8,-3.6,-5.4,-7.2];\n  for(let i=0;i<5;i++){\n    const s=sc[i],z=zs[i];\n    p(0.90*s,0.70*s,1.20*s,'#909090',0,0.37*s,z);\n    p(0.22*s,0.22*s,0.22*s,'#707070',-0.42*s,0.52*s,z);p(0.22*s,0.22*s,0.22*s,'#707070',0.42*s,0.52*s,z);\n    p(0.18*s,0.30*s,0.18*s,'#505050',0,0.80*s,z);\n    p(0.14*s,0.12*s,0.14*s,'#707070',-0.30*s,0.66*s,z+0.20*s);p(0.14*s,0.12*s,0.14*s,'#707070',0.30*s,0.66*s,z-0.20*s);\n  }\n  p(0.12,0.12,0.06,'#e8e060',-0.22,0.54,0.58);p(0.12,0.12,0.06,'#e8e060',0.22,0.54,0.58);\n  return g;\n}",
  "buildZubat": "function buildZubat(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.48,0.46,0.36,'#9060c0',0,0.24,0);p(0.36,0.20,0.26,'#7040b0',0,0.06,0.04);\n  p(0.10,0.24,0.08,'#9060c0',-0.20,0.52,0);p(0.06,0.16,0.05,'#d090f8',-0.20,0.52,0.03);\n  p(0.10,0.24,0.08,'#9060c0',0.20,0.52,0);p(0.06,0.16,0.05,'#d090f8',0.20,0.52,0.03);\n  p(0.40,0.14,0.10,'#e83060',0,0.14,0.20);p(0.30,0.08,0.06,'#1a1a1a',0,0.14,0.24);\n  p(0.06,0.10,0.06,'#f8f8f8',-0.08,0.09,0.22);p(0.06,0.10,0.06,'#f8f8f8',0.08,0.09,0.22);\n  p(0.26,0.10,0.52,'#7040b0',-0.34,0.30,0);p(0.44,0.08,0.60,'#7040b0',-0.70,0.24,0);p(0.36,0.06,0.44,'#604090',-1.04,0.18,0);\n  p(0.26,0.10,0.52,'#7040b0',0.34,0.30,0);p(0.44,0.08,0.60,'#7040b0',0.70,0.24,0);p(0.36,0.06,0.44,'#604090',1.04,0.18,0);\n  p(0.08,0.06,0.46,'#8050b0',-0.54,0.28,-0.06);p(0.08,0.06,0.40,'#8050b0',-0.88,0.22,-0.04);\n  p(0.08,0.06,0.46,'#8050b0',0.54,0.28,-0.06);p(0.08,0.06,0.40,'#8050b0',0.88,0.22,-0.04);\n  p(0.16,0.08,0.08,'#1a1a1a',-0.18,0.02,0.04);p(0.16,0.08,0.08,'#1a1a1a',0.18,0.02,0.04);\n  p(0.06,0.04,0.12,'#1a1a1a',-0.22,0.00,0.10);p(0.06,0.04,0.12,'#1a1a1a',0.22,0.00,0.10);\n  return g;\n}",
  "buildGolbat": "function buildGolbat(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.60,0.50,0.42,'#7040b0',0,0.26,0);p(0.44,0.18,0.30,'#5a3090',0,0.06,0);\n  p(0.60,0.44,0.44,'#7040b0',0,0.60,0);\n  p(0.72,0.30,0.12,'#e83060',0,0.52,0.24);p(0.64,0.22,0.08,'#1a1a1a',0,0.52,0.28);\n  p(0.08,0.16,0.07,'#f8f8f8',-0.22,0.60,0.30);p(0.08,0.16,0.07,'#f8f8f8',0.22,0.60,0.30);\n  p(0.08,0.12,0.07,'#f8f8f8',-0.14,0.43,0.30);p(0.08,0.12,0.07,'#f8f8f8',0.14,0.43,0.30);\n  p(0.30,0.08,0.08,'#e83060',0,0.49,0.32);\n  p(0.12,0.26,0.08,'#7040b0',-0.26,0.82,0);p(0.12,0.26,0.08,'#7040b0',0.26,0.82,0);\n  p(0.12,0.10,0.05,'#e83060',-0.20,0.70,0.21);p(0.12,0.10,0.05,'#e83060',0.20,0.70,0.21);\n  p(0.30,0.10,0.64,'#8050c0',-0.42,0.34,0);p(0.54,0.08,0.74,'#6040a0',-0.84,0.26,0);p(0.42,0.06,0.56,'#5030a0',-1.18,0.18,0);\n  p(0.30,0.10,0.64,'#8050c0',0.42,0.34,0);p(0.54,0.08,0.74,'#6040a0',0.84,0.26,0);p(0.42,0.06,0.56,'#5030a0',1.18,0.18,0);\n  p(0.08,0.06,0.62,'#9060c0',-0.66,0.30,-0.05);p(0.08,0.06,0.62,'#9060c0',0.66,0.30,-0.05);\n  p(0.06,0.04,0.14,'#1a1a1a',-0.20,0.00,0.11);p(0.06,0.04,0.14,'#1a1a1a',0.20,0.00,0.11);\n  return g;\n}",
  "buildPidgey": "function buildPidgey(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.44,0.42,0.60,'#c09060',0,0.22,0);p(0.30,0.30,0.44,'#e8d8b0',0,0.20,0.20);\n  p(0.44,0.40,0.40,'#c09060',0,0.56,0.14);p(0.36,0.16,0.32,'#a07040',0,0.74,0.10);\n  p(0.08,0.14,0.06,'#1a1a1a',0,0.88,0.06);\n  p(0.10,0.08,0.22,'#e8c040',0,0.54,0.38);p(0.08,0.06,0.18,'#1a1a1a',0,0.49,0.38);\n  p(0.10,0.10,0.05,'#1a1a1a',-0.16,0.62,0.34);p(0.05,0.05,0.04,'#f8f8f8',-0.16,0.63,0.37);\n  p(0.10,0.10,0.05,'#1a1a1a',0.16,0.62,0.34);p(0.05,0.05,0.04,'#f8f8f8',0.16,0.63,0.37);\n  p(0.08,0.32,0.56,'#a07040',-0.30,0.26,0);p(0.06,0.22,0.42,'#906030',-0.46,0.20,-0.18);\n  p(0.08,0.32,0.56,'#a07040',0.30,0.26,0);p(0.06,0.22,0.42,'#906030',0.46,0.20,-0.18);\n  p(0.24,0.14,0.26,'#a07040',0,0.22,-0.38);\n  p(0.08,0.10,0.18,'#906030',-0.10,0.20,-0.52);p(0.08,0.10,0.18,'#906030',0.10,0.20,-0.52);\n  p(0.08,0.22,0.08,'#e8c040',-0.12,0.04,0.20);p(0.08,0.22,0.08,'#e8c040',0.12,0.04,0.20);\n  p(0.14,0.04,0.08,'#e8c040',-0.18,0.00,0.26);p(0.14,0.04,0.08,'#e8c040',0.06,0.00,0.24);\n  p(0.14,0.04,0.08,'#e8c040',0.18,0.00,0.26);p(0.14,0.04,0.08,'#e8c040',-0.06,0.00,0.24);\n  return g;\n}",
  "buildFearow": "function buildFearow(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.54,0.48,0.72,'#c06030',0,0.24,0);p(0.36,0.30,0.52,'#e8d0a0',0,0.22,0.18);\n  p(0.24,0.40,0.22,'#c06030',0,0.58,0.22);p(0.40,0.36,0.36,'#c06030',0,0.78,0.18);\n  p(0.08,0.08,0.54,'#f0c040',0,0.78,0.50);p(0.06,0.06,0.46,'#1a1a1a',0,0.74,0.50);\n  p(0.12,0.22,0.10,'#c06030',0,0.96,0.14);p(0.08,0.14,0.06,'#e8d0a0',0.04,1.08,0.12);\n  p(0.10,0.10,0.05,'#1a1a1a',-0.16,0.82,0.36);p(0.14,0.14,0.04,'#f0d080',-0.16,0.82,0.34);\n  p(0.10,0.10,0.05,'#1a1a1a',0.16,0.82,0.36);p(0.14,0.14,0.04,'#f0d080',0.16,0.82,0.34);\n  p(0.10,0.38,0.68,'#c06030',-0.36,0.28,-0.02);p(0.08,0.28,0.54,'#a04820',-0.60,0.22,-0.12);p(0.06,0.18,0.38,'#804010',-0.78,0.16,-0.22);\n  p(0.10,0.38,0.68,'#c06030',0.36,0.28,-0.02);p(0.08,0.28,0.54,'#a04820',0.60,0.22,-0.12);p(0.06,0.18,0.38,'#804010',0.78,0.16,-0.22);\n  p(0.06,0.06,0.44,'#904020',-0.46,0.14,-0.20);p(0.06,0.06,0.44,'#904020',0.46,0.14,-0.20);\n  p(0.28,0.16,0.32,'#c06030',0,0.22,-0.48);\n  p(0.08,0.10,0.24,'#a04820',-0.12,0.20,-0.62);p(0.08,0.10,0.24,'#a04820',0.12,0.20,-0.62);\n  p(0.08,0.24,0.08,'#f0c040',-0.12,0.04,0.28);p(0.08,0.24,0.08,'#f0c040',0.12,0.04,0.28);\n  p(0.24,0.06,0.18,'#f0c040',0,0.01,0.32);\n  return g;\n}",
  "buildBeedrill": "function buildBeedrill(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.26,0.46,0.30,'#f8d840',0,0.36,0);p(0.28,0.10,0.32,'#1a1a1a',0,0.52,0);\n  p(0.22,0.64,0.22,'#f8d840',0,0.34,-0.38);\n  p(0.24,0.10,0.24,'#1a1a1a',0,0.18,-0.36);p(0.24,0.10,0.24,'#1a1a1a',0,0.44,-0.44);\n  p(0.14,0.18,0.14,'#f8d840',0,0.10,-0.60);p(0.06,0.06,0.22,'#1a1a1a',0,0.06,-0.74);\n  p(0.30,0.28,0.28,'#f8d840',0,0.68,0.12);\n  p(0.14,0.18,0.08,'#e83060',-0.12,0.70,0.24);p(0.06,0.07,0.05,'#f8f8f8',-0.12,0.72,0.27);\n  p(0.14,0.18,0.08,'#e83060',0.12,0.70,0.24);p(0.06,0.07,0.05,'#f8f8f8',0.12,0.72,0.27);\n  p(0.04,0.04,0.22,'#1a1a1a',-0.10,0.86,0.10);p(0.04,0.04,0.22,'#1a1a1a',0.10,0.86,0.10);\n  p(0.08,0.08,0.54,'#1a1a1a',-0.28,0.52,0.22);p(0.06,0.06,0.10,'#f0f080',-0.28,0.52,0.50);\n  p(0.08,0.08,0.54,'#1a1a1a',0.28,0.52,0.22);p(0.06,0.06,0.10,'#f0f080',0.28,0.52,0.50);\n  p(0.06,0.28,0.52,'#f0f0f8',-0.32,0.52,-0.10);p(0.04,0.20,0.40,'#d8e8f8',-0.52,0.48,-0.14);\n  p(0.06,0.28,0.52,'#f0f0f8',0.32,0.52,-0.10);p(0.04,0.20,0.40,'#d8e8f8',0.52,0.48,-0.14);\n  p(0.06,0.22,0.42,'#f0f0f8',-0.28,0.30,-0.12);p(0.06,0.22,0.42,'#f0f0f8',0.28,0.30,-0.12);\n  p(0.08,0.08,0.16,'#1a1a1a',-0.20,0.42,0.08);p(0.08,0.08,0.16,'#1a1a1a',0.20,0.42,0.08);\n  return g;\n}",
  "buildButterfree": "function buildButterfree(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.22,0.60,0.20,'#f0f0f8',0,0.36,0);p(0.20,0.24,0.18,'#d0d0e0',0,0.60,0);\n  p(0.14,0.22,0.14,'#d0d0e8',0,0.10,-0.14);p(0.28,0.28,0.26,'#f0f0f8',0,0.80,0.04);\n  p(0.12,0.16,0.08,'#e83060',-0.10,0.82,0.16);p(0.12,0.16,0.08,'#e83060',0.10,0.82,0.16);\n  p(0.04,0.04,0.26,'#1a1a1a',-0.08,0.98,0.06);p(0.08,0.08,0.06,'#9060c0',-0.10,1.10,0.06);\n  p(0.04,0.04,0.26,'#1a1a1a',0.08,0.98,0.06);p(0.08,0.08,0.06,'#9060c0',0.10,1.10,0.06);\n  p(0.06,0.58,0.74,'#f8f8ff',-0.40,0.56,-0.06);p(0.04,0.44,0.58,'#d8d0f8',-0.64,0.52,-0.12);\n  p(0.04,0.30,0.38,'#9060c0',-0.54,0.64,-0.08);p(0.04,0.12,0.12,'#9060c0',-0.46,0.72,-0.04);\n  p(0.06,0.58,0.74,'#f8f8ff',0.40,0.56,-0.06);p(0.04,0.44,0.58,'#d8d0f8',0.64,0.52,-0.12);\n  p(0.04,0.30,0.38,'#9060c0',0.54,0.64,-0.08);p(0.04,0.12,0.12,'#9060c0',0.46,0.72,-0.04);\n  p(0.06,0.42,0.58,'#f0f0ff',-0.34,0.18,-0.04);p(0.04,0.30,0.44,'#d8d0f8',-0.54,0.14,-0.10);p(0.04,0.22,0.28,'#9060c0',-0.44,0.22,-0.06);\n  p(0.06,0.42,0.58,'#f0f0ff',0.34,0.18,-0.04);p(0.04,0.30,0.44,'#d8d0f8',0.54,0.14,-0.10);p(0.04,0.22,0.28,'#9060c0',0.44,0.22,-0.06);\n  p(0.18,0.06,0.08,'#1a1a1a',-0.12,0.04,0.08);p(0.18,0.06,0.08,'#1a1a1a',0.12,0.04,0.08);\n  return g;\n}",
  "buildVoltorb": "function buildVoltorb(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.72,0.36,0.72,'#e83030',0,0.52,0);p(0.44,0.42,0.52,'#e83030',-0.32,0.44,0);p(0.44,0.42,0.52,'#e83030',0.32,0.44,0);\n  p(0.52,0.38,0.36,'#e83030',0,0.44,0.32);p(0.50,0.30,0.28,'#e83030',0,0.46,-0.28);\n  p(0.54,0.14,0.54,'#e83030',0,0.64,0);p(0.38,0.10,0.38,'#e83030',0,0.72,0);\n  p(0.80,0.08,0.76,'#1a1a1a',0,0.30,0);p(0.46,0.08,0.58,'#1a1a1a',-0.30,0.30,0);p(0.46,0.08,0.58,'#1a1a1a',0.30,0.30,0);\n  p(0.72,0.36,0.72,'#f8f8f8',0,0.10,0);p(0.44,0.38,0.52,'#f0f0f0',-0.32,0.14,0);p(0.44,0.38,0.52,'#f0f0f0',0.32,0.14,0);\n  p(0.52,0.32,0.36,'#f0f0f0',0,0.12,0.30);p(0.38,0.08,0.38,'#f0f0f0',0,0.04,0);\n  p(0.16,0.14,0.06,'#f8f8f8',-0.20,0.42,0.35);p(0.10,0.09,0.04,'#1a1a1a',-0.20,0.42,0.38);\n  p(0.16,0.14,0.06,'#f8f8f8',0.20,0.42,0.35);p(0.10,0.09,0.04,'#1a1a1a',0.20,0.42,0.38);\n  p(0.18,0.08,0.05,'#1a1a1a',-0.20,0.52,0.35);p(0.18,0.08,0.05,'#1a1a1a',0.20,0.52,0.35);\n  p(0.24,0.08,0.05,'#1a1a1a',0,0.30,0.37);\n  return g;\n}",
  "buildElectrode": "function buildElectrode(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.90,0.44,0.90,'#f8f8f8',0,0.60,0);p(0.54,0.52,0.64,'#f0f0f0',-0.38,0.52,0);p(0.54,0.52,0.64,'#f0f0f0',0.38,0.52,0);\n  p(0.64,0.46,0.44,'#f0f0f0',0,0.52,0.38);p(0.62,0.36,0.34,'#f0f0f0',0,0.54,-0.34);\n  p(0.68,0.18,0.68,'#f8f8f8',0,0.82,0);p(0.48,0.12,0.48,'#f8f8f8',0,0.92,0);\n  p(0.96,0.10,0.92,'#1a1a1a',0,0.36,0);p(0.58,0.10,0.72,'#1a1a1a',-0.36,0.36,0);p(0.58,0.10,0.72,'#1a1a1a',0.36,0.36,0);\n  p(0.90,0.44,0.90,'#e83030',0,0.14,0);p(0.54,0.48,0.64,'#e83030',-0.38,0.20,0);p(0.54,0.48,0.64,'#e83030',0.38,0.20,0);\n  p(0.64,0.40,0.44,'#e83030',0,0.16,0.36);p(0.48,0.10,0.48,'#e83030',0,0.06,0);\n  p(0.20,0.16,0.06,'#1a1a1a',-0.24,0.50,0.44);p(0.12,0.10,0.04,'#f8f8f8',-0.24,0.50,0.47);\n  p(0.20,0.16,0.06,'#1a1a1a',0.24,0.50,0.44);p(0.12,0.10,0.04,'#f8f8f8',0.24,0.50,0.47);\n  p(0.22,0.09,0.05,'#1a1a1a',-0.24,0.62,0.44);p(0.22,0.09,0.05,'#1a1a1a',0.24,0.62,0.44);\n  p(0.30,0.09,0.05,'#1a1a1a',0,0.36,0.46);\n  return g;\n}",
  "buildJigglypuff": "function buildJigglypuff(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.80,0.70,0.80,'#ffb0c8',0,0.38,0);p(0.56,0.52,0.60,'#ffb0c8',-0.34,0.34,0);p(0.56,0.52,0.60,'#ffb0c8',0.34,0.34,0);\n  p(0.64,0.56,0.64,'#ffb0c8',0,0.38,0.32);p(0.60,0.42,0.50,'#ffb0c8',0,0.38,-0.28);\n  p(0.64,0.22,0.62,'#ffb0c8',0,0.68,0);p(0.46,0.14,0.44,'#ffb0c8',0,0.78,0);\n  p(0.14,0.14,0.06,'#f0f0ff',-0.22,0.48,0.40);p(0.28,0.28,0.08,'#6898f8',-0.22,0.48,0.42);p(0.10,0.10,0.05,'#1a1a1a',-0.22,0.48,0.45);\n  p(0.14,0.14,0.06,'#f0f0ff',0.22,0.48,0.40);p(0.28,0.28,0.08,'#6898f8',0.22,0.48,0.42);p(0.10,0.10,0.05,'#1a1a1a',0.22,0.48,0.45);\n  p(0.28,0.06,0.05,'#e05878',0,0.36,0.41);\n  p(0.08,0.12,0.06,'#ffb0c8',-0.28,0.74,0.06);p(0.06,0.08,0.05,'#e890b0',0,0.82,0.06);\n  p(0.26,0.24,0.14,'#ffb0c8',-0.44,0.26,0.12);p(0.14,0.10,0.10,'#ffb0c8',-0.54,0.14,0.14);\n  p(0.26,0.24,0.14,'#ffb0c8',0.44,0.26,0.12);p(0.14,0.10,0.10,'#ffb0c8',0.54,0.14,0.14);\n  p(0.22,0.18,0.22,'#ffb0c8',-0.24,0.04,0.08);p(0.22,0.18,0.22,'#ffb0c8',0.24,0.04,0.08);\n  p(0.10,0.06,0.14,'#ffb0c8',-0.28,0.00,0.16);p(0.10,0.06,0.14,'#ffb0c8',0.22,0.00,0.16);\n  return g;\n}",
  "buildAbra": "function buildAbra(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),tmat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.48,0.44,0.54,'#e8c840',0,0.24,0);p(0.34,0.28,0.40,'#c0a030',0,0.24,0.14);\n  p(0.36,0.36,0.38,'#e8c840',0,0.60,0.10);p(0.28,0.20,0.26,'#e8c840',0,0.52,0.32);\n  p(0.22,0.08,0.06,'#1a1a1a',-0.10,0.64,0.26);p(0.22,0.08,0.06,'#1a1a1a',0.10,0.64,0.26);\n  p(0.10,0.22,0.08,'#e8c840',-0.18,0.82,0.06);p(0.06,0.14,0.06,'#c0a030',-0.18,0.82,0.09);\n  p(0.10,0.22,0.08,'#e8c840',0.18,0.82,0.06);p(0.06,0.14,0.06,'#c0a030',0.18,0.82,0.09);\n  p(0.40,0.36,0.36,'#c0a030',0,0.06,0);\n  p(0.16,0.26,0.14,'#e8c840',-0.28,0.40,0.04);p(0.14,0.22,0.12,'#c0a030',-0.38,0.24,0.08);\n  p(0.16,0.26,0.14,'#e8c840',0.28,0.40,0.04);p(0.14,0.22,0.12,'#c0a030',0.38,0.24,0.08);\n  p(0.12,0.10,0.16,'#c0a030',-0.42,0.16,0.14);p(0.12,0.10,0.16,'#c0a030',0.42,0.16,0.14);\n  p(0.10,0.06,0.06,'#c0a030',-0.48,0.14,0.18);p(0.10,0.06,0.06,'#c0a030',0.48,0.14,0.18);\n  p(0.30,0.10,0.22,'#c0a030',-0.08,0.08,-0.22);p(0.26,0.10,0.22,'#c0a030',0.08,0.08,-0.22);\n  p(0.28,0.08,0.06,'#c0a030',0,0.48,0.02);\n  p(0.06,0.28,0.06,'#e8c840',0,0.54,-0.26);p(0.06,0.14,0.14,'#c0a030',0,0.42,-0.34);\n  return g;\n}",
  "buildAlakazam": "function buildAlakazam(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),tmat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.36,0.60,0.30,'#e8c840',0,0.32,0);p(0.26,0.34,0.22,'#c0a030',0,0.32,0.12);\n  p(0.44,0.44,0.40,'#e8c840',0,0.76,0.04);p(0.22,0.14,0.20,'#c0a030',0,0.62,0.24);\n  p(0.36,0.08,0.06,'#d0b050',-0.02,0.64,0.24);p(0.36,0.08,0.06,'#d0b050',0.02,0.60,0.24);\n  p(0.10,0.10,0.06,'#1a1a1a',-0.14,0.80,0.20);p(0.10,0.10,0.06,'#1a1a1a',0.14,0.80,0.20);\n  p(0.10,0.28,0.08,'#e8c840',-0.22,0.96,0.02);p(0.10,0.28,0.08,'#e8c840',0.22,0.96,0.02);\n  p(0.26,0.48,0.14,'#e8c840',-0.32,0.36,0.04);p(0.20,0.38,0.12,'#c0a030',-0.44,0.22,0.06);\n  p(0.26,0.48,0.14,'#e8c840',0.32,0.36,0.04);p(0.20,0.38,0.12,'#c0a030',0.44,0.22,0.06);\n  p(0.06,0.06,0.46,'#d0b050',-0.52,0.14,0.20);p(0.04,0.04,0.06,'#f0d880',-0.52,0.14,0.44);\n  p(0.06,0.06,0.46,'#d0b050',0.52,0.14,0.20);p(0.04,0.04,0.06,'#f0d880',0.52,0.14,0.44);\n  p(0.20,0.44,0.20,'#e8c840',-0.14,0.10,0.02);p(0.20,0.44,0.20,'#e8c840',0.14,0.10,0.02);\n  p(0.24,0.10,0.28,'#c0a030',-0.14,0.00,0.06);p(0.24,0.10,0.28,'#c0a030',0.14,0.00,0.06);\n  p(0.08,0.30,0.08,'#c0a030',0,0.14,-0.20);p(0.12,0.08,0.14,'#c0a030',0,0.04,-0.28);\n  return g;\n}",
  "buildGengar": "function buildGengar(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),tmat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.82,0.70,0.78,'#7850a0',0,0.38,0);p(0.60,0.52,0.60,'#604080',-0.38,0.30,0);p(0.60,0.52,0.60,'#604080',0.38,0.30,0);\n  p(0.66,0.54,0.64,'#7850a0',0,0.38,0.30);p(0.58,0.44,0.52,'#7850a0',0,0.40,-0.28);\n  p(0.74,0.40,0.70,'#7850a0',0,0.78,0);\n  p(0.18,0.26,0.08,'#604080',-0.36,1.02,-0.08);p(0.18,0.26,0.08,'#604080',0.36,1.02,-0.08);\n  p(0.12,0.20,0.08,'#604080',-0.20,1.10,-0.04);p(0.12,0.20,0.08,'#604080',0.20,1.10,-0.04);\n  p(0.22,0.18,0.08,'#e83060',-0.20,0.80,0.34);p(0.14,0.10,0.05,'#1a1a1a',-0.20,0.80,0.38);\n  p(0.22,0.18,0.08,'#e83060',0.20,0.80,0.34);p(0.14,0.10,0.05,'#1a1a1a',0.20,0.80,0.38);\n  p(0.54,0.18,0.08,'#f8f8f8',0,0.62,0.38);p(0.44,0.10,0.06,'#1a1a1a',0,0.62,0.40);\n  p(0.06,0.14,0.06,'#f8f8f8',-0.18,0.56,0.38);p(0.06,0.14,0.06,'#f8f8f8',-0.06,0.56,0.38);\n  p(0.06,0.14,0.06,'#f8f8f8',0.06,0.56,0.38);p(0.06,0.14,0.06,'#f8f8f8',0.18,0.56,0.38);\n  p(0.30,0.26,0.22,'#7850a0',-0.54,0.34,0.12);p(0.22,0.20,0.18,'#604080',-0.66,0.20,0.14);\n  p(0.30,0.26,0.22,'#7850a0',0.54,0.34,0.12);p(0.22,0.20,0.18,'#604080',0.66,0.20,0.14);\n  p(0.14,0.10,0.08,'#604080',-0.72,0.14,0.20);p(0.14,0.10,0.08,'#604080',0.72,0.14,0.20);\n  p(0.60,0.22,0.56,'#604080',0,0.06,0);\n  return g;\n}",
  "buildDoduo": "function buildDoduo(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.60,0.42,0.52,'#c8a840',0,0.22,0);p(0.44,0.24,0.38,'#e8c860',0,0.20,0.14);\n  p(0.12,0.54,0.12,'#c8a840',-0.14,0.62,0.04);p(0.12,0.54,0.12,'#c8a840',0.14,0.62,0.04);\n  p(0.36,0.34,0.32,'#c8a840',-0.14,0.96,0.04);p(0.36,0.34,0.32,'#c8a840',0.14,0.96,0.04);\n  p(0.10,0.08,0.22,'#e84820',-0.14,0.96,0.22);p(0.10,0.08,0.22,'#e84820',0.14,0.96,0.22);\n  p(0.10,0.10,0.05,'#1a1a1a',-0.22,1.02,0.18);p(0.10,0.10,0.05,'#1a1a1a',-0.06,1.02,0.18);\n  p(0.10,0.10,0.05,'#1a1a1a',0.06,1.02,0.18);p(0.10,0.10,0.05,'#1a1a1a',0.22,1.02,0.18);\n  p(0.10,0.16,0.08,'#c8a840',-0.22,1.10,0.02);p(0.10,0.16,0.08,'#c8a840',0.22,1.10,0.02);\n  p(0.20,0.16,0.18,'#c8a840',-0.16,0.08,0.06);p(0.20,0.16,0.18,'#c8a840',0.16,0.08,0.06);\n  p(0.08,0.28,0.08,'#e84820',-0.18,0.00,0.08);p(0.08,0.28,0.08,'#e84820',0.18,0.00,0.08);\n  p(0.22,0.06,0.14,'#e84820',-0.18,-0.02,0.14);p(0.22,0.06,0.14,'#e84820',0.18,-0.02,0.14);\n  p(0.08,0.06,0.10,'#e84820',-0.24,-0.02,0.12);p(0.08,0.06,0.10,'#e84820',0.22,-0.02,0.12);\n  p(0.26,0.20,0.24,'#c8a840',0,0.40,-0.28);p(0.08,0.14,0.08,'#e8c860',0,0.28,-0.34);\n  return g;\n}",
  "buildRapidash": "function buildRapidash(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.52,0.48,0.88,'#f8f8f8',0,0.42,0);p(0.38,0.34,0.70,'#f0f0f0',0,0.40,0.10);\n  p(0.32,0.40,0.30,'#f8f8f8',0,0.70,0.36);p(0.26,0.42,0.28,'#f8f8f8',0,0.88,0.52);\n  p(0.36,0.34,0.34,'#f8f8f8',0,1.04,0.60);p(0.18,0.10,0.26,'#f8f8f8',0,0.96,0.78);\n  p(0.06,0.06,0.20,'#f0f080',0,1.06,0.90);\n  p(0.10,0.10,0.05,'#1a1a1a',-0.14,1.10,0.74);p(0.10,0.10,0.05,'#1a1a1a',0.14,1.10,0.74);\n  p(0.06,0.24,0.06,'#f89020',0,0.88,0.36);p(0.10,0.36,0.10,'#f89020',0,1.02,0.26);\n  p(0.14,0.48,0.10,'#f89020',-0.10,1.00,0.20);p(0.14,0.48,0.10,'#f89020',0.10,1.00,0.20);\n  p(0.10,0.30,0.08,'#f8d840',-0.06,1.08,0.22);p(0.10,0.30,0.08,'#f8d840',0.06,1.08,0.22);\n  p(0.16,0.42,0.16,'#f8f8f8',-0.18,0.16,0.28);p(0.12,0.34,0.12,'#f0f0f0',-0.18,-0.04,0.28);p(0.16,0.10,0.20,'#f0f0f0',-0.18,-0.16,0.28);\n  p(0.16,0.42,0.16,'#f8f8f8',0.18,0.16,0.28);p(0.12,0.34,0.12,'#f0f0f0',0.18,-0.04,0.28);p(0.16,0.10,0.20,'#f0f0f0',0.18,-0.16,0.28);\n  p(0.16,0.42,0.16,'#f8f8f8',-0.18,0.16,-0.36);p(0.12,0.34,0.12,'#f0f0f0',-0.18,-0.04,-0.36);p(0.16,0.10,0.20,'#f0f0f0',-0.18,-0.16,-0.36);\n  p(0.16,0.42,0.16,'#f8f8f8',0.18,0.16,-0.36);p(0.12,0.34,0.12,'#f0f0f0',0.18,-0.04,-0.36);p(0.16,0.10,0.20,'#f0f0f0',0.18,-0.16,-0.36);\n  p(0.08,0.10,0.24,'#f89020',0,0.42,-0.52);p(0.10,0.18,0.12,'#f8d840',0,0.54,-0.62);p(0.08,0.26,0.08,'#f89020',0,0.64,-0.58);\n  return g;\n}",
  "buildHaunter": "function buildHaunter(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),tmat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.66,0.60,0.62,'#9060b8',0,0.40,0);p(0.48,0.44,0.48,'#7040a0',-0.32,0.32,0);p(0.48,0.44,0.48,'#7040a0',0.32,0.32,0);\n  p(0.56,0.50,0.54,'#9060b8',0,0.40,0.24);p(0.52,0.42,0.46,'#7040a0',0,0.38,-0.22);\n  p(0.62,0.48,0.60,'#9060b8',0,0.76,0);\n  p(0.16,0.24,0.08,'#7040a0',-0.34,1.00,-0.04);p(0.16,0.24,0.08,'#7040a0',0.34,1.00,-0.04);\n  p(0.22,0.20,0.08,'#e83060',-0.18,0.78,0.30);p(0.12,0.12,0.05,'#1a1a1a',-0.18,0.78,0.34);\n  p(0.22,0.20,0.08,'#e83060',0.18,0.78,0.30);p(0.12,0.12,0.05,'#1a1a1a',0.18,0.78,0.34);\n  p(0.40,0.14,0.08,'#f8f8f8',0,0.62,0.32);\n  p(0.06,0.12,0.06,'#f8f8f8',-0.12,0.56,0.34);p(0.06,0.12,0.06,'#f8f8f8',0.12,0.56,0.34);\n  p(0.26,0.24,0.22,'#c080ff',-0.72,0.56,0.16);p(0.08,0.18,0.08,'#c080ff',-0.82,0.46,0.20);p(0.08,0.18,0.08,'#c080ff',-0.72,0.44,0.22);\n  p(0.26,0.24,0.22,'#c080ff',0.72,0.56,0.16);p(0.08,0.18,0.08,'#c080ff',0.82,0.46,0.20);p(0.08,0.18,0.08,'#c080ff',0.72,0.44,0.22);\n  p(0.52,0.18,0.48,'#7040a0',0,0.06,0);\n  p(0.12,0.16,0.10,'#7040a0',-0.18,0.00,-0.08);p(0.12,0.16,0.10,'#7040a0',0,0.00,-0.10);p(0.12,0.16,0.10,'#7040a0',0.18,0.00,-0.08);\n  return g;\n}",
  "buildCaterpie": "function buildCaterpie(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.38,0.34,0.34,'#60c040',0,0.18,0.30);p(0.26,0.22,0.24,'#78d858',0,0.18,0.30);\n  p(0.10,0.10,0.05,'#1a1a1a',-0.12,0.24,0.45);p(0.10,0.10,0.05,'#1a1a1a',0.12,0.24,0.45);\n  p(0.04,0.04,0.14,'#e83030',0,0.32,0.40);p(0.06,0.08,0.06,'#e83030',0,0.40,0.44);\n  p(0.40,0.36,0.32,'#60c040',0,0.19,0);p(0.28,0.14,0.26,'#f8f840',0,0.10,0.02);\n  p(0.06,0.06,0.08,'#60c040',-0.20,0.06,0.04);p(0.06,0.06,0.08,'#60c040',0.20,0.06,0.04);\n  p(0.38,0.34,0.30,'#60c040',0,0.18,-0.32);p(0.26,0.12,0.24,'#f8f840',0,0.10,-0.30);\n  p(0.06,0.06,0.08,'#60c040',-0.20,0.06,-0.30);p(0.06,0.06,0.08,'#60c040',0.20,0.06,-0.30);\n  p(0.34,0.30,0.28,'#60c040',0,0.16,-0.62);p(0.24,0.10,0.22,'#f8f840',0,0.10,-0.60);\n  p(0.06,0.06,0.08,'#60c040',-0.18,0.06,-0.60);p(0.06,0.06,0.08,'#60c040',0.18,0.06,-0.60);\n  p(0.30,0.26,0.22,'#60c040',0,0.14,-0.88);p(0.08,0.18,0.06,'#e83030',0,0.28,-0.96);\n  p(0.06,0.06,0.08,'#78d858',-0.18,0.06,0.30);p(0.06,0.06,0.08,'#78d858',0.18,0.06,0.30);\n  p(0.06,0.06,0.08,'#78d858',-0.18,0.06,-0.62);p(0.06,0.06,0.08,'#78d858',0.18,0.06,-0.62);\n  return g;\n}",
  "buildMagnemite": "function buildMagnemite(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.56,0.52,0.50,'#a0a8b8',0,0.40,0);p(0.40,0.36,0.36,'#b8c0d0',0,0.40,0.12);\n  p(0.14,0.14,0.06,'#f0f0f0',0,0.40,0.25);p(0.10,0.10,0.04,'#1a1a1a',0,0.40,0.28);\n  p(0.08,0.08,0.06,'#e8d840',-0.10,0.46,0.24);p(0.08,0.08,0.06,'#e8d840',0.10,0.46,0.24);\n  p(0.08,0.08,0.06,'#e8d840',-0.10,0.34,0.24);p(0.08,0.08,0.06,'#e8d840',0.10,0.34,0.24);\n  p(0.38,0.12,0.12,'#808898',-0.44,0.40,0);p(0.14,0.22,0.14,'#e83030',-0.64,0.48,0);p(0.14,0.22,0.14,'#f8f8f8',-0.64,0.32,0);\n  p(0.38,0.12,0.12,'#808898',0.44,0.40,0);p(0.14,0.22,0.14,'#e83030',0.64,0.48,0);p(0.14,0.22,0.14,'#f8f8f8',0.64,0.32,0);\n  p(0.06,0.06,0.04,'#c0c8d8',-0.20,0.54,0.24);p(0.06,0.06,0.04,'#c0c8d8',0.20,0.54,0.24);\n  p(0.06,0.06,0.04,'#c0c8d8',-0.20,0.26,0.24);p(0.06,0.06,0.04,'#c0c8d8',0.20,0.26,0.24);\n  p(0.06,0.06,0.04,'#c0c8d8',-0.26,0.40,0.24);p(0.06,0.06,0.04,'#c0c8d8',0.26,0.40,0.24);\n  p(0.30,0.08,0.08,'#808898',0,0.40,-0.26);\n  p(0.04,0.04,0.18,'#e8d840',0,0.50,-0.28);p(0.04,0.04,0.18,'#e8d840',0,0.30,-0.28);\n  p(0.12,0.04,0.04,'#808898',-0.44,0.50,0);p(0.12,0.04,0.04,'#808898',0.44,0.50,0);\n  p(0.18,0.18,0.04,'#c0c8d8',0,0.40,0.14);\n  return g;\n}",
  "buildGeodude": "function buildGeodude(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.64,0.58,0.62,'#c8a870',0,0.32,0);p(0.48,0.42,0.48,'#b89050',-0.28,0.28,0.10);p(0.48,0.42,0.48,'#b89050',0.28,0.28,0.10);\n  p(0.52,0.44,0.50,'#c8a870',0,0.32,0.22);p(0.46,0.36,0.44,'#b89050',0,0.30,-0.20);\n  p(0.46,0.22,0.44,'#c8a870',0,0.58,0);\n  p(0.14,0.14,0.06,'#f0f0e8',-0.14,0.42,0.30);p(0.08,0.09,0.04,'#1a1a1a',-0.14,0.42,0.33);\n  p(0.14,0.14,0.06,'#f0f0e8',0.14,0.42,0.30);p(0.08,0.09,0.04,'#1a1a1a',0.14,0.42,0.33);\n  p(0.22,0.08,0.06,'#604020',-0.14,0.54,0.30);p(0.22,0.08,0.06,'#604020',0.14,0.54,0.30);\n  p(0.28,0.06,0.05,'#604020',0,0.30,0.33);\n  p(0.16,0.14,0.14,'#a08050',-0.34,0.48,-0.12);p(0.12,0.10,0.10,'#604020',-0.28,0.56,0.14);\n  p(0.16,0.14,0.14,'#a08050',0.34,0.48,-0.12);p(0.12,0.10,0.10,'#604020',0.28,0.56,0.14);\n  p(0.20,0.16,0.18,'#c8a870',-0.46,0.28,0.12);p(0.24,0.20,0.22,'#a08050',-0.64,0.18,0.16);\n  p(0.08,0.08,0.10,'#604020',-0.72,0.12,0.20);p(0.08,0.08,0.10,'#604020',-0.60,0.10,0.22);\n  p(0.20,0.16,0.18,'#c8a870',0.46,0.28,0.12);p(0.24,0.20,0.22,'#a08050',0.64,0.18,0.16);\n  p(0.08,0.08,0.10,'#604020',0.72,0.12,0.20);p(0.08,0.08,0.10,'#604020',0.60,0.10,0.22);\n  return g;\n}",
  "buildHero_dragon_ball_no_complete": "function buildHero_dragon_ball_no_complete(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(1.00,0.78,1.18,'#e67e22',0,0.39,0);\n  p(0.56,0.56,0.12,'#f3d9b1',0,0.36,0.53);\n  p(0.34,0.20,0.24,'#e67e22',0,0.76,0.42);\n  p(0.72,0.52,0.66,'#e67e22',0,1.06,0.70);\n  p(0.46,0.24,0.26,'#e67e22',0,0.98,1.10);\n  p(0.10,0.16,0.14,'#f3d9b1',-0.20,1.38,0.56);\n  p(0.10,0.16,0.14,'#f3d9b1',0.20,1.38,0.56);\n  const wGeo=geo(0.14,0.78,0.92);\n  const wMatL=new THREE.MeshToonMaterial({color:'#2aa6a4',flatShading:true});\n  const wMatR=new THREE.MeshToonMaterial({color:'#2aa6a4',flatShading:true});\n  const wL=new THREE.Mesh(wGeo,wMatL); wL.position.set(-0.57,0.82,-0.02); wL.castShadow=true; g.add(wL);\n  const wR=new THREE.Mesh(wGeo,wMatR); wR.position.set(0.57,0.82,-0.02);  wR.castShadow=true; g.add(wR);\n  p(0.24,0.28,0.28,'#e67e22',-0.24,0.14,0.12);\n  p(0.24,0.28,0.28,'#e67e22',0.24,0.14,0.12);\n  p(0.24,0.24,0.68,'#e67e22',0,0.30,-0.90);\n  p(0.18,0.18,0.18,'#f4c542',0,0.34,-1.32);\n  return g;\n}",
  "buildCollectible": "function buildCollectible(){\n  const g=new THREE.Group();\n  const gem=new THREE.Mesh(geo(0.42,0.42,0.42),mat('#f4c542'));\n  gem.position.set(0,0.21,0); gem.castShadow=true; g.add(gem);\n  const star=new THREE.Mesh(geo(0.18,0.18,0.10),mat('#8c3b2a'));\n  star.position.set(0,0.21,0.21); star.castShadow=true; g.add(star);\n  const glow=new THREE.PointLight('#f4c542',0.8,5,2);\n  glow.position.set(0,0.2,0.2); g.add(glow);\n  return g;\n}",
  "buildPlatformTile": "function buildPlatformTile(){\n  const g=new THREE.Group();\n  const tile=new THREE.Mesh(geo(6.0,0.12,TILE.depth),mat('#d8c7a1'));\n  tile.position.set(0,0.06,0); tile.receiveShadow=true; g.add(tile);\n  const border=new THREE.Mesh(geo(5.7,0.02,TILE.depth-0.18),mat('#caa56a'));\n  border.position.set(0,0.13,0); g.add(border);\n  return g;\n}",
  "buildDecoA": "function buildDecoA(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.34,0.18,0.20,'#1a1a1a',0,0.09,0);p(0.62,0.52,0.34,'#caa56a',0,0.44,0);\n  p(0.40,0.36,0.34,'#f3d9b1',0,0.88,0.04);p(0.24,0.22,0.12,'#f2f2f0',0,0.76,0.21);\n  p(0.05,0.05,0.08,'#1a1a1a',-0.08,0.90,0.20);p(0.05,0.05,0.08,'#1a1a1a',0.08,0.90,0.20);\n  return g;\n}",
  "buildDecoB": "function buildDecoB(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.46,0.24,0.24,'#8c3b2a',0,0.12,0);p(0.92,0.66,0.46,'#8c3b2a',0,0.57,0);\n  p(0.52,0.38,0.36,'#caa56a',0,1.07,0.06);\n  p(0.12,0.14,0.12,'#f3d9b1',-0.26,1.24,0.02);p(0.12,0.14,0.12,'#f3d9b1',0.26,1.24,0.02);\n  return g;\n}",
  "buildDecoC": "function buildDecoC(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.12,0.26,0.12,'#5e2418',-0.08,0.13,0);p(0.12,0.26,0.12,'#5e2418',0.08,0.13,0);\n  p(0.42,0.42,0.24,'#2aa6a4',0,0.47,0);p(0.30,0.28,0.24,'#f3d9b1',0,0.82,0.02);\n  p(0.32,0.12,0.26,'#5e2418',0,0.96,0.02);p(0.10,0.16,0.12,'#5e2418',-0.15,0.84,0.10);\n  return g;\n}",
  "buildBackgroundProp": "function buildBackgroundProp(){\n  const g=new THREE.Group();\n  function p(w,h,d,c,x,y,z){const m=new THREE.Mesh(geo(w,h,d),mat(c));m.position.set(x,y,z);m.castShadow=true;g.add(m);}\n  p(0.34,0.28,0.18,'#5e2418',0,0.14,0);p(0.52,0.50,0.26,'#caa56a',0,0.53,0);\n  p(0.66,0.16,0.16,'#caa56a',0,0.54,0.12);p(0.32,0.30,0.28,'#f3d9b1',0,0.93,0.02);\n  p(0.34,0.12,0.30,'#5e2418',0,1.08,0.02);\n  return g;\n}",
  "buildStaticWorld": "function buildStaticWorld(){\n  bgGroup=new THREE.Group(); scene.add(bgGroup);\n  const groundMat=mat('#e6d29a');\n  const sideGeo=geo(8,0.2,60);\n  const lG=new THREE.Mesh(sideGeo,groundMat); lG.position.set(-7.5,-0.24,-18); bgGroup.add(lG);\n  const rG=new THREE.Mesh(sideGeo,groundMat); rG.position.set(7.5,-0.24,-18); bgGroup.add(rG);\n  for(let i=0;i<4;i++){\n    const prop=buildBackgroundProp(); prop.scale.setScalar(1.6);\n    prop.position.set(i%2===0?-6.0:6.0,0,-12-i*8); bgGroup.add(prop);\n  }\n}",
  "buildHero_jungle": "function buildHero_jungle() {\n  const g = new THREE.Group();\n  g.name = 'hero';\n  addPart(g, geos.heroBody, materials.heroDark, 0, 0.69, 0, 'body');\n  addPart(g, geos.heroChest, materials.heroChest, 0, 0.64, 0.33, 'chest_plate');\n  addPart(g, geos.heroHead, materials.heroDark, 0, 1.35, 0.10, 'head');\n  addPart(g, geos.heroBrow, materials.heroDark, 0, 1.46, 0.31, 'brow');\n  addPart(g, geos.heroMuzzle, materials.heroChest, 0, 1.24, 0.34, 'muzzle');\n  addPart(g, geos.eye, materials.black, -0.14, 1.33, 0.37, 'eye_l');\n  addPart(g, geos.eye, materials.black, 0.14, 1.33, 0.37, 'eye_r');\n  addPart(g, geos.arm, materials.heroDark, -0.62, 0.49, 0.05, 'arm_l');\n  addPart(g, geos.arm, materials.heroDark, 0.62, 0.49, 0.05, 'arm_r');\n  addPart(g, geos.leg, materials.heroDark, -0.22, 0.17, 0, 'leg_l');\n  addPart(g, geos.leg, materials.heroDark, 0.22, 0.17, 0, 'leg_r');\n\n  g.position.set(0, PHYSICS.playerGroundY, SPAWN.heroZ);\n  g.userData = { hitTimer: 0, hitFlash: 0, collectTimer: 0 };\n  return g;\n}",
  "buildObstacleGround": "function buildObstacleGround() {\n  const g = new THREE.Group();\n  g.name = 'obstacle_ground';\n  addPart(g, geos.logBody, materials.earth, 0, 0.18, 0, 'log_body');\n  addPart(g, geos.logCap, materials.bark, -0.64, 0.18, 0, 'end_cap_l');\n  addPart(g, geos.logCap, materials.bark, 0.64, 0.18, 0, 'end_cap_r');\n  g.userData = { bobAmp: 0.08, bobPhase: Math.random() * Math.PI * 2, baseY: 0 };\n  return g;\n}",
  "buildObstacleAerial": "function buildObstacleAerial() {\n  const g = new THREE.Group();\n  g.name = 'obstacle_aerial';\n  addPart(g, geos.toucanBody, materials.heroDark, 0, 0.21, 0, 'body');\n  addPart(g, geos.toucanHead, materials.heroDark, 0, 0.48, 0.20, 'head');\n  addPart(g, geos.toucanBeakBase, materials.fruitOrange, 0, 0.48, 0.6, 'beak_base');\n  addPart(g, geos.toucanBeakTip, materials.fruitGold, 0, 0.5, 0.95, 'beak_tip');\n  addPart(g, geos.toucanWing, materials.fruitGold, -0.56, 0.24, 0, 'wing_l');\n  addPart(g, geos.toucanWing, materials.fruitGold, 0.56, 0.24, 0, 'wing_r');\n  addPart(g, geos.toucanEye, materials.black, -0.1, 0.52, 0.34, 'eye_l');\n  addPart(g, geos.toucanEye, materials.black, 0.1, 0.52, 0.34, 'eye_r');\n  g.userData = { bobAmp: 0.10, bobPhase: Math.random() * Math.PI * 2, baseY: 0, wingFlip: Math.random() * 10 };\n  return g;\n}",
  "buildBackgroundWall": "function buildBackgroundWall(x, z, scaleY) {\n  const mesh = new THREE.Mesh(geos.backgroundBlock, materials.moss);\n  mesh.position.set(x, 0.9 * scaleY, z);\n  mesh.scale.y = scaleY;\n  return mesh;\n}",
  "buildCanopy": "function buildCanopy(z, y) {\n  const mesh = new THREE.Mesh(geos.canopyBlock, materials.moss);\n  mesh.position.set(0, y, z);\n  return mesh;\n}",
  "buildHero_pokemon": "function buildHero_pokemon() {\n  const hero = new THREE.Group();\n\n  // Body\n  const bodyGeom = buildSphere(0.38, 8, 6);\n  const body     = new THREE.Mesh(bodyGeom, MAT_BODY);\n  body.position.set(0, 0.4, 0);\n  body.castShadow = true;\n  hero.add(body);\n\n  // Belly patch\n  const bellyGeom = buildSphere(0.22, 6, 5);\n  const belly = new THREE.Mesh(bellyGeom, MAT_BELLY);\n  belly.position.set(0, 0.34, 0.3);\n  belly.scale.set(1, 0.7, 0.4);\n  hero.add(belly);\n\n  // Bulb on back\n  const bulbGeom = buildSphere(0.22, 6, 5);\n  const bulb = new THREE.Mesh(bulbGeom, MAT_BULB);\n  bulb.position.set(0, 0.72, -0.2);\n  hero.add(bulb);\n\n  // Bulb inner (lighter)\n  const bulbInnerGeom = buildSphere(0.12, 5, 4);\n  const bulbInner = new THREE.Mesh(bulbInnerGeom, MAT_BELLY);\n  bulbInner.position.set(0, 0.85, -0.14);\n  hero.add(bulbInner);\n\n  // Eyes\n  const eyeGeom = buildSphere(0.07, 5, 4);\n  const eyeL = new THREE.Mesh(eyeGeom, MAT_EYE);\n  const eyeR = eyeL.clone();\n  eyeL.position.set(-0.18, 0.52, 0.32);\n  eyeR.position.set( 0.18, 0.52, 0.32);\n  hero.add(eyeL, eyeR);\n\n  // Legs (4)\n  const legGeom = buildBox(0.15, 0.22, 0.18);\n  const legPositions = [\n    [-0.22, 0.12, 0.16], [ 0.22, 0.12, 0.16],\n    [-0.20, 0.12,-0.12], [ 0.20, 0.12,-0.12],\n  ];\n  legPositions.forEach(p => {\n    const leg = new THREE.Mesh(legGeom, MAT_BODY);\n    leg.position.set(...p);\n    leg.castShadow = true;\n    hero.add(leg);\n  });\n\n  return hero;\n}",
  "buildHero_wreck_it": "function buildHero_wreck_it() {\n  const group = new THREE.Group();\n  group.add(makePart(GEO.g026_052_028, MAT.brown,     -0.22, 0.26,  0,    false)); // leg_l\n  group.add(makePart(GEO.g026_052_028, MAT.brown,      0.22, 0.26,  0,    false)); // leg_r\n  group.add(makePart(GEO.g100_092_062, MAT.red,         0,   0.98,  0,    false)); // torso\n  group.add(makePart(GEO.g014_076_008, MAT.brown,     -0.20, 1.06,  0.27, false)); // overall_strap_l\n  group.add(makePart(GEO.g014_076_008, MAT.brown,      0.20, 1.06,  0.27, false)); // overall_strap_r\n  group.add(makePart(GEO.g022_052_022, MAT.red,        -0.61, 0.96,  0.02, false)); // arm_l\n  group.add(makePart(GEO.g022_052_022, MAT.red,         0.61, 0.96,  0.02, false)); // arm_r\n  group.add(makePart(GEO.g034_028_034, MAT.skin,       -0.61, 0.74,  0.23, true));  // fist_l\n  group.add(makePart(GEO.g034_028_034, MAT.skin,        0.61, 0.74,  0.23, true));  // fist_r\n  group.add(makePart(GEO.g070_054_056, MAT.skin,        0,    1.75,  0.05, false)); // head\n  group.add(makePart(GEO.g076_018_038, MAT.darkBrown,   0,    2.11,  0.02, false)); // hair_main\n  group.add(makePart(GEO.g020_016_018, MAT.darkBrown,  -0.24, 2.19,  0.10, false)); // hair_lump_l\n  group.add(makePart(GEO.g016_012_016, MAT.darkBrown,   0.22, 2.17, -0.02, false)); // hair_lump_r\n  return group;\n}",
  "buildCandyCastleTower": "function buildCandyCastleTower() {\n  const group = new THREE.Group();\n  group.add(makePart(GEO.g110_140_110, MAT.blue,  0, 0.70, 0, false)); // tower_base\n  group.add(makePart(GEO.g072_062_072, MAT.pink,  0, 1.71, 0, false)); // tower_mid\n  group.add(makePart(GEO.g040_026_040, MAT.cream, 0, 2.15, 0, true));  // tower_cap\n  return group;\n}",
  "buildLollipopPost": "function buildLollipopPost() {\n  const group = new THREE.Group();\n  group.add(makePart(GEO.g014_180_014, MAT.brown, 0, 0.90, 0,    false)); // pole\n  group.add(makePart(GEO.g062_062_012, MAT.pink,  0, 1.74, 0,    true));  // sign_face\n  group.add(makePart(GEO.g046_010_013, MAT.cream, 0, 1.74, 0.01, false)); // stripe_h\n  group.add(makePart(GEO.g010_046_013, MAT.cream, 0, 1.74, 0.01, false)); // stripe_v\n  return group;\n}",
  "buildArcadeBeacon": "function buildArcadeBeacon() {\n  const group = new THREE.Group();\n  group.add(makePart(GEO.g046_046_046, MAT.blue, 0, 1.60, 0, true));  // beacon_body\n  group.add(makePart(GEO.g062_010_062, MAT.gold, 0, 1.93, 0, false)); // halo\n  return group;\n}",
  "buildDistantSugarHills": "function buildDistantSugarHills() {\n  const group = new THREE.Group();\n  group.add(makePart(GEO.g800_160_220, MAT.blue,  0,    0.80, -22, false)); // hill_back\n  group.add(makePart(GEO.g640_120_200, MAT.pink, -1.20, 0.60, -20, false)); // hill_mid\n  group.add(makePart(GEO.g520_096_180, MAT.cream, 1.60, 0.48, -18, false)); // hill_front\n  return group;\n}",
  "buildFruitCollectible": "function buildFruitCollectible() {\n  const group = new THREE.Group();\n  addPart(group, WORLD_GEOMETRY.fruitBase, WORLD_MATERIALS.accent, 0, 0.12, 0, 'fruit_base');\n  addPart(group, WORLD_GEOMETRY.fruitMid, WORLD_MATERIALS.accent, 0, 0.31, 0, 'fruit_mid');\n  addPart(group, WORLD_GEOMETRY.fruitTop, WORLD_MATERIALS.obstacleAlt, 0, 0.46, 0, 'fruit_top');\n  addPart(group, WORLD_GEOMETRY.bananaStem, WORLD_MATERIALS.decorMain, 0, 0.64, 0, 'fruit_stem');\n  group.userData.type = 'fruit';\n  return group;\n}",
  "buildCheckpointBanana": "function buildCheckpointBanana() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.36, 0.16, 0.22), WORLD_MATERIALS.checkpoint, -0.18, 0.3, 0, 'banana_l', { rotation: { z: -0.25 } });\n  addPart(group, new THREE.BoxGeometry(0.36, 0.16, 0.22), WORLD_MATERIALS.checkpoint, 0.18, 0.3, 0, 'banana_r', { rotation: { z: 0.25 } });\n  addPart(group, WORLD_GEOMETRY.bananaStem, WORLD_MATERIALS.decorMain, 0, 0.54, 0, 'banana_stem');\n  addPart(group, WORLD_GEOMETRY.pedestalMini, WORLD_MATERIALS.emissive, 0, 0.02, 0, 'banana_glow');\n  group.userData.type = 'checkpoint';\n  return group;\n}",
  "buildToucanGlider": "function buildToucanGlider() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.0, 0.42, 0.6), WORLD_MATERIALS.obstacleMain, 0, 0.2, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.44, 0.34, 0.34), WORLD_MATERIALS.obstacleMain, 0, 0.46, 0.2, 'head');\n  addPart(group, new THREE.BoxGeometry(0.24, 0.18, 0.44), WORLD_MATERIALS.obstacleAlt, 0, 0.44, 0.6, 'beak');\n  addPart(group, new THREE.BoxGeometry(0.14, 0.12, 0.92), WORLD_MATERIALS.accent, -0.56, 0.26, 0, 'wing_l');\n  addPart(group, new THREE.BoxGeometry(0.14, 0.12, 0.92), WORLD_MATERIALS.accent, 0.56, 0.26, 0, 'wing_r');\n  group.userData.animKind = 'fly';\n  group.userData.baseY = 1.8;\n  group.userData.collisionHalf = COLLISION.aerialObstacleHalf;\n  return group;\n}",
  "buildBambooSpikes": "function buildBambooSpikes() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.2, 0.18, 0.6), WORLD_MATERIALS.obstacleAlt, 0, 0.09, 0, 'base');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.72, 0.18), WORLD_MATERIALS.obstacleMain, -0.28, 0.42, 0, 'spike_l');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.86, 0.18), WORLD_MATERIALS.obstacleMain, 0, 0.49, 0, 'spike_m');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.68, 0.18), WORLD_MATERIALS.obstacleMain, 0.28, 0.38, 0, 'spike_r');\n  group.userData.animKind = 'still';\n  group.userData.baseY = 0;\n  group.userData.collisionHalf = COLLISION.groundObstacleHalf;\n  return group;\n}",
  "buildBambooBundle": "function buildBambooBundle() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.1, 0.62, 0.62), WORLD_MATERIALS.obstacleMain, 0, 0.31, 0, 'bundle');\n  addPart(group, new THREE.BoxGeometry(1.2, 0.08, 0.08), WORLD_MATERIALS.accent, 0, 0.22, 0.24, 'band_1');\n  addPart(group, new THREE.BoxGeometry(1.2, 0.08, 0.08), WORLD_MATERIALS.accent, 0, 0.4, -0.24, 'band_2');\n  group.userData.animKind = 'roll';\n  group.userData.baseY = 1.1;\n  group.userData.collisionHalf = COLLISION.aerialObstacleHalf;\n  return group;\n}",
  "buildStoneWall": "function buildStoneWall(options) {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.2, 1.0, 0.44), WORLD_MATERIALS.obstacleMain, 0, 0.5, 0, 'wall');\n  addPart(group, new THREE.BoxGeometry(1.28, 0.18, 0.48), WORLD_MATERIALS.obstacleAlt, 0, 0.96, 0, 'cap');\n  if (options && options.wallOfHonor) {\n    addPart(group, new THREE.BoxGeometry(0.14, 0.14, 0.08), WORLD_MATERIALS.accent, -0.18, 0.55, 0.24, 'honor_l');\n    addPart(group, new THREE.BoxGeometry(0.14, 0.14, 0.08), WORLD_MATERIALS.accent, 0.18, 0.55, 0.24, 'honor_r');\n  }\n  group.userData.animKind = 'still';\n  group.userData.baseY = 0;\n  group.userData.collisionHalf = COLLISION.groundObstacleHalf;\n  return group;\n}",
  "buildDragonKite": "function buildDragonKite() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.1, 0.16, 0.82), WORLD_MATERIALS.obstacleAlt, 0, 0.1, 0, 'wing');\n  addPart(group, new THREE.BoxGeometry(0.34, 0.22, 0.28), WORLD_MATERIALS.obstacleMain, 0, 0.18, 0.1, 'head');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.12, 0.9), WORLD_MATERIALS.accent, 0, 0.02, -0.48, 'tail');\n  group.userData.animKind = 'glide';\n  group.userData.baseY = 1.8;\n  group.userData.collisionHalf = COLLISION.aerialObstacleHalf;\n  return group;\n}",
  "buildDumpster": "function buildDumpster() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.1, 0.82, 0.72), WORLD_MATERIALS.obstacleMain, 0, 0.41, 0, 'bin');\n  addPart(group, new THREE.BoxGeometry(1.12, 0.12, 0.76), WORLD_MATERIALS.obstacleAlt, 0, 0.86, -0.02, 'lid');\n  group.userData.animKind = 'still';\n  group.userData.baseY = 0;\n  group.userData.collisionHalf = COLLISION.groundObstacleHalf;\n  return group;\n}",
  "buildTaxi": "function buildTaxi() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.14, 0.52, 0.72), WORLD_MATERIALS.obstacleAlt, 0, 0.26, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.66, 0.26, 0.56), WORLD_MATERIALS.neutral, 0, 0.56, -0.02, 'roof');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.18), WORLD_MATERIALS.dark, -0.34, 0.05, 0.22, 'wheel_lf');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.18), WORLD_MATERIALS.dark, 0.34, 0.05, 0.22, 'wheel_rf');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.18), WORLD_MATERIALS.dark, -0.34, 0.05, -0.22, 'wheel_lr');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.18), WORLD_MATERIALS.dark, 0.34, 0.05, -0.22, 'wheel_rr');\n  group.userData.animKind = 'hover_roll';\n  group.userData.baseY = 1.25;\n  group.userData.collisionHalf = COLLISION.aerialObstacleHalf;\n  return group;\n}",
  "buildSkullRock": "function buildSkullRock() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.04, 0.84, 0.76), WORLD_MATERIALS.obstacleMain, 0, 0.42, 0, 'skull');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.12), WORLD_MATERIALS.dark, -0.18, 0.52, 0.28, 'eye_l');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.18, 0.12), WORLD_MATERIALS.dark, 0.18, 0.52, 0.28, 'eye_r');\n  addPart(group, new THREE.BoxGeometry(0.14, 0.18, 0.1), WORLD_MATERIALS.dark, 0, 0.34, 0.3, 'nose');\n  group.userData.animKind = 'still';\n  group.userData.baseY = 0;\n  group.userData.collisionHalf = COLLISION.groundObstacleHalf;\n  return group;\n}",
  "buildVulture": "function buildVulture() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.72, 0.32, 0.46), WORLD_MATERIALS.obstacleMain, 0, 0.18, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.28, 0.22, 0.22), WORLD_MATERIALS.obstacleMain, 0, 0.42, 0.18, 'head');\n  addPart(group, new THREE.BoxGeometry(0.16, 0.08, 0.2), WORLD_MATERIALS.accent, 0, 0.36, 0.34, 'beak');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.08, 0.9), WORLD_MATERIALS.obstacleAlt, -0.46, 0.22, 0, 'wing_l');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.08, 0.9), WORLD_MATERIALS.obstacleAlt, 0.46, 0.22, 0, 'wing_r');\n  group.userData.animKind = 'vulture';\n  group.userData.baseY = 1.9;\n  group.userData.collisionHalf = COLLISION.aerialObstacleHalf;\n  return group;\n}",
  "buildMonkeyLookout": "function buildMonkeyLookout() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.9, 0.1, 0.2), WORLD_MATERIALS.obstacleMain, 0, 0.42, 0, 'branch');\n  addPart(group, new THREE.BoxGeometry(0.34, 0.3, 0.28), WORLD_MATERIALS.neutral, 0, 0.72, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.24, 0.22, 0.22), WORLD_MATERIALS.neutral, 0.08, 0.98, 0.06, 'head');\n  addPart(group, new THREE.BoxGeometry(0.1, 0.24, 0.1), WORLD_MATERIALS.obstacleMain, -0.18, 0.68, 0.04, 'arm_l');\n  addPart(group, new THREE.BoxGeometry(0.1, 0.28, 0.1), WORLD_MATERIALS.obstacleMain, 0.18, 0.62, 0.08, 'arm_r');\n  addPart(group, new THREE.BoxGeometry(0.1, 0.1, 0.48), WORLD_MATERIALS.obstacleMain, -0.22, 0.58, -0.18, 'tail');\n  group.userData.animKind = 'watcher';\n  return group;\n}",
  "buildParrotHover": "function buildParrotHover() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.38, 0.34, 0.3), WORLD_MATERIALS.obstacleAlt, 0, 0.82, 0.06, 'body');\n  addPart(group, new THREE.BoxGeometry(0.26, 0.24, 0.24), WORLD_MATERIALS.obstacleAlt, 0, 1.06, 0.14, 'head');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.1, 0.2), WORLD_MATERIALS.accent, 0, 1.0, 0.34, 'beak');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.14, 0.58), WORLD_MATERIALS.accent, -0.24, 0.84, 0.08, 'wing_l');\n  addPart(group, new THREE.BoxGeometry(0.12, 0.14, 0.58), WORLD_MATERIALS.accent, 0.24, 0.84, 0.08, 'wing_r');\n  group.userData.animKind = 'hover';\n  return group;\n}",
  "buildLemurWatcher": "function buildLemurWatcher() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.3, 0.34, 0.24), WORLD_MATERIALS.decorAlt, 0, 0.72, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.24, 0.22, 0.22), WORLD_MATERIALS.decorMain, 0, 0.98, 0.04, 'head');\n  addPart(group, new THREE.BoxGeometry(0.1, 0.62, 0.1), WORLD_MATERIALS.decorMain, 0.18, 0.42, -0.08, 'tail');\n  group.userData.animKind = 'watcher';\n  return group;\n}",
  "buildElephantBackdrop": "function buildElephantBackdrop() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(1.2, 0.72, 0.72), WORLD_MATERIALS.decorAlt, 0, 0.52, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.46, 0.42, 0.42), WORLD_MATERIALS.decorAlt, 0.48, 0.6, 0.02, 'head');\n  addPart(group, new THREE.BoxGeometry(0.16, 0.42, 0.16), WORLD_MATERIALS.decorMain, 0.58, 0.26, 0.18, 'trunk');\n  group.userData.animKind = 'slow';\n  return group;\n}",
  "buildBambooStalk": "function buildBambooStalk() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.2, 1.8, 0.2), WORLD_MATERIALS.decorMain, 0, 0.9, 0, 'stalk');\n  addPart(group, new THREE.BoxGeometry(0.5, 0.1, 0.24), WORLD_MATERIALS.groundAccent, -0.22, 1.45, 0, 'leaf_l', { rotation: { z: 0.35 } });\n  addPart(group, new THREE.BoxGeometry(0.5, 0.1, 0.24), WORLD_MATERIALS.groundAccent, 0.22, 1.3, 0, 'leaf_r', { rotation: { z: -0.35 } });\n  group.userData.animKind = 'sway';\n  return group;\n}",
  "buildFireflyCluster": "function buildFireflyCluster() {\n  const group = new THREE.Group();\n  for (let i = 0; i < 3; i++) {\n    const lightOrb = addPart(group, new THREE.BoxGeometry(0.06, 0.06, 0.06), WORLD_MATERIALS.emissive, randRange(-0.2, 0.2), randRange(0.8, 1.3), randRange(-0.2, 0.2), 'orb_' + i);\n    const point = new THREE.PointLight(0xffeb88, 0.25, 3, 2);\n    point.position.copy(lightOrb.position);\n    group.add(point);\n  }\n  group.userData.animKind = 'fireflies';\n  return group;\n}",
  "buildGuardTower": "function buildGuardTower() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.8, 1.8, 0.8), WORLD_MATERIALS.decorMain, 0, 0.9, 0, 'tower');\n  addPart(group, new THREE.BoxGeometry(1.0, 0.16, 1.0), WORLD_MATERIALS.obstacleAlt, 0, 1.74, 0, 'roof');\n  group.userData.animKind = 'slow';\n  return group;\n}",
  "buildLanternPost": "function buildLanternPost() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.12, 1.8, 0.12), WORLD_MATERIALS.decorMain, 0, 0.9, 0, 'post');\n  addPart(group, new THREE.BoxGeometry(0.38, 0.38, 0.38), WORLD_MATERIALS.emissive, 0, 1.54, 0, 'lantern');\n  group.userData.animKind = 'lantern';\n  return group;\n}",
  "buildNeonSign": "function buildNeonSign() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.18, 1.4, 0.18), WORLD_MATERIALS.decorMain, 0, 0.7, 0, 'post');\n  addPart(group, new THREE.BoxGeometry(0.92, 0.34, 0.14), WORLD_MATERIALS.sign, 0.44, 1.18, 0, 'panel');\n  group.userData.animKind = 'neon';\n  return group;\n}",
  "buildHydrant": "function buildHydrant() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.26, 0.44, 0.26), WORLD_MATERIALS.obstacleAlt, 0, 0.22, 0, 'body');\n  addPart(group, new THREE.BoxGeometry(0.46, 0.14, 0.14), WORLD_MATERIALS.obstacleAlt, 0, 0.3, 0, 'crossbar');\n  group.userData.animKind = 'still';\n  return group;\n}",
  "buildStreetlamp": "function buildStreetlamp() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.12, 1.9, 0.12), WORLD_MATERIALS.decorMain, 0, 0.95, 0, 'pole');\n  addPart(group, new THREE.BoxGeometry(0.52, 0.08, 0.08), WORLD_MATERIALS.decorMain, 0.18, 1.72, 0, 'arm');\n  addPart(group, new THREE.BoxGeometry(0.22, 0.3, 0.22), WORLD_MATERIALS.emissive, 0.42, 1.5, 0, 'lamp');\n  group.userData.animKind = 'lamp';\n  return group;\n}",
  "buildCactus": "function buildCactus() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.34, 1.1, 0.34), WORLD_MATERIALS.decorMain, 0, 0.55, 0, 'stem');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.52, 0.18), WORLD_MATERIALS.decorMain, -0.22, 0.68, 0, 'arm_l');\n  addPart(group, new THREE.BoxGeometry(0.18, 0.46, 0.18), WORLD_MATERIALS.decorMain, 0.22, 0.56, 0, 'arm_r');\n  group.userData.animKind = 'slow';\n  return group;\n}",
  "buildBones": "function buildBones() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.6, 0.12, 0.12), WORLD_MATERIALS.decorAlt, -0.14, 0.06, 0, 'bone_l', { rotation: { z: 0.35 } });\n  addPart(group, new THREE.BoxGeometry(0.6, 0.12, 0.12), WORLD_MATERIALS.decorAlt, 0.14, 0.06, 0, 'bone_r', { rotation: { z: -0.35 } });\n  group.userData.animKind = 'still';\n  return group;\n}",
  "buildDustDevil": "function buildDustDevil() {\n  const group = new THREE.Group();\n  addPart(group, new THREE.BoxGeometry(0.18, 1.2, 0.18), WORLD_MATERIALS.groundAccent, 0, 0.6, 0, 'core');\n  addPart(group, new THREE.BoxGeometry(0.44, 0.08, 0.44), WORLD_MATERIALS.obstacleAlt, 0, 0.22, 0, 'ring_1');\n  addPart(group, new THREE.BoxGeometry(0.62, 0.08, 0.62), WORLD_MATERIALS.obstacleAlt, 0, 0.64, 0, 'ring_2');\n  addPart(group, new THREE.BoxGeometry(0.78, 0.08, 0.78), WORLD_MATERIALS.obstacleAlt, 0, 1.0, 0, 'ring_3');\n  group.userData.animKind = 'dust_devil';\n  return group;\n}",
  "buildBiomeObstacle": "function buildBiomeObstacle(biome, type, options) {\n  const key = type === 'ground' ? biome.obstacleKinds.ground : biome.obstacleKinds.aerial;\n  const builder = OBSTACLE_BUILDERS[key];\n  if (!builder) return buildFallenLog();\n  const obstacle = builder(options || {});\n  obstacle.name = key;\n  obstacle.userData.type = type;\n  return obstacle;\n}",
  "buildBiomeDecor": "function buildBiomeDecor(kind) {\n  const builder = DECOR_BUILDERS[kind] || buildMonkeyLookout;\n  const decor = builder();\n  decor.name = kind;\n  return decor;\n}",
  "buildSelectionScene": "function buildSelectionScene() {\n  clearGroup(selectionGroup);\n  selectionDisplays.length = 0;\n  applyBiomeMaterials(getBiome(0));\n  scene.background.setHex(getBiome(0).background);\n  scene.fog.color.setHex(getBiome(0).fog);\n  scene.fog.near = 18;\n  scene.fog.far = 52;\n  for (let i = 0; i < SELECT_LAYOUT.length; i++) {\n    const layout = SELECT_LAYOUT[i];\n    const display = createSelectionDisplay(layout.key);\n    display.position.set(layout.x, 0, layout.z);\n    selectionGroup.add(display);\n    selectionDisplays.push(display);\n  }\n  camera.position.set(CAMERA.select.x, CAMERA.select.y, CAMERA.select.z);\n  tempCameraLook.set(CAMERA.select.lookAt.x, CAMERA.select.lookAt.y, CAMERA.select.lookAt.z);\n  camera.lookAt(tempCameraLook);\n}",
  "buildPatternForBiome": "function buildPatternForBiome() {\n  const base = JSON.parse(JSON.stringify(pickOne(PATTERN_LIBRARY)));\n  const used = {};\n  for (let i = 0; i < base.obstacles.length; i++) {\n    if (base.obstacles[i].lane === null) {\n      let lane = randInt(0, 2);\n      while (used[lane]) lane = randInt(0, 2);\n      used[lane] = true;\n      base.obstacles[i].lane = lane;\n    }\n  }\n  for (let i = 0; i < base.fruits.length; i++) {\n    if (base.fruits[i].lane === null) base.fruits[i].lane = randInt(0, 2);\n  }\n\n  if (currentBiome.specialHazard === 'bamboo_wall' && totalRunTime > 8 && Math.random() < 0.18) {\n    const lane = randInt(0, 1);\n    base.obstacles.push({ type: 'ground', lane, zOffset: -1.1 });\n    base.obstacles.push({ type: 'aerial', lane: lane + 1, zOffset: -2.2 });\n  }\n  if (currentBiome.specialHazard === 'double_step' && totalRunTime > 12 && Math.random() < 0.2) {\n    const lane = randInt(0, 2);\n    base.obstacles.push({ type: 'ground', lane, zOffset: -1.25 });\n  }\n  if (bananaHoardTimer > 0) {\n    const extra = [];\n    for (let i = 0; i < base.fruits.length; i++) {\n      extra.push({ lane: clamp(base.fruits[i].lane - 1, 0, 2), zOffset: base.fruits[i].zOffset - 0.4 });\n      extra.push({ lane: clamp(base.fruits[i].lane + 1, 0, 2), zOffset: base.fruits[i].zOffset - 0.8 });\n    }\n    base.fruits = base.fruits.concat(extra);\n  }\n  return base;\n}"
}