# Terrain Integration Advanced

Building games must reconcile procedural or sculpted terrain with grid-based building systems. This reference covers the techniques used by Valheim, Rust, ARK, and similar games to handle slopes, anchor foundations, and optionally modify terrain to accommodate structures.

## The Terrain Challenge

Grid-based building assumes flat surfaces. Real terrain has slopes, bumps, and irregular features. Games solve this tension through three approaches: strict ground contact rules (Valheim), terrain-ignorant snapping (Rust), or hybrid systems with auto-pillars (ARK/Conan).

### Valheim's Ground Contact Rule

Valheim enforces a simple rule: foundations must touch the ground to gain 100% stability. This creates emergent building constraints:

```javascript
// Valheim-style ground contact check
function checkGroundContact(foundation, terrain) {
  const corners = foundation.getCornerPositions();
  let contactCount = 0;
  let totalDistance = 0;

  for (const corner of corners) {
    const groundY = terrain.getHeightAt(corner.x, corner.z);
    const distance = corner.y - groundY;
    
    if (distance <= 0.1) { // Within 10cm of ground
      contactCount++;
    }
    totalDistance += Math.abs(distance);
  }

  return {
    hasContact: contactCount >= 1,
    fullContact: contactCount === corners.length,
    avgDistance: totalDistance / corners.length,
    stability: contactCount >= 1 ? 1.0 : 0.0
  };
}
```

The consequence: players must work with terrain, using pillars or multiple foundation heights to build on slopes. This creates varied, organic-looking structures.

### Rust's Grid Independence

Rust foundations ignore terrain after placement. A foundation placed at height Y stays at height Y regardless of ground level. This simplifies building but creates floating structures on uneven ground.

```javascript
// Rust-style: terrain only affects initial placement height
function placeFoundationRust(foundation, position, terrain) {
  // Snap to grid
  const snapped = snapToGrid(position, GRID_SIZE);
  
  // Initial height from terrain (one-time)
  const groundY = terrain.getHeightAt(snapped.x, snapped.z);
  snapped.y = Math.max(groundY, snapped.y);
  
  // From here, terrain is irrelevant
  foundation.position.copy(snapped);
  foundation.isGrounded = true; // Always grounded if placed
  return { valid: true, position: snapped };
}
```

### ARK's Auto-Pillar System

ARK generates pillars automatically when foundations are placed above terrain. This maintains visual grounding while allowing flexible placement.

```javascript
// ARK-style auto-pillar generation
function placeWithAutoPillars(foundation, position, terrain) {
  const groundY = terrain.getHeightAt(position.x, position.z);
  const gapHeight = position.y - groundY;
  
  if (gapHeight <= 0.2) {
    // Close enough to ground, no pillars needed
    return { foundation, pillars: [] };
  }
  
  // Calculate pillar count
  const pillarHeight = 2.0; // Standard pillar height
  const pillarsNeeded = Math.ceil(gapHeight / pillarHeight);
  
  // Generate pillar stack
  const pillars = [];
  for (let i = 0; i < pillarsNeeded; i++) {
    pillars.push({
      type: 'pillar',
      position: new THREE.Vector3(
        position.x,
        groundY + (i * pillarHeight),
        position.z
      ),
      autoGenerated: true
    });
  }
  
  return { foundation, pillars };
}
```

## Slope Analysis

Before placing any foundation, analyze the terrain slope to determine buildability.

### Slope Calculation

