Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions src/editor/core/MapPreviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,8 @@ export function MapPreviewModal({ maxPlayers, onLaunch, onCancel }: MapPreviewMo
const [gameSpeed, setGameSpeed] = useState<GameSpeed>('normal');
const [fogOfWar, setFogOfWar] = useState(false);
const [aiDifficulty, setAiDifficulty] = useState<AIDifficulty>('medium');
const [numPlayers, setNumPlayers] = useState(2);

// Clamp player count if maxPlayers changes
useEffect(() => {
if (numPlayers > maxPlayers) {
setNumPlayers(maxPlayers);
}
}, [maxPlayers, numPlayers]);
const [numPlayersRaw, setNumPlayers] = useState(2);
const numPlayers = Math.min(numPlayersRaw, maxPlayers);

const handleLaunch = useCallback(() => {
onLaunch({ startingResources, gameSpeed, fogOfWar, aiDifficulty, numPlayers });
Expand All @@ -54,10 +48,7 @@ export function MapPreviewModal({ maxPlayers, onLaunch, onCancel }: MapPreviewMo
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/70 backdrop-blur-sm"
onClick={onCancel}
/>
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" onClick={onCancel} />

{/* Modal */}
<div className="relative bg-void-950 border border-void-700/50 rounded-xl shadow-2xl shadow-void-900/50 w-full max-w-sm mx-4">
Expand Down Expand Up @@ -120,7 +111,8 @@ export function MapPreviewModal({ maxPlayers, onLaunch, onCancel }: MapPreviewMo
className={`w-10 h-5 rounded-full transition-all duration-200 relative
${fogOfWar ? 'bg-void-500' : 'bg-void-800'}`}
>
<div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all duration-200
<div
className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all duration-200
${fogOfWar ? 'left-5' : 'left-0.5'}`}
/>
</button>
Expand Down
101 changes: 77 additions & 24 deletions src/editor/core/panels/SettingsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
'use client';

import { useState, useEffect, useRef } from 'react';
import type { EditorConfig, EditorState, EditorMapData, EditorObject } from '../../config/EditorConfig';
import type {
EditorConfig,
EditorState,
EditorMapData,
EditorObject,
} from '../../config/EditorConfig';
import { Section, ToggleSwitch } from './shared';
import {
generateBorderDecorations,
Expand All @@ -17,7 +22,9 @@ export interface SettingsPanelProps {
state: EditorState;
visibility: { labels: boolean; grid: boolean; categories: Record<string, boolean> };
onBiomeChange: (biomeId: string) => void;
onMetadataUpdate: (updates: Partial<Pick<EditorMapData, 'name' | 'width' | 'height' | 'biomeId'>>) => void;
onMetadataUpdate: (
updates: Partial<Pick<EditorMapData, 'name' | 'width' | 'height' | 'biomeId'>>
) => void;
onToggleLabels: () => void;
onToggleGrid: () => void;
onToggleCategory: (category: string) => void;
Expand All @@ -43,6 +50,16 @@ export function SettingsPanel({
const [borderScale, setBorderScale] = useState(2.0); // Average of scaleMin/scaleMax
const [isGenerating, setIsGenerating] = useState(false);
const isInitialMount = useRef(true);
const stateRef = useRef(state);
const onUpdateObjectsRef = useRef(onUpdateObjects);

useEffect(() => {
stateRef.current = state;
}, [state]);

useEffect(() => {
onUpdateObjectsRef.current = onUpdateObjects;
}, [onUpdateObjects]);

// Auto-regenerate border decorations when style, density, or scale changes (if decorations exist)
useEffect(() => {
Expand All @@ -53,8 +70,10 @@ export function SettingsPanel({
}

// Only auto-regenerate if there are existing border decorations
if (!state.mapData || !onUpdateObjects) return;
const currentCount = countBorderDecorations(state.mapData.objects);
const mapData = stateRef.current.mapData;
const updateObjects = onUpdateObjectsRef.current;
if (!mapData || !updateObjects) return;
const currentCount = countBorderDecorations(mapData.objects);
if (currentCount === 0) return;

// Regenerate with new settings
Expand All @@ -66,8 +85,8 @@ export function SettingsPanel({
scaleMax: borderScale * 1.4,
};

const newObjects = generateBorderDecorations(state.mapData, settings);
onUpdateObjects(newObjects);
const newObjects = generateBorderDecorations(mapData, settings);
updateObjects(newObjects);
}, [borderStyle, borderDensity, borderScale]);

if (!state.mapData) return null;
Expand Down Expand Up @@ -104,7 +123,10 @@ export function SettingsPanel({
<Section title="Map Info" icon="📋" theme={theme}>
<div className="space-y-3">
<div>
<label className="text-[10px] uppercase tracking-wider" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider"
style={{ color: theme.text.muted }}
>
Name
</label>
<input
Expand All @@ -121,7 +143,10 @@ export function SettingsPanel({
</div>
<div className="grid grid-cols-2 gap-2">
<div>
<label className="text-[10px] uppercase tracking-wider" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider"
style={{ color: theme.text.muted }}
>
Width
</label>
<input
Expand All @@ -137,7 +162,10 @@ export function SettingsPanel({
/>
</div>
<div>
<label className="text-[10px] uppercase tracking-wider" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider"
style={{ color: theme.text.muted }}
>
Height
</label>
<input
Expand Down Expand Up @@ -167,11 +195,14 @@ export function SettingsPanel({
px-3 py-2 rounded-lg text-xs transition-all
${state.activeBiome === biome.id ? 'ring-1' : 'hover:bg-white/5'}
`}
style={{
backgroundColor: state.activeBiome === biome.id ? `${theme.primary}20` : theme.surface,
color: state.activeBiome === biome.id ? theme.text.primary : theme.text.muted,
'--tw-ring-color': theme.primary,
} as React.CSSProperties}
style={
{
backgroundColor:
state.activeBiome === biome.id ? `${theme.primary}20` : theme.surface,
color: state.activeBiome === biome.id ? theme.text.primary : theme.text.muted,
'--tw-ring-color': theme.primary,
} as React.CSSProperties
}
>
{biome.name}
</button>
Expand All @@ -194,11 +225,11 @@ export function SettingsPanel({
label="Show Grid"
theme={theme}
/>
<div className="my-2 h-px" style={{ backgroundColor: theme.border }} />
<div
className="my-2 h-px"
style={{ backgroundColor: theme.border }}
/>
<div className="text-[10px] uppercase tracking-wider mb-1" style={{ color: theme.text.muted }}>
className="text-[10px] uppercase tracking-wider mb-1"
style={{ color: theme.text.muted }}
>
Categories
</div>
{categories.map((category) => (
Expand All @@ -218,19 +249,34 @@ export function SettingsPanel({
<Section title="Border Decorations" icon="🪨" theme={theme}>
<div className="space-y-3">
<div>
<label className="text-[10px] uppercase tracking-wider block mb-1.5" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider block mb-1.5"
style={{ color: theme.text.muted }}
>
Style
</label>
<div className="grid grid-cols-3 gap-1">
{(['rocks', 'crystals', 'trees', 'mixed', 'alien', 'dead_trees'] as BorderDecorationStyle[]).map((style) => (
{(
[
'rocks',
'crystals',
'trees',
'mixed',
'alien',
'dead_trees',
] as BorderDecorationStyle[]
).map((style) => (
<button
key={style}
onClick={() => setBorderStyle(style)}
className="px-2 py-1.5 rounded text-[11px] transition-all capitalize hover:bg-white/5"
style={{
backgroundColor: borderStyle === style ? `${theme.primary}20` : theme.surface,
color: borderStyle === style ? theme.text.primary : theme.text.muted,
border: borderStyle === style ? `1px solid ${theme.primary}` : '1px solid transparent',
border:
borderStyle === style
? `1px solid ${theme.primary}`
: '1px solid transparent',
}}
>
{style.replace('_', ' ')}
Expand All @@ -241,7 +287,10 @@ export function SettingsPanel({

<div>
<div className="flex justify-between items-center mb-1.5">
<label className="text-[10px] uppercase tracking-wider" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider"
style={{ color: theme.text.muted }}
>
Density
</label>
<span className="text-[10px] font-mono" style={{ color: theme.text.secondary }}>
Expand All @@ -261,7 +310,10 @@ export function SettingsPanel({

<div>
<div className="flex justify-between items-center mb-1.5">
<label className="text-[10px] uppercase tracking-wider" style={{ color: theme.text.muted }}>
<label
className="text-[10px] uppercase tracking-wider"
style={{ color: theme.text.muted }}
>
Scale
</label>
<span className="text-[10px] font-mono" style={{ color: theme.text.secondary }}>
Expand Down Expand Up @@ -308,7 +360,8 @@ export function SettingsPanel({
</div>

<div className="text-[10px]" style={{ color: theme.text.muted }}>
Places decorative {borderStyle} around the map edges to create an imposing boundary wall.
Places decorative {borderStyle} around the map edges to create an imposing boundary
wall.
</div>
</div>
</Section>
Expand Down
Loading