From feefbbb8fcc495bdacb574e12421cf80fe90b58b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Feb 2026 03:01:25 +0000 Subject: [PATCH] feat: add preview settings modal to map editor When clicking Preview in the map editor, a modal now appears letting you configure game settings before launching: AI difficulty, player count, starting resources, game speed, and fog of war. Previously these were hardcoded to fog off / insane resources. https://claude.ai/code/session_01V8xLd2yqzjK5gt4MB5p1uU --- src/app/game/setup/editor/page.tsx | 95 +++++++++++++----- src/editor/core/MapPreviewModal.tsx | 150 ++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+), 27 deletions(-) create mode 100644 src/editor/core/MapPreviewModal.tsx diff --git a/src/app/game/setup/editor/page.tsx b/src/app/game/setup/editor/page.tsx index b6d10520..0ee80dfc 100644 --- a/src/app/game/setup/editor/page.tsx +++ b/src/app/game/setup/editor/page.tsx @@ -6,6 +6,7 @@ import { EditorCore, VOIDSTRIKE_EDITOR_CONFIG } from '@/editor'; import { voidstrikeDataProvider } from '@/editor/providers/voidstrike'; import { useGameSetupStore, loadEditorMapDataFromStorage } from '@/store/gameSetupStore'; import { debugInitialization } from '@/utils/debugLogger'; +import { MapPreviewModal, type PreviewSettings } from '@/editor/core/MapPreviewModal'; import type { EditorMapData } from '@/editor'; import type { MapListItem } from '@/editor/core/EditorHeader'; import type { MapData } from '@/data/maps/MapTypes'; @@ -42,6 +43,10 @@ function EditorPageContent() { // Track current map data for preview const currentMapDataRef = useRef(null); + // Preview modal state + const [showPreviewModal, setShowPreviewModal] = useState(false); + const [pendingPreviewData, setPendingPreviewData] = useState<{ editor: EditorMapData; game: MapData } | null>(null); + // Load stored editor map data from IndexedDB (returning from preview) useEffect(() => { const loadStoredData = async () => { @@ -94,28 +99,54 @@ function EditorPageContent() { return; } - debugInitialization.log('Preview map:', gameData); + // Show the preview settings modal + setPendingPreviewData({ editor: data, game: gameData }); + setShowPreviewModal(true); + }; - // Store custom map in game setup store - const store = useGameSetupStore.getState(); + const handleLaunchPreview = useCallback((settings: PreviewSettings) => { + if (!pendingPreviewData) return; - // Store the editor map data so we can restore it when returning from preview - store.setEditorMapData(data); + const { editor: editorData, game: gameData } = pendingPreviewData; - store.setCustomMap(gameData); + debugInitialization.log('Preview map with settings:', settings); - // Configure for preview: 1 human vs 1 AI + const store = useGameSetupStore.getState(); + + // Reset and configure store.reset(); - store.setCustomMap(gameData); // Re-set after reset - store.setEditorMapData(data); // Re-set after reset - store.setEditorPreview(true); // Mark as editor preview for "Back to Editor" button - store.setFogOfWar(false); // Disable fog for easier testing - store.setStartingResources('insane'); // Start with lots of resources for testing + store.setCustomMap(gameData); + store.setEditorMapData(editorData); + store.setEditorPreview(true); + store.setStartingResources(settings.startingResources); + store.setGameSpeed(settings.gameSpeed); + store.setFogOfWar(settings.fogOfWar); + + // Configure player slots: 1 human + (numPlayers - 1) AI + // Slot 1 is already human from reset(). Add extra AI slots as needed. + for (let i = 2; i < settings.numPlayers; i++) { + store.addPlayerSlot(); + } + + // Set AI difficulty on all AI slots + const currentSlots = useGameSetupStore.getState().playerSlots; + for (const slot of currentSlots) { + if (slot.type === 'ai') { + store.setPlayerSlotAIDifficulty(slot.id, settings.aiDifficulty); + } + } + store.startGame(); - // Navigate to game + setShowPreviewModal(false); + setPendingPreviewData(null); router.push('/game'); - }; + }, [pendingPreviewData, router]); + + const handleCancelPreview = useCallback(() => { + setShowPreviewModal(false); + setPendingPreviewData(null); + }, []); const handleLoadMap = useCallback((mapId: string) => { setCurrentMapId(mapId); @@ -142,19 +173,29 @@ function EditorPageContent() { } return ( - + <> + + + {showPreviewModal && pendingPreviewData && ( + + )} + ); } diff --git a/src/editor/core/MapPreviewModal.tsx b/src/editor/core/MapPreviewModal.tsx new file mode 100644 index 00000000..0c697ea4 --- /dev/null +++ b/src/editor/core/MapPreviewModal.tsx @@ -0,0 +1,150 @@ +'use client'; + +import { useState, useCallback, useEffect } from 'react'; +import { SettingSelect } from '@/components/game-setup'; +import type { StartingResources, GameSpeed, AIDifficulty } from '@/store/gameSetupStore'; + +export interface PreviewSettings { + startingResources: StartingResources; + gameSpeed: GameSpeed; + fogOfWar: boolean; + aiDifficulty: AIDifficulty; + numPlayers: number; +} + +interface MapPreviewModalProps { + maxPlayers: number; + onLaunch: (settings: PreviewSettings) => void; + onCancel: () => void; +} + +export function MapPreviewModal({ maxPlayers, onLaunch, onCancel }: MapPreviewModalProps) { + const [startingResources, setStartingResources] = useState('insane'); + const [gameSpeed, setGameSpeed] = useState('normal'); + const [fogOfWar, setFogOfWar] = useState(false); + const [aiDifficulty, setAiDifficulty] = useState('medium'); + const [numPlayers, setNumPlayers] = useState(2); + + // Clamp player count if maxPlayers changes + useEffect(() => { + if (numPlayers > maxPlayers) { + setNumPlayers(maxPlayers); + } + }, [maxPlayers, numPlayers]); + + const handleLaunch = useCallback(() => { + onLaunch({ startingResources, gameSpeed, fogOfWar, aiDifficulty, numPlayers }); + }, [startingResources, gameSpeed, fogOfWar, aiDifficulty, numPlayers, onLaunch]); + + // Close on Escape + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') onCancel(); + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [onCancel]); + + // Build player count options from 2 to maxPlayers + const playerOptions = []; + for (let i = 2; i <= maxPlayers; i++) { + playerOptions.push({ value: String(i), label: `${i} Players` }); + } + + return ( +
+ {/* Backdrop */} +
+ + {/* Modal */} +
+ {/* Header */} +
+

Preview Settings

+

Configure your map test

+
+ + {/* Settings */} +
+ {maxPlayers > 2 && ( + setNumPlayers(parseInt(v, 10))} + /> + )} + + + + + + + +
+ Fog of War + +
+
+ + {/* Footer */} +
+ + +
+
+
+ ); +}