diff --git a/src/KubikonPlayer/KubikonPlayer.jsx b/src/KubikonPlayer/KubikonPlayer.jsx index c23dcf3..c4b6540 100644 --- a/src/KubikonPlayer/KubikonPlayer.jsx +++ b/src/KubikonPlayer/KubikonPlayer.jsx @@ -711,15 +711,25 @@ const KubikonPlayer = () => { const s = sceneRef.current; if (!s || !s._isPlaying) return; const locked = !!document.pointerLockElement; - // Lock потерян, мы НЕ в UI-cursor mode → пользователь нажал ESC - if (!locked && s.player && !s.player._uiCursorMode) { - // Синхронно ставим флаг — listener PlayerController сработает - // следующим и увидит true, не вызовет _onExitRequest. - s.player._uiCursorMode = true; - // Открываем меню в следующий тик (state-update React) - setChatOpen(false); - setTopMenuOpen(true); - } + if (locked || !s.player || s.player._uiCursorMode) return; + // Lock потерян. НЕ всякая потеря = ESC! В third-person отпускание + // ПКМ (orbit-камера) тоже снимает lock — это НЕ выход в меню. + // Меню открываем ТОЛЬКО если lock был «постоянным» (perma-режим: + // first/lockfirst/sideview/shiftLock) — там потеря lock = реальный ESC. + const p = s.player; + const permaLock = ( + p._cameraMode === 'first' || + p._cameraMode === 'lockfirst' || + p._cameraMode === 'sideview' || + p._shiftLock + ); + // _rmbHeld был выставлен при входе в lock; если ПКМ отпущена в third — + // это orbit-завершение, не меню. + if (!permaLock) return; + // Реальный ESC в perma-режиме → открываем меню. + p._uiCursorMode = true; + setChatOpen(false); + setTopMenuOpen(true); }; // capture-фаза, чтобы успеть раньше PlayerController document.addEventListener('pointerlockchange', onLockChange, true);