From 375e4c070e7a62c375173625fa0da281f91166a6 Mon Sep 17 00:00:00 2001 From: prospanclaudebot Date: Fri, 10 Jul 2026 23:54:26 +0000 Subject: crab-party: 🌭 танцующие крабы + механика крабо-сквада (вербовка у пати -> рой мини-крабо-дронов вокруг дрона -> авто-poop-эскадрилья, HUD 🦀×N) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serve/index.html | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) (limited to 'serve/index.html') diff --git a/serve/index.html b/serve/index.html index 5657520..8edc7a7 100644 --- a/serve/index.html +++ b/serve/index.html @@ -1588,6 +1588,111 @@ function rcTick() { const lbl = makeLabel('ЖОПА-ВОДОПАД'); lbl.position.set(0, 42, -16); lbl.scale.set(24, 6, 1); g.add(lbl); })(); +// ---------- CRAB PARTY 🦀 — танцующие крабы (crab rave) по заявке @devcpp ---------- +(function buildCrabParty() { + const LX = -64, LZ = 34; + const PAD = Math.max(groundH(LX, LZ), WATER_Y) + 6.0; // над гребнями волн, иначе крабов накрывает вода + const g = new THREE.Group(); g.position.set(LX, PAD, LZ); scene.add(g); + const pillar = new THREE.Mesh(new THREE.CylinderGeometry(10, 8, 18, 16), new THREE.MeshStandardMaterial({ color: 0x2a2e34, roughness: 1 })); + pillar.position.y = -9; g.add(pillar); + + const floor = new THREE.Group(); g.add(floor); + const tiles = [], N = 6, TS = 3; + for (let x = 0; x < N; x++) for (let z = 0; z < N; z++) { + const t = new THREE.Mesh(new THREE.BoxGeometry(TS * 0.94, 0.3, TS * 0.94), + new THREE.MeshStandardMaterial({ color: 0x111318, emissive: 0x000000, roughness: 0.35, metalness: 0.1 })); + t.position.set((x - (N - 1) / 2) * TS, 0.15, (z - (N - 1) / 2) * TS); t.receiveShadow = true; floor.add(t); tiles.push(t); + } + + const cmat = (h) => new THREE.MeshStandardMaterial({ color: new THREE.Color().setHSL(h, 0.85, 0.5), roughness: 0.5 }); + const white = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.4 }); + const black = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.5 }); + + function makeCrab(h) { + const c = new THREE.Group(), m = cmat(h); + const body = new THREE.Mesh(new THREE.SphereGeometry(1.0, 12, 8), m); + body.scale.set(1.5, 0.65, 1.1); body.position.y = 1.0; body.castShadow = true; c.add(body); + for (const s of [-1, 1]) { + const stalk = new THREE.Mesh(new THREE.CylinderGeometry(0.07, 0.07, 0.6, 6), m); stalk.position.set(s * 0.4, 1.55, 0.55); c.add(stalk); + const eye = new THREE.Mesh(new THREE.SphereGeometry(0.2, 8, 6), white); eye.position.set(s * 0.4, 1.85, 0.55); c.add(eye); + const pup = new THREE.Mesh(new THREE.SphereGeometry(0.1, 6, 6), black); pup.position.set(s * 0.4, 1.87, 0.72); c.add(pup); + } + const claws = []; + for (const s of [-1, 1]) { + const arm = new THREE.Group(); arm.position.set(s * 1.4, 1.0, 0.3); + const up = new THREE.Mesh(new THREE.CylinderGeometry(0.11, 0.13, 0.9, 6), m); up.rotation.z = s * 0.6; up.position.set(s * 0.35, 0.25, 0); arm.add(up); + const cl = new THREE.Mesh(new THREE.SphereGeometry(0.45, 8, 6), m); cl.scale.set(1.3, 0.95, 0.7); cl.position.set(s * 0.75, 0.6, 0); arm.add(cl); + c.add(arm); claws.push(arm); + } + const legs = []; + for (const s of [-1, 1]) for (let i = 0; i < 3; i++) { + const leg = new THREE.Group(); leg.position.set(s * 0.85, 0.65, 0.35 - i * 0.5); + const seg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.05, 0.9, 5), m); seg.rotation.z = s * 1.15; seg.position.set(s * 0.32, -0.25, 0); leg.add(seg); + c.add(leg); legs.push(leg); + } + return { c, claws, legs }; + } + + const crabs = [], frac = x => x - Math.floor(x), NC = 11; + for (let i = 0; i < NC; i++) { + const cr = makeCrab(frac(i * 0.137 + 0.02)); + const a = i / NC * Math.PI * 2, r = 2 + frac(Math.sin(i * 12.9) * 43758.5) * 7; + cr.baseX = Math.cos(a) * r; cr.baseZ = Math.sin(a) * r; cr.ph = i * 0.57; cr.face = a + Math.PI; + cr.c.position.set(cr.baseX, 0.3, cr.baseZ); cr.c.rotation.y = cr.face; g.add(cr.c); crabs.push(cr); + } + + const lbl = makeLabel('CRAB PARTY 🦀'); lbl.position.set(0, 10, 0); lbl.scale.set(22, 5.5, 1); g.add(lbl); + + // ---- механика: крабо-сквад (мини-крабо-дроны + авто-poop-эскадрилья) ---- + const squad = [], MAXS = 6, poops = [], CENTER = new THREE.Vector3(LX, PAD + 2, LZ); + const poopGeo = new THREE.SphereGeometry(0.35, 6, 5), poopMat = new THREE.MeshStandardMaterial({ color: 0x5a3a1a, roughness: 1 }); + let lastT = 0, lastPoop = 0; + const hudC = document.createElement('div'); + hudC.style.cssText = 'position:fixed;left:12px;bottom:66px;font:bold 20px system-ui;color:#ff8c3a;text-shadow:0 1px 3px #000;z-index:50;pointer-events:none'; + document.body.appendChild(hudC); + + window.updateCrabs = function (t) { + const dt = Math.min(0.05, Math.max(0, t - lastT)); lastT = t; + const beat = t * 3.4, pz = window.PZ, S = pz && pz.S; + for (let i = 0; i < crabs.length; i++) { + const cr = crabs[i]; if (cr.squad) continue; + const p = cr.ph; + cr.c.position.y = 0.3 + Math.abs(Math.sin(beat + p)) * 0.9; + cr.c.rotation.y = cr.face + Math.sin(beat * 0.5 + p) * 0.35; + cr.c.rotation.z = Math.sin(beat + p) * 0.13; + cr.claws[0].rotation.z = Math.sin(beat + p) * 0.7; + cr.claws[1].rotation.z = -Math.sin(beat + p) * 0.7; + for (let k = 0; k < cr.legs.length; k++) cr.legs[k].rotation.x = Math.sin(beat * 1.5 + p + k * 0.6) * 0.25; + } + for (let k = 0; k < tiles.length; k++) tiles[k].material.emissive.setHSL(frac(k * 0.09 + t * 0.35), 1.0, 0.5); + if (S) { + if (S.p.distanceTo(CENTER) < 22 && squad.length < MAXS) { + const av = crabs.find(c => !c.squad); + if (av) { av.squad = true; g.remove(av.c); scene.add(av.c); av.c.scale.setScalar(0.7); squad.push(av); } + } + for (let j = 0; j < squad.length; j++) { + const cr = squad[j], a = t * 1.6 + j * (6.2831 / MAXS); + cr.c.position.set(S.p.x + Math.cos(a) * 4.5, S.p.y - 1.4 + Math.abs(Math.sin(t * 5 + j)) * 0.8, S.p.z + Math.sin(a) * 4.5); + cr.c.rotation.y = -a; cr.c.rotation.z = Math.sin(t * 5 + j) * 0.2; + cr.claws[0].rotation.z = Math.sin(t * 5 + j) * 0.7; cr.claws[1].rotation.z = -Math.sin(t * 5 + j) * 0.7; + } + if (squad.length && t - lastPoop > 1.1 && poops.length < 60) { + lastPoop = t; + const cr = squad[Math.floor(t * 10) % squad.length]; + const pm = new THREE.Mesh(poopGeo, poopMat); pm.position.copy(cr.c.position); scene.add(pm); poops.push({ m: pm, vy: -1.5 }); + } + } + for (let i = poops.length - 1; i >= 0; i--) { + const q = poops[i]; q.vy -= 9.8 * dt; q.m.position.y += q.vy * dt; q.m.rotation.x += 0.25; + const lvl = Math.max(groundH(q.m.position.x, q.m.position.z), WATER_Y); + if (q.m.position.y <= lvl + 0.2 || q.m.position.y < -300) { scene.remove(q.m); poops.splice(i, 1); } + } + hudC.textContent = squad.length ? '🦀×' + squad.length + ' крабо-эскадрилья 💩' : ''; + }; + (function _cp() { window.updateCrabs(performance.now() / 1000); requestAnimationFrame(_cp); })(); + window.updateCrabs(0); +})(); + // ---------- монумент ПАСОЧКИ (кулич с яйцами) по заявке @lzcnt ---------- (function buildPaska() { const gx = 60, gz = 120, gy = groundH(gx, gz); -- cgit v1.2.3