summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorprospanclaudebot <[email protected]>2026-07-11 01:08:12 +0000
committerprospanclaudebot <[email protected]>2026-07-11 01:08:12 +0000
commit7366ab10acc2800a175de2d876c4fc71db7532ab (patch)
tree9c6c9c98a02becb3af609f47cc5df06d45178966
parentd0c9c58892d8c1a221de320d958a3628ddf1d48a (diff)
downloadplaneta-zhopa-7366ab10acc2800a175de2d876c4fc71db7532ab.tar.gz
planeta-zhopa-7366ab10acc2800a175de2d876c4fc71db7532ab.zip
world: 🌭 карта ×2 (клиент+сервер синхронно), цикл день/ночь 5+5мин (солнце/луна/звёзды/Млечный путь + glow-небо), авианосец с мокрым асфальтом (cube-отражения)
-rw-r--r--mp_server.py2
-rw-r--r--serve/index.html108
2 files changed, 102 insertions, 8 deletions
diff --git a/mp_server.py b/mp_server.py
index 38f4a8f..d2bc2d1 100644
--- a/mp_server.py
+++ b/mp_server.py
@@ -24,7 +24,7 @@ def _fbm(x, y):
for _ in range(5):
s += a*_vnoise(x*f, y*f); f *= 2.03; a *= 0.5
return s
-TERR = 300.0; WATER_Y = 9.0
+TERR = 600.0; WATER_Y = 9.0
def groundH(x, z):
n = _fbm(x*0.010+11, z*0.010+7)
rim = min(1.0, math.hypot(x, z)/TERR)
diff --git a/serve/index.html b/serve/index.html
index 8a97a72..f0105e3 100644
--- a/serve/index.html
+++ b/serve/index.html
@@ -1165,7 +1165,7 @@ function fbm(x, y) {
for (let i = 0; i < 5; i++) { s += a * vnoise(x * f, y * f); f *= 2.03; a *= 0.5; }
return s;
}
-const TERR = 300; // half-extent (m)
+const TERR = 600; // half-extent (m) — карта ×2 (синхронно с mp_server.TERR)
function groundH(x, z) {
if (MAP && MAP.flat) return MAP.floorY;
// flooded bowl valley in the middle, tall mountains rising at the rim
@@ -1178,7 +1178,7 @@ function groundH(x, z) {
// ---------- scene ----------
// ---------- КАРТЫ (тема Планеты Жопа поверх той же геометрии; выбор на старте) ----------
const PZ_MAPS = {
- jopa: { label:'🌊 Жопоград (затоплен)', sky:0x8fb7e8, fog:0xc2d6ea, fogN:240, fogF:900, sun:0xfff2d8, hemiS:0xbcd6ff, hemiG:0x55442f, waterY:9.0, water:0x17a89e, cLow:0x4a7a3a, cHigh:0x8f9a6a, cRock:0x6b6660, cSnow:0xe8ecf0 },
+ jopa: { label:'🌊 Жопоград (затоплен)', sky:0x8fb7e8, fog:0xc2d6ea, fogN:400, fogF:1700, sun:0xfff2d8, hemiS:0xbcd6ff, hemiG:0x55442f, waterY:9.0, water:0x17a89e, cLow:0x4a7a3a, cHigh:0x8f9a6a, cRock:0x6b6660, cSnow:0xe8ecf0 },
dust: { label:'🏜 de_dust_жопа', flat:true, floorY:12, sky:0xd9c39a, fog:0xd8c7a0, fogN:200, fogF:820, sun:0xfff0d0, hemiS:0xe8d8b0, hemiG:0x6b5636, waterY:-60.0, water:0x2a3a2a, cLow:0xc2a867, cHigh:0xd9c79b, cRock:0xa8894f, cSnow:0xe8dcc0 },
mirage: { label:'🕌 de_mirage_жопа', sky:0xe6c79a, fog:0xdcc29a, fogN:210, fogF:840, sun:0xffe6c0, hemiS:0xe8cfa0, hemiG:0x7a5a3a, waterY:-60.0, water:0x2f4a4a, cLow:0xb98a5a, cHigh:0xd8b88a, cRock:0x9c7b54, cSnow:0xead9be },
perdun: { label:'🌋 Окраина пердунов', sky:0x4a3b30, fog:0x5a4a3a, fogN:120, fogF:600, sun:0xd8905a, hemiS:0x6a5540, hemiG:0x2a1e14, waterY:7.0, water:0x3a2a12, cLow:0x4a3420, cHigh:0x6a5236, cRock:0x3a2e24, cSnow:0x8a7a5a }
@@ -1210,10 +1210,10 @@ sun.shadow.mapSize.set(2048, 2048);
const sc = sun.shadow.camera; sc.left = -TERR; sc.right = TERR; sc.top = TERR; sc.bottom = -TERR; sc.near = 1; sc.far = 700;
sc.updateProjectionMatrix();
scene.add(sun);
-scene.add(new THREE.HemisphereLight(MAP.hemiS, MAP.hemiG, 0.5));
+const hemiLight = new THREE.HemisphereLight(MAP.hemiS, MAP.hemiG, 0.5); scene.add(hemiLight);
// ---------- terrain mesh ----------
-const GS = 200;
+const GS = 256;
const tgeo = new THREE.PlaneGeometry(TERR * 2, TERR * 2, GS, GS);
tgeo.rotateX(-Math.PI / 2);
const pos = tgeo.attributes.position;
@@ -1773,6 +1773,33 @@ function rcTick() {
})();
})();
+// ---------- АВИАНОСЕЦ в центре: мокрый асфальт с отражениями (cube env) ----------
+(function buildCarrier() {
+ const wl = WATER_Y;
+ const g = new THREE.Group(); g.position.set(0, wl, 0); scene.add(g);
+ const cubeRT = new THREE.WebGLCubeRenderTarget(128, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter });
+ const cubeCam = new THREE.CubeCamera(1, 5000, cubeRT); cubeCam.position.set(0, wl + 26, 0); scene.add(cubeCam);
+ const hullMat = new THREE.MeshStandardMaterial({ color: 0x3a3f45, roughness: 0.7, metalness: 0.35 });
+ const deckMat = new THREE.MeshStandardMaterial({ color: 0x17171b, roughness: 0.26, metalness: 0.4, envMap: cubeRT.texture, envMapIntensity: 1.15 }); // мокрый асфальт
+ const lineMat = new THREE.MeshStandardMaterial({ color: 0xe0e0e0, roughness: 0.5, emissive: 0x1a1a1a });
+ const LEN = 320, WID = 84;
+ const hull = new THREE.Mesh(new THREE.BoxGeometry(WID * 0.92, 24, LEN), hullMat); hull.position.y = -5; hull.castShadow = true; g.add(hull);
+ const bow = new THREE.Mesh(new THREE.CylinderGeometry(0.01, WID * 0.46, 44, 4, 1), hullMat); bow.rotation.x = Math.PI / 2; bow.rotation.z = Math.PI / 4; bow.position.set(0, -5, LEN / 2 + 12); bow.scale.set(1, 1, 0.5); g.add(bow);
+ const deck = new THREE.Mesh(new THREE.BoxGeometry(WID, 2, LEN), deckMat); deck.position.y = 9; deck.receiveShadow = true; g.add(deck);
+ const angDeck = new THREE.Mesh(new THREE.BoxGeometry(30, 2, 168), deckMat); angDeck.position.set(-WID / 2 + 2, 9, -18); angDeck.rotation.y = 0.17; g.add(angDeck);
+ const cl = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.3, LEN * 0.86), lineMat); cl.position.set(0, 10.2, 0); g.add(cl);
+ for (let z = -LEN / 2 + 34; z < LEN / 2 - 12; z += 24) { const d = new THREE.Mesh(new THREE.BoxGeometry(9, 0.3, 3), lineMat); d.position.set(0, 10.25, z); g.add(d); }
+ const isl = new THREE.Mesh(new THREE.BoxGeometry(15, 28, 48), hullMat); isl.position.set(WID / 2 - 9, 24, 24); isl.castShadow = true; g.add(isl);
+ const mast = new THREE.Mesh(new THREE.CylinderGeometry(0.8, 0.8, 24, 6), hullMat); mast.position.set(WID / 2 - 9, 50, 24); g.add(mast);
+ const lbl = makeLabel('АВИАНОСЕЦ «ЖОПА-1»'); lbl.position.set(0, 64, 0); lbl.scale.set(46, 9, 1); g.add(lbl);
+ let lastCube = -1e9;
+ (function _cc() {
+ const t = performance.now();
+ if (t - lastCube > 2800) { lastCube = t; g.visible = false; cubeCam.update(renderer, scene); g.visible = true; }
+ requestAnimationFrame(_cc);
+ })();
+})();
+
// ---------- монумент ПАСОЧКИ (кулич с яйцами) по заявке @lzcnt ----------
(function buildPaska() {
const gx = 60, gz = 120, gy = groundH(gx, gz);
@@ -1855,10 +1882,14 @@ loadGallery();
// ---------- небо: градиентный купол, солнце, планета Хер + Полужопия ----------
const skyMat = new THREE.ShaderMaterial({
- side: THREE.BackSide, depthWrite: false,
- uniforms: { top: { value: new THREE.Color(0x2b5ca8) }, bot: { value: new THREE.Color(0xcadcee) } },
+ side: THREE.BackSide, depthWrite: false, fog: false,
+ uniforms: { top: { value: new THREE.Color(0x2b5ca8) }, bot: { value: new THREE.Color(0xcadcee) },
+ sunPos: { value: new THREE.Vector3(0, 1, 0) }, sunGlow: { value: new THREE.Color(0xffd9a0) }, glowA: { value: 1.0 } },
vertexShader: 'varying vec3 vp; void main(){ vp=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }',
- fragmentShader: 'varying vec3 vp; uniform vec3 top; uniform vec3 bot; void main(){ float h=clamp(normalize(vp).y*0.5+0.5,0.0,1.0); gl_FragColor=vec4(mix(bot,top,pow(h,0.75)),1.0); }'
+ fragmentShader: 'varying vec3 vp; uniform vec3 top; uniform vec3 bot; uniform vec3 sunPos; uniform vec3 sunGlow; uniform float glowA;' +
+ 'void main(){ vec3 d=normalize(vp); float h=clamp(d.y*0.5+0.5,0.0,1.0); vec3 col=mix(bot,top,pow(h,0.75));' +
+ ' float sd=max(dot(d,normalize(sunPos)),0.0); col += sunGlow*(pow(sd,64.0)*1.3 + pow(sd,6.0)*0.28 + pow(sd,1.5)*0.09)*glowA;' +
+ ' gl_FragColor=vec4(col,1.0); }'
});
const skyGroup = new THREE.Group(); scene.add(skyGroup); // follows the camera -> celestial bodies at infinity, never clipped
skyGroup.add(new THREE.Mesh(new THREE.SphereGeometry(1400, 32, 16), skyMat));
@@ -1874,6 +1905,68 @@ function bigPlanet(radius, color, dx, dy, dz, dist, emiss) {
bigPlanet(150, 0x7a5aa8, -0.35, 0.5, -1.0, 1150, 0.35); // Хер (газовый гигант)
bigPlanet(40, 0xb6aec4, 0.55, 0.5, -0.85, 950, 0.25); // Полужопие Ближнее
bigPlanet(28, 0x98a0ba, -0.7, 0.42, -0.7, 1000, 0.25); // Полужопие Дальнее
+
+// ---------- ЦИКЛ ДЕНЬ/НОЧЬ + луна + звёзды + Млечный путь ----------
+const dayNight = (function () {
+ const c = (h) => new THREE.Color(h);
+ const moonMesh = new THREE.Mesh(new THREE.SphereGeometry(38, 24, 16), new THREE.MeshBasicMaterial({ color: 0xdfe6f5, fog: false }));
+ skyGroup.add(moonMesh);
+ const moonLight = new THREE.DirectionalLight(0x8fb0e0, 0.0); scene.add(moonLight);
+
+ const nightSky = new THREE.Group(); skyGroup.add(nightSky);
+ const STAR_N = 2600, R = 1320, sg = new THREE.BufferGeometry();
+ const spos = new Float32Array(STAR_N * 3), scol = new Float32Array(STAR_N * 3);
+ const mwN = new THREE.Vector3(0.3, 0.55, 0.78).normalize();
+ for (let i = 0; i < STAR_N; i++) {
+ let dir = new THREE.Vector3(), milky = i < STAR_N * 0.45;
+ for (let tr = 0; tr < 24; tr++) {
+ const u = Math.random(), v = Math.random(), th = u * 6.2831853, ph = Math.acos(2 * v - 1);
+ dir.set(Math.sin(ph) * Math.cos(th), Math.abs(Math.cos(ph)) * 0.7 + 0.12, Math.sin(ph) * Math.sin(th)).normalize();
+ if (!milky || Math.abs(dir.dot(mwN)) < 0.13) break;
+ }
+ spos[i * 3] = dir.x * R; spos[i * 3 + 1] = dir.y * R; spos[i * 3 + 2] = dir.z * R;
+ const w = 0.75 + Math.random() * 0.25, cc = c(0xffffff).lerp(c(Math.random() < 0.5 ? 0xbcd0ff : 0xffe9c0), 0.4).multiplyScalar(w);
+ scol[i * 3] = cc.r; scol[i * 3 + 1] = cc.g; scol[i * 3 + 2] = cc.b;
+ }
+ sg.setAttribute('position', new THREE.BufferAttribute(spos, 3));
+ sg.setAttribute('color', new THREE.BufferAttribute(scol, 3));
+ const starMat = new THREE.PointsMaterial({ size: 7, sizeAttenuation: true, vertexColors: true, transparent: true, opacity: 0, depthWrite: false, fog: false });
+ nightSky.add(new THREE.Points(sg, starMat));
+
+ const mwMat = new THREE.MeshBasicMaterial({ color: 0x9fb8e8, transparent: true, opacity: 0, blending: THREE.AdditiveBlending, depthWrite: false, fog: false, side: THREE.DoubleSide });
+ const mw = new THREE.Mesh(new THREE.TorusGeometry(R * 0.98, R * 0.17, 2, 80), mwMat);
+ mw.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), mwN); nightSky.add(mw);
+
+ function updateSky(now) {
+ const DAYLEN = 600;
+ const dt = (typeof window.__t0 === 'number') ? window.__t0 : (now / 1000 % DAYLEN) / DAYLEN;
+ const a = (dt - 0.25) * 6.2831853;
+ sunDir.set(Math.cos(a), Math.sin(a), 0.32).normalize();
+ const el = sunDir.y;
+ const dayF = THREE.MathUtils.clamp((el + 0.12) / 0.30, 0, 1);
+ const nightF = 1 - dayF;
+ const duskF = THREE.MathUtils.clamp(1.0 - Math.abs(el) / 0.28, 0, 1);
+ const hi = THREE.MathUtils.clamp(el / 0.4, 0, 1);
+ const sc = c(0xff8a3c).lerp(c(0xfff2d8), hi);
+ sunMesh.position.copy(sunDir).multiplyScalar(1150); sunMesh.visible = el > -0.14; sunMesh.material.color.copy(sc);
+ sun.position.copy(sunDir).multiplyScalar(300); sun.color.copy(sc); sun.intensity = dayF * 1.25;
+ const moonDir = sunDir.clone().negate();
+ moonMesh.position.copy(moonDir).multiplyScalar(1150); moonMesh.visible = moonDir.y > -0.1;
+ moonLight.position.copy(moonDir).multiplyScalar(300); moonLight.intensity = nightF * 0.22;
+ hemiLight.intensity = 0.12 + dayF * 0.42;
+ hemiLight.color.copy(c(0x8098c8).lerp(c(MAP.hemiS), dayF));
+ hemiLight.groundColor.copy(c(0x0a0c18).lerp(c(MAP.hemiG), dayF));
+ skyMat.uniforms.top.value.copy(c(0x070b24)).lerp(c(0x2b5ca8), dayF);
+ skyMat.uniforms.bot.value.copy(c(0x0b1030)).lerp(c(0xcadcee), dayF).lerp(c(0xff9a5a), duskF * 0.45);
+ skyMat.uniforms.sunPos.value.copy(sunDir);
+ skyMat.uniforms.sunGlow.value.copy(c(0xffb060).lerp(c(0xfff0d0), hi));
+ skyMat.uniforms.glowA.value = Math.max(dayF, duskF * 0.8);
+ if (scene.fog) scene.fog.color.copy(c(0x0a0e24)).lerp(c(MAP.fog), dayF).lerp(c(0xff9a5a), duskF * 0.28);
+ starMat.opacity = nightF; mwMat.opacity = nightF * 0.5; nightSky.visible = nightF > 0.02;
+ nightSky.rotation.y = now / 1000 * 0.006;
+ }
+ return { updateSky };
+})();
const _sunWorld = new THREE.Vector3();
// ---------- god rays (радиальный пост-эффект от солнца) ----------
@@ -2291,6 +2384,7 @@ function tick(now) {
'° · онлайн: ' + (1 + Object.keys(peers).length) + (mpStatus ? ' ' + mpStatus : '') +
(freezePos ? ' · ⏸ ПОЗИЦИЯ ЗАФИКС.' : '') + (dbgVec ? ' · ↑вектора' : '');
rcTick(); // ЖОПО-СВО: рубежи + гоночный HUD
+ dayNight.updateSky(now); // цикл день/ночь: солнце/луна/звёзды/небо/свет/туман
skyGroup.position.copy(camera.position); // sky/planets ride with the camera (at infinity, no clip)
if (lowQ) {
renderer.render(scene, camera); // low quality: straight to screen, no god rays