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