feat(control): grid snap, NPC context actions, and overlay dim fix

Re-enable users-branch UI, snap session tokens to square/hex grid from the control preview, refine inactive/open-info NPC menus, block marker actions while an effect brush is active, and dim materials/NPC overlays only when they are open.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-08-06 13:48:59 +08:00
parent e53d1ea934
commit 4456eb0277
24 changed files with 563 additions and 41 deletions
@@ -38,6 +38,10 @@
border-style: dashed;
}
.trapNonInteractive {
pointer-events: none;
}
.label {
position: absolute;
left: 50%;
@@ -21,6 +21,8 @@ type Props = {
viewport: { x: number; y: number; w: number; h: number } | null;
/** Пульт: показывать все ловушки + RMB меню. Презентация: только revealed. */
mode: 'control' | 'presentation';
/** На пульте: false — без контекстного меню (например, активна кисть эффектов). */
interactive?: boolean;
onReveal?: (trapId: string) => void;
onActivate?: (trapId: string) => void;
onDisarm?: (trapId: string) => void;
@@ -47,6 +49,7 @@ export function SceneTrapsOverlay({
session,
viewport,
mode,
interactive = true,
onReveal,
onActivate,
onDisarm,
@@ -54,6 +57,10 @@ export function SceneTrapsOverlay({
const [menu, setMenu] = useState<{ trapId: string; x: number; y: number } | null>(null);
const [activationFx, setActivationFx] = useState<ActivationFx | null>(null);
useEffect(() => {
if (!interactive) setMenu(null);
}, [interactive]);
useEffect(() => {
const act = session?.lastActivation;
if (!act) return;
@@ -112,6 +119,7 @@ export function SceneTrapsOverlay({
rt.status === 'active' ? styles.trapActive : '',
rt.status === 'disarmed' ? styles.trapDisarmed : '',
mode === 'control' && !rt.revealed ? styles.trapGmHidden : '',
mode === 'control' && !interactive ? styles.trapNonInteractive : '',
]
.filter(Boolean)
.join(' ');
@@ -121,7 +129,7 @@ export function SceneTrapsOverlay({
className={cls}
style={{ left, top, width: sizePx, height: sizePx }}
onContextMenu={
mode === 'control'
mode === 'control' && interactive
? (e) => {
e.preventDefault();
e.stopPropagation();
@@ -163,7 +171,7 @@ export function SceneTrapsOverlay({
}}
/>
) : null}
{menu && mode === 'control'
{menu && mode === 'control' && interactive
? createPortal(
<div
role="menu"