BolyosCsaba
feat: Immersive Vibe Development Studio — initial release
94c4245
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;
}