mirror of
https://github.com/iamlukethedev/Claw3D.git
synced 2026-07-30 11:12:32 +00:00
Add HQ entry from city view
Co-authored-by: Luke The Dev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
co-authored by
Luke The Dev
parent
e7766a7242
commit
cb9dd9739b
@@ -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. */}
|
||||
<SceneFloorAndWalls showRemoteOffice={cityScapeMode} />
|
||||
<SceneFloorAndWalls
|
||||
showRemoteOffice={cityScapeMode}
|
||||
onEnterHeadquarters={handleEnterOfficeFromCity}
|
||||
/>
|
||||
|
||||
{/* Wall pictures — procedural, no async loading. */}
|
||||
<SceneWallPictures showRemoteOffice={cityScapeMode} />
|
||||
@@ -7373,6 +7383,17 @@ export function RetroOffice3D({
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
{!immersiveOverlayActive && !cityScapeMode ? (
|
||||
<div className="absolute top-3 left-3 z-20">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleReturnToCity}
|
||||
className="rounded-md border border-cyan-500/35 bg-[#071018]/92 px-3 py-1.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-cyan-100 transition-colors backdrop-blur-sm hover:border-cyan-300/55 hover:text-white"
|
||||
>
|
||||
Back to City
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{!readOnly && settingsModalOpen ? (
|
||||
<div className="absolute inset-0 z-30 flex items-start justify-end overflow-y-auto bg-black/35 p-4 backdrop-blur-[1px]">
|
||||
<div className="flex max-h-[calc(100vh-2rem)] w-full max-w-sm flex-col overflow-hidden rounded-xl border border-cyan-500/20 bg-[#05090d]/95 shadow-2xl">
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<group position={hqPosition}>
|
||||
<group
|
||||
position={hqPosition}
|
||||
onPointerOver={(event) => {
|
||||
event.stopPropagation();
|
||||
setHqHovered(true);
|
||||
}}
|
||||
onPointerOut={(event) => {
|
||||
event.stopPropagation();
|
||||
setHqHovered(false);
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onEnterHeadquarters?.();
|
||||
}}
|
||||
>
|
||||
<mesh position={[0, 0.01, 0.18]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<planeGeometry args={[3.2, 2.8]} />
|
||||
<meshBasicMaterial
|
||||
color={hqHovered ? "#fde68a" : "#bbf7d0"}
|
||||
transparent
|
||||
opacity={hqHovered ? 0.24 : 0.14}
|
||||
/>
|
||||
</mesh>
|
||||
<TowerBlock
|
||||
position={[-0.52, 0, -0.26]}
|
||||
width={0.9}
|
||||
@@ -782,6 +813,22 @@ function FullCityScene({ centerX, centerZ }: { centerX: number; centerZ: number
|
||||
trimColor="#dcfce7"
|
||||
windowColor="#bfdbfe"
|
||||
/>
|
||||
<mesh position={[0, 0.02, -1.02]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<planeGeometry args={[1.85, 0.34]} />
|
||||
<meshBasicMaterial
|
||||
color={hqHovered ? "#fef08a" : "#e0f2fe"}
|
||||
transparent
|
||||
opacity={0.92}
|
||||
/>
|
||||
</mesh>
|
||||
<mesh position={[0, 0.03, -1.02]}>
|
||||
<planeGeometry args={[1.7, 0.18]} />
|
||||
<meshBasicMaterial color="#0f172a" transparent opacity={0.98} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0.04, -1.02]}>
|
||||
<planeGeometry args={[1.58, 0.12]} />
|
||||
<meshBasicMaterial color={hqHovered ? "#fef08a" : "#f8fafc"} transparent opacity={1} />
|
||||
</mesh>
|
||||
</group>
|
||||
<CityTrafficLayer
|
||||
cityRoadSpanX={CITY_GRID_CELL_SIZE * CITY_GRID_COLUMNS * 0.82}
|
||||
@@ -861,8 +908,10 @@ const buildCityGrid = (): CityGridCell[] => {
|
||||
|
||||
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 <FullCityScene centerX={localOfficeCenterX} centerZ={localOfficeCenterZ} />;
|
||||
return (
|
||||
<FullCityScene
|
||||
centerX={localOfficeCenterX}
|
||||
centerZ={localOfficeCenterZ}
|
||||
onEnterHeadquarters={onEnterHeadquarters}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user