Interiors with TG (updated) + ешьу Fix MID

This commit is contained in:
2025-08-26 17:47:05 +03:00
parent e5c6f9edc9
commit 32453d6282
3 changed files with 66 additions and 26 deletions

View File

@@ -1 +1 @@
{"time":"2025-02-14T12:10:49.296Z","lastReal":1755514421720}
{"time":"2025-04-20T19:17:09.736Z","lastReal":1756219619275}

View File

@@ -85,6 +85,16 @@ let onlineUsers = {};
const organizationsRouter = require('./server/organizations')(io, onlineUsers);
app.use('/api/organizations', organizationsRouter);
// Инициализация игрового времени (ускорение 8x) и вещание клиентам
try {
if (GameTime && typeof GameTime === 'function') {
global.__gameTimeInstance = global.__gameTimeInstance || new GameTime(io, 8);
console.log('GameTime таймер запущен');
}
} catch (e) {
console.error('GameTime не запущен:', e);
}
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) return next(new Error('No token'));

View File

@@ -73,7 +73,7 @@ function Game({ avatarUrl, gender }) {
});
const [inventory, setInventory] = useState([]);
const [showInventory, setShowInventory] = useState(false);
const [gameTime, setGameTime] = useState('');
const [gameTime, setGameTime] = useState(null);
const [balance, setBalance] = useState(() => {
const p = JSON.parse(sessionStorage.getItem('user_profile') || '{}');
return p.balance ?? 0;
@@ -736,6 +736,13 @@ function Game({ avatarUrl, gender }) {
objGltf.scene.scale.set(o.scale, o.scale, o.scale);
intGroup.add(objGltf.scene);
// Добавляем меши объекта как коллайдеры интерьера
objGltf.scene.traverse((child) => {
if (child.isMesh && child.geometry) {
colliders.push(child);
}
});
// Если это NPC внутри интерьера — добавим кликабельную хит‑зону
const isNpc = (o.type === 'npc') || (typeof o.model_url === 'string' && o.model_url.includes('/models/npc/'));
if (isNpc) {
@@ -791,6 +798,10 @@ function Game({ avatarUrl, gender }) {
}
}
intGroup.add(mesh);
// Плейсхолдер не рендерим, но используем как коллайдер
try { mesh.visible = false; } catch (_) {}
// Плейсхолдер без GLTF тоже участвует в коллизиях
colliders.push(mesh);
}
// Если сервер пометил объект как «интерактивный/маркер» — кликабельная зона
@@ -802,7 +813,8 @@ function Game({ avatarUrl, gender }) {
hit.position.set(o.x, o.y + 1.0, o.z);
hit.userData.interactable = true;
hit.userData.payload = { type: o.type || 'marker', id: o.id || null, label: o.label || 'Интерактив' };
hit.visible = true; // невидим визуально (opacity≈0), но кликабелен
hit.visible = true; // кликабелен
try { if (hit.material) hit.material.visible = false; } catch (_) {}
intGroup.add(hit);
interiorInteractablesRef.current.push(hit);
}
@@ -3325,9 +3337,10 @@ useEffect(() => {
}
function loadInteriorPlaceholder(int) {
// Упрощённый невидимый placeholder с кликабельной зоной
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(2, 2, 2),
new THREE.MeshStandardMaterial({ color: 0x00ffcc })
new THREE.MeshBasicMaterial({ visible: false })
);
mesh.position.set(int.pos_x, int.pos_y, int.pos_z);
mesh.userData.interiorId = int.id;
@@ -3793,16 +3806,28 @@ useEffect(() => {
const tryMove = (dirVec) => {
const candidate = player.position.clone().addScaledVector(dirVec, speed * delta);
// Обновляем AABB игрока (простая капсула не используется, только коробка)
const half = 0.3; // половина ширины
const height = 1.8;
const half = 0.25; // чуточку уже, чтобы не цепляться за стены
const height = 1.7; // немного ниже, чтобы не пересекать потолок
const playerBox = new THREE.Box3(
new THREE.Vector3(candidate.x - half, candidate.y, candidate.z - half),
new THREE.Vector3(candidate.x + half, candidate.y + height, candidate.z + half)
);
const hits = (interiorCollidersRef.current || []).some((mesh) => {
// Обновляем мировые матрицы статических коллайдеров для корректных AABB
try { interiorGroupRef.current && interiorGroupRef.current.updateMatrixWorld(true); } catch (_) {}
// В интерьере учитываем только внутренние коллайдеры, без городских объектов
const blockingMeshes = Array.isArray(interiorCollidersRef.current)
? interiorCollidersRef.current
: [];
let hits = false;
for (const mesh of blockingMeshes) {
if (!mesh) continue;
const box = new THREE.Box3().setFromObject(mesh);
return box.intersectsBox(playerBox);
});
// небольшой зазор, чтобы скользить вдоль стен
const expanded = box.clone().expandByScalar(0.01);
if (expanded.intersectsBox(playerBox)) { hits = true; break; }
}
if (!hits) {
player.position.copy(candidate);
}
@@ -4088,7 +4113,12 @@ useEffect(() => {
X: {playerCoords.x} Y: {playerCoords.y} Z: {playerCoords.z}
</div>
<div style={{ position: 'absolute', bottom: 20, left: 20, zIndex: 1000, background: 'rgba(0,0,0,0.6)', color: '#fff', padding: '4px 8px', borderRadius: 4 }}>
{new Date(gameTime).toLocaleString()}
{(() => {
if (!gameTime) return 'Загрузка времени...';
// Сервер шлёт ISO (gameTime.js -> toISOString). Отображаем игровое время (ускоренное в 8 раз)
const d = new Date(gameTime);
return d.toLocaleString();
})()}
</div>
{/* Кнопка карты мира */}
<button