Матрешка RP
by AstroPirate77601 lines82.4 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover"><title>Матрешка RP</title><style>
*{margin:0;padding:0;box-sizing:border-box;font-family:Arial,sans-serif}
html,body{background:#000;color:#fff;overflow:hidden;height:100vh;width:100vw;position:fixed;touch-action:none}
#rw{position:fixed;top:0;left:0;width:100vw;height:100vh;transform-origin:top left;overflow:hidden;background:#000}
#g{position:absolute;inset:0}canvas{display:block!important;width:100%!important;height:100%!important}
#lib{position:fixed;inset:0;background:linear-gradient(180deg,#1e4a7a,#0a1420);display:none;align-items:center;justify-content:center;flex-direction:column;z-index:300;padding:20px;text-align:center}
#lib.on{display:flex}#lib h1{color:#ffd166;margin-bottom:16px}#lib p{color:#8fa0b3;margin-bottom:20px;max-width:400px;font-size:14px}
#lib button{padding:16px 32px;font-size:16px;font-weight:900;border:none;border-radius:12px;background:linear-gradient(180deg,#4caf50,#2e7d32);color:#fff;cursor:pointer}
#lp{width:min(300px,80vw);height:8px;background:rgba(255,255,255,.15);border-radius:4px;overflow:hidden;margin-top:14px;display:none}
#lp.on{display:block}#lp div{height:100%;width:0%;background:linear-gradient(90deg,#ffd166,#06d6a0);transition:width .3s}
.scr{position:fixed;inset:0;background:linear-gradient(180deg,#1e4a7a,#8ec4e4 60%,#8a8880);display:none;align-items:center;justify-content:center;z-index:100;padding:20px;overflow-y:auto}
.scr.on{display:flex}
.pnl{background:#1c2230;border:1px solid rgba(255,255,255,.15);border-radius:18px;padding:22px;width:100%;max-width:440px;max-height:92vh;overflow-y:auto}
.pnl h1{font-size:22px;text-align:center;margin-bottom:12px;color:#ffd166}
.pnl h2{font-size:14px;text-align:center;margin-bottom:10px;color:#8fa0b3}
.pnl input{display:block;width:100%;padding:12px;font-size:15px;margin-bottom:10px;border-radius:10px;border:2px solid rgba(255,255,255,.25);background:#0d1117;color:#fff;outline:none}
.pnl button{display:block;width:100%;padding:13px;font-size:14px;font-weight:900;border:none;border-radius:10px;cursor:pointer;background:linear-gradient(180deg,#4caf50,#2e7d32);color:#fff;margin-bottom:8px}
.pnl button.alt{background:linear-gradient(180deg,#3a5f9c,#244170)}
.pnl button.red{background:linear-gradient(180deg,#8a3b3b,#5e2727)}
.pnl button.gold{background:linear-gradient(180deg,#d4a017,#8a6b0d)}
.pnl button.yan{background:linear-gradient(180deg,#fc3f1d,#c42f10)}
.note{background:rgba(255,209,102,.15);border-left:4px solid #ffd166;padding:8px;border-radius:6px;font-size:11px;color:#ffd166;margin-bottom:8px}
#hud{position:absolute;inset:0;display:none;pointer-events:none;z-index:10}#hud.on{display:block}
#tb{position:absolute;top:8px;left:8px;display:flex;gap:5px;flex-wrap:wrap;align-items:center}
.st{background:rgba(0,0,0,.7);padding:5px 9px;border-radius:8px;font-size:12px;font-weight:700;border:1px solid rgba(255,255,255,.2)}
.st.d{background:linear-gradient(180deg,#ff6b9d,#c41e5c)}
.bt{pointer-events:auto;background:linear-gradient(180deg,#e67e22,#b85c10);border:2px solid rgba(255,255,255,.3);color:#fff;border-radius:8px;padding:6px 9px;font-weight:800;cursor:pointer;font-size:13px}
#phB{background:linear-gradient(180deg,#4caf50,#2e7d32)!important}
#prB{background:linear-gradient(180deg,#ff6b9d,#c41e5c)!important}
#tr{position:absolute;top:8px;right:8px;display:flex;gap:5px;pointer-events:auto}
#tr button{background:rgba(0,0,0,.7);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:8px;padding:7px 9px;font-size:15px;cursor:pointer}
#wh{position:absolute;top:54px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.75);padding:5px 12px;border-radius:8px;font-size:12px;font-weight:700;z-index:12;display:none;color:#fff}
#wh.on{display:block}
#clock{position:absolute;top:8px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.75);padding:5px 14px;border-radius:8px;font-size:13px;font-weight:700;z-index:12;color:#fff;border:1px solid rgba(255,255,255,.2)}
#js{position:absolute;bottom:26px;left:26px;width:140px;height:140px;border-radius:50%;background:rgba(255,255,255,.15);border:2px solid rgba(255,255,255,.4);pointer-events:auto;touch-action:none;z-index:20}
#js.right{left:auto;right:26px}
#jk{position:absolute;top:50%;left:50%;width:60px;height:60px;margin:-30px 0 0 -30px;border-radius:50%;background:rgba(255,255,255,.8);pointer-events:none;will-change:transform}
#ar{position:absolute;bottom:26px;right:26px;display:flex;flex-direction:column;gap:9px;pointer-events:auto;z-index:20}
#ar.left{right:auto;left:26px}#ar.top{bottom:auto;top:80px}
#ar button{width:58px;height:58px;border-radius:50%;background:rgba(255,255,255,.25);border:2px solid rgba(255,255,255,.6);color:#fff;font-size:18px;font-weight:800;cursor:pointer;touch-action:none}
#hint{position:absolute;bottom:180px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.85);padding:6px 12px;border-radius:8px;font-size:12px;display:none;font-weight:600;z-index:15}
#hint.on{display:block}
#am{position:absolute;bottom:160px;right:36px;background:rgba(0,0,0,.85);padding:5px 10px;border-radius:8px;font-weight:900;font-size:15px;color:#ffd166;display:none;border:2px solid #ffd166}
#am.on{display:block}
#speedo{position:absolute;bottom:26px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.85);padding:8px 20px;border-radius:12px;font-weight:900;font-size:20px;color:#ffd166;display:none;border:2px solid #ffd166;z-index:15;min-width:140px;text-align:center}
#speedo.on{display:block}#speedo small{display:block;font-size:11px;color:#8fa0b3;font-weight:600}
#to{position:fixed;top:60px;left:50%;transform:translateX(-50%);background:rgba(20,20,20,.95);padding:10px 16px;border-radius:10px;font-size:13px;font-weight:700;opacity:0;transition:.3s;border:2px solid #4caf50;pointer-events:none;z-index:9999;max-width:90vw}
#to.show{opacity:1}
#cs{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(0);background:linear-gradient(160deg,#1c2230,#11151d);border:3px solid #ffd166;border-radius:18px;padding:24px 32px;text-align:center;z-index:10002;transition:transform .3s;pointer-events:none}
#cs.on{transform:translate(-50%,-50%) scale(1)}
#cs .ic{font-size:60px;margin-bottom:8px}#cs .tt{font-size:20px;font-weight:900}#cs .ss{font-size:14px;color:#8fa0b3}
#cscr{position:fixed;inset:0;background:rgba(0,0,0,.95);display:none;align-items:center;justify-content:center;flex-direction:column;z-index:10000;padding:20px}
#cscr.on{display:flex}
#caseW{position:relative;width:min(500px,90vw);height:130px;background:#0d1119;border:3px solid #ffd166;border-radius:12px;overflow:hidden;margin-bottom:16px}
#caseS{display:flex;height:100%;align-items:center;transition:transform 3.5s cubic-bezier(.15,.85,.35,1)}
.ci{min-width:110px;height:110px;margin:0 6px;background:#1a2438;border:2px solid #555;border-radius:10px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:11px;font-weight:700;text-align:center;padding:5px;flex-shrink:0}
.ci .ic{font-size:32px;margin-bottom:3px}
#cptr{position:absolute;top:-8px;left:50%;transform:translateX(-50%);width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-top:18px solid #ffd166;z-index:10}
#cres{position:fixed;inset:0;background:rgba(0,0,0,.9);display:none;align-items:center;justify-content:center;z-index:10001}
#cres.on{display:flex}
#cres .bx{background:linear-gradient(160deg,#1c2230,#11151d);border:3px solid #ffd166;border-radius:18px;padding:24px;text-align:center;max-width:90vw}
#cres .icn{font-size:70px;margin-bottom:10px}#cres .ttl{font-size:22px;font-weight:900;margin-bottom:8px}#cres .sb{font-size:14px;color:#8fa0b3;margin-bottom:16px}
#phS{position:fixed;inset:0;background:rgba(0,0,0,.95);display:none;align-items:center;justify-content:center;z-index:9998;padding:20px}
#phS.on{display:flex}
.pb{width:min(320px,90vw);height:min(600px,90vh);background:#1c2230;border-radius:30px;border:8px solid #2a2a2a;padding:26px 14px 14px;display:flex;flex-direction:column;position:relative}
.pa{width:100%;padding:14px;background:linear-gradient(160deg,#2a3a55,#1a2438);border-radius:14px;margin-bottom:8px;cursor:pointer;display:flex;align-items:center;gap:12px}
.pa .ai{font-size:28px}.pa .ap b{display:block;font-size:14px}.pa .ap small{color:#8fa0b3;font-size:11px}
.phb{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);width:50px;height:50px;border-radius:50%;background:rgba(255,255,255,.2);border:2px solid rgba(255,255,255,.4);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:20px;color:#fff}
.it{display:flex;justify-content:space-between;align-items:center;padding:10px;background:rgba(255,255,255,.06);border-radius:8px;margin-bottom:6px}
.it .inf{flex:1}.it .inf b{display:block;font-size:13px}.it .inf small{color:#8fa0b3;font-size:10px}
.it button{width:auto;padding:8px 12px;background:linear-gradient(180deg,#e67e22,#a04d10);border:none;border-radius:6px;color:#fff;font-weight:800;cursor:pointer;font-size:12px}
#bigMap{display:block;width:100%;background:#1a2010;border-radius:8px;border:2px solid rgba(255,255,255,.2)}
</style></head><body>
<div id="lib"><h1>🚀 МАТРЕШКА RP</h1><p>Нужен интернет 1 раз.</p><button id="lb" onclick="loadThree()">⬇ Скачать и играть</button><div id="lp"><div id="lpB"></div></div></div>
<div id="rw"><div id="g"></div><div id="hud">
<div id="clock">🕐 12:00</div>
<div id="wh">☀️ Ясно</div>
<div id="tb">
<button class="bt" onclick="openScr('shop')">🛒</button>
<button class="bt" onclick="openScr('food')">🍔</button>
<button class="bt" onclick="openScr('tech')">🚗</button>
<button class="bt" onclick="openScr('gun')">🔫</button>
<button class="bt" onclick="openScr('caseScr')">🎁</button>
<button class="bt" onclick="openScr('promoScr')" id="prB">🎟️</button>
<button class="bt" onclick="openScr('mapScr')">🗺️</button>
<button class="bt" onclick="openPhone()" id="phB">📱</button>
<div class="st">💰 <span id="mn">0</span></div>
<div class="st d">💎 <span id="dn">0</span></div>
</div>
<div id="tr"><button onclick="openScr('menu')">☰</button><button onclick="tCam()">👁</button></div>
<div id="am">🔫 <span id="amV">12</span></div>
<div id="speedo">0 <small>км/ч</small></div>
<div id="hint"></div>
<div id="js"><div id="jk"></div></div>
<div id="ar"><button id="bJ">⤒</button><button id="bE">E</button><button id="bF">F</button></div>
</div></div>
<div id="cs"><div class="ic" id="csi">🎲</div><div class="tt" id="cst"></div><div class="ss" id="css"></div></div>
<div id="cscr"><h1 style="color:#ffd166;margin-bottom:16px;text-align:center" id="cTtl">🎁 КЕЙС</h1><div id="caseW"><div id="cptr"></div><div id="caseS"></div></div></div>
<div id="cres"><div class="bx"><div class="icn" id="cIc">🎁</div><div class="ttl" id="cTt"></div><div class="sb" id="cSb"></div><button style="padding:14px 28px;font-size:15px;font-weight:900;border:none;border-radius:10px;background:linear-gradient(180deg,#4caf50,#2e7d32);color:#fff;cursor:pointer" onclick="closeCase()">OK</button></div></div>
<div id="phS"><div class="pb"><div style="text-align:center;color:#8fa0b3;font-size:12px;margin-bottom:14px">📱 ТЕЛЕФОН</div><div style="flex:1;overflow-y:auto">
<div class="pa" onclick="closePhone();openScr('yandex')"><div class="ai">🔍</div><div class="ap"><b>Яндекс</b></div></div>
<div class="pa" onclick="closePhone();openScr('shop')"><div class="ai">🛒</div><div class="ap"><b>Магазин</b></div></div>
<div class="pa" onclick="closePhone();openScr('food')"><div class="ai">🍔</div><div class="ap"><b>Еда</b></div></div>
<div class="pa" onclick="closePhone();openScr('tech')"><div class="ai">🚗</div><div class="ap"><b>Техника</b></div></div>
<div class="pa" onclick="closePhone();openScr('gun')"><div class="ai">🔫</div><div class="ap"><b>Оружие</b></div></div>
<div class="pa" onclick="closePhone();openScr('cas')"><div class="ai">🎰</div><div class="ap"><b>Казино</b></div></div>
<div class="pa" onclick="closePhone();openScr('caseScr')"><div class="ai">🎁</div><div class="ap"><b>Кейсы</b></div></div>
<div class="pa" onclick="closePhone();openScr('promoScr')"><div class="ai">🎟️</div><div class="ap"><b>Промокоды</b></div></div>
<div class="pa" onclick="closePhone();openScr('mapScr')"><div class="ai">🗺️</div><div class="ap"><b>Карта</b></div></div>
<div class="pa" onclick="closePhone();openScr('settingsScr')"><div class="ai">⚙️</div><div class="ap"><b>Настройки</b></div></div>
<div class="pa" onclick="closePhone();openScr('adminLog')"><div class="ai">⚡</div><div class="ap"><b>Админ</b><small>2015</small></div></div>
</div><div class="phb" onclick="closePhone()">⌂</div></div></div>
<div class="scr on" id="start"><div class="pnl"><h1>🚉 МАТРЕШКА RP</h1><div class="note">🏡 30+ домов с входом · 🌿 Толстая трава · ⛰️ Дома на горе</div><h2 id="cText">Выбери спавн</h2>
<button id="bC" style="display:none;background:linear-gradient(180deg,#2196f3,#1565c0)" onclick="cont()">▶ Продолжить</button>
<button onclick="startGame(1)">🚉 Вокзал №1</button>
<button class="alt" onclick="startGame(2)">🚉 Вокзал №2</button>
<button class="alt" onclick="startGame(3)">🏘️ Пригород</button>
<button class="alt" onclick="startGame(4)">⛰️ Гора</button></div></div>
<div class="scr" id="yandex"><div class="pnl"><h1 style="color:#fc3f1d">🔍 ЯНДЕКС</h1>
<input type="text" id="yaQ" placeholder="Что найти?">
<button class="yan" onclick="yaSearch()">🔍 Искать</button>
<button class="alt" onclick="closeScr('yandex')">← Назад</button></div></div>
<div class="scr" id="shop"><div class="pnl"><h1>🛒 МАГАЗИН</h1><h2>💰 <span id="sM">0</span> · 💎 <span id="sD">0</span></h2>
<div class="it"><div class="inf"><b>💎 100 донат</b><small>100 000 ₽</small></div><button onclick="buyDonate()">100К</button></div>
<button class="alt" onclick="closeScr('shop')">← Закрыть</button></div></div>
<div class="scr" id="food"><div class="pnl"><h1>🍔 ЕДА</h1><h2>💰 <span id="fM">0</span> ₽</h2>
<div class="it"><div class="inf"><b>🍦 Мороженое</b></div><button onclick="buyFood('Мороженое',2000,5)">2К</button></div>
<div class="it"><div class="inf"><b>🍔 Бургер</b></div><button onclick="buyFood('Бургер',5000,15,2000)">5К</button></div>
<div class="it"><div class="inf"><b>🍕 Пицца</b></div><button onclick="buyFood('Пицца',10000,30,5000)">10К</button></div>
<button class="alt" onclick="closeScr('food')">← Закрыть</button></div></div>
<div class="scr" id="tech"><div class="pnl"><h1>🚗 ТЕХНИКА</h1><h2>💰 <span id="tM">0</span></h2>
<div class="it"><div class="inf"><b>🚙 УАЗ-469</b></div><button onclick="buyTech('uaz')">100К</button></div>
<div class="it"><div class="inf"><b>🏎️ Ламборгини</b></div><button onclick="buyTech('lambo')">1.5М</button></div>
<div class="it"><div class="inf"><b>🚗 Лада</b></div><button onclick="buyTech('lada')">25К</button></div>
<div class="it"><div class="inf"><b>🏍️ Yamaha</b></div><button onclick="buyTech('moto')">80К</button></div>
<div class="it"><div class="inf"><b>🚁 Вертолёт</b></div><button onclick="buyTech('heli')">500К</button></div>
<div class="it"><div class="inf"><b>✈️ Самолёт</b></div><button onclick="buyTech('plane')">800К</button></div>
<div class="it"><div class="inf"><b>🎖️ ТАНК</b></div><button onclick="buyTech('tank')">5М</button></div>
<button class="alt" onclick="closeScr('tech')">← Закрыть</button></div></div>
<div class="scr" id="gun"><div class="pnl"><h1>🔫 ОРУЖИЕ</h1>
<div class="it"><div class="inf"><b>🔫 Пистолет</b></div><button onclick="buyGun()">50К</button></div>
<div class="it"><div class="inf"><b>📦 Патроны</b></div><button onclick="buyAmmo()">5К</button></div>
<button class="alt" onclick="closeScr('gun')">← Закрыть</button></div></div>
<div class="scr" id="cas"><div class="pnl"><h1>🎰 КАЗИНО</h1><h2>67% · ×2</h2><input type="number" id="bet" value="100" min="1"><button onclick="casBet(parseInt(document.getElementById('bet').value))">🎲 Играть</button><button class="alt" onclick="closeScr('cas')">← Закрыть</button></div></div>
<div class="scr" id="caseScr"><div class="pnl"><h1>🎁 КЕЙСЫ</h1>
<div class="it"><div class="inf"><b>🎁 Кейс удачи</b></div><button onclick="startCase()">💎 100</button></div>
<div class="it"><div class="inf"><b>💠 ПРЕМИУМ</b></div><button onclick="startPremCase()">💎 600</button></div>
<button class="alt" onclick="closeScr('caseScr')">← Закрыть</button></div></div>
<div class="scr" id="promoScr"><div class="pnl"><h1>🎟️ ПРОМОКОДЫ</h1>
<div class="note">🎁 Промокод на крыше Вокзала №1</div>
<input type="text" id="promoInput" placeholder="ПРОМОКОД">
<button onclick="usePromo()">✅ Активировать</button>
<button class="alt" onclick="closeScr('promoScr')">← Закрыть</button></div></div>
<div class="scr" id="settingsScr"><div class="pnl"><h1>⚙ НАСТРОЙКИ</h1>
<h2>🕹️ Джойстик</h2>
<button class="alt" onclick="setJoyPos('left')">◀ Слева</button>
<button class="alt" onclick="setJoyPos('right')">▶ Справа</button>
<h2 style="margin-top:14px">🎮 Кнопки</h2>
<button class="alt" onclick="setBtnPos('right')">▶ Справа</button>
<button class="alt" onclick="setBtnPos('left')">◀ Слева</button>
<button class="alt" onclick="setBtnPos('top')">▲ Сверху</button>
<button class="red" onclick="resetLayout()">⟲ Сбросить</button>
<button class="alt" onclick="closeScr('settingsScr')">← Закрыть</button></div></div>
<div class="scr" id="mapScr"><div class="pnl" style="max-width:520px"><h1>🗺️ КАРТА</h1><canvas id="bigMap" width="500" height="500"></canvas>
<button class="alt" onclick="closeScr('mapScr')" style="margin-top:10px">← Закрыть</button></div></div>
<div class="scr" id="menu"><div class="pnl"><h1>☰ МЕНЮ</h1><button onclick="closeAll()">▶ Продолжить</button><button class="alt" onclick="openScr('settingsScr')">⚙ Настройки</button><button class="alt" onclick="openScr('adminLog')">⚡ Админ</button><button class="red" onclick="clearSave()">🗑 Сброс</button><button class="red" onclick="location.reload()">⟲ Меню</button></div></div>
<div class="scr" id="adminLog"><div class="pnl"><h1>⚡ АДМИН</h1><input type="password" id="aP" placeholder="Пароль"><button onclick="tryAdmin()">Войти</button><button class="red" onclick="closeScr('adminLog');openScr('menu')">← Назад</button></div></div>
<div class="scr" id="admin"><div class="pnl"><h1>⚡ АДМИН</h1>
<button class="gold" onclick="adminMoney(200000)">💰 +200 000 ₽</button>
<button class="gold" onclick="adminMoney(1000000)">💰 +1 000 000 ₽</button>
<button style="background:linear-gradient(180deg,#ff6b9d,#c41e5c)" onclick="adminDonate(200)">💎 +200</button>
<h2 style="margin-top:12px">🌤️ ПОГОДА</h2>
<button class="alt" onclick="setWeather('clear')">☀️ Ясно</button>
<button class="alt" onclick="setWeather('rain')">🌧️ Дождь</button>
<button class="alt" onclick="setWeather('fog')">🌫️ Туман</button>
<h2>🕐 ВРЕМЯ</h2>
<button class="alt" onclick="setTimeManual(8)">🌅 Утро</button>
<button class="alt" onclick="setTimeManual(12)">☀️ День</button>
<button class="alt" onclick="setTimeManual(18)">🌇 Закат</button>
<button class="alt" onclick="setTimeManual(0)">🌙 Ночь</button>
<h2>🚗 ТЕХНИКА</h2>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:5px">
<button onclick="admUAZ()">🚙 УАЗ</button>
<button onclick="admLambo()">🏎️ Ламбо</button>
<button onclick="admTank()">🎖️ ТАНК</button>
<button onclick="admHeli()">🚁</button>
</div>
<button class="red" onclick="closeScr('admin')">← Назад</button></div></div>
<script>
function openScr(i){var e=document.getElementById(i);if(e)e.classList.add('on');if(i==='mapScr')setTimeout(drawMap,100);}
function closeScr(i){var e=document.getElementById(i);if(e)e.classList.remove('on');}
function toast(t){var e=document.getElementById('to');e.textContent=t;e.classList.add('show');clearTimeout(window._t);window._t=setTimeout(function(){e.classList.remove('show');},2000);}
function yaSearch(){var q=document.getElementById('yaQ').value.trim();if(!q){toast('❌');return;}try{window.open('https://yandex.ru/search/?text='+encodeURIComponent(q),'_blank');}catch(e){}}
function applyRot(){var w=document.getElementById('rw');var iw=innerWidth,ih=innerHeight;w.style.transform='';w.style.transformOrigin='top left';if(ih>iw){w.style.width=ih+'px';w.style.height=iw+'px';w.style.transform='rotate(90deg) translateY(-'+iw+'px)';}else{w.style.width='100vw';w.style.height='100vh';}setTimeout(function(){if(window.renderer&&window.camera){var cw=w.clientWidth||Math.max(iw,ih);var ch=w.clientHeight||Math.min(iw,ih);camera.aspect=cw/ch;camera.updateProjectionMatrix();renderer.setSize(cw,ch,false);}},80);}
function goLand(){try{document.documentElement.requestFullscreen().catch(function(){});}catch(e){}
setTimeout(function(){try{if(screen.orientation&&screen.orientation.lock)screen.orientation.lock('landscape').catch(function(){});}catch(e){}},300);setTimeout(applyRot,400);setTimeout(applyRot,900);}
addEventListener('resize',function(){setTimeout(applyRot,100);});
addEventListener('orientationchange',function(){setTimeout(applyRot,200);});
setTimeout(applyRot,300);
function setJoyPos(p){var j=document.getElementById('js');if(p==='right')j.classList.add('right');else j.classList.remove('right');localStorage.setItem('joyPos',p);toast('🕹️ '+p);}
function setBtnPos(p){var a=document.getElementById('ar');a.classList.remove('left','top');if(p==='left')a.classList.add('left');else if(p==='top')a.classList.add('top');localStorage.setItem('btnPos',p);toast('🎮 '+p);}
function resetLayout(){setJoyPos('left');setBtnPos('right');toast('⟲');}
function loadLayout(){setJoyPos(localStorage.getItem('joyPos')||'left');setBtnPos(localStorage.getItem('btnPos')||'right');}
setTimeout(loadLayout,500);
</script>
<script>
(function(){var U='https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js',DB='mr',S='f',K='t3';
function odb(){return new Promise(function(r,j){var x=indexedDB.open(DB,1);x.onupgradeneeded=function(){x.result.createObjectStore(S);};x.onsuccess=function(){r(x.result);};x.onerror=function(){j(x.error);};});}
function dg(db,k){return new Promise(function(r,j){var t=db.transaction(S,'readonly').objectStore(S).get(k);t.onsuccess=function(){r(t.result);};t.onerror=function(){j(t.error);};});}
function dp(db,k,v){return new Promise(function(r,j){var t=db.transaction(S,'readwrite').objectStore(S).put(v,k);t.onsuccess=function(){r();};t.onerror=function(){j(t.error);};});}
function inj(c){var s=document.createElement('script');s.textContent=c;document.head.appendChild(s);setTimeout(function(){if(typeof THREE!=='undefined'&&typeof startWhenReady==='function')startWhenReady();},80);}
function fn(){return new Promise(function(r,j){var x=new XMLHttpRequest();x.open('GET',U,true);x.onprogress=function(e){if(e.lengthComputable){document.getElementById('lp').classList.add('on');document.getElementById('lpB').style.width=Math.round(e.loaded/e.total*100)+'%';}};x.onload=function(){if(x.status===200)r(x.responseText);else j(new Error('HTTP'));};x.onerror=function(){j(new Error('нет'));};x.send();});}
window.loadThree=function(){document.getElementById('lb').style.display='none';odb().then(function(db){return dg(db,K).then(function(c){if(c){inj(c);document.getElementById('lib').classList.remove('on');return;}return fn().then(function(code){inj(code);return dp(db,K,code).then(function(){document.getElementById('lib').classList.remove('on');});});});}).catch(function(){fn().then(function(code){inj(code);document.getElementById('lib').classList.remove('on');}).catch(function(e){toast('Ошибка: '+e.message);});});};
addEventListener('DOMContentLoaded',function(){odb().then(function(db){return dg(db,K).then(function(c){if(c)inj(c);else document.getElementById('lib').classList.add('on');});}).catch(function(){document.getElementById('lib').classList.add('on');});});})();
</script>
<script>
var state={money:100000,fortune:0,hasGun:false,ammo:0,maxAmmo:12,donate:0};
var scene,camera,renderer,clock,drivingCar=null,playerBody=null,gunMesh=null;
var gameStarted=false,paused=false,camMode=1,nearCar=null,nearHouse=null,inHouse=null;
var keys={},colliders=[],cars=[],npcs=[],trafficCars=[],houses=[],grassTufts=[];
var player={pos:null,yaw:0,pitch:0,vy:0,onGround:true,speed:5.4,moving:false};
var _v1,_v2,_v3,clamp=function(v,a,b){return Math.max(a,Math.min(b,v));},PR=0.6;
var sunLight,timeH=12,weather='clear',rainMesh,autoTime=true;
var usedPromos={};var savedOutsidePos=null;
function usePromo(){var inp=document.getElementById('promoInput');var code=inp.value.trim().toUpperCase();
if(!code){toast('❌');return;}if(usedPromos[code]){toast('❌ Уже использован');return;}
if(code==='2026'){usedPromos[code]=true;state.money+=500000;state.donate+=100;updateHUD();toast('🎉 +500К, +100💎');}
else toast('❌ Неверный код');inp.value='';}
function createGrassTexture(){
var cv=document.createElement('canvas');cv.width=256;cv.height=256;var ctx=cv.getContext('2d');
ctx.fillStyle='#3d6a2a';ctx.fillRect(0,0,256,256);
for(var i=0;i<8000;i++){var x=Math.random()*256,y=Math.random()*256,h=3+Math.random()*6,w=1+Math.random()*2;var s=Math.floor(70+Math.random()*80);ctx.fillStyle='rgb('+(s-30)+','+s+','+(s-40)+')';ctx.fillRect(x,y,w,h);}
for(var j=0;j<300;j++){var cx=Math.random()*256,cy=Math.random()*256,r=5+Math.random()*20;ctx.fillStyle='rgba(30,60,20,'+(0.15+Math.random()*0.25)+')';ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fill();}
var t=new THREE.CanvasTexture(cv);t.wrapS=THREE.RepeatWrapping;t.wrapT=THREE.RepeatWrapping;t.repeat.set(120,120);return t;}
function createRoadTexture(){
var cv=document.createElement('canvas');cv.width=256;cv.height=256;var ctx=cv.getContext('2d');
ctx.fillStyle='#2e3138';ctx.fillRect(0,0,256,256);
for(var i=0;i<500;i++){var x=Math.random()*256,y=Math.random()*256;ctx.fillStyle='rgba(80,80,80,'+(0.1+Math.random()*0.2)+')';ctx.fillRect(x,y,2+Math.random()*3,1);}
ctx.fillStyle='#f0e48a';for(var j=0;j<256;j+=32){ctx.fillRect(124,j,8,16);}
var t=new THREE.CanvasTexture(cv);t.wrapS=THREE.RepeatWrapping;t.wrapT=THREE.RepeatWrapping;t.repeat.set(1,30);return t;}
function createBuildingTexture(color){
var cv=document.createElement('canvas');cv.width=128;cv.height=256;var ctx=cv.getContext('2d');
ctx.fillStyle=color;ctx.fillRect(0,0,128,256);
for(var fy=0;fy<8;fy++){for(var fx=0;fx<4;fx++){
ctx.fillStyle='rgba(180,220,255,0.7)';ctx.fillRect(8+fx*30,10+fy*30,22,22);
ctx.strokeStyle='rgba(40,40,40,0.9)';ctx.lineWidth=1.5;ctx.strokeRect(8+fx*30,10+fy*30,22,22);}}
return new THREE.CanvasTexture(cv);}
function saveGame(){if(!gameStarted)return;try{localStorage.setItem('mr14',JSON.stringify({ts:Date.now(),x:player.pos.x,y:player.pos.y,z:player.pos.z,yaw:player.yaw,pitch:player.pitch,money:state.money,hasGun:state.hasGun,ammo:state.ammo,donate:state.donate,usedPromos:usedPromos,timeH:timeH}));}catch(e){}}
function loadSave(){try{var r=localStorage.getItem('mr14');if(!r)return null;var d=JSON.parse(r);if(Date.now()-d.ts>20*60*1000){localStorage.removeItem('mr14');return null;}return d;}catch(e){return null;}}
function clearSave(){localStorage.removeItem('mr14');toast('Сброшено');document.getElementById('bC').style.display='none';}
function cont(){var d=loadSave();if(!d){toast('Нет');return;}player.pos.set(d.x,d.y,d.z);player.yaw=d.yaw;player.pitch=d.pitch;state.money=d.money;state.hasGun=!!d.hasGun;state.ammo=d.ammo;state.donate=d.donate||0;usedPromos=d.usedPromos||{};timeH=d.timeH||12;document.getElementById('start').classList.remove('on');goLand();startGameWithPlayer();}
var joyA=false,joyId=null,joyX=0,joyY=0,joyCX=0,joyCY=0;
function initJoy(){var j=document.getElementById('js'),k=document.getElementById('jk'),md=60;
function st(e){e.preventDefault();joyA=true;var t=e.changedTouches?e.changedTouches[0]:e;joyId=t.identifier!==undefined?t.identifier:'m';var r=j.getBoundingClientRect();joyCX=r.left+r.width/2;joyCY=r.top+r.height/2;mv(e);}
function mv(e){if(!joyA)return;var t=null;if(e.changedTouches){for(var i=0;i<e.changedTouches.length;i++){if(e.changedTouches[i].identifier===joyId){t=e.changedTouches[i];break;}}}else{t=e;}if(!t)return;
var dx=t.clientX-joyCX,dy=t.clientY-joyCY;var isR=document.getElementById('rw').style.transform.indexOf('rotate')>-1;var vx,vy;
if(isR){vx=dy;vy=-dx;}else{vx=dx;vy=dy;}
var dd=Math.hypot(vx,vy);if(dd>md){vx=vx/dd*md;vy=vy/dd*md;}
k.style.transform='translate('+vx+'px,'+vy+'px)';joyX=vx/md;joyY=vy/md;}
function en(){joyA=false;joyId=null;joyX=0;joyY=0;k.style.transform='';}
j.addEventListener('touchstart',st,{passive:false});j.addEventListener('touchmove',mv,{passive:false});j.addEventListener('touchend',en);j.addEventListener('touchcancel',en);j.addEventListener('mousedown',st);
addEventListener('mousemove',function(e){if(joyA&&joyId==='m')mv(e);});addEventListener('mouseup',function(e){if(joyId==='m')en(e);});}
var mc={};function mat(c,f){var k=c+'|'+(f?1:0);if(!mc[k])mc[k]=f?new THREE.MeshBasicMaterial({color:c}):new THREE.MeshLambertMaterial({color:c});return mc[k];}
function m(g,mt,x,y,z,p){var mm=new THREE.Mesh(g,mt);mm.position.set(x,y,z);(p||scene).add(mm);return mm;}
function makeHuman(){var g=new THREE.Group();var sk=[0xf2c9a0,0xd9a06e,0xa86840],sh=[0x3498db,0xe74c3c,0x2ecc71,0xf39c12,0x9b59b6],pa=[0x2c3e50,0x1a1a1a,0x4a4238];var skin=mat(sk[(Math.random()*sk.length)|0]),shirt=mat(sh[(Math.random()*sh.length)|0]),pant=mat(pa[(Math.random()*pa.length)|0]);m(new THREE.SphereGeometry(0.22,6,5),skin,0,1.72,0,g);m(new THREE.SphereGeometry(0.23,6,4,0,Math.PI*2,0,Math.PI/2),mat(0x1a1a1a),0,1.72,0,g);m(new THREE.CylinderGeometry(0.19,0.23,0.68,6),shirt,0,1.15,0,g);var la=new THREE.Group();la.position.set(-0.3,1.44,0);m(new THREE.CylinderGeometry(0.06,0.07,0.63,4),shirt,0,-0.32,0,la);g.add(la);var ra=new THREE.Group();ra.position.set(0.3,1.44,0);m(new THREE.CylinderGeometry(0.06,0.07,0.63,4),shirt,0,-0.32,0,ra);g.add(ra);var ll=new THREE.Group();ll.position.set(-0.11,0.82,0);m(new THREE.CylinderGeometry(0.09,0.075,0.82,4),pant,0,-0.41,0,ll);m(new THREE.BoxGeometry(0.15,0.09,0.25),mat(0x1a1a1a),0,-0.8,0.04,ll);g.add(ll);var rl=new THREE.Group();rl.position.set(0.11,0.82,0);m(new THREE.CylinderGeometry(0.09,0.075,0.82,4),pant,0,-0.41,0,rl);m(new THREE.BoxGeometry(0.15,0.09,0.25),mat(0x1a1a1a),0,-0.8,0.04,rl);g.add(rl);g.userData={la:la,ra:ra,ll:ll,rl:rl,wp:Math.random()*6.28};return g;}
function animH(h,dt,mv){var u=h.userData;if(!u)return;if(mv){u.wp+=dt*7;var sw=Math.sin(u.wp);u.la.rotation.x=sw*0.85;u.ra.rotation.x=-sw*0.85;u.ll.rotation.x=-sw*0.75;u.rl.rotation.x=sw*0.75;}else{u.la.rotation.x*=0.85;u.ra.rotation.x*=0.85;u.ll.rotation.x*=0.85;u.rl.rotation.x*=0.85;}}
function addCol(x,z,w,d){colliders.push({minX:x-w/2,maxX:x+w/2,minZ:z-d/2,maxZ:z+d/2});}
function collides(x,z){for(var i=0;i<colliders.length;i++){var c=colliders[i];if(x>c.minX-0.6&&x<c.maxX+0.6&&z>c.minZ-0.6&&z<c.maxZ+0.6)return true;}return false;}
function circleCol(x,z){for(var i=0;i<colliders.length;i++){var c=colliders[i];var cx=Math.max(c.minX,Math.min(x,c.maxX)),cz=Math.max(c.minZ,Math.min(z,c.maxZ));var dx=x-cx,dz=z-cz;if(dx*dx+dz*dz<PR*PR)return true;}return false;}
/* ==== КУСТИКИ ТРАВЫ ==== */
function makeGrassTuft(x,z){
var g=new THREE.Group();
var col=0x4a7a2e;
for(var i=0;i<6;i++){
var blade=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.5+Math.random()*0.3,0.05),mat(col));
blade.position.set((Math.random()-0.5)*0.4,0.25,(Math.random()-0.5)*0.4);
blade.rotation.y=Math.random()*Math.PI;
blade.rotation.z=(Math.random()-0.5)*0.3;
g.add(blade);}
g.position.set(x,0,z);scene.add(g);return g;}
/* ==== 2-ЭТАЖНЫЙ ДОМ С ВОРОТАМИ, ГАЗОНОМ, САДОМ, ТЕПЛИЦЕЙ ==== */
function buildPrivateHouse(x,z,idx){
var g=new THREE.Group();
var wallColor=[0xf0e8d0,0xe8d5b8,0xf5e8c0,0xede0c8][idx%4];
var roofColor=[0x8a3a3a,0x6a2a2a,0x4a3a2a,0x7a4a2a][idx%4];
/* === ГАЗОН перед домом === */
var lawn=new THREE.Mesh(new THREE.PlaneGeometry(22,18),mat(0x5a9a3a));
lawn.rotation.x=-Math.PI/2;lawn.position.set(0,0.06,4);g.add(lawn);
/* === ОСНОВАНИЕ/ФУНДАМЕНТ === */
m(new THREE.BoxGeometry(12,0.6,10),mat(0x8a8a7a),0,0.3,-2,g);
/* === 1 ЭТАЖ === */
m(new THREE.BoxGeometry(11,3.4,9),mat(wallColor),0,2.3,-2,g);
/* === 2 ЭТАЖ === */
m(new THREE.BoxGeometry(10,3,8),mat(wallColor),0,5.5,-2,g);
/* === КРЫША === */
var roof=new THREE.Mesh(new THREE.ConeGeometry(8.5,3,4),mat(roofColor));
roof.position.set(0,8.5,-2);roof.rotation.y=Math.PI/4;g.add(roof);
/* === ОКНА 1 этажа === */
var winMat=new THREE.MeshLambertMaterial({color:0x88bbdd,transparent:true,opacity:0.55});
m(new THREE.BoxGeometry(2,1.6,0.1),mat(0x2a2f38),-3.5,2.3,2.55,g);
m(new THREE.BoxGeometry(1.7,1.3,0.1),winMat,-3.5,2.3,2.65,g);
m(new THREE.BoxGeometry(2,1.6,0.1),mat(0x2a2f38),3.5,2.3,2.55,g);
m(new THREE.BoxGeometry(1.7,1.3,0.1),winMat,3.5,2.3,2.65,g);
/* === ДВЕРЬ === */
m(new THREE.BoxGeometry(1.8,2.8,0.1),mat(0x4a2e1a),0,1.9,2.55,g);
m(new THREE.SphereGeometry(0.08,6,6),mat(0xd4af37),0.6,1.9,2.65,g);
/* === ОКНА 2 этажа === */
m(new THREE.BoxGeometry(1.8,1.5,0.1),mat(0x2a2f38),-3,5.5,2.05,g);
m(new THREE.BoxGeometry(1.5,1.2,0.1),winMat,-3,5.5,2.15,g);
m(new THREE.BoxGeometry(1.8,1.5,0.1),mat(0x2a2f38),3,5.5,2.05,g);
m(new THREE.BoxGeometry(1.5,1.2,0.1),winMat,3,5.5,2.15,g);
m(new THREE.BoxGeometry(1.8,1.5,0.1),mat(0x2a2f38),0,5.5,2.05,g);
m(new THREE.BoxGeometry(1.5,1.2,0.1),winMat,0,5.5,2.15,g);
/* === БАЛКОН === */
m(new THREE.BoxGeometry(8,0.15,1.6),mat(0x8a8a8a),0,3.9,3.2,g);
m(new THREE.BoxGeometry(8,0.7,0.08),mat(0xcccccc),0,4.3,4,g);
/* === ВОРОТА (2 створки) === */
var gateMat=mat(0x4a5a3a);
var gateL=new THREE.Mesh(new THREE.BoxGeometry(2.2,2.6,0.15),gateMat);
gateL.position.set(-1.15,1.3,7.5);g.add(gateL);
var gateR=new THREE.Mesh(new THREE.BoxGeometry(2.2,2.6,0.15),gateMat);
gateR.position.set(1.15,1.3,7.5);g.add(gateR);
m(new THREE.BoxGeometry(0.2,3,0.2),mat(0x333333),-2.3,1.5,7.5,g);
m(new THREE.BoxGeometry(0.2,3,0.2),mat(0x333333),2.3,1.5,7.5,g);
/* === ЗАБОР вокруг участка === */
var fence=mat(0xdddddd);
for(var fx=-11;fx<=11;fx+=1.6){
if(Math.abs(fx)<2.3)continue;
m(new THREE.BoxGeometry(0.12,1.3,0.12),fence,fx,0.65,9.5,g);
m(new THREE.BoxGeometry(0.12,1.3,0.12),fence,fx,0.65,-7,g);}
for(var fz=-7;fz<=9.5;fz+=1.6){
m(new THREE.BoxGeometry(0.12,1.3,0.12),fence,-11,0.65,fz,g);
m(new THREE.BoxGeometry(0.12,1.3,0.12),fence,11,0.65,fz,g);}
/* === САД: 3 дерева сбоку === */
for(var ti=0;ti<3;ti++){
var tx=-8+ti*2.5,tz=-4-ti*0.8;
m(new THREE.CylinderGeometry(0.3,0.4,3,6),mat(0x6b4423),tx,1.5,tz,g);
m(new THREE.SphereGeometry(1.6,6,5),mat(0x2e8b2e),tx,3.5,tz,g);}
/* === ТЕПЛИЦА в углу === */
var gh=new THREE.Group();
m(new THREE.BoxGeometry(4,2.2,3),new THREE.MeshLambertMaterial({color:0xc8e8c8,transparent:true,opacity:0.4}),0,1.1,0,gh);
m(new THREE.BoxGeometry(4.1,0.2,3.1),mat(0xaaaaaa),0,2.3,0,gh);
for(var wi=0;wi<3;wi++){m(new THREE.BoxGeometry(0.15,2,0.15),mat(0xdddddd),-1.9+wi*1.9,1,1.5,gh);}
/* Растения внутри */
m(new THREE.BoxGeometry(3.5,0.4,0.6),mat(0x6b4423),0,0.9,-0.7,gh);
m(new THREE.SphereGeometry(0.35,5,5),mat(0x4a9a3a),-1,1.15,-0.7,gh);
m(new THREE.SphereGeometry(0.35,5,5),mat(0x4a9a3a),0,1.15,-0.7,gh);
m(new THREE.SphereGeometry(0.35,5,5),mat(0x4a9a3a),1,1.15,-0.7,gh);
gh.position.set(7.5,0,-4);g.add(gh);
/* === ДОРОЖКА от ворот к двери === */
var path=new THREE.Mesh(new THREE.PlaneGeometry(2,5),mat(0x9a9a8a));
path.rotation.x=-Math.PI/2;path.position.set(0,0.08,5);g.add(path);
g.position.set(x,0,z);scene.add(g);
addCol(x,z,11,9);
/* Регистрируем дом */
houses.push({x:x,z:z,doorX:x,doorZ:z+2.7,idx:idx,w:11,d:9});}
/* === ИНТЕРЬЕР ДОМА (единый шаблон, игрок телепортируется сюда) === */
var interiorRoom=null;
function buildInterior(){
var g=new THREE.Group();
var W=10,D=10,H=4;
m(new THREE.BoxGeometry(W,0.2,D),mat(0x8a6030),0,0.1,0,g);
m(new THREE.BoxGeometry(W,0.2,D),mat(0xf5ead8),0,H,0,g);
m(new THREE.BoxGeometry(0.2,H,D),mat(0xe8dcc0),-W/2,H/2,0,g);
m(new THREE.BoxGeometry(0.2,H,D),mat(0xe8dcc0),W/2,H/2,0,g);
m(new THREE.BoxGeometry(W,H,0.2),mat(0xe8dcc0),0,H/2,-D/2,g);
/* Передняя стена с дверью */
m(new THREE.BoxGeometry(3.5,H,0.2),mat(0xe8dcc0),-3.25,H/2,D/2,g);
m(new THREE.BoxGeometry(3.5,H,0.2),mat(0xe8dcc0),3.25,H/2,D/2,g);
m(new THREE.BoxGeometry(3,H-2,0.2),mat(0xe8dcc0),0,H-(H-2)/2,D/2,g);
/* Кровать */
m(new THREE.BoxGeometry(2,0.4,3),mat(0x6a3a1a),-3,0.4,-2.5,g);
m(new THREE.BoxGeometry(1.9,0.2,2.9),mat(0xdddddd),-3,0.7,-2.5,g);
m(new THREE.BoxGeometry(1.9,0.3,0.8),mat(0x4a9ad0),-3,0.8,-3.5,g);
/* ТВ */
m(new THREE.BoxGeometry(2.5,1.5,0.15),mat(0x1a1a1a),3,2.2,-4.8,g);
m(new THREE.BoxGeometry(2.2,1.2,0.05),new THREE.MeshBasicMaterial({color:0x2a4a8a}),3,2.2,-4.7,g);
/* Шкаф */
m(new THREE.BoxGeometry(2.5,3,1),mat(0x5a3a1a),-4,1.5,3,g);
/* Стол */
m(new THREE.BoxGeometry(2.5,0.15,1.5),mat(0x8a4a2a),3,1,2.5,g);
m(new THREE.CylinderGeometry(0.15,0.15,0.9,4),mat(0x5a3a1a),2,0.5,2,g);
m(new THREE.CylinderGeometry(0.15,0.15,0.9,4),mat(0x5a3a1a),4,0.5,2,g);
m(new THREE.CylinderGeometry(0.15,0.15,0.9,4),mat(0x5a3a1a),2,0.5,3,g);
m(new THREE.CylinderGeometry(0.15,0.15,0.9,4),mat(0x5a3a1a),4,0.5,3,g);
/* Стул */
m(new THREE.BoxGeometry(0.6,0.1,0.6),mat(0x8a4a2a),3,0.6,3.5,g);
m(new THREE.BoxGeometry(0.6,1,0.1),mat(0x8a4a2a),3,1.1,3.8,g);
/* Люстра */
m(new THREE.SphereGeometry(0.35,6,6),mat(0xfff0c0,true),0,3.6,0,g);
var light=new THREE.PointLight(0xffe8c0,0.9,15);light.position.set(0,3.3,0);g.add(light);
/* Окна (вид на улицу) */
m(new THREE.BoxGeometry(2.5,1.5,0.05),new THREE.MeshBasicMaterial({color:0x88bbdd}),-4,2.3,-4.9,g);
m(new THREE.BoxGeometry(2.5,1.5,0.05),new THREE.MeshBasicMaterial({color:0x88bbdd}),4,2.3,-4.9,g);
g.position.set(0,-1000,0);g.visible=false;scene.add(g);interiorRoom=g;}
/* === ВХОД/ВЫХОД === */
function enterHouse(){if(!nearHouse||inHouse)return;
savedOutsidePos={x:player.pos.x,y:player.pos.y,z:player.pos.z,yaw:player.yaw,pitch:player.pitch};
player.pos.set(0,-999.5,-3.5);player.yaw=Math.PI;player.pitch=0;
if(interiorRoom)interiorRoom.visible=true;
inHouse=nearHouse;document.getElementById('hint').textContent='[E] 🚪 Выйти из дома';document.getElementById('hint').classList.add('on');
toast('🏠 Вы вошли в дом №'+(nearHouse.idx+1));}
function exitHouse(){if(!inHouse)return;
if(savedOutsidePos){player.pos.set(savedOutsidePos.x,savedOutsidePos.y,savedOutsidePos.z);player.yaw=savedOutsidePos.yaw;player.pitch=savedOutsidePos.pitch;}
if(interiorRoom)interiorRoom.visible=false;
inHouse=null;toast('🚪 Вы вышли');}
/* === ЗДАНИЯ ==== */
function buildBld(x,z,w,d,floors,color){var g=new THREE.Group();var h=floors*3.4+1;var tex=createBuildingTexture(color);var bodyMat=new THREE.MeshLambertMaterial({map:tex});m(new THREE.BoxGeometry(w,h,d),bodyMat,0,h/2,0,g);m(new THREE.BoxGeometry(w+0.4,0.6,d+0.4),mat(0x333333),0,h+0.3,0,g);m(new THREE.BoxGeometry(w+0.6,1,d+0.6),mat(0x222222),0,0.5,0,g);m(new THREE.BoxGeometry(2,3,0.2),mat(0x4a2e1a),0,1.5,d/2+0.15,g);g.position.set(x,0,z);scene.add(g);addCol(x,z,w,d);}
function buildShopBld(x,z,color,title){var g=new THREE.Group();var w=14,h=5,d=10;m(new THREE.BoxGeometry(w,h,d),mat(color),0,h/2,0,g);m(new THREE.BoxGeometry(w+0.5,0.5,d+0.5),mat(0x333333),0,h+0.25,0,g);var glass=new THREE.MeshLambertMaterial({color:0x88bbdd,transparent:true,opacity:0.6});m(new THREE.BoxGeometry(w-2,3,0.2),glass,0,2,d/2+0.15,g);var cv=document.createElement('canvas');cv.width=512;cv.height=120;var cx=cv.getContext('2d');cx.fillStyle='#111';cx.fillRect(0,0,512,120);cx.fillStyle='#ffd166';cx.font='bold 60px Arial';cx.textAlign='center';cx.textBaseline='middle';cx.fillText(title,256,60);var sign=new THREE.Mesh(new THREE.PlaneGeometry(w*0.85,2),new THREE.MeshBasicMaterial({map:new THREE.CanvasTexture(cv),side:THREE.DoubleSide}));sign.position.set(0,h+1.5,d/2+0.3);g.add(sign);g.position.set(x,0,z);scene.add(g);addCol(x,z,w,d);}
function buildStation(x,z,title,promo){var g=new THREE.Group();scene.add(g);var W=50,D=26,H=11;m(new THREE.BoxGeometry(W,0.6,D),mat(0xbfb8a8),x,0.3,z,g);m(new THREE.BoxGeometry(W+4,0.8,D+4),mat(0x8c3a3a),x,H,z,g);var cm=mat(0xd8cfc0);for(var cx=-W/2+6;cx<=W/2-6;cx+=16){m(new THREE.CylinderGeometry(0.6,0.7,H,6),cm,x+cx,H/2+0.6,z-D/2+5,g);m(new THREE.CylinderGeometry(0.6,0.7,H,6),cm,x+cx,H/2+0.6,z+D/2-5,g);}var railMat=mat(0x8a8a8a),sleepMat=mat(0x4a3a2a);for(var rz=-500;rz<=500;rz+=5){m(new THREE.BoxGeometry(2.5,0.15,0.3),sleepMat,x-8,0.08,z+rz,g);}m(new THREE.BoxGeometry(0.15,0.12,1000),railMat,x-8.6,0.2,z,g);m(new THREE.BoxGeometry(0.15,0.12,1000),railMat,x-7.4,0.2,z,g);
if(promo){var cv=document.createElement('canvas');cv.width=512;cv.height=180;var cx2=cv.getContext('2d');cx2.fillStyle='#ffd166';cx2.fillRect(0,0,512,180);cx2.fillStyle='#8a1e1e';cx2.font='bold 70px Arial';cx2.textAlign='center';cx2.textBaseline='middle';cx2.fillText('🎁 ПРОМОКОД',256,50);cx2.font='bold 100px Arial';cx2.fillStyle='#000';cx2.fillText('2026',256,135);var sign2=new THREE.Mesh(new THREE.PlaneGeometry(28,10),new THREE.MeshBasicMaterial({map:new THREE.CanvasTexture(cv),side:THREE.DoubleSide}));sign2.position.set(x,H+6,z);scene.add(sign2);}}
function buildLamp(x,z){var g=new THREE.Group();m(new THREE.CylinderGeometry(0.15,0.15,7,4),mat(0x333333),0,3.5,0,g);var lp=new THREE.Mesh(new THREE.SphereGeometry(0.4,4,4),new THREE.MeshBasicMaterial({color:0xfff0c0}));lp.position.set(0,7,0);g.add(lp);g.userData.lamp=lp;g.position.set(x,0,z);scene.add(g);lamps.push(g);}
var lamps=[];
function createUAZ(x,z,rot){var g=new THREE.Group();g.rotation.order='YXZ';var body=mat(0x3e5230),dark=mat(0x1a1a1a),chrome=mat(0xdddddd),tire=mat(0x141414),rim=mat(0xb8b8b8);var glass=new THREE.MeshLambertMaterial({color:0x1a2530,transparent:true,opacity:0.5});var light=mat(0xfff8d0,true),red=mat(0xff2a2a,true);var L=4.1,W=2.0;m(new THREE.BoxGeometry(W,0.75,L*0.96),body,0,0.85,0,g);var hood=m(new THREE.BoxGeometry(W-0.1,0.5,L*0.32),body);hood.position.set(0,1.45,-L*0.28);hood.rotation.x=-0.08;g.add(hood);m(new THREE.BoxGeometry(W-0.05,0.85,L*0.55),body,0,1.95,0.15,g);m(new THREE.BoxGeometry(W-0.1,0.08,L*0.53),body,0,2.4,0.15,g);var ws=m(new THREE.BoxGeometry(W-0.15,0.75,0.08),glass);ws.position.set(0,2.05,-L*0.13);ws.rotation.x=-0.25;g.add(ws);m(new THREE.BoxGeometry(0.06,0.7,L*0.5),glass,W/2-0.02,2.05,0.15,g);m(new THREE.BoxGeometry(0.06,0.7,L*0.5),glass,-W/2+0.02,2.05,0.15,g);m(new THREE.BoxGeometry(W-0.2,0.55,0.08),glass,0,2.0,L*0.42,g);m(new THREE.BoxGeometry(W-0.2,0.45,0.06),dark,0,1.4,-L/2-0.05,g);for(var i=0;i<7;i++){m(new THREE.BoxGeometry(0.06,0.36,0.08),chrome,-0.65+i*0.22,1.4,-L/2-0.08,g);}[-0.62,0.62].forEach(function(wx){var f=new THREE.Mesh(new THREE.CylinderGeometry(0.16,0.16,0.08,12),light);f.position.set(wx,1.4,-L/2-0.06);f.rotation.x=Math.PI/2;g.add(f);});m(new THREE.BoxGeometry(W+0.15,0.18,0.15),chrome,0,0.95,-L/2-0.1,g);m(new THREE.BoxGeometry(W+0.15,0.18,0.15),chrome,0,0.95,L/2+0.1,g);var sp=new THREE.Mesh(new THREE.CylinderGeometry(0.4,0.4,0.18,14),tire);sp.position.set(0,1.7,-L*0.35);sp.rotation.x=Math.PI/2;g.add(sp);var wr=0.5,wb=L*0.34,wo=W/2+0.02;var tg=new THREE.CylinderGeometry(wr,wr,0.35,16),rg=new THREE.CylinderGeometry(wr*0.55,wr*0.55,0.37,10);[[-wo,-wb],[wo,-wb],[-wo,wb],[wo,wb]].forEach(function(p){m(tg,tire,p[0],wr,p[1],g).rotation.z=Math.PI/2;m(rg,rim,p[0],wr,p[1],g).rotation.z=Math.PI/2;});g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);cars.push({group:g,owned:false,price:100000,speed:0,rot:rot,name:'УАЗ-469',top:42,acc:14,isFlying:false,altitude:0,type:'uaz'});return g;}
function createLambo(x,z,rot,color){var g=new THREE.Group();g.rotation.order='YXZ';var body=mat(color||0xffcc00),dark=mat(0x1a1a1a),tire=mat(0x141414),rim=mat(0xc8c8c8);var glass=new THREE.MeshLambertMaterial({color:0x0a1520,transparent:true,opacity:0.35});var light=mat(0xfff8d0,true),red=mat(0xff2a2a,true);var L=4.5,W=2.1,wr=0.4;m(new THREE.BoxGeometry(W,0.45,L*0.98),body,0,0.55,0,g);m(new THREE.BoxGeometry(W-0.15,0.35,L*0.95),body,0,0.9,0,g);var nose=new THREE.Mesh(new THREE.BoxGeometry(W-0.4,0.35,L*0.3),body);nose.position.set(0,0.65,-L*0.42);nose.rotation.x=0.15;g.add(nose);m(new THREE.BoxGeometry(W-0.3,0.5,L*0.5),body,0,1.25,-0.15,g);var ws=new THREE.Mesh(new THREE.BoxGeometry(W-0.5,0.6,0.08),glass);ws.position.set(0,1.4,-L*0.32);ws.rotation.x=0.65;g.add(ws);m(new THREE.BoxGeometry(W-0.4,0.5,0.08),glass,0,1.35,L*0.15,g).rotation.x=-0.5;m(new THREE.BoxGeometry(0.06,0.4,L*0.4),glass,W/2-0.22,1.3,-0.1,g);m(new THREE.BoxGeometry(0.06,0.4,L*0.4),glass,-W/2+0.22,1.3,-0.1,g);m(new THREE.BoxGeometry(W-0.25,0.1,0.4),dark,0,1.5,L*0.44,g);m(new THREE.BoxGeometry(0.1,0.3,0.25),dark,-W/2+0.25,1.3,L*0.44,g);m(new THREE.BoxGeometry(0.1,0.3,0.25),dark,W/2-0.25,1.3,L*0.44,g);m(new THREE.BoxGeometry(W,0.2,0.2),dark,0,0.4,-L/2-0.05,g);[-W*0.36,W*0.36].forEach(function(wx){m(new THREE.BoxGeometry(0.45,0.15,0.08),light,wx,0.85,-L/2-0.12,g);});[-W*0.36,W*0.36].forEach(function(wx){m(new THREE.BoxGeometry(0.4,0.15,0.08),red,wx,0.85,L/2+0.08,g);});var tg=new THREE.CylinderGeometry(wr,wr,0.4,16),rg=new THREE.CylinderGeometry(wr*0.65,wr*0.65,0.42,12);var wb=L*0.34,wo=W/2+0.03;[[-wo,-wb],[wo,-wb],[-wo,wb],[wo,wb]].forEach(function(p){m(tg,tire,p[0],wr,p[1],g).rotation.z=Math.PI/2;m(rg,rim,p[0],wr,p[1],g).rotation.z=Math.PI/2;});g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);cars.push({group:g,owned:false,price:1500000,speed:0,rot:rot,name:'Ламборгини',top:90,acc:32,isFlying:false,altitude:0,type:'sport'});return g;}
function createCar(x,z,rot,color,name,price,type){var g=new THREE.Group();g.rotation.order='YXZ';var body=mat(color),dark=mat(0x1a1a1a),tire=mat(0x141414),rim=mat(0xc8c8c8);var glass=new THREE.MeshLambertMaterial({color:0x1a2530,transparent:true,opacity:0.5});var light=mat(0xfff8d0,true),red=mat(0xff2a2a,true);type=type||'sedan';var L,W,wr;if(type==='sport'){L=4.4;W=2.05;wr=0.38;}else if(type==='suv'){L=4.9;W=2.3;wr=0.5;}else{L=4.6;W=2.15;wr=0.42;}m(new THREE.BoxGeometry(W,0.5,L*0.98),body,0,0.7,0,g);m(new THREE.BoxGeometry(W-0.1,0.35,L*0.98),body,0,1.05,0,g);if(type==='sport'){m(new THREE.BoxGeometry(W-0.2,0.5,L*0.6),body,0,1.3,-0.15,g);m(new THREE.BoxGeometry(W-0.35,0.5,0.1),glass,0,1.5,-L*0.28,g).rotation.x=0.5;m(new THREE.BoxGeometry(W-0.3,0.4,0.1),glass,0,1.35,L*0.15,g).rotation.x=-0.4;m(new THREE.BoxGeometry(0.08,0.4,L*0.4),glass,W/2-0.18,1.4,-0.05,g);m(new THREE.BoxGeometry(0.08,0.4,L*0.4),glass,-W/2+0.18,1.4,-0.05,g);m(new THREE.BoxGeometry(W-0.3,0.08,0.35),dark,0,1.4,L*0.44,g);}else if(type==='suv'){m(new THREE.BoxGeometry(W-0.1,0.85,L*0.6),body,0,1.7,-0.1,g);m(new THREE.BoxGeometry(W-0.4,0.7,0.1),glass,0,1.85,-L*0.3,g).rotation.x=0.28;m(new THREE.BoxGeometry(0.08,0.65,L*0.55),glass,W/2-0.18,1.8,-0.1,g);m(new THREE.BoxGeometry(0.08,0.65,L*0.55),glass,-W/2+0.18,1.8,-0.1,g);m(new THREE.BoxGeometry(W-0.2,0.1,L*0.55),body,0,2.15,-0.1,g);}else{m(new THREE.BoxGeometry(W-0.15,0.55,L*0.45),body,0,1.4,-0.1,g);m(new THREE.BoxGeometry(W-0.35,0.5,0.1),glass,0,1.5,-L*0.28,g).rotation.x=0.35;m(new THREE.BoxGeometry(W-0.3,0.45,0.1),glass,0,1.45,L*0.2,g).rotation.x=-0.3;m(new THREE.BoxGeometry(0.08,0.45,L*0.4),glass,W/2-0.18,1.48,-0.1,g);m(new THREE.BoxGeometry(0.08,0.45,L*0.4),glass,-W/2+0.18,1.48,-0.1,g);m(new THREE.BoxGeometry(W-0.25,0.08,L*0.42),body,0,1.68,-0.1,g);}m(new THREE.BoxGeometry(W+0.05,0.25,0.15),dark,0,0.55,-L/2-0.05,g);m(new THREE.BoxGeometry(W+0.05,0.25,0.15),dark,0,0.55,L/2+0.05,g);[-W*0.35,W*0.35].forEach(function(wx){m(new THREE.BoxGeometry(0.4,0.18,0.06),light,wx,0.95,-L/2-0.09,g);});[-W*0.35,W*0.35].forEach(function(wx){m(new THREE.BoxGeometry(0.35,0.18,0.06),red,wx,0.95,L/2+0.09,g);});var tg=new THREE.CylinderGeometry(wr,wr,0.3,12),rg=new THREE.CylinderGeometry(wr*0.6,wr*0.6,0.32,8);var wb=L*0.32,wo=W/2+0.02;[[-wo,-wb],[wo,-wb],[-wo,wb],[wo,wb]].forEach(function(p){m(tg,tire,p[0],wr,p[1],g).rotation.z=Math.PI/2;m(rg,rim,p[0],wr,p[1],g).rotation.z=Math.PI/2;});g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);var top=type==='sport'?75:(type==='suv'?58:55),acc=type==='sport'?24:(type==='suv'?20:18);cars.push({group:g,owned:false,price:price,speed:0,rot:rot,name:name,top:top,acc:acc,isFlying:false,altitude:0,type:type});return g;}
function createTank(x,z,rot){var g=new THREE.Group();g.rotation.order='YXZ';var b=mat(0x4a5a2a),dk=mat(0x1a1a1a),ti=mat(0x141414),L=7,W=3.4;m(new THREE.BoxGeometry(W,0.9,L),b,0,0.9,0,g);m(new THREE.BoxGeometry(W-0.3,0.4,L-0.4),b,0,1.4,0,g);m(new THREE.BoxGeometry(3,1.2,3.5),b,0,2.0,-0.2,g);m(new THREE.CylinderGeometry(0.25,0.25,3.5,8),dk,0,2.3,-2.2,g).rotation.x=Math.PI/2;var tg=new THREE.CylinderGeometry(0.6,0.6,0.45,10),wb=L*0.35,wo=W/2+0.05;[[-wo,-wb],[wo,-wb],[-wo,wb],[wo,wb]].forEach(function(p){m(tg,ti,p[0],0.6,p[1],g).rotation.z=Math.PI/2;});g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);var t={group:g,owned:false,price:5000000,speed:0,rot:rot,name:'ТАНК',top:35,acc:8,isFlying:false,altitude:0,type:'tank',cooldown:0};cars.push(t);return t;}
function createMoto(x,z,rot,color,name){var g=new THREE.Group();g.rotation.order='YXZ';m(new THREE.BoxGeometry(0.32,0.35,1.6),mat(color),0,0.85,0,g);var tg=new THREE.CylinderGeometry(0.42,0.42,0.18,8),ti=mat(0x141414);[[0,-0.95],[0,0.95]].forEach(function(p){m(tg,ti,p[0],0.42,p[1],g).rotation.z=Math.PI/2;});g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);cars.push({group:g,owned:false,price:80000,speed:0,rot:rot,name:name,top:80,acc:45,isFlying:false,altitude:0,type:'moto'});}
function createHeli(x,z,color){var g=new THREE.Group();m(new THREE.SphereGeometry(1.2,6,5),mat(color||0xd94141),0,2,0,g).scale.set(1,0.9,1.4);m(new THREE.CylinderGeometry(0.15,0.15,5,4),mat(0x1a1a1a),0,2,0.3,g).rotation.x=Math.PI/2;var r=new THREE.Mesh(new THREE.BoxGeometry(7,0.08,0.4),mat(0x1a1a1a));r.position.set(0,3.2,0);g.add(r);g.userData.rot=r;g.position.set(x,0,z);scene.add(g);cars.push({group:g,owned:false,price:500000,speed:0,rot:0,name:'Вертолёт',top:55,acc:22,isFlying:true,altitude:0,type:'heli'});}
function createPlane(x,z,color){var g=new THREE.Group();m(new THREE.CylinderGeometry(0.9,0.5,7,6),mat(color||0xe8e8e8),0,1.5,0,g).rotation.x=Math.PI/2;m(new THREE.BoxGeometry(9,0.15,1.6),mat(color||0xe8e8e8),0,1.5,-0.5,g);var p=new THREE.Mesh(new THREE.BoxGeometry(3,0.1,0.15),mat(0x1a1a1a));p.position.set(0,1.5,-3.8);g.add(p);g.userData.pr=p;g.position.set(x,0,z);scene.add(g);cars.push({group:g,owned:false,price:800000,speed:0,rot:0,name:'Самолёт',top:65,acc:28,isFlying:true,altitude:0,type:'plane'});}
function spawnTraffic(){var cols=[0xd94141,0x3498db,0x2ecc71,0xf1c40f,0xe67e22,0x9b59b6,0x1abc9c,0xe74c3c,0xecf0f1,0x34495e];var lanes=[{x:-7.5,d:-1},{x:-3.5,d:-1},{x:3.5,d:1},{x:7.5,d:1}];for(var i=0;i<12;i++){var ln=lanes[i%lanes.length];var z=-1400+Math.random()*2800;var x=ln.x+(Math.random()-0.5)*1.2;var rot=ln.d===1?0:Math.PI;trafficCars.push(createTrafficCar(cols[(Math.random()*cols.length)|0],x,z,rot,ln));}}
function createTrafficCar(color,x,z,rot,lane){var g=new THREE.Group();g.rotation.order='YXZ';var body=mat(color),dark=mat(0x1a1a1a),tire=mat(0x141414),rim=mat(0xc8c8c8);var glass=new THREE.MeshLambertMaterial({color:0x1a2530,transparent:true,opacity:0.5});var light=mat(0xfff8d0,true),red=mat(0xff2a2a,true);var L=4.6,W=2.15,wr=0.42;m(new THREE.BoxGeometry(W,0.5,L*0.98),body,0,0.7,0,g);m(new THREE.BoxGeometry(W-0.1,0.35,L*0.98),body,0,1.05,0,g);m(new THREE.BoxGeometry(W-0.15,0.55,L*0.45),body,0,1.4,-0.1,g);m(new THREE.BoxGeometry(W-0.35,0.5,0.1),glass,0,1.5,-L*0.28,g).rotation.x=0.35;m(new THREE.BoxGeometry(W-0.3,0.45,0.1),glass,0,1.45,L*0.2,g).rotation.x=-0.3;m(new THREE.BoxGeometry(0.08,0.45,L*0.4),glass,W/2-0.18,1.48,-0.1,g);m(new THREE.BoxGeometry(0.08,0.45,L*0.4),glass,-W/2+0.18,1.48,-0.1,g);m(new THREE.BoxGeometry(W+0.05,0.25,0.15),dark,0,0.55,-L/2-0.05,g);m(new THREE.BoxGeometry(W+0.05,0.25,0.15),dark,0,0.55,L/2+0.05,g);[-W*0.35,W*0.35].forEach(function(wx){m(new THREE.BoxGeometry(0.4,0.18,0.06),light,wx,0.95,-L/2-0.09,g);});[-W*0.35,W*0.35].forEach(function(wx){m(new THREE.BoxGeometry(0.35,0.18,0.06),red,wx,0.95,L/2+0.09,g);});var tg=new THREE.CylinderGeometry(wr,wr,0.3,10),rg=new THREE.CylinderGeometry(wr*0.6,wr*0.6,0.32,8);var wb=L*0.32,wo=W/2+0.02;[[-wo,-wb],[wo,-wb],[-wo,wb],[wo,wb]].forEach(function(p){m(tg,tire,p[0],wr,p[1],g).rotation.z=Math.PI/2;m(rg,rim,p[0],wr,p[1],g).rotation.z=Math.PI/2;});var drv=makeHuman();drv.scale.set(0.85,0.85,0.85);drv.position.set(-W*0.22,0.35,-L*0.05);drv.rotation.y=Math.PI;g.add(drv);g.position.set(x,0,z);g.rotation.y=rot;scene.add(g);return {group:g,rot:rot,speed:20+Math.random()*15,top:40+Math.random()*20,targetRot:rot,drv:drv,alive:true,turnCd:5+Math.random()*10};}
function updateTraffic(dt){for(var i=0;i<trafficCars.length;i++){var tc=trafficCars[i];if(!tc.alive)continue;var dx=player.pos.x-tc.group.position.x,dz=player.pos.z-tc.group.position.z;var d2=dx*dx+dz*dz;var slow=1;if(d2<25&&!drivingCar)slow=0.1;tc.speed+=(tc.top*slow-tc.speed)*dt*1.5;_v1.set(-Math.sin(tc.rot),0,-Math.cos(tc.rot));tc.group.position.addScaledVector(_v1,tc.speed*dt);tc.turnCd-=dt;if(tc.turnCd<=0){tc.turnCd=8+Math.random()*15;if(Math.random()<0.3){var nr=tc.rot+(Math.random()<0.5?Math.PI/2:-Math.PI/2);if(!collides(tc.group.position.x-Math.sin(nr)*8,tc.group.position.z-Math.cos(nr)*8))tc.targetRot=nr;}}var df=tc.targetRot-tc.rot;while(df>Math.PI)df-=Math.PI*2;while(df<-Math.PI)df+=Math.PI*2;tc.rot+=df*Math.min(1,dt*2);tc.group.rotation.y=tc.rot;if(tc.group.position.z>1500||tc.group.position.z<-1500)tc.targetRot=tc.rot+Math.PI;if(tc.group.position.x>240||tc.group.position.x<-240)tc.targetRot=tc.rot+Math.PI;if(tc.drv)animH(tc.drv,dt,false);}}
function buildWorld(){
/* ГАЗОН */
var gnd=new THREE.Mesh(new THREE.PlaneGeometry(5000,5000),new THREE.MeshLambertMaterial({map:createGrassTexture()}));
gnd.rotation.x=-Math.PI/2;scene.add(gnd);
/* ВОДА */
var water=new THREE.Mesh(new THREE.PlaneGeometry(10000,10000),new THREE.MeshLambertMaterial({color:0x1a5585,transparent:true,opacity:0.9}));
water.rotation.x=-Math.PI/2;water.position.y=-0.5;scene.add(water);
var sand=new THREE.Mesh(new THREE.PlaneGeometry(4600,4600),mat(0xe8d5a0));sand.rotation.x=-Math.PI/2;sand.position.y=0.02;scene.add(sand);
/* ДОРОГИ */
var roadMat=new THREE.MeshLambertMaterial({map:createRoadTexture()});
for(var rx=-1800;rx<=1800;rx+=300){var r1=new THREE.Mesh(new THREE.PlaneGeometry(20,3600),roadMat.clone());r1.material.map=createRoadTexture();r1.rotation.x=-Math.PI/2;r1.position.set(rx,0.1,0);scene.add(r1);}
for(var rz=-1800;rz<=1800;rz+=300){var r2=new THREE.Mesh(new THREE.PlaneGeometry(3600,20),roadMat.clone());r2.material.map=createRoadTexture();r2.rotation.x=-Math.PI/2;r2.position.set(0,0.1,rz);scene.add(r2);}
/* ГОРЫ */
var mt=[mat(0x6b7280),mat(0x545c6a),mat(0x3f4654)],sn=mat(0xf3f6fb);
for(var i=0;i<20;i++){var z=-1800+i*200+Math.random()*60,x=400+Math.random()*200,h=180+Math.random()*200,r=80+Math.random()*70;m(new THREE.ConeGeometry(r,h,6),mt[i%3],x,h/2-5,z);m(new THREE.ConeGeometry(r*0.32,h*0.28,6),sn,x,h*0.86-5,z);}
/* ЗДАНИЯ */
var cols=['#d4cfc5','#c9b8a8','#a8b8c9','#d8d0c0','#b8a898','#8a8f98','#9c8d7a','#7f8a95'];
for(var bx=-1500;bx<=1500;bx+=300){for(var bz=-1500;bz<=1500;bz+=300){if(Math.abs(bx)<200&&Math.abs(bz)<200)continue;if(bx>200&&bx<800)continue;var cnt=1+Math.floor(Math.random()*2);for(var k=0;k<cnt;k++){var ix=bx+(Math.random()-0.5)*160,iz=bz+(Math.random()-0.5)*160;if(Math.abs(ix%300)<50||Math.abs(iz%300)<50)continue;var w=15+Math.random()*15,d=15+Math.random()*15,fl=3+Math.floor(Math.random()*12);buildBld(ix,iz,w,d,fl,cols[(Math.random()*cols.length)|0]);}}}
buildStation(55,-1050,'ВОКЗАЛ №1',true);
buildStation(55,1050,'ВОКЗАЛ №2',false);
buildShopBld(-250,-150,0xe74c3c,'ПРОДУКТЫ');
buildShopBld(250,150,0x3498db,'АВТОСАЛОН');
buildShopBld(-150,400,0x27ae60,'ТЕХНИКА');
buildShopBld(150,-400,0x9b59b6,'КАЗИНО');
/* ФОНАРИ */
for(var lx=-1800;lx<=1800;lx+=300){for(var lz=-1800;lz<=1800;lz+=300){buildLamp(lx+18,lz);buildLamp(lx-18,lz);}}
/* 30+ ЧАСТНЫХ ДОМОВ (в ряд по 6 = 36 штук) */
var hIdx=0;
for(var row=0;row<6;row++){for(var col=0;col<6;col++){
var hx=400+col*30,hz=-800+row*35;
buildPrivateHouse(hx,hz,hIdx);hIdx++;}}
/* ДОМА НА ГОРЕ */
for(var i2=0;i2<8;i2++){
var a2=(i2/8)*Math.PI*2;
var gx=500+Math.cos(a2)*150,gz=Math.sin(a2)*300;
buildPrivateHouse(gx,gz,100+i2);hIdx++;}
/* МАШИНЫ */
createUAZ(120,-300,0);
createLambo(120,300,0,0xffcc00);
var carNames=['Лада','Kia','Camry','BMW','Ferrari','Porsche','Tesla','Audi','Toyota','Honda','Nissan','Mazda','Subaru','Volvo','Jaguar','Bentley'];
var carColors=[0xd94141,0xf0c419,0x1a1e26,0x1f1f1f,0xff2a2a,0x27ae60,0xe8e8e8,0x1a3a8a,0xe74c3c,0x3498db,0x9b59b6,0xf39c12,0x1abc9c,0x34495e,0x2c3e50,0x7f8c8d];
var carTypes=['sedan','suv','sport'];
for(var i3=0;i3<18;i3++){var row2=Math.floor(i3/6),col2=i3%6;var px=100-30+col2*11,pz=-340-row2*12;createCar(px,pz,0,carColors[i3%carColors.length],carNames[i3%carNames.length],30000+i3*20000,carTypes[i3%3]);}
/* НПС */
for(var n=0;n<15;n++){var a=Math.random()*Math.PI*2,d2=15+Math.random()*50;var h=makeHuman();h.position.set(55+Math.cos(a)*d2,0,-1050+Math.sin(a)*d2);scene.add(h);npcs.push({group:h,dir:Math.random()*6.28,speed:0.5+Math.random()*0.8,timer:0});}
for(var n2=0;n2<15;n2++){var a2=Math.random()*Math.PI*2,d3=15+Math.random()*50;var h2=makeHuman();h2.position.set(55+Math.cos(a2)*d3,0,1050+Math.sin(a2)*d3);scene.add(h2);npcs.push({group:h2,dir:Math.random()*6.28,speed:0.5+Math.random()*0.8,timer:0});}
/* КУСТИКИ ТРАВЫ - ТОЛСТАЯ ТРАВА */
for(var gi=0;gi<500;gi++){
var gx2=(Math.random()-0.5)*3500,gz2=(Math.random()-0.5)*3500;
if(Math.abs(gx2%300)<25||Math.abs(gz2%300)<25)continue;
makeGrassTuft(gx2,gz2);}
spawnTraffic();
buildInterior();}
function updateDayNight(dt){
if(autoTime){timeH+=dt*0.15;if(timeH>=24)timeH-=24;}
var hh=Math.floor(timeH),mm=Math.floor((timeH-hh)*60);
document.getElementById('clock').textContent='🕐 '+String(hh).padStart(2,'0')+':'+String(mm).padStart(2,'0');
var sunAngle=(timeH/24)*Math.PI*2-Math.PI/2;
sunLight.position.set(Math.cos(sunAngle)*300,Math.sin(sunAngle)*300,100);
var bright;
if(timeH<5||timeH>21)bright=0.15;
else if(timeH<8)bright=0.15+((timeH-5)/3)*0.85;
else if(timeH<18)bright=1.0;
else bright=1.0-((timeH-18)/3)*0.85;
sunLight.intensity=bright*(weather==='clear'?1:0.5);
var skyR,skyG,skyB;
if(timeH<5||timeH>21){skyR=0.05;skyG=0.08;skyB=0.18;}
else if(timeH<8){var t=(timeH-5)/3;skyR=0.05+t*0.5;skyG=0.08+t*0.7;skyB=0.18+t*0.75;}
else if(timeH<18){skyR=0.55;skyG=0.75;skyB=0.93;}
else{var t2=(timeH-18)/3;skyR=0.55-t2*0.5;skyG=0.75-t2*0.67;skyB=0.93-t2*0.75;}
if(weather==='rain'){skyR=0.35;skyG=0.4;skyB=0.45;}else if(weather==='fog'){skyR=0.7;skyG=0.75;skyB=0.82;}
scene.background=new THREE.Color(skyR,skyG,skyB);scene.fog.color.setRGB(skyR,skyG,skyB);
var lampOn=timeH<6||timeH>19;
for(var i=0;i<lamps.length;i++){var l=lamps[i];if(l.userData.lamp)l.userData.lamp.material.color.setHex(lampOn?0xffffaa:0x888877);}}
function setWeather(w){weather=w;
if(w==='rain'&&rainMesh)rainMesh.visible=true;else if(rainMesh)rainMesh.visible=false;
document.getElementById('wh').textContent=(w==='clear'?'☀️ Ясно':w==='rain'?'🌧️ Дождь':'🌫️ Туман');
if(w==='fog'){scene.fog.near=20;scene.fog.far=200;}else{scene.fog.near=200;scene.fog.far=1500;}
toast(w==='clear'?'☀️ Ясно':w==='rain'?'🌧️ Дождь':'🌫️ Туман');}
function setTimeManual(h){timeH=h;autoTime=false;updateDayNight(0);toast('🕐 '+h+':00');}
function init(){if(window.scene)return;try{
scene=new THREE.Scene();window.scene=scene;scene.background=new THREE.Color(0x9fd0ee);scene.fog=new THREE.Fog(0x9fd0ee,200,1500);
camera=new THREE.PerspectiveCamera(75,innerWidth/innerHeight,0.05,4000);window.camera=camera;
renderer=new THREE.WebGLRenderer({antialias:false,powerPreference:'high-performance'});window.renderer=renderer;renderer.setSize(innerWidth,innerHeight);renderer.setPixelRatio(Math.min(devicePixelRatio,1));
document.getElementById('g').appendChild(renderer.domElement);
scene.add(new THREE.HemisphereLight(0xffffff,0x4a5a3a,0.5));
sunLight=new THREE.DirectionalLight(0xfff3d6,1.0);scene.add(sunLight);
scene.add(new THREE.AmbientLight(0x888888,0.4));
var cnt=120,geo=new THREE.BufferGeometry(),pos=new Float32Array(cnt*3);
for(var i=0;i<cnt;i++){pos[i*3]=(Math.random()-0.5)*80;pos[i*3+1]=Math.random()*50;pos[i*3+2]=(Math.random()-0.5)*80;}
geo.setAttribute('position',new THREE.BufferAttribute(pos,3));
rainMesh=new THREE.Points(geo,new THREE.PointsMaterial({color:0xaaccff,size:0.35,transparent:true,opacity:0.75}));rainMesh.visible=false;scene.add(rainMesh);
buildWorld();
playerBody=makeHuman();playerBody.position.set(22,0,-960);scene.add(playerBody);
gunMesh=new THREE.Group();m(new THREE.BoxGeometry(0.12,0.12,0.7),mat(0x2a2a2a),0,0,-0.35,gunMesh);gunMesh.scale.set(1.2,1.2,1.2);gunMesh.position.set(0.42,-0.32,-0.55);gunMesh.visible=false;camera.add(gunMesh);scene.add(camera);
clock=new THREE.Clock();bindControls();initJoy();animate();updateHUD();
var s=loadSave();if(s){document.getElementById('bC').style.display='block';if(s.usedPromos)usedPromos=s.usedPromos;}
}catch(e){toast('Ошибка: '+e.message);}}
function bindControls(){addEventListener('keydown',function(e){var k=e.key.toLowerCase();keys[k]=true;if(k===' ')e.preventDefault();if(!gameStarted||paused)return;if(k==='e')doE();if(k==='f')doF();if(k==='c')tCam();if(k===' ')tryJump();if(k==='q'&&drivingCar&&drivingCar.type==='tank')tankShoot();});addEventListener('keyup',function(e){keys[e.key.toLowerCase()]=false;});var locked=false;addEventListener('pointerlockchange',function(){locked=document.pointerLockElement===renderer.domElement;});addEventListener('mousemove',function(e){if(!gameStarted||paused)return;if(locked||(e.buttons&1)){player.yaw-=e.movementX*0.0022;player.pitch-=e.movementY*0.0022;player.pitch=clamp(player.pitch,-1.35,1.35);}});var look=null;renderer.domElement.addEventListener('touchstart',function(e){if(!gameStarted||paused)return;var t=e.changedTouches[0];look={id:t.identifier,x:t.clientX,y:t.clientY};},{passive:true});renderer.domElement.addEventListener('touchmove',function(e){if(!gameStarted||paused||!look){look=null;return;}for(var i=0;i<e.changedTouches.length;i++){var t=e.changedTouches[i];if(t.identifier===look.id){var isR=document.getElementById('rw').style.transform.indexOf('rotate')>-1,dx=t.clientX-look.x,dy=t.clientY-look.y;if(isR){player.yaw-=dy*0.005;player.pitch-=dx*0.005;}else{player.yaw-=dx*0.005;player.pitch-=dy*0.005;}player.pitch=clamp(player.pitch,-1.35,1.35);look.x=t.clientX;look.y=t.clientY;}}e.preventDefault();},{passive:false});renderer.domElement.addEventListener('touchend',function(){look=null;});document.getElementById('bE').addEventListener('pointerdown',function(e){e.preventDefault();doE();});document.getElementById('bF').addEventListener('pointerdown',function(e){e.preventDefault();doF();});document.getElementById('bJ').addEventListener('pointerdown',function(e){e.preventDefault();tryJump();});}
function shoot(){if(!state.hasGun||paused)return;if(state.ammo<=0){toast('Нет патронов');return;}state.ammo--;document.getElementById('amV').textContent=state.ammo;var ray=new THREE.Raycaster();ray.setFromCamera(new THREE.Vector2(0,0),camera);ray.far=100;var tg=npcs.map(function(n){return n.group;});var h=ray.intersectObjects(tg,true);if(h.length>0){var r=h[0].object;while(r.parent&&!npcs.some(function(n){return n.group===r;}))r=r.parent;var v=npcs.find(function(n){return n.group===r;});if(v){scene.remove(v.group);npcs.splice(npcs.indexOf(v),1);state.money+=100;updateHUD();toast('💥 +100 ₽');}}}
function doE(){if(inHouse){exitHouse();return;}
if(drivingCar&&drivingCar.type==='tank'){tankShoot();return;}
if(state.hasGun&&!drivingCar){shoot();return;}
if(drivingCar)return;
if(nearHouse){enterHouse();return;}
if(nearCar){if(!nearCar.owned){if(state.money<nearCar.price){toast('❌ '+nearCar.price.toLocaleString('ru-RU')+' ₽');return;}state.money-=nearCar.price;nearCar.owned=true;updateHUD();toast('✅ '+nearCar.name);}else toggleCar();}}
function doF(){if(drivingCar){toggleCar();return;}if(nearCar)toggleCar();}
function tCam(){camMode=camMode===1?3:1;if(gunMesh)gunMesh.visible=state.hasGun&&camMode===1&&!drivingCar;toast(camMode===1?'1-е лицо':'3-е лицо');}
function toggleCar(){if(drivingCar){var c=drivingCar;c.speed=0;c.altitude=0;c.group.position.y=0;player.pos.set(c.group.position.x+4.5,0,c.group.position.z+3);drivingCar=null;document.getElementById('speedo').classList.remove('on');if(playerBody)playerBody.visible=true;if(gunMesh)gunMesh.visible=state.hasGun&&camMode===1;toast('Вышли');return;}if(!nearCar)return;if(!nearCar.owned){if(state.money<nearCar.price){toast('❌');return;}state.money-=nearCar.price;nearCar.owned=true;updateHUD();return;}drivingCar=nearCar;document.getElementById('speedo').classList.add('on');if(playerBody)playerBody.visible=false;if(gunMesh)gunMesh.visible=false;toast('🚗 '+nearCar.name);}
function tryJump(){if(drivingCar&&drivingCar.isFlying){drivingCar.altitude=Math.min(drivingCar.altitude+30,300);return;}if(drivingCar)return;if(player.onGround){player.vy=8.5;player.onGround=false;}}
function tankShoot(){if(!drivingCar||drivingCar.type!=='tank'||drivingCar.cooldown>0)return;drivingCar.cooldown=2.5;var c=drivingCar.group;var sh=new THREE.Mesh(new THREE.SphereGeometry(0.35,6,5),new THREE.MeshBasicMaterial({color:0xffaa00}));sh.position.copy(c.position);sh.position.y=2.3;_v1.set(-Math.sin(drivingCar.rot),0,-Math.cos(drivingCar.rot));sh.position.addScaledVector(_v1,3.5);scene.add(sh);var dir=_v1.clone(),t0=Date.now();var iv=setInterval(function(){var dt=(Date.now()-t0)/1000;if(dt>2.5||sh.position.y<0.3){clearInterval(iv);scene.remove(sh);var ex=new THREE.Mesh(new THREE.SphereGeometry(2,6,5),new THREE.MeshBasicMaterial({color:0xff6b1a,transparent:true,opacity:0.9}));ex.position.copy(sh.position);scene.add(ex);setTimeout(function(){scene.remove(ex);},700);return;}sh.position.addScaledVector(dir,2.4);sh.position.y-=0.3;},50);}
function updatePlayer(dt){_v1.set(-Math.sin(player.yaw),0,-Math.cos(player.yaw));_v2.set(Math.cos(player.yaw),0,-Math.sin(player.yaw));_v3.set(0,0,0);if(Math.abs(joyY)>0.1)_v3.addScaledVector(_v1,-joyY);if(Math.abs(joyX)>0.1)_v3.addScaledVector(_v2,joyX);if(keys['w'])_v3.add(_v1);if(keys['s'])_v3.sub(_v1);if(keys['d'])_v3.add(_v2);if(keys['a'])_v3.sub(_v2);player.moving=_v3.lengthSq()>0.01;if(player.moving)_v3.normalize();var sp=player.speed*(keys['shift']?1.9:1),dx=_v3.x*sp*dt,dz=_v3.z*sp*dt;
if(inHouse){
if(player.pos.x+dx>-4.5&&player.pos.x+dx<4.5)player.pos.x+=dx;
if(player.pos.z+dz>-4.5&&player.pos.z+dz<4.5)player.pos.z+=dz;
}else{
if(!circleCol(player.pos.x+dx,player.pos.z))player.pos.x+=dx;
if(!circleCol(player.pos.x,player.pos.z+dz))player.pos.z+=dz;
player.pos.x=clamp(player.pos.x,-2000,2000);player.pos.z=clamp(player.pos.z,-2000,2000);}
player.vy-=26*dt;player.pos.y+=player.vy*dt;if(player.pos.y<=0){player.pos.y=0;player.vy=0;player.onGround=true;}}
function updateCar(dt){var c=drivingCar,isM=c.type==='moto',isF=c.isFlying,isT=c.type==='tank';var ACC=c.acc||18,MAXF=c.top||38,MAXB=isF?-5:(isM?-8:(isT?-5:-14));var th=0;if(Math.abs(joyY)>0.1)th=-joyY;if(keys['w'])th+=1;if(keys['s'])th-=1;th=clamp(th,-1,1);if(th>0.1)c.speed+=ACC*dt*th;else if(th<-0.1)c.speed+=ACC*dt*th;else c.speed*=(1-(isT?2:1.6)*dt);c.speed=clamp(c.speed,MAXB,MAXF);var st=joyX;if(keys['a'])st-=1;if(keys['d'])st+=1;st=clamp(st,-1,1);var tS=isM?2.8:(isF?1.5:(isT?1.3:1.9));c.rot+=st*dt*tS*(c.speed>=0?1:-1)*Math.min(1,Math.abs(c.speed)/8);c.group.rotation.y=c.rot;_v1.set(-Math.sin(c.rot),0,-Math.cos(c.rot));c.group.position.addScaledVector(_v1,c.speed*dt);c.group.position.x=clamp(c.group.position.x,-2000,2000);c.group.position.z=clamp(c.group.position.z,-2000,2000);if(isF){if(keys['w'])c.altitude=Math.min(c.altitude+dt*15,300);else if(keys['s'])c.altitude=Math.max(c.altitude-dt*15,0);else c.altitude*=(1-0.5*dt);c.group.position.y=1+c.altitude;if(c.group.userData.rot)c.group.userData.rot.rotation.y+=dt*20;if(c.group.userData.pr)c.group.userData.pr.rotation.z+=dt*25;}else c.group.position.y=0;if(isT&&c.cooldown>0)c.cooldown-=dt;player.pos.set(c.group.position.x,0,c.group.position.z);player.pos.y=c.group.position.y;
var sp2=Math.abs(c.speed)*3.6;document.getElementById('speedo').innerHTML=Math.floor(sp2)+' <small>км/ч</small>';}
function updateNPCs(dt){for(var i=0;i<npcs.length;i++){var n=npcs[i];n.timer-=dt;if(n.timer<=0){n.timer=2+Math.random()*3;n.dir+=(Math.random()-0.5)*2;}var nx=n.group.position.x+Math.sin(n.dir)*n.speed*dt,nz=n.group.position.z+Math.cos(n.dir)*n.speed*dt,mv=false;if(!collides(nx,nz)){n.group.position.x=nx;n.group.position.z=nz;mv=true;}else n.dir+=Math.PI;n.group.rotation.y=Math.atan2(Math.sin(n.dir),Math.cos(n.dir));animH(n.group,dt,mv);}}
function updateNear(){nearCar=null;nearHouse=null;
if(inHouse){document.getElementById('hint').textContent='[E] 🚪 Выйти из дома';document.getElementById('hint').classList.add('on');return;}
if(drivingCar){document.getElementById('hint').classList.remove('on');return;}
/* Дома */
for(var hi=0;hi<houses.length;hi++){var hh=houses[hi];var d3=Math.hypot(player.pos.x-hh.doorX,player.pos.z-hh.doorZ);if(d3<4){nearHouse=hh;break;}}
var bD=9;for(var j=0;j<cars.length;j++){var c=cars[j];var d2=c.group.position.distanceTo(player.pos);if(d2<9&&d2<bD){bD=d2;nearCar=c;}}
var h=document.getElementById('hint');
if(nearHouse){h.textContent='[E] 🏠 Войти в дом №'+(nearHouse.idx+1);h.classList.add('on');return;}
if(nearCar){h.textContent=nearCar.owned?'[F] 🚗 '+nearCar.name:(nearCar.type==='tank'?'[Q] Танк ':'[E] ')+nearCar.name+' '+nearCar.price.toLocaleString('ru-RU')+' ₽';h.classList.add('on');return;}
if(state.hasGun){h.textContent='[E] 🔫';h.classList.add('on');return;}
h.classList.remove('on');}
function updateCamera(){if(drivingCar){var c=drivingCar.group;var d=drivingCar.type==='tank'?16:14,h=drivingCar.type==='tank'?8:7;_v1.set(Math.sin(c.rotation.y),0,Math.cos(c.rotation.y));camera.position.copy(c.position).addScaledVector(_v1,d);camera.position.y+=h;camera.lookAt(c.position.x,c.position.y+1.5,c.position.z);return;}if(camMode===1){camera.position.set(player.pos.x,player.pos.y+1.75,player.pos.z);camera.rotation.set(player.pitch,player.yaw,0,'YXZ');if(playerBody)playerBody.visible=false;}else{if(playerBody)playerBody.visible=true;_v1.set(Math.sin(player.yaw),0,Math.cos(player.yaw));camera.position.copy(player.pos).addScaledVector(_v1,6.5);camera.position.y+=2.6;camera.lookAt(player.pos.x,player.pos.y+1.3,player.pos.z);}}
function animate(){requestAnimationFrame(animate);if(!clock)return;var dt=Math.min(clock.getDelta(),0.05);
if(gameStarted&&!paused){if(drivingCar)updateCar(dt);else updatePlayer(dt);updateNPCs(dt);updateTraffic(dt);updateNear();updateDayNight(dt);
if(playerBody){playerBody.position.set(player.pos.x,player.pos.y,player.pos.z);playerBody.rotation.y=player.yaw+Math.PI;if(!drivingCar&&!inHouse)animH(playerBody,dt,player.moving);}
if(rainMesh&&rainMesh.visible){var p=rainMesh.geometry.attributes.position.array;for(var i=1;i<p.length;i+=3){p[i]-=40*dt;if(p[i]<0)p[i]=50;}rainMesh.geometry.attributes.position.needsUpdate=true;rainMesh.position.set(player.pos.x,0,player.pos.z);}}
updateCamera();if(renderer&&scene&&camera)renderer.render(scene,camera);}
function updateHUD(){document.getElementById('mn').textContent=Math.floor(state.money).toLocaleString('ru-RU');document.getElementById('dn').textContent=state.donate;document.getElementById('sM').textContent=Math.floor(state.money).toLocaleString('ru-RU');document.getElementById('sD').textContent=state.donate;document.getElementById('fM').textContent=Math.floor(state.money).toLocaleString('ru-RU');document.getElementById('tM').textContent=Math.floor(state.money).toLocaleString('ru-RU');}
window.startGameWithPlayer=function(){if(!window.scene){toast('Загрузка...');setTimeout(window.startGameWithPlayer,200);return;}document.getElementById('hud').classList.add('on');document.getElementById('wh').classList.add('on');gameStarted=true;paused=false;if(state.hasGun){gunMesh.visible=camMode===1;document.getElementById('am').classList.add('on');document.getElementById('amV').textContent=state.ammo;}setTimeout(function(){try{renderer.domElement.requestPointerLock();}catch(e){}},100);updateHUD();applyRot();};
window.startGame=function(s){if(typeof THREE==='undefined'){toast('Загрузка...');setTimeout(function(){window.startGame(s);},300);return;}if(!window.scene){init();setTimeout(function(){window.startGame(s);},300);return;}var sp;if(s===2)sp={x:22,z:1010,yaw:Math.PI};else if(s===3)sp={x:400,z:-700,yaw:0};else if(s===4)sp={x:500,z:0,yaw:0};else sp={x:22,z:-1010,yaw:0};player.pos=new THREE.Vector3(sp.x,0,sp.z);player.yaw=sp.yaw;player.pitch=0;player.vy=0;player.onGround=true;if(playerBody)playerBody.position.copy(player.pos);document.getElementById('start').classList.remove('on');goLand();startGameWithPlayer();toast('💰 '+Math.floor(state.money).toLocaleString('ru-RU')+' ₽');};
window.buyDonate=function(){if(state.money<100000)return;state.money-=100000;state.donate+=100;updateHUD();toast('💎 +100');};
window.buyFood=function(n,p,f,mb){if(state.money<p)return;state.money-=p;if(mb)state.money+=mb;state.fortune=Math.min(100,state.fortune+f);updateHUD();toast('🍔 '+n);};
window.buyTech=function(t){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw),px=player.pos.x+fx*10,pz=player.pos.z+fz*10;if(t==='uaz'){if(state.money<100000){toast('❌ 100 000 ₽');return;}state.money-=100000;createUAZ(px,pz,player.yaw);cars[cars.length-1].owned=true;}else if(t==='lambo'){if(state.money<1500000){toast('❌ 1 500 000 ₽');return;}state.money-=1500000;createLambo(px,pz,player.yaw,0xffcc00);cars[cars.length-1].owned=true;}else if(t==='lada'){if(state.money<25000)return;state.money-=25000;createCar(px,pz,player.yaw,0xd94141,'Лада',25000,'sedan');cars[cars.length-1].owned=true;}else if(t==='moto'){if(state.money<80000)return;state.money-=80000;createMoto(px,pz,player.yaw,0x2a3a6a,'Yamaha');cars[cars.length-1].owned=true;}else if(t==='heli'){if(state.money<500000)return;state.money-=500000;createHeli(px,pz,0xd94141);cars[cars.length-1].owned=true;}else if(t==='plane'){if(state.money<800000)return;state.money-=800000;createPlane(px,pz,0xe8e8e8);cars[cars.length-1].owned=true;}else if(t==='tank'){if(state.money<5000000)return;state.money-=5000000;createTank(px,pz,player.yaw);cars[cars.length-1].owned=true;}updateHUD();toast('✅ Куплено');};
window.buyGun=function(){if(state.money<50000)return;state.money-=50000;state.hasGun=true;state.ammo=state.maxAmmo;if(gunMesh)gunMesh.visible=camMode===1;document.getElementById('am').classList.add('on');document.getElementById('amV').textContent=state.ammo;updateHUD();toast('🔫');};
window.buyAmmo=function(){if(!state.hasGun||state.money<5000)return;state.money-=5000;state.ammo+=12;document.getElementById('amV').textContent=state.ammo;updateHUD();};
window.casBet=function(a){if(!a||a<1||state.money<a)return;state.money-=a;var r=document.getElementById('cs'),ic=document.getElementById('csi'),tt=document.getElementById('cst'),ss=document.getElementById('css');if(Math.random()<0.67){state.money+=a*2;ic.textContent='🎉';tt.textContent='ПОБЕДА!';tt.style.color='#6fdc6c';ss.textContent='+'+a*2;toast('🎉 +'+a*2);}else{ic.textContent='😢';tt.textContent='Проигрыш';tt.style.color='#ff6b6b';ss.textContent='-'+a;toast('😢 -'+a);}r.classList.add('on');setTimeout(function(){r.classList.remove('on');},1400);updateHUD();};
window.tryAdmin=function(){if(document.getElementById('aP').value==='2015'){closeScr('adminLog');openScr('admin');toast('⚡');}else toast('❌');};
window.adminMoney=function(s){state.money+=s;updateHUD();toast('💰 +'+s.toLocaleString('ru-RU'));};
window.adminDonate=function(s){state.donate+=s;updateHUD();toast('💎 +'+s);};
window.setWeather=setWeather;window.setTimeManual=setTimeManual;
window.admUAZ=function(){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw);createUAZ(player.pos.x+fx*8,player.pos.z+fz*8,player.yaw);cars[cars.length-1].owned=true;toast('🚙');};
window.admLambo=function(){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw);createLambo(player.pos.x+fx*8,player.pos.z+fz*8,player.yaw,0xffcc00);cars[cars.length-1].owned=true;toast('🏎️');};
window.admHeli=function(){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw);createHeli(player.pos.x+fx*12,player.pos.z+fz*12,0xd94141);cars[cars.length-1].owned=true;toast('🚁');};
window.admTank=function(){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw);createTank(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw);cars[cars.length-1].owned=true;toast('🎖️');};
window.openPhone=function(){if(!gameStarted)return;paused=true;if(document.pointerLockElement)document.exitPointerLock();document.getElementById('phS').classList.add('on');};
window.closePhone=function(){document.getElementById('phS').classList.remove('on');paused=false;if(gameStarted)try{renderer.domElement.requestPointerLock();}catch(e){}};
window.closeAll=function(){document.querySelectorAll('.scr').forEach(function(s){s.classList.remove('on');});paused=false;if(gameStarted)try{renderer.domElement.requestPointerLock();}catch(e){}};
var casePrizes=[{id:'m5',icon:'💰',name:'5 000 ₽',chance:55,color:'#ffd166'},{id:'m10',icon:'💰',name:'10 000 ₽',chance:15,color:'#9b59b6'},{id:'car',icon:'🚗',name:'Машина',chance:5,color:'#3498db'},{id:'bmw',icon:'🏎️',name:'BMW',chance:1,color:'#ffd700'},{id:'moto',icon:'🏍️',name:'Мото',chance:0.09,color:'#ff2d95'},{id:'empty',icon:'❌',name:'Пусто',chance:23.91,color:'#8fa0b3'}];
var premPrizes=[{id:'m100k',icon:'💰',name:'100 000 ₽',chance:60,color:'#00d4ff'},{id:'m200k',icon:'💰',name:'200 000 ₽',chance:20,color:'#9b59b6'},{id:'car',icon:'🚗',name:'Машина',chance:10,color:'#3498db'},{id:'lambo',icon:'🏎️',name:'Ламбо',chance:5,color:'#ffcc00'},{id:'uaz',icon:'🚙',name:'УАЗ',chance:4,color:'#3e5230'},{id:'house',icon:'🏠',name:'+500К',chance:1,color:'#ffd700'}];
function rollPrize(prizes){var r=Math.random()*100,c=0;for(var i=0;i<prizes.length;i++){c+=prizes[i].chance;if(r<c)return prizes[i];}return prizes[prizes.length-1];}
function spinCase(prizes,title){var reward=rollPrize(prizes);var strip=document.getElementById('caseS');document.getElementById('cTtl').textContent=title;strip.style.transition='none';strip.style.transform='translateX(0)';var items=[];for(var j=0;j<30;j++){var p=prizes[(Math.random()*prizes.length)|0];if(j===25)p=reward;items.push(p);}strip.innerHTML=items.map(function(p){return '<div class="ci" style="border-color:'+p.color+'"><div class="ic">'+p.icon+'</div><div>'+p.name+'</div></div>';}).join('');openScr('cscr');paused=true;if(document.pointerLockElement)document.exitPointerLock();setTimeout(function(){strip.style.transition='transform 3.5s cubic-bezier(.15,.85,.35,1)';strip.style.transform='translateX(-2848px)';},50);setTimeout(function(){closeScr('cscr');applyCaseReward(reward);document.getElementById('cIc').textContent=reward.icon;document.getElementById('cTt').textContent=reward.name;document.getElementById('cTt').style.color=reward.color;document.getElementById('cSb').textContent='Из кейса';document.getElementById('cres').classList.add('on');},3600);}
function applyCaseReward(reward){var fx=-Math.sin(player.yaw),fz=-Math.cos(player.yaw);var id=reward.id;if(id==='m5')state.money+=5000;else if(id==='m10')state.money+=10000;else if(id==='m100k')state.money+=100000;else if(id==='m200k')state.money+=200000;else if(id==='car'){createCar(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw,0xd94141,'Машина',0,'sedan');cars[cars.length-1].owned=true;}else if(id==='bmw'){createCar(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw,0x1f1f1f,'BMW',0,'sedan');cars[cars.length-1].owned=true;}else if(id==='moto'){createMoto(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw,0x2a3a6a,'Yamaha');cars[cars.length-1].owned=true;}else if(id==='lambo'){createLambo(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw,0xffcc00);cars[cars.length-1].owned=true;}else if(id==='uaz'){createUAZ(player.pos.x+fx*10,player.pos.z+fz*10,player.yaw);cars[cars.length-1].owned=true;}else if(id==='house'){state.money+=500000;}updateHUD();}
window.startCase=function(){if(state.donate<100){toast('❌ 100 донат');return;}state.donate-=100;updateHUD();spinCase(casePrizes,'🎁 КЕЙС УДАЧИ');};
window.startPremCase=function(){if(state.donate<600){toast('❌ 600 донат');return;}state.donate-=600;updateHUD();spinCase(premPrizes,'💠 ПРЕМИУМ КЕЙС');};
window.closeCase=function(){document.getElementById('cres').classList.remove('on');paused=false;if(gameStarted)try{renderer.domElement.requestPointerLock();}catch(e){}};
function drawMap(){var cv=document.getElementById('bigMap');if(!cv)return;var ctx=cv.getContext('2d'),W=cv.width,H=cv.height;ctx.fillStyle='#4d7a3a';ctx.fillRect(0,0,W,H);ctx.fillStyle='#1a5585';ctx.fillRect(0,0,W,30);ctx.fillRect(0,H-30,W,30);ctx.fillRect(0,0,30,H);ctx.fillRect(W-30,0,30,H);ctx.fillStyle='#e8d5a0';ctx.fillRect(30,30,W-60,H-60);ctx.fillStyle='#4d7a3a';ctx.fillRect(50,50,W-100,H-100);ctx.strokeStyle='#35383f';ctx.lineWidth=4;for(var i=-1800;i<=1800;i+=300){var px=W/2+i/2000*W/2;ctx.beginPath();ctx.moveTo(px,50);ctx.lineTo(px,H-50);ctx.stroke();ctx.beginPath();ctx.moveTo(50,px);ctx.lineTo(W-50,px);ctx.stroke();}
/* Дома - жёлтые точки */
ctx.fillStyle='#d4a017';for(var hi=0;hi<houses.length;hi++){var hx=W/2+houses[hi].x/2000*W/2,hy=H/2+houses[hi].z/2000*H/2;if(hx<30||hx>W-30||hy<30||hy>H-30)continue;ctx.fillRect(hx-3,hy-3,6,6);}
function dot(x,z,c,icon){var px=W/2+x/2000*W/2,py=H/2+z/2000*H/2;ctx.fillStyle=c;ctx.beginPath();ctx.arc(px,py,10,0,6.28);ctx.fill();ctx.strokeStyle='#fff';ctx.lineWidth=2;ctx.stroke();ctx.fillStyle='#fff';ctx.font='bold 11px Arial';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(icon,px,py);}
dot(55,-1050,'#00c853','🚉');dot(55,1050,'#00c853','🚉');dot(-250,-150,'#e74c3c','🛒');dot(250,150,'#3498db','🚗');dot(-150,400,'#27ae60','🔧');dot(150,-400,'#9b59b6','🎰');
var px=W/2+player.pos.x/2000*W/2,py=H/2+player.pos.z/2000*H/2;ctx.fillStyle='#ff2a2a';ctx.beginPath();ctx.arc(px,py,8,0,6.28);ctx.fill();ctx.strokeStyle='#fff';ctx.lineWidth=2;ctx.stroke();}
var _initDone=false;function startWhenReady(){if(_initDone)return;if(typeof THREE==='undefined'){setTimeout(startWhenReady,100);return;}_initDone=true;player.pos=new THREE.Vector3(22,0,-960);_v1=new THREE.Vector3();_v2=new THREE.Vector3();_v3=new THREE.Vector3();init();applyRot();}
addEventListener('three-ready',startWhenReady);addEventListener('load',function(){setTimeout(startWhenReady,300);});setTimeout(startWhenReady,500);
addEventListener('beforeunload',saveGame);addEventListener('pagehide',saveGame);document.addEventListener('visibilitychange',function(){if(document.hidden)saveGame();});
</script></body></html>Game Source: Матрешка RP
Creator: AstroPirate77
Libraries: three
Complexity: complex (601 lines, 82.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: rp-astropirate77" to link back to the original. Then publish at arcadelab.ai/publish.