```javascript
/**
 * TerrainAnalyzer - Analyzes terrain for building suitability
 */
export class TerrainAnalyzer {
  constructor(terrain, options = {}) {
    this.terrain = terrain;
    this.sampleResolution = options.sampleResolution ?? 0.5;
    this.heightCache = new Map();
  }

  /**
   * Get terrain height at position (with caching)
   */
  getHeightAt(x, z) {
    const key = `${Math.round(x * 10)},${Math.round(z * 10)}`;
    if (this.heightCache.has(key)) {
      return this.heightCache.get(key);
    }

    const height = this._sampleHeight(x, z);
    this.heightCache.set(key, height);
    return height;
  }

  _sampleHeight(x, z) {
    // Raycast down to find terrain
    const raycaster = new THREE.Raycaster(
      new THREE.Vector3(x, 1000, z),
      new THREE.Vector3(0, -1, 0)
    );
    
    const intersects = raycaster.intersectObject(this.terrain, true);
    return intersects.length > 0 ? intersects[0].point.y : 0;
  }

  /**
   * Analyze slope at a position
   * @param {Vector3} center - Center point to analyze
   * @param {Object} options - Analysis options
   * @returns {Object} Slope analysis results
   */
  analyzeSlope(center, options = {}) {
    const radius = options.radius ?? 2;
    const samples = options.samples ?? 8;
    
    // Sample heights in a circle around center
    const heights = [];
    const centerHeight = this.getHeightAt(center.x, center.z);
    
    for (let i = 0; i < samples; i++) {
      const angle = (i / samples) * Math.PI * 2;
      const x = center.x + Math.cos(angle) * radius;
      const z = center.z + Math.sin(angle) * radius;
      heights.push({
        x, z,
        height: this.getHeightAt(x, z),
        angle
      });
    }

    // Calculate slope metrics
    const minHeight = Math.min(...heights.map(h => h.height));
    const maxHeight = Math.max(...heights.map(h => h.height));
    const heightDiff = maxHeight - minHeight;
    
    // Slope angle (degrees)
    const slopeAngle = Math.atan2(heightDiff, radius * 2) * (180 / Math.PI);
    
    // Calculate normal vector
    const normal = this.calculateNormal(center, heights);
    
    // Determine dominant slope direction
    let maxSlope = 0;
    let slopeDirection = new THREE.Vector3();
    
    for (const sample of heights) {
      const slope = Math.abs(sample.height - centerHeight) / radius;
      if (slope > maxSlope) {
        maxSlope = slope;
        slopeDirection.set(
          sample.x - center.x,
          0,
          sample.z - center.z
        ).normalize();
      }
    }

    return {
      angle: slopeAngle,
      normal,
      minHeight,
      maxHeight,
      heightDiff,
      slopeDirection,
      canBuild: slopeAngle <= (options.maxSlope ?? 45),
      samples: heights
    };
  }

  /**
   * Calculate terrain normal at position
   */
  calculateNormal(center, samples) {
    // Use cross product of height differences
    const north = samples.find(s => s.angle < 0.1 || s.angle > Math.PI * 2 - 0.1);
    const east = samples.find(s => Math.abs(s.angle - Math.PI / 2) < 0.1);
    const south = samples.find(s => Math.abs(s.angle - Math.PI) < 0.1);
    const west = samples.find(s => Math.abs(s.angle - Math.PI * 1.5) < 0.1);

    if (!north || !south || !east || !west) {
      return new THREE.Vector3(0, 1, 0); // Default up
    }

    const dx = east.height - west.height;
    const dz = north.height - south.height;
    
    const normal = new THREE.Vector3(-dx, 2, -dz).normalize();
    return normal;
  }

  /**
   * Check if area is flat enough for building
   */
  isBuildable(center, size, maxSlope = 30) {
    const halfSize = size / 2;
    const corners = [
      { x: center.x - halfSize, z: center.z - halfSize },
      { x: center.x + halfSize, z: center.z - halfSize },
      { x: center.x + halfSize, z: center.z + halfSize },
      { x: center.x - halfSize, z: center.z + halfSize }
    ];

    const heights = corners.map(c => this.getHeightAt(c.x, c.z));
    const maxDiff = Math.max(...heights) - Math.min(...heights);
    const slope = Math.atan2(maxDiff, size) * (180 / Math.PI);

    return {
      buildable: slope <= maxSlope,
      slope,
      heightVariation: maxDiff,
      suggestedHeight: Math.max(...heights)
    };
  }

  /**
   * Find best foundation height for position
   */
  findOptimalHeight(center, foundationSize) {
    const analysis = this.isBuildable(center, foundationSize);
    
    if (analysis.buildable) {
      // Use highest corner height to avoid clipping
      return analysis.suggestedHeight;
    }
    
    // For steep slopes, return average + offset
    const avgHeight = (analysis.suggestedHeight + 
      this.getHeightAt(center.x, center.z)) / 2;
    return avgHeight + analysis.heightVariation * 0.5;
  }

  /**
   * Clear height cache (call after terrain modification)
   */
  clearCache() {
    this.heightCache.clear();
  }
}
```

### Slope Thresholds by Game Type

| Game Type | Max Slope | Behavior |
|-----------|-----------|----------|
| Survival (Valheim) | 20-30° | Strict, must work with terrain |
| Base Builder (Rust) | 45° | Moderate, some floating OK |
| Sandbox (ARK) | 60° | Permissive, auto-pillars fill gaps |
| Creative (Minecraft) | 90° | No limits, floating allowed |

## Foundation Placement

### The FoundationPlacer Class

```javascript
/**
 * FoundationPlacer - Handles foundation placement with terrain awareness
 */
export class FoundationPlacer {
  constructor(options = {}) {
    this.mode = options.mode ?? 'valheim';
    this.maxSlope = options.maxSlope ?? 30;
    this.autoLevel = options.autoLevel ?? true;
    this.gridSize = options.gridSize ?? 4;
    this.pillarThreshold = options.pillarThreshold ?? 0.5;
  }

  /**
   * Attempt to place a foundation
   */
  place(foundation, position, analyzer) {
    // Snap to grid first
    const snapped = this.snapToGrid(position);
    
    // Analyze terrain at placement location
    const slope = analyzer.analyzeSlope(snapped, {
      radius: this.gridSize / 2,
      maxSlope: this.maxSlope
    });

    // Mode-specific placement logic
    switch (this.mode) {
      case 'valheim':
        return this.placeValheim(foundation, snapped, slope, analyzer);
      case 'rust':
        return this.placeRust(foundation, snapped, slope, analyzer);
      case 'ark':
        return this.placeArk(foundation, snapped, slope, analyzer);
      default:
        return this.placeValheim(foundation, snapped, slope, analyzer);
    }
  }

  /**
   * Valheim-style: Must touch ground for stability
   */
  placeValheim(foundation, position, slope, analyzer) {
    if (!slope.canBuild) {
      return {
        valid: false,
        reason: `Slope too steep (${slope.angle.toFixed(1)}° > ${this.maxSlope}°)`,
        slope: slope.angle
      };
    }

    // Find height where foundation touches ground
    const groundHeight = this.findGroundContactHeight(position, analyzer);
    
    // Check if foundation would be buried
    const centerGround = analyzer.getHeightAt(position.x, position.z);
    if (groundHeight - centerGround > 1.0) {
      return {
        valid: false,
        reason: 'Foundation would be too elevated from center',
        heightDiff: groundHeight - centerGround
      };
    }

    foundation.position.set(position.x, groundHeight, position.z);
    foundation.isGrounded = true;
    
    return {
      valid: true,
      height: groundHeight,
      slope: slope.angle,
      stability: 1.0,
      groundContact: true
    };
  }

  /**
   * Rust-style: Grid-based, terrain mostly ignored
   */
  placeRust(foundation, position, slope, analyzer) {
    // Get ground height at center only
    const groundHeight = analyzer.getHeightAt(position.x, position.z);
    const placementHeight = Math.max(groundHeight, position.y);
    
    foundation.position.set(position.x, placementHeight, position.z);
    foundation.isGrounded = true; // Always grounded in Rust mode
    
    return {
      valid: true,
      height: placementHeight,
      slope: slope.angle,
      stability: 1.0,
      groundContact: Math.abs(placementHeight - groundHeight) < 0.5
    };
  }

  /**
   * ARK-style: Auto-generate pillars for gaps
   */
  placeArk(foundation, position, slope, analyzer) {
    const groundHeight = analyzer.getHeightAt(position.x, position.z);
    const gapHeight = position.y - groundHeight;

    // Determine if pillars needed
    const pillars = [];
    if (gapHeight > this.pillarThreshold) {
      const pillarCount = Math.ceil(gapHeight / 2.0);
      
      for (let i = 0; i < pillarCount; i++) {
        pillars.push({
          type: 'pillar',
          position: new THREE.Vector3(
            position.x,
            groundHeight + (i * 2.0),
            position.z
          ),
          autoGenerated: true,
          material: foundation.material
        });
      }
    }

    foundation.position.copy(position);
    foundation.isGrounded = true;

    return {
      valid: true,
      height: position.y,
      slope: slope.angle,
      stability: 1.0,
      pillars,
      pillarsGenerated: pillars.length
    };
  }

  /**
   * Find height where foundation corners touch ground
   */
  findGroundContactHeight(center, analyzer) {
    const halfSize = this.gridSize / 2;
    const corners = [
      { x: center.x - halfSize, z: center.z - halfSize },
      { x: center.x + halfSize, z: center.z - halfSize },
      { x: center.x + halfSize, z: center.z + halfSize },
      { x: center.x - halfSize, z: center.z + halfSize }
    ];

    const heights = corners.map(c => analyzer.getHeightAt(c.x, c.z));
    
    // Foundation sits on highest corner (lowest would bury corners)
    return Math.max(...heights);
  }

  /**
   * Snap position to building grid
   */
  snapToGrid(position) {
    return new THREE.Vector3(
      Math.round(position.x / this.gridSize) * this.gridSize,
      position.y,
      Math.round(position.z / this.gridSize) * this.gridSize
    );
  }
}
```

## Auto-Leveling Systems

When terrain is too uneven, some games flatten it automatically around foundations.

### Terrain Modifier

```javascript
/**
 * TerrainModifier - Modify terrain heightmap for building
 */
export class TerrainModifier {
  constructor(terrain, options = {}) {
    this.terrain = terrain;
    this.heightmap = terrain.userData.heightmap; // Assumes heightmap stored
    this.resolution = options.resolution ?? 1;
    this.maxModification = options.maxModification ?? 5; // Max height change
    this.modificationHistory = []; // For undo
  }

  /**
   * Flatten terrain in an area
   */
  flatten(center, radius, targetHeight = null) {
    const affected = this.getAffectedVertices(center, radius);
    const currentHeight = targetHeight ?? this.getAverageHeight(affected);
    
    const modifications = [];
    
    for (const vertex of affected) {
      const distance = Math.sqrt(
        Math.pow(vertex.x - center.x, 2) + 
        Math.pow(vertex.z - center.z, 2)
      );
      
      // Smooth falloff at edges
      const influence = 1 - Math.pow(distance / radius, 2);
      const newHeight = THREE.MathUtils.lerp(
        vertex.originalHeight,
        currentHeight,
        Math.max(0, influence)
      );
      
      // Clamp to max modification
      const clampedHeight = THREE.MathUtils.clamp(
        newHeight,
        vertex.originalHeight - this.maxModification,
        vertex.originalHeight + this.maxModification
      );
      
      modifications.push({
        vertex,
        oldHeight: vertex.height,
        newHeight: clampedHeight
      });
      
      this.setVertexHeight(vertex, clampedHeight);
    }

    this.modificationHistory.push({
      type: 'flatten',
      center: center.clone(),
      radius,
      modifications
    });

    this.updateTerrainMesh();
    
    return {
      success: true,
      verticesModified: modifications.length,
      targetHeight: currentHeight
    };
  }

  /**
   * Raise terrain in an area
   */
  raise(center, radius, amount) {
    const affected = this.getAffectedVertices(center, radius);
    const modifications = [];
    
    for (const vertex of affected) {
      const distance = Math.sqrt(
        Math.pow(vertex.x - center.x, 2) + 
        Math.pow(vertex.z - center.z, 2)
      );
      
      const influence = 1 - (distance / radius);
      const raise = amount * Math.max(0, influence);
      const newHeight = Math.min(
        vertex.height + raise,
        vertex.originalHeight + this.maxModification
      );
      
      modifications.push({
        vertex,
        oldHeight: vertex.height,
        newHeight
      });
      
      this.setVertexHeight(vertex, newHeight);
    }

    this.modificationHistory.push({
      type: 'raise',
      center: center.clone(),
      radius,
      amount,
      modifications
    });

    this.updateTerrainMesh();
    
    return { success: true, verticesModified: modifications.length };
  }

  /**
   * Lower terrain in an area
   */
  lower(center, radius, amount) {
    return this.raise(center, radius, -amount);
  }

  /**
   * Undo last modification
   */
  undo() {
    const last = this.modificationHistory.pop();
    if (!last) return { success: false, reason: 'Nothing to undo' };
    
    for (const mod of last.modifications) {
      this.setVertexHeight(mod.vertex, mod.oldHeight);
    }
    
    this.updateTerrainMesh();
    return { success: true, type: last.type };
  }

  /**
   * Get vertices within radius of center
   */
  getAffectedVertices(center, radius) {
    const vertices = [];
    const geometry = this.terrain.geometry;
    const position = geometry.attributes.position;
    
    for (let i = 0; i < position.count; i++) {
      const x = position.getX(i);
      const z = position.getZ(i);
      const distance = Math.sqrt(
        Math.pow(x - center.x, 2) + 
        Math.pow(z - center.z, 2)
      );
      
      if (distance <= radius) {
        vertices.push({
          index: i,
          x, z,
          height: position.getY(i),
          originalHeight: this.heightmap?.[i] ?? position.getY(i)
        });
      }
    }
    
    return vertices;
  }

  /**
   * Get average height of vertices
   */
  getAverageHeight(vertices) {
    if (vertices.length === 0) return 0;
    const sum = vertices.reduce((acc, v) => acc + v.height, 0);
    return sum / vertices.length;
  }

  /**
   * Set height of a vertex
   */
  setVertexHeight(vertex, height) {
    const position = this.terrain.geometry.attributes.position;
    position.setY(vertex.index, height);
    vertex.height = height;
  }

  /**
   * Update terrain mesh after modifications
   */
  updateTerrainMesh() {
    const geometry = this.terrain.geometry;
    geometry.attributes.position.needsUpdate = true;
    geometry.computeVertexNormals();
    geometry.computeBoundingSphere();
    
    // Update collision mesh if separate
    if (this.terrain.userData.collisionMesh) {
      this.terrain.userData.collisionMesh.geometry.copy(geometry);
    }
  }

  /**
   * Serialize modifications for saving/networking
   */
  serialize() {
    return this.modificationHistory.map(mod => ({
      type: mod.type,
      center: { x: mod.center.x, y: mod.center.y, z: mod.center.z },
      radius: mod.radius,
      amount: mod.amount,
      modifications: mod.modifications.map(m => ({
        index: m.vertex.index,
        oldHeight: m.oldHeight,
        newHeight: m.newHeight
      }))
    }));
  }

  /**
   * Apply serialized modifications (for loading/networking)
   */
  deserialize(data) {
    const position = this.terrain.geometry.attributes.position;
    
    for (const mod of data) {
      for (const m of mod.modifications) {
        position.setY(m.index, m.newHeight);
      }
    }
    
    this.updateTerrainMesh();
  }
}
```

## Pillar Generation

For slopes too steep to flatten, auto-generate support pillars.

```javascript
/**
 * PillarGenerator - Auto-generate support structures for uneven terrain
 */
export class PillarGenerator {
  constructor(options = {}) {
    this.pillarHeight = options.pillarHeight ?? 2.0;
    this.pillarWidth = options.pillarWidth ?? 0.5;
    this.minGap = options.minGap ?? 0.3; // Below this, no pillar needed
    this.maxPillars = options.maxPillars ?? 10; // Safety limit
  }

  /**
   * Generate pillars for a foundation
   */
  generateForFoundation(foundation, analyzer) {
    const corners = this.getFoundationCorners(foundation);
    const pillars = [];

    for (const corner of corners) {
      const groundY = analyzer.getHeightAt(corner.x, corner.z);
      const gap = foundation.position.y - groundY;
      
      if (gap > this.minGap) {
        const cornerPillars = this.generatePillarStack(
          corner,
          groundY,
          foundation.position.y,
          foundation.material
        );
        pillars.push(...cornerPillars);
      }
    }

    // Check center if foundation is large
    if (foundation.width >= 4 || foundation.depth >= 4) {
      const centerGround = analyzer.getHeightAt(
        foundation.position.x,
        foundation.position.z
      );
      const centerGap = foundation.position.y - centerGround;
      
      if (centerGap > this.minGap) {
        const centerPillars = this.generatePillarStack(
          foundation.position,
          centerGround,
          foundation.position.y,
          foundation.material
        );
        pillars.push(...centerPillars);
      }
    }

    return {
      pillars,
      totalHeight: pillars.reduce((sum, p) => sum + this.pillarHeight, 0),
      resourceCost: this.calculateResourceCost(pillars, foundation.material)
    };
  }

  /**
   * Generate a vertical stack of pillars
   */
  generatePillarStack(position, bottomY, topY, material) {
    const gap = topY - bottomY;
    const count = Math.min(
      Math.ceil(gap / this.pillarHeight),
      this.maxPillars
    );
    
    const pillars = [];
    for (let i = 0; i < count; i++) {
      const pillarY = bottomY + (i * this.pillarHeight);
      
      // Last pillar might be partial height
      const isLast = i === count - 1;
      const height = isLast 
        ? (topY - pillarY) 
        : this.pillarHeight;
      
      pillars.push({
        id: `pillar_${Date.now()}_${i}`,
        type: 'pillar',
        position: new THREE.Vector3(position.x, pillarY, position.z),
        height,
        width: this.pillarWidth,
        material,
        autoGenerated: true,
        supports: i === count - 1 ? 'foundation' : 'pillar'
      });
    }

    return pillars;
  }

  /**
   * Get corner positions of foundation
   */
  getFoundationCorners(foundation) {
    const hw = (foundation.width ?? 4) / 2;
    const hd = (foundation.depth ?? 4) / 2;
    const pos = foundation.position;

    return [
      new THREE.Vector3(pos.x - hw, pos.y, pos.z - hd),
      new THREE.Vector3(pos.x + hw, pos.y, pos.z - hd),
      new THREE.Vector3(pos.x + hw, pos.y, pos.z + hd),
      new THREE.Vector3(pos.x - hw, pos.y, pos.z + hd)
    ];
  }

  /**
   * Calculate resource cost for pillars
   */
  calculateResourceCost(pillars, material) {
    const baseCost = {
      WOOD: { wood: 20 },
      STONE: { stone: 30 },
      METAL: { metal: 10 }
    };

    const cost = baseCost[material?.name] ?? baseCost.WOOD;
    const total = {};
    
    for (const resource of Object.keys(cost)) {
      total[resource] = cost[resource] * pillars.length;
    }

    return total;
  }
}
```

## Networking Terrain Modifications

Terrain changes must synchronize across clients in multiplayer.

```javascript
/**
 * Network messages for terrain modification
 */
const TerrainNetworkMessages = {
  FLATTEN_REQUEST: 'terrain:flatten:request',
  FLATTEN_RESULT: 'terrain:flatten:result',
  RAISE_REQUEST: 'terrain:raise:request',
  LOWER_REQUEST: 'terrain:lower:request',
  MODIFICATION_SYNC: 'terrain:modification:sync',
  FULL_SYNC: 'terrain:full:sync'
};

/**
 * TerrainNetworkManager - Sync terrain modifications
 */
export class TerrainNetworkManager {
  constructor(modifier, connection, options = {}) {
    this.modifier = modifier;
    this.connection = connection;
    this.isServer = options.isServer ?? false;
    this.pendingModifications = new Map();
    
    this.setupHandlers();
  }

  setupHandlers() {
    if (this.isServer) {
      this.connection.on(TerrainNetworkMessages.FLATTEN_REQUEST, 
        this.handleFlattenRequest.bind(this));
      this.connection.on(TerrainNetworkMessages.RAISE_REQUEST,
        this.handleRaiseRequest.bind(this));
    } else {
      this.connection.on(TerrainNetworkMessages.MODIFICATION_SYNC,
        this.handleModificationSync.bind(this));
      this.connection.on(TerrainNetworkMessages.FULL_SYNC,
        this.handleFullSync.bind(this));
    }
  }

  /**
   * Client: Request terrain flatten
   */
  requestFlatten(center, radius) {
    const requestId = `flatten_${Date.now()}`;
    
    this.pendingModifications.set(requestId, {
      type: 'flatten',
      center,
      radius,
      timestamp: Date.now()
    });

    this.connection.send(TerrainNetworkMessages.FLATTEN_REQUEST, {
      requestId,
      center: { x: center.x, y: center.y, z: center.z },
      radius
    });

    return requestId;
  }

  /**
   * Server: Handle flatten request
   */
  handleFlattenRequest(data, clientId) {
    const center = new THREE.Vector3(data.center.x, data.center.y, data.center.z);
    
    // Validate request
    if (!this.validateModificationRequest(center, data.radius, clientId)) {
      this.connection.sendTo(clientId, TerrainNetworkMessages.FLATTEN_RESULT, {
        requestId: data.requestId,
        success: false,
        reason: 'Modification not allowed'
      });
      return;
    }

    // Apply modification
    const result = this.modifier.flatten(center, data.radius);
    
    // Broadcast to all clients
    this.connection.broadcast(TerrainNetworkMessages.MODIFICATION_SYNC, {
      type: 'flatten',
      center: data.center,
      radius: data.radius,
      targetHeight: result.targetHeight
    });

    // Confirm to requesting client
    this.connection.sendTo(clientId, TerrainNetworkMessages.FLATTEN_RESULT, {
      requestId: data.requestId,
      success: true,
      ...result
    });
  }

  /**
   * Client: Apply synced modification
   */
  handleModificationSync(data) {
    const center = new THREE.Vector3(data.center.x, data.center.y, data.center.z);
    
    switch (data.type) {
      case 'flatten':
        this.modifier.flatten(center, data.radius, data.targetHeight);
        break;
      case 'raise':
        this.modifier.raise(center, data.radius, data.amount);
        break;
      case 'lower':
        this.modifier.lower(center, data.radius, data.amount);
        break;
    }
  }

  /**
   * Client: Handle full terrain sync (on join)
   */
  handleFullSync(data) {
    this.modifier.deserialize(data.modifications);
  }

  /**
   * Server: Validate modification is allowed
   */
  validateModificationRequest(center, radius, clientId) {
    // Check build permissions
    // Check resource costs
    // Check modification limits
    return true; // Implement based on game rules
  }
}
```

## Integration Checklist

When implementing terrain integration:

- [ ] Choose foundation mode (Valheim/Rust/ARK) based on game type
- [ ] Implement slope analysis with appropriate thresholds
- [ ] Add foundation placement with ground contact checks
- [ ] Decide on auto-leveling (yes/no, degree of modification)
- [ ] Implement pillar generation for gaps
- [ ] Add terrain modification if enabled (flatten/raise/lower)
- [ ] Network terrain changes in multiplayer
- [ ] Cache terrain heights for performance
- [ ] Clear cache when terrain is modified
- [ ] Test on extreme slopes and terrain features
- [ ] Integrate with structural-physics for stability values

## Related References

- `structural-physics` skill - Stability calculations for grounded pieces
- `performance-at-scale` skill - Spatial queries for terrain sampling
- `multiplayer-building` skill - Networking patterns for terrain sync
