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
3 changes: 1 addition & 2 deletions src/data/maps/core/ElevationMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ export const BIOME_THEMES: Record<BiomeType, BiomeTheme> = {
/** Standard elevation levels mapped to 0-255 range */
export const ELEVATION = {
VOID: 0, // Below terrain (chasms)
WATER_DEEP: 10, // Deep water level (impassable)
WATER_SHALLOW: 30, // Shallow water level (walkable but slow)
WATER: 25, // Water surface level (must be > LOW - CLIFF_THRESHOLD to avoid shoreline cliffs)
LOW: 60, // Ground level
MID: 140, // Natural expansion level
HIGH: 220, // Main base level
Expand Down
26 changes: 12 additions & 14 deletions src/data/maps/core/ElevationMapGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function paintRamp(

/**
* Paint a feature (water, forest, etc.) on a circular area
* Water features also set elevation to ensure proper depth rendering
* Water features set elevation to ELEVATION.WATER - water surface is always level
*/
function paintFeatureCircle(
grid: GenerationGrid,
Expand All @@ -208,9 +208,8 @@ function paintFeatureCircle(
feature: TerrainFeature
): void {
const r2 = radius * radius;
// Determine water elevation based on feature type
const waterElevation = feature === 'water_deep' ? ELEVATION.WATER_DEEP :
feature === 'water_shallow' ? ELEVATION.WATER_SHALLOW : null;
// Water surface is always at the same level (deep vs shallow is depth BELOW surface)
const isWater = feature === 'water_deep' || feature === 'water_shallow';

for (let y = Math.floor(cy - radius); y <= Math.ceil(cy + radius); y++) {
for (let x = Math.floor(cx - radius); x <= Math.ceil(cx + radius); x++) {
Expand All @@ -221,9 +220,9 @@ function paintFeatureCircle(
// Don't overwrite ramps
if (!grid.ramps[y][x]) {
grid.features[y][x] = feature;
// Set water elevation to ensure water renders below terrain
if (waterElevation !== null) {
grid.elevation[y][x] = waterElevation;
// Set water elevation - water surface is always level
if (isWater) {
grid.elevation[y][x] = ELEVATION.WATER;
}
Comment on lines +223 to +226

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid shoreline cliffs from fixed water elevation

Setting water cells to ELEVATION.WATER (20) makes the elevation difference to normal ground (ELEVATION.LOW = 60) exactly CLIFF_THRESHOLD (40 in src/data/pathfinding.config.ts). Because isCliffCell flags any diff ≥ threshold, any land (or shallow-water) cell adjacent to water now becomes a cliff and therefore unwalkable. This makes shores impassable on maps where water meets low ground, which is a regression from the previous behavior where water inherited the surrounding elevation. Consider excluding water neighbors from cliff checks or choosing a water elevation that doesn’t trip the threshold.

Useful? React with 👍 / 👎.

}
}
Expand All @@ -234,7 +233,7 @@ function paintFeatureCircle(

/**
* Paint a feature on a rectangular area
* Water features also set elevation to ensure proper depth rendering
* Water features set elevation to ELEVATION.WATER - water surface is always level
*/
function paintFeatureRect(
grid: GenerationGrid,
Expand All @@ -244,19 +243,18 @@ function paintFeatureRect(
height: number,
feature: TerrainFeature
): void {
// Determine water elevation based on feature type
const waterElevation = feature === 'water_deep' ? ELEVATION.WATER_DEEP :
feature === 'water_shallow' ? ELEVATION.WATER_SHALLOW : null;
// Water surface is always at the same level (deep vs shallow is depth BELOW surface)
const isWater = feature === 'water_deep' || feature === 'water_shallow';

for (let py = Math.floor(y); py < Math.ceil(y + height); py++) {
for (let px = Math.floor(x); px < Math.ceil(x + width); px++) {
if (py >= 0 && py < grid.height && px >= 0 && px < grid.width) {
// Don't overwrite ramps
if (!grid.ramps[py][px]) {
grid.features[py][px] = feature;
// Set water elevation to ensure water renders below terrain
if (waterElevation !== null) {
grid.elevation[py][px] = waterElevation;
// Set water elevation - water surface is always level
if (isWater) {
grid.elevation[py][px] = ELEVATION.WATER;
}
}
}
Expand Down
34 changes: 27 additions & 7 deletions src/editor/services/LLMMapGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,24 @@ const SYSTEM_PROMPT = `You are an expert RTS map designer creating professional
- Place ramps FROM high ground TO low ground

### Water Features
- shallow water: walkable at 0.6x speed, unbuildable
- deep water: impassable (for island maps/lakes)
- Use for naval maps, visual interest, or strategic slow zones
Water bodies should be created with LAYERED commands for realistic appearance:
1. First paint a LARGER shallow water area (the shoreline/edges)
2. Then paint a SMALLER deep water area in the CENTER (this overwrites the inner shallow)
This creates natural shores where shallow water surrounds deep water.

Example lake (radius 25 at position 100,100):
{ "cmd": "water", "x": 100, "y": 100, "radius": 25, "depth": "shallow" } // Outer shore
{ "cmd": "water", "x": 100, "y": 100, "radius": 18, "depth": "deep" } // Inner deep

Example river (width 20, length 80):
{ "cmd": "water", "x": 50, "y": 60, "width": 20, "height": 80, "depth": "shallow" } // Full width
{ "cmd": "water", "x": 53, "y": 60, "width": 14, "height": 80, "depth": "deep" } // Center channel

Properties:
- shallow water: walkable at 0.6x speed, unbuildable (shore/edges)
- deep water: impassable (center of lakes, deep rivers)
- ALWAYS create shallow water FIRST, then deep water on top for proper transitions
- Shore width should be 5-8 cells for good visual appearance

### Strategic Features
- Watch towers: Place at contested locations for vision control
Expand Down Expand Up @@ -791,14 +806,19 @@ function buildUserPrompt(settings: MapGenerationSettings, size: { width: number;
'**Map Type**: Island/Naval map',
'- Use deep water to separate land masses',
'- Create island bases connected by shallow water crossings or bridges',
'- Consider naval gameplay with water-based choke points',
'- IMPORTANT: Always paint shallow water FIRST (larger), then deep water on TOP (smaller)',
'- This creates natural shorelines where shallow water surrounds deep water',
''
);
} else if (settings.includeWater) {
lines.push(
'**Water Features**: Include water features',
'- Use shallow water for strategic slow zones',
'- Deep water for lakes or map boundaries',
'**Water Features**: Include lakes, rivers, or ponds',
'- IMPORTANT: Create water bodies in layers:',
' 1. Paint shallow water FIRST (larger area for shoreline)',
' 2. Paint deep water on TOP (smaller, in center)',
'- This creates natural transitions (shallow shores around deep centers)',
'- Shore width should be 5-8 cells',
'- Example lake: shallow radius 25, then deep radius 18 at same center',
''
);
}
Expand Down
7 changes: 2 additions & 5 deletions src/rendering/water/UnifiedWaterMesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,8 @@ export class UnifiedWaterMesh {

// Calculate world-space height
// In Three.js: Y is up, ground plane is X-Z
const height = elevation * HEIGHT_SCALE + WATER_SURFACE_OFFSET;

// Shallow water renders slightly higher for visual blending
const heightOffset = isDeep ? 0 : 0.02;
const finalHeight = height + heightOffset;
// All water (deep and shallow) renders at the same height - water surface is always level
const finalHeight = elevation * HEIGHT_SCALE + WATER_SURFACE_OFFSET;

// Cell size (usually 1, but may be larger if merged)
const halfSize = size / 2;
Expand Down
Loading