Source: mapgen/tectonics.js

/**
 * Retrieves the cell at the specified coordinates from the world map.
 * Returns "edge" if the coordinates are out of bounds.
 *
 * @param {number} x - The x-coordinate.
 * @param {number} y - The y-coordinate.
 * @returns {object|string} The cell at the specified coordinates or "edge" if out of bounds.
 */

function xy(x, y) {
    if (x < 0 || y < 0 || x >= world.width || y >= world.height) {
      return "edge"; // Coordinates are out of bounds
    }
    return world.map[y][x]; // Return the cell at the specified coordinates
  }
  
  
  /**
   * Initializes spreading centers for tectonic activity.
   * Generates a random number of spreading centers based on the world's width
   * and distributes them across the width of the world.
   */
  function createSpreadingCenters() {
    const numCenters = getRandomInt(5, 45); // Random number of spreading centers
    const spacing = Math.floor(world.width / numCenters);
  
    for (let i = 0; i < numCenters; i++) {
      world.tectonics.spreadingCenters.push(spacing * i);
    }
  
    world.tectonics.spreadingLine = [];
  }
  
  
  
  /**
   * Creates a spreading line starting from a given center.
   * The line progresses vertically and adjusts the center randomly,
   * ensuring it stays within the map boundaries.
   *
   * @param {number} center - The starting x-coordinate for the spreading line.
   */
  function createSpreadingLine(center) {
    const verticalAdjuster = Math.floor(world.height / getRandomInt(1, 15));
    const horizontalAdjuster = Math.floor(world.height / getRandomInt(1, 15));
    const start = verticalAdjuster;
    const end = world.height - verticalAdjuster;
    const widthStart = horizontalAdjuster;
    const widthEnd = world.width - horizontalAdjuster;
  
    for (let y = start; y < end; y++) {
      const direction = getRandomInt(0, 100) < 50 ? -1 : 1;
      center += direction * getRandomInt(1, 20);
  
      if (center < widthStart) {
        center = widthStart + getRandomInt(1, 50);
      } else if (center >= widthEnd) {
        center = widthEnd - 1 - getRandomInt(1, 50);
      }
  
      const cell = xy(center, y);
      cell.spreading = true;
      world.tectonics.spreadingLine.push(cell);
    }
  }
  
  /**
   * Creates a horizontal spreading line across the world map.
   * The line starts from a random y-coordinate and progresses horizontally,
   * adjusting the y-coordinate randomly while ensuring it stays within vertical boundaries.
   */
  function createHSpreadLine() {
    let y = getRandomInt(1, world.height - 1);
  
    for (let x = 1; x < world.width; x++) {
      y += getRandomInt(0, 100) < 50 ? -getRandomInt(1, 20) : getRandomInt(1, 20);
  
      if (y < 1) {
        y = 1;
      } else if (y > world.height - 1) {
        y = world.height - 1 - getRandomInt(1, 20);
      }
  
      const cell = xy(x, y);
      cell.spreading = true;
      world.tectonics.spreadingLine.push(cell);
    }
  }
  
  /**
   * Simulates the spreading process over a specified number of iterations.
   * Each iteration involves clearing rain, emitting magma from spreading centers,
   * spreading the magma, and setting moisture levels.
   *
   * @param {number} num - The number of iterations for the spreading process.
   */
  function spreadProcess(num) {
    clearRain();
  
    for (let i = 0; i < num; i++) {
      spreadingCenterEmits();
      spread();
      setMoisture();
    }
  }
  
  
  let spreadNum = 0;
  
  
  
  /**
   * Emits magma from the spreading centers.
   * Increases the magma and elevation of cells in the spreading line by a random amount.
   */
  function spreadingCenterEmits() {
    world.tectonics.spreadingLine.forEach((cell, index) => {
      const add = getRandomInt(0, 255);
      cell.magma += add;
      cell.elevation += add;
      cell.id = index;
    });
  }
  
  /**
   * Emits a small amount of magma from the spreading centers.
   * Increases the magma and elevation of cells in the spreading line by a small random amount.
   */
  function spreadingCenterEmitsSmall() {
    world.tectonics.spreadingLine.forEach(cell => {
      const add = getRandomInt(1, 5);
      cell.magma += add;
      cell.elevation += add;
    });
  }
  
  /**
   * Spreads magma from cells with magma to their adjacent cells.
   * Rolls magma from one cell to its neighboring cells based on random directions.
   */
  function spread() {
    for (let y = 0; y < world.height; y++) {
      for (let x = 0; x < world.width; x++) {
        const cell = xy(x, y);
        if (cell.magma > 0) {
          try {
            const neighbors = [
              xy(x - 1, y),     // west
              xy(x + 1, y),     // east
              xy(x + 1, y + 1), // northeast
              xy(x - 1, y - 1), // southwest
              xy(x, y + 1),     // north
              xy(x, y - 1),     // south
              xy(x - 1, y + 1), // northwest
              xy(x + 1, y - 1)  // southeast
            ];
  
            const rand = getRandomInt(0, 7);
            rollMagma(neighbors[rand], cell);
            neighbors.forEach(neighbor => rollMagma(neighbor, cell));
          } catch {
            // Handle any errors that occur during the magma rolling process
          }
        }
      }
    }
  }
  
  /**
   * Transfers magma from one cell to another.
   * Ensures that the magma levels between cells are balanced based on a random multiplier.
   *
   * @param {Object} newCell - The cell to which magma is transferred.
   * @param {Object} oldCell - The cell from which magma is transferred.
   */
  function rollMagma(newCell, oldCell) {
    const mult = getRandomInt(1, 15);
    if (newCell !== "edge" && oldCell !== "edge" && newCell.magma < oldCell.magma) {
      const diff = oldCell.magma - newCell.magma;
      const div = Math.floor(diff / mult);
      newCell.magma += div;
      newCell.elevation += div;
      oldCell.magma -= div;
      oldCell.elevation -= div;
    }
  }