From cb9dd9739b5ea04fef9bd0fc6fcc862005e2d713 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 5 Apr 2026 17:43:42 +0000 Subject: [PATCH] Add HQ entry from city view Co-authored-by: Luke The Dev --- src/features/retro-office/RetroOffice3D.tsx | 25 +++++++- .../retro-office/scene/environment.tsx | 63 +++++++++++++++++-- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/features/retro-office/RetroOffice3D.tsx b/src/features/retro-office/RetroOffice3D.tsx index 3376725..98f77b9 100644 --- a/src/features/retro-office/RetroOffice3D.tsx +++ b/src/features/retro-office/RetroOffice3D.tsx @@ -2704,12 +2704,13 @@ export function RetroOffice3D({ target: [number, number, number]; zoom?: number; } | null>(null); + const [sceneMode, setSceneMode] = useState<"city" | "office">("city"); + const cityScapeMode = sceneMode === "city"; const LOCAL_CAMERA_TARGET = useMemo( () => toWorld(LOCAL_OFFICE_CANVAS_WIDTH / 2, LOCAL_OFFICE_CANVAS_HEIGHT / 2), [], ); - const cityScapeMode = true; const cityCameraTarget = LOCAL_CAMERA_TARGET; const CAM_POS = useMemo<[number, number, number]>(() => { const activeTarget = cityScapeMode ? cityCameraTarget : LOCAL_CAMERA_TARGET; @@ -2728,6 +2729,12 @@ export function RetroOffice3D({ () => ({ pos: CAM_POS, target: cameraTarget, zoom: cameraZoom }), [CAM_POS, cameraTarget, cameraZoom] ); + const handleEnterOfficeFromCity = useCallback(() => { + setSceneMode("office"); + }, []); + const handleReturnToCity = useCallback(() => { + setSceneMode("city"); + }, []); const canvasResetKey = useMemo( () => [ @@ -5460,7 +5467,10 @@ export function RetroOffice3D({ /> {/* Floor + walls — always visible, no async loading. */} - + {/* Wall pictures — procedural, no async loading. */} @@ -7373,6 +7383,17 @@ export function RetroOffice3D({ )} ) : null} + {!immersiveOverlayActive && !cityScapeMode ? ( +
+ +
+ ) : null} {!readOnly && settingsModalOpen ? (
diff --git a/src/features/retro-office/scene/environment.tsx b/src/features/retro-office/scene/environment.tsx index 92d6289..e27adfc 100644 --- a/src/features/retro-office/scene/environment.tsx +++ b/src/features/retro-office/scene/environment.tsx @@ -1,6 +1,6 @@ "use client"; -import { memo, useMemo, useRef, type ReactNode } from "react"; +import { memo, useMemo, useRef, useState, type ReactNode } from "react"; import { useFrame } from "@react-three/fiber"; import type * as THREE from "three"; import { @@ -653,8 +653,17 @@ function RoadTile({ ); } -function FullCityScene({ centerX, centerZ }: { centerX: number; centerZ: number }) { +function FullCityScene({ + centerX, + centerZ, + onEnterHeadquarters, +}: { + centerX: number; + centerZ: number; + onEnterHeadquarters?: () => void; +}) { const cityGrid = useMemo(() => buildCityGrid(), []); + const [hqHovered, setHqHovered] = useState(false); const baseOffsetX = centerX - ((CITY_GRID_COLUMNS - 1) * CITY_GRID_CELL_SIZE) / 2; const baseOffsetZ = centerZ - ((CITY_GRID_ROWS - 1) * CITY_GRID_CELL_SIZE) / 2; const cityWidth = CITY_GRID_COLUMNS * CITY_GRID_CELL_SIZE; @@ -753,7 +762,29 @@ function FullCityScene({ centerX, centerZ }: { centerX: number; centerZ: number /> ); })} - + { + event.stopPropagation(); + setHqHovered(true); + }} + onPointerOut={(event) => { + event.stopPropagation(); + setHqHovered(false); + }} + onClick={(event) => { + event.stopPropagation(); + onEnterHeadquarters?.(); + }} + > + + + + + + + + + + + + + + + + { export const FloorAndWalls = memo(function FloorAndWalls({ showRemoteOffice = true, + onEnterHeadquarters, }: { showRemoteOffice?: boolean; + onEnterHeadquarters?: () => void; }) { const districtWidth = CANVAS_W * SCALE; const districtHeight = CANVAS_H * SCALE; @@ -919,7 +968,13 @@ export const FloorAndWalls = memo(function FloorAndWalls({ const groundHeight = showRemoteOffice ? districtHeight : localOfficeHeight; if (showRemoteOffice) { - return ; + return ( + + ); } return (