create_event_layer.js

import { createFamily } from "./create_family.js";
import { random, shuffle } from "./random_utils.js";
import { yieldToEventLoop } from "./utils.js";

// Same rationale as create_historical_layer.js's YIELD_INTERVAL_MS: this runs in a worker
// with no other threads, so periodic yields keep other pending Comlink calls from being
// stuck behind this loop for its whole duration on a map with many farms.
const YIELD_INTERVAL_MS = 20;

/**
 * Enriches a location map with event-driven narrative content. Compared to the historical layer
 * generator, this one is programmatic because the content is more difficult to integrate.
 *
 * @param {Object} options - Event layer options
 * @param {Map} options.map - A location map keyed by location type (e.g. "Farm"). Modified in place.
 * @returns {void}
 */
export async function createEventLayer({ map } = {}) {
  if (!map) {
    throw new Error("Map is required to create an event layer.");
  }
  const enriched = new Set();
  const farms = map.get("Farm");
  if (farms) {
    const enrich = Math.ceil(farms.length / 10);
    console.log(`enriching locations: ${farms.length}/${10} = ${enrich} Farm`);
    let lastYield = performance.now();

    shuffle(farms);
    for (let i = 0; i < enrich; i++) {
      let farm = farms[i];
      if (performance.now() - lastYield > YIELD_INTERVAL_MS) {
        await yieldToEventLoop();
        lastYield = performance.now();
      }
      const family = createFamily({ generations: random([1, 2, 2, 3]) });
      farm.name = `${family.familyName} Family Farm`;
      farm.description.unshift(family.toRoster());
      farm.description.unshift(family.toFamilyTree());
      farm.description.unshift(`This farm is still inhabited by the ${family.familyName} family.`);
      farm.revision = (farm.revision ?? 0) + 1;
      enriched.add(farm.guid);
    }
  }
  return [...enriched];
}