diff --git a/procedural/.gitignore b/procedural/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/procedural/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/procedural/CLAUDE.md b/procedural/CLAUDE.md deleted file mode 100644 index b342a2bc..00000000 --- a/procedural/CLAUDE.md +++ /dev/null @@ -1,112 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Common Development Commands - -```bash -# Install dependencies -npm install - -# Development server (Vite) -npm run dev - -# Build for production -npm run build - -# Preview production build -npm run preview - -# Generate map via CLI (work in progress) -node cli.js --preset default --output map.json -node cli.js --config myconfig.json --seed myseed --output mymap.json -``` - -## High-Level Architecture - -This project is being ported from a tightly-coupled browser application to a headless procedural generation engine with separate presentation layer. - -### Project Structure - -``` -/procedural -├── src/ -│ ├── engine/ # Headless map generation engine -│ │ ├── main.js # Orchestrator - exports generate(config) function -│ │ ├── modules/ # Generation modules (biomes, cultures, rivers, etc.) -│ │ └── utils/ # Environment-agnostic utilities -│ │ -│ └── viewer/ # Web viewer application -│ ├── config-*.js # Configuration management -│ └── libs/ # Browser-specific libraries -│ -├── cli.js # Command-line interface -├── main.js # Viewer entry point (Vite) -└── index.html # Web app HTML -``` - -### Core Architecture Flow - -1. **Configuration** → `generate(config)` → **MapData** - - Config object defines all generation parameters (see `src/viewer/config-schema.md`) - - Engine is pure JavaScript with no browser dependencies - - Returns serializable MapData object - -2. **Module Pattern** - - Each module exports pure functions - - No global state manipulation - - Receives data, returns new data - - No IIFE wrappers or window dependencies - -### Key Modules - -- **Heightmap**: Generates terrain elevation -- **Features**: Marks geographic features (land, ocean, lakes) -- **Rivers**: Generates river systems -- **Biomes**: Assigns biomes based on climate -- **Cultures**: Places and expands cultures -- **BurgsAndStates**: Generates settlements and political entities -- **Routes**: Creates trade and travel routes - -## Configuration System - -Configuration drives the entire generation process. See `src/viewer/config-schema.md` for complete TypeScript interface. - -Key sections: -- `graph`: Canvas dimensions and cell count -- `heightmap`: Terrain template selection -- `cultures`: Number and type of cultures -- `burgs`: States and settlements -- `debug`: Logging flags (TIME, WARN, INFO) - -## Important Development Notes - -1. **Ongoing Port**: Moving from DOM/SVG manipulation to pure data generation per `PORT_PLAN.md` - -2. **Module Refactoring Pattern**: - ```javascript - // OLD: window.Module = (function() { ... })(); - // NEW: export function generateModule(data, config, utils) { ... } - ``` - -3. **No Browser Dependencies in Engine**: - - No `window`, `document`, or DOM access - - No direct SVG/D3 manipulation - - All rendering logic stays in viewer - -4. **Utility Organization**: - - Generic utilities in `src/engine/utils/` - - Specialized utilities imported as needed - - PRNG (Alea) for reproducible generation - -5. **Data Flow**: - - Grid (coarse Voronoi mesh) → Pack (refined mesh) - - Sequential module execution builds up complete map - - Each module adds its data to the growing structure - -## Testing Approach - -Currently no formal test suite. When adding tests: -- Focus on engine modules (pure functions) -- Test with known seeds for reproducibility -- Validate output data structures \ No newline at end of file diff --git a/procedural/TREE.md b/procedural/TREE.md deleted file mode 100644 index 7bfd67e9..00000000 --- a/procedural/TREE.md +++ /dev/null @@ -1,195 +0,0 @@ -procedural -├── .gitignore -├── NEXT_STEPS.md -├── PORT_PLAN.md -├── TREE.md -├── cli.js -├── index.html -├── main.js -├── package-lock.json -├── package.json -├── public -│ ├── assets -│ │ ├── charges -│ │ │ ├── agnusDei.svg -│ │ │ ├── anchor.svg -│ │ │ ├── angel.svg -│ │ │ ├── annulet.svg -. -. -. -│ │ │ ├── wolfHeadErased.svg -│ │ │ ├── wolfPassant.svg -│ │ │ ├── wolfRampant.svg -│ │ │ ├── wolfStatant.svg -│ │ │ ├── wyvern.svg -│ │ │ └── wyvernWithWingsDisplayed.svg -│ │ ├── heightmaps -│ │ │ ├── africa-centric.png -│ │ │ ├── arabia.png -│ │ │ ├── atlantics.png -│ │ │ ├── britain.png -│ │ │ ├── caribbean.png -│ │ │ ├── east-asia.png -│ │ │ ├── eurasia.png -│ │ │ ├── europe-accented.png -│ │ │ ├── europe-and-central-asia.png -│ │ │ ├── europe-central.png -│ │ │ ├── europe-north.png -│ │ │ ├── europe.png -│ │ │ ├── greenland.png -│ │ │ ├── hellenica.png -│ │ │ ├── iceland.png -│ │ │ ├── import-rules.txt -│ │ │ ├── indian-ocean.png -│ │ │ ├── mediterranean-sea.png -│ │ │ ├── middle-east.png -│ │ │ ├── north-america.png -│ │ │ ├── us-centric.png -│ │ │ ├── us-mainland.png -│ │ │ ├── world-from-pacific.png -│ │ │ └── world.png -│ │ └── images -│ │ ├── Discord.png -│ │ ├── Facebook.png -│ │ ├── Pinterest.png -│ │ ├── Reddit.png -│ │ ├── Twitter.png -│ │ ├── icons -│ │ │ ├── favicon-16x16.png -│ │ │ ├── favicon-32x32.png -│ │ │ ├── icon_x512.png -│ │ │ ├── maskable_icon_x128.png -│ │ │ ├── maskable_icon_x192.png -│ │ │ ├── maskable_icon_x384.png -│ │ │ └── maskable_icon_x512.png -│ │ ├── kiwiroo.png -│ │ ├── pattern1.png -│ │ ├── pattern2.png -│ │ ├── pattern3.png -│ │ ├── pattern4.png -│ │ ├── pattern5.png -│ │ ├── pattern6.png -│ │ ├── preview.png -│ │ └── textures -│ │ ├── antique-big.jpg -│ │ ├── antique-small.jpg -│ │ ├── folded-paper-big.jpg -│ │ ├── folded-paper-small.jpg -│ │ ├── gray-paper.jpg -│ │ ├── iran-small.jpg -│ │ ├── marble-big.jpg -│ │ ├── marble-blue-big.jpg -│ │ ├── marble-blue-small.jpg -│ │ ├── marble-small.jpg -│ │ ├── mars-big.jpg -│ │ ├── mars-small.jpg -│ │ ├── mauritania-small.jpg -│ │ ├── mercury-big.jpg -│ │ ├── mercury-small.jpg -│ │ ├── ocean.jpg -│ │ ├── pergamena-small.jpg -│ │ ├── plaster.jpg -│ │ ├── soiled-paper-vertical.png -│ │ ├── soiled-paper.jpg -│ │ ├── spain-small.jpg -│ │ ├── timbercut-big.jpg -│ │ └── timbercut-small.jpg -│ └── vite.svg -├── src -│ ├── default_prompt.md -│ ├── engine -│ │ ├── main.js -│ │ ├── modules -│ │ │ ├── biomes.js -│ │ │ ├── burgs-and-states.js -│ │ │ ├── coa-generator.js -│ │ │ ├── coa-renderer.js -│ │ │ ├── cultures-generator.js -│ │ │ ├── features.js -│ │ │ ├── fonts.js -│ │ │ ├── heightmap-generator.js -│ │ │ ├── lakes.js -│ │ │ ├── markers-generator.js -│ │ │ ├── military-generator.js -│ │ │ ├── names-generator.js -│ │ │ ├── ocean-layers.js -│ │ │ ├── provinces-generator.js -│ │ │ ├── religions-generator.js -│ │ │ ├── resample.js -│ │ │ ├── river-generator.js -│ │ │ ├── routes-generator.js -│ │ │ ├── submap.js -│ │ │ ├── voronoi.js -│ │ │ └── zones-generator.js -│ │ └── utils -│ │ ├── alea.js -│ │ ├── arrayUtils.js -│ │ ├── cell.js -│ │ ├── colorUtils.js -│ │ ├── commonUtils.js -│ │ ├── debugUtils.js -│ │ ├── flatqueue.js -│ │ ├── functionUtils.js -│ │ ├── geography.js -│ │ ├── graphUtils.js -│ │ ├── index.js -│ │ ├── languageUtils.js -│ │ ├── lineclip.js -│ │ ├── nodeUtils.js -│ │ ├── numberUtils.js -│ │ ├── pathUtils.js -│ │ ├── polyfills.js -│ │ ├── polylabel.js -│ │ ├── probabilityUtils.js -│ │ ├── simplify.js -│ │ ├── stringUtils.js -│ │ └── unitUtils.js -│ ├── libs -│ │ └── delaunator.min.js -│ └── viewer -│ ├── _config_data -│ │ ├── biomes_config.md -│ │ ├── burgs-and-states_config.md -│ │ ├── coa-generator_config.md -│ │ ├── coa-renderer_config.md -│ │ ├── cultures-generator_config.md -│ │ ├── features_config.md -│ │ ├── fonts_config.md -│ │ ├── heightmap-generator_config.md -│ │ ├── lakes_config.md -│ │ ├── markers-generator_config.md -│ │ ├── military-generator.js_config.md -│ │ ├── names-generator_config.md -│ │ ├── ocean-layers_config.md -│ │ ├── provinces-generator.js_config.md -│ │ ├── religions-generator_config.md -│ │ ├── resample_config.md -│ │ ├── river-generator_config.md -│ │ ├── routes-generator_config.md -│ │ ├── submap_config.md -│ │ ├── voronoi_config.md -│ │ └── zones_config.md -│ ├── config-builder.js -│ ├── config-integration.md -│ ├── config-presets.js -│ ├── config-schema.md -│ ├── config-validator.js -│ ├── libs -│ │ ├── dropbox-sdk.min.js -│ │ ├── indexedDB.js -│ │ ├── jquery-3.1.1.min.js -│ │ ├── jquery-ui.css -│ │ ├── jquery-ui.min.js -│ │ ├── jquery.ui.touch-punch.min.js -│ │ ├── jszip.min.js -│ │ ├── loopsubdivison.min.js -│ │ ├── objexporter.min.js -│ │ ├── openwidget.min.js -│ │ ├── orbitControls.min.js -│ │ ├── rgbquant.min.js -│ │ ├── shorthands.js -│ │ ├── three.min.js -│ │ └── umami.js -│ └── main.js -└── style.css diff --git a/procedural/cli.js b/procedural/cli.js deleted file mode 100644 index abf4aadd..00000000 --- a/procedural/cli.js +++ /dev/null @@ -1,42 +0,0 @@ -// cli.js -import { generate } from './src/engine/main.js'; -import { getPreset } from './src/viewer/config-presets.js'; -import { validateConfig, sanitizeConfig } from './src/viewer/config-validator.js'; -import fs from 'fs'; - -// Load config from file -function loadConfig(filepath) { - const jsonString = fs.readFileSync(filepath, 'utf8'); - return JSON.parse(jsonString); -} - -// Generate from CLI -async function generateFromCLI(options) { - let config; - - if (options.preset) { - config = getPreset(options.preset); - } else if (options.config) { - config = loadConfig(options.config); - } else { - config = getPreset('default'); - } - - // Override with CLI arguments - if (options.seed) config.seed = options.seed; - if (options.width) config.graph.width = parseInt(options.width); - if (options.height) config.graph.height = parseInt(options.height); - - // Validate and fix - const validation = validateConfig(config); - if (!validation.valid) { - console.warn('Config validation warnings:', validation.warnings); - config = sanitizeConfig(config); - } - - // Generate - const mapData = generate(config); - - // Save output - fs.writeFileSync(options.output || 'map.json', JSON.stringify(mapData)); -} diff --git a/procedural/docs/CALL_PATTERN.md b/procedural/docs/CALL_PATTERN.md deleted file mode 100644 index 7bab895c..00000000 --- a/procedural/docs/CALL_PATTERN.md +++ /dev/null @@ -1,218 +0,0 @@ -# Call Pattern Issues in Engine Refactoring - -## Overview - -During the refactoring of the Fantasy Map Generator from a browser-dependent application to a headless engine, we've encountered systematic issues with how modules are called and how they access their dependencies. This document catalogues these patterns and provides a systematic approach to identifying and fixing them. - -## The Problem - -The original codebase used global variables and browser-specific APIs. The refactored engine uses dependency injection, but there are mismatches between: - -1. **Function signatures** - What parameters functions expect -2. **Function calls** - What parameters are actually passed -3. **Data access patterns** - How modules access configuration and utilities - -## Common Anti-Patterns Found - -### 1. Config Nesting Mismatch - -**Problem**: Modules expect config properties at the root level, but they're nested under specific sections. - -**Example**: -```javascript -// ❌ Module expects: -config.culturesInput -config.culturesInSetNumber - -// ✅ But config actually has: -config.cultures.culturesInput -config.cultures.culturesInSetNumber -``` - -**Pattern**: `config.{property}` vs `config.{section}.{property}` - -### 2. Missing Config Parameter - -**Problem**: Modules expect full `config` object but are passed only a subsection. - -**Example**: -```javascript -// ❌ Incorrect call: -Biomes.define(pack, grid, config.biomes, Utils) - -// ✅ Correct call (module needs config.debug): -Biomes.define(pack, grid, config, Utils) -``` - -**Pattern**: Modules need `config.debug` but receive `config.{section}` - -### 3. Missing Module Dependencies - -**Problem**: Function signature doesn't include `modules` parameter but code tries to access module dependencies. - -**Example**: -```javascript -// ❌ Function signature: -function generate(pack, grid, config, utils) { - // Code tries to use Names module - utils.Names.getNameBases() // ❌ Names not in utils -} - -// ✅ Correct signature: -function generate(pack, grid, config, utils, modules) { - const { Names } = modules; - Names.getNameBases() // ✅ Correct access -} -``` - - -### 4. Missing Parameter Propagation - -**Problem**: Functions call other functions without passing required parameters. - -**Example**: -```javascript -// ❌ Missing parameters: -Lakes.defineClimateData(h) - -// ✅ Should pass all required params: -Lakes.defineClimateData(pack, grid, h, config, utils) -``` - - -### 5. Global Variable References - -**Problem**: Functions reference global variables that don't exist in headless environment. - -**Example**: -```javascript -// ❌ References undefined globals: -function clipPoly(points, secure = 0) { - return polygonclip(points, [0, 0, graphWidth, graphHeight], secure); - // ^^^^^^^^^^^ ^^^^^^^^^^^^ undefined -} - -// ✅ Get from config: -function clipPoly(points, config, secure = 0) { - const graphWidth = config.graph.width || 1000; - const graphHeight = config.graph.height || 1000; - return polygonclip(points, [0, 0, graphWidth, graphHeight], secure); -} -``` - - -### 6. Context-Aware Wrappers - -**Problem**: Utility functions expect parameters that aren't available in calling context. - -**Example**: -```javascript -// ❌ isLand expects pack but called without it: -neighbors[cellId].filter(isLand) - -// ✅ Create context-aware wrapper or pass explicitly: -neighbors[cellId].filter(i => isLand(i, pack)) -``` - - -## Systematic Detection Strategy - -### 1. Function Signature Analysis - -For each exported function, check: -```bash -# Find function exports -grep -n "export.*function\|export const.*=" src/engine/modules/*.js - -# Check what parameters they expect vs receive -``` - -### 2. Config Access Pattern Audit - -```bash -# Find config property access -grep -rn "config\." src/engine/modules/ | grep -v "config\.debug" - -# Check if properties exist in config structure -``` - -### 3. Module Dependency Check - -```bash -# Find modules object usage -grep -rn "modules\." src/engine/modules/ - -# Find utils object access to modules -grep -rn "utils\.[A-Z]" src/engine/modules/ -``` - -### 4. Global Reference Detection - -```bash -# Find potential global references -grep -rn "\b[A-Z_][A-Z_]*\b" src/engine/ | grep -v "import\|export\|const\|let\|var" -``` - -### 5. Function Call Parameter Mismatch - -```bash -# Find function calls and compare with signatures -grep -rn "\.generate\|\.define\|\.markup" src/engine/main.js -``` - -## Systematic Fix Pattern - -Document actual config structure from `config-builder.js` into CONFIG_STRUCTURE.md. - -Then take every file in `javascript_files_list.md` through these four steps. -### Step 1: Audit Function Signatures -1. List all exported functions in modules -2. Document expected parameters -3. Check all call sites -4. Identify mismatches - -### Step 2: Config Structure Mapping -1. Find all `config.{property}` accesses in modules -2. Map correct paths (`config.section.property`) -3. Ensure every single module/parameter/path related to config is resolved. - -### Step 3: Dependency Injection Fix -1. Ensure all functions receive required parameters -2. Add `modules` parameter where needed -3. Update all call sites to pass correct parameters - -### Step 4: Global Reference Elimination -1. Find all global variable references -2. Determine correct source (config, utils, passed parameters) -3. Update function signatures if needed - -For each file (log this detail into `javascript_files_list.md`): -- [ ] Every function signature matches all call sites -- [ ] All required parameters are passed -- [ ] Config properties accessed via correct path -- [ ] No global variable references -- [ ] Module dependencies properly injected -- [ ] Error handling for missing dependencies - -## Example Systematic Fix - -```javascript -// 1. Document current signature -function someModule(pack, config, utils) { ... } - -// 2. Document all call sites -someModule(pack, config.section, utils) // ❌ Wrong config -someModule(pack, grid, config, utils) // ❌ Missing grid param - -// 3. Determine correct signature -function someModule(pack, grid, config, utils, modules) { ... } - -// 4. Update all call sites -someModule(pack, grid, config, utils, modules) // ✅ Correct - -// 5. Update internal property access -// config.property → config.section.property -// utils.Module → modules.Module -``` - -This systematic approach will help identify and fix all parameter passing issues before they cause runtime errors. \ No newline at end of file diff --git a/procedural/docs/CONFIG_STRUCTURE.md b/procedural/docs/CONFIG_STRUCTURE.md deleted file mode 100644 index 685ff271..00000000 --- a/procedural/docs/CONFIG_STRUCTURE.md +++ /dev/null @@ -1,113 +0,0 @@ -# Config Structure Reference - -Based on `src/viewer/config-builder.js`, the config object has this nested structure: - -```javascript -config = { - seed: String, // Random seed for generation - - graph: { - width: Number, // Canvas width (default: 1920) - height: Number, // Canvas height (default: 1080) - cellsDesired: Number // Target number of cells (default: 10000) - }, - - map: { - coordinatesSize: Number, // Coordinate display size (default: 1) - latitude: Number // Map latitude (default: 0) - }, - - heightmap: { - templateId: String // Template ID (default: "continents") - }, - - temperature: { - heightExponent: Number, // Height effect on temp (default: 1.8) - temperatureScale: String, // "C" or "F" (default: "C") - temperatureBase: Number // Base temperature (default: 25) - }, - - precipitation: { - winds: Array, // Wind patterns (default: []) - moisture: Number // Moisture level (default: 1) - }, - - features: {}, // No UI config - biomes: {}, // No UI config - - lakes: { - lakeElevationLimit: Number, // Max lake elevation (default: 50) - heightExponent: Number // Height exponent (default: 2) - }, - - rivers: { - resolveDepressionsSteps: Number, // Depression resolution steps (default: 1000) - cellsCount: Number // Cell count for rivers - }, - - oceanLayers: { - outline: String // Ocean layer outline (default: "-1,-2,-3") - }, - - cultures: { - culturesInput: Number, // Number of cultures (default: 12) - culturesInSetNumber: Number, // Cultures in set (default: 15) - culturesSet: String, // Culture set name (default: "european") - sizeVariety: Number, // Size variety (default: 1) - neutralRate: Number, // Neutral expansion rate (default: 1) - emblemShape: String, // Emblem shape (default: "random") - emblemShapeGroup: String // Emblem shape group (default: "Diversiform") - }, - - burgs: { - statesNumber: Number, // Number of states (default: 15) - sizeVariety: Number, // Size variety (default: 1) - manorsInput: Number, // Number of manors (default: 1000) - growthRate: Number, // Burg growth rate (default: 1) - statesGrowthRate: Number // State growth rate (default: 1) - }, - - religions: { - religionsNumber: Number, // Number of religions (default: 5) - growthRate: Number // Religion growth rate (default: 1) - }, - - provinces: { - provincesRatio: Number // Provinces ratio (default: 50) - }, - - military: { - year: Number, // Year (default: 1400) - eraShort: String, // Short era name (default: "AD") - era: String // Full era name (default: "Anno Domini") - }, - - markers: { - culturesSet: String // Culture set for markers (default: "european") - }, - - zones: { - globalModifier: Number // Global zone modifier (default: 1) - }, - - debug: { - TIME: Boolean, // Time debugging (default: false) - WARN: Boolean, // Warning messages (default: true) - INFO: Boolean, // Info messages (default: false) - ERROR: Boolean // Error messages (default: true) - } -} -``` - -## Common Access Patterns - -### ❌ Wrong (flat access): -- `config.culturesInput` → Should be `config.cultures.culturesInput` -- `config.statesNumber` → Should be `config.burgs.statesNumber` -- `config.religionsNumber` → Should be `config.religions.religionsNumber` - -### ✅ Correct (nested access): -- `config.cultures.culturesInput` -- `config.burgs.statesNumber` -- `config.religions.religionsNumber` -- `config.debug.TIME` \ No newline at end of file diff --git a/procedural/docs/Data_Mismatch_Task_Activity.md b/procedural/docs/Data_Mismatch_Task_Activity.md deleted file mode 100644 index 58847ee7..00000000 --- a/procedural/docs/Data_Mismatch_Task_Activity.md +++ /dev/null @@ -1,242 +0,0 @@ -# Data Mismatch Task Activity Log - -This file logs all completed activities for fixing data mismatches in the Fantasy Map Generator. - -## Task 1: Add Property Checks to All Modules - -**Status**: Completed -**Date**: 2025-08-05 - -### Files Modified: - -#### 1. src/engine/modules/heightmap-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `graph` object and `graph.cells` structure -- Check for `config.heightmap.templateId` -- Check for `config.debug` section - -#### 2. src/engine/modules/lakes.js -**Changes made**: Added property validation checks at the start of the `detectCloseLakes` function -- Check for `pack.cells` and `pack.features` structures -- Check for `pack.cells.c` (neighbors) and `pack.cells.f` (features) -- Check for `heights` array - -#### 3. src/engine/modules/burgs-and-states.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.culture` from Cultures module -- Check for `pack.cells.s` (suitability) from Cell ranking -- Check for `pack.cultures` from Cultures module -- Check for `config.statesNumber` - -#### 4. src/engine/modules/cultures-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.s` (suitability) from Cell ranking -- Check for `config.culturesInput` and `config.culturesInSetNumber` -- Check for `config.debug` section - -#### 5. src/engine/modules/biomes.js -**Changes made**: Added property validation checks at the start of the `define` function -- Check for `pack.cells.h` (heights) from heightmap processing -- Check for `grid.cells.temp` and `grid.cells.prec` from geography module -- Check for `pack.cells.g` (grid reference) from pack generation -- Check for `config.debug` section - -#### 6. src/engine/modules/features.js -**Changes made**: Added property validation checks to two functions: -- `markupGrid` function: Check for `grid.cells.h` (heights), `grid.cells.c` (neighbors), and `config.debug` -- `markupPack` function: Check for `pack.cells.h` (heights), `pack.cells.c` (neighbors), and `grid.features` - -#### 7. src/engine/modules/river-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.h` (heights) from heightmap processing -- Check for `pack.cells.t` (distance field) from features module -- Check for `pack.features` from features module -- Check for `modules.Lakes` dependency -- Check for `config.debug` section - -#### 8. src/engine/modules/religions-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.culture` from Cultures module -- Check for `pack.cells.state` from BurgsAndStates module -- Check for `pack.cultures` from Cultures module -- Check for `config.religionsNumber` -- Check for `config.debug` section - -#### 9. src/engine/modules/provinces-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.state` from BurgsAndStates module -- Check for `pack.cells.burg` from BurgsAndStates module -- Check for `pack.states` from BurgsAndStates module -- Check for `pack.burgs` from BurgsAndStates module -- Check for `config.debug` section - -#### 10. src/engine/modules/routes-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.burg` from BurgsAndStates module -- Check for `pack.burgs` from BurgsAndStates module -- Check for `pack.cells.h` (heights) from heightmap processing -- Check for `pack.cells.t` (distance field) from features module - -#### 11. src/engine/modules/military-generator.js -**Changes made**: Added property validation checks at the start of the `generate` function -- Check for `pack.cells.state` from BurgsAndStates module -- Check for `pack.states` from BurgsAndStates module -- Check for `pack.burgs` from BurgsAndStates module -- Check for `config.debug` section - -### Summary: -Added property validation checks to 11 critical engine modules. Each module now validates required dependencies and configuration sections at startup, providing clear error messages when properties are missing. This implements Fix 1 from the Data_Mismatch_Tasks.md plan - adding simple property checks to fail fast with helpful error messages. - -All checks follow the pattern: -```javascript -// Check required properties exist -if (!requiredProperty) { - throw new Error("ModuleName requires requiredProperty from DependencyModule"); -} -``` - -This ensures clear dependency tracking and early error detection when modules are called with missing prerequisites. - -## Task 2: Update Config Validator for Missing Fields - -**Status**: Completed -**Date**: 2025-08-05 - -### Files Modified: - -#### 1. src/viewer/config-validator.js -**Changes made**: Added simple required fields validation as specified in the task - -**Functions added**: -- `validateRequiredFields(config, result)` - Validates specific required fields for modules -- `getCultureSetMax(culturesSet)` - Helper function to get maximum cultures for culture sets - -**Required fields validated**: -- `cultures.culturesInSetNumber` - Validates based on culturesSet maximum -- `rivers.cellsCount` - Validates against graph.cellsDesired or defaults to 10000 - -**Implementation**: Added simple check for missing fields with warnings that show what the default values would be. - -### Summary: -Updated the existing config validator to implement **Fix 2** from the Data_Mismatch_Tasks.md plan by adding the specific required fields validation as shown in the task example. The validator now checks for missing `cultures.culturesInSetNumber` and `rivers.cellsCount` fields and provides warnings when they are missing. - -## Task 3: Update Documentation with Property Timeline - -**Status**: Completed -**Date**: 2025-08-05 - -### Files Modified: - -#### 1. docs/FMG Data Model.md -**Changes made**: Added comprehensive Property Availability Timeline section as specified in the task - -**New section added**: "Property Availability Timeline" -- **Grid Properties section**: Documents when each grid property becomes available during generation -- **Pack Properties section**: Documents when each pack property becomes available during generation -- **Module Execution Flow**: Added mermaid flowchart diagram showing complete module execution sequence - -**Properties documented**: -- Grid properties: `cells.h`, `cells.f`, `cells.t`, `cells.temp`, `cells.prec` -- Pack properties: `cells.h`, `cells.f`, `cells.t`, `cells.fl`, `cells.r`, `cells.biome`, `cells.s`, `cells.pop`, `cells.culture`, `cells.burg`, `cells.state`, `cells.religion`, `cells.province` - -**Mermaid diagram**: Visual flowchart showing the complete generation pipeline from initial grid through all modules to final map data, with annotations showing what properties each module adds. - -### Summary: -Implemented **Fix 3** from the Data_Mismatch_Tasks.md plan by adding the Property Availability Timeline section to the existing documentation. This addresses the "Pack/Grid structure differences" issue by clearly documenting when each property becomes available during the generation process. - -The documentation now provides: -1. **Clear reference for developers** - Shows exactly when each property is available -2. **Module dependency tracking** - Visual flow shows which modules depend on others -3. **Pack vs Grid clarification** - Distinguishes between grid (coarse mesh) and pack (refined mesh) properties -4. **Complete generation pipeline** - Mermaid diagram shows the full execution flow from main.js - -This helps developers understand data availability and prevents undefined reference errors by showing the exact timeline of when properties are added to the data structures. - -## Task 4: Add Requirement Comments to All Modules - -**Status**: Completed -**Date**: 2025-08-05 - -### Files Modified: - -#### 1. src/engine/modules/heightmap-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: graph.cells, config.heightmap.templateId, config.debug -- **PROVIDES**: grid.cells.h (height values) - -#### 2. src/engine/modules/lakes.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `detectCloseLakes` function -- **REQUIRES**: pack.cells, pack.features, pack.cells.c, pack.cells.f, heights array -- **PROVIDES**: Updated pack.features with closed property - -#### 3. src/engine/modules/features.js -**Changes made**: Added JSDoc-style requirement comment blocks to both main functions -- **markupGrid REQUIRES**: grid.cells.h, grid.cells.c, config.debug -- **markupGrid PROVIDES**: grid.cells.f, grid.cells.t, grid.features -- **markupPack REQUIRES**: pack.cells.h, pack.cells.c, grid.features -- **markupPack PROVIDES**: pack.cells.f, pack.cells.t, pack.features - -#### 4. src/engine/modules/biomes.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `define` function -- **REQUIRES**: pack.cells.h, grid.cells.temp, grid.cells.prec, pack.cells.g, config.debug -- **PROVIDES**: pack.cells.biome - -#### 5. src/engine/modules/cultures-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.s, config.culturesInput, config.culturesInSetNumber -- **PROVIDES**: pack.cells.culture, pack.cultures - -#### 6. src/engine/modules/burgs-and-states.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.culture, pack.cells.s, pack.cultures, config.statesNumber -- **PROVIDES**: pack.burgs, pack.states, pack.cells.burg, pack.cells.state - -#### 7. src/engine/modules/river-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.h, pack.cells.t, pack.features, modules.Lakes, config.debug -- **PROVIDES**: pack.cells.fl, pack.cells.r, pack.cells.conf - -#### 8. src/engine/modules/religions-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.culture, pack.cells.state, pack.cultures, config.religionsNumber, config.debug -- **PROVIDES**: pack.cells.religion, pack.religions - -#### 9. src/engine/modules/provinces-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.state, pack.cells.burg, pack.states, pack.burgs, config.debug -- **PROVIDES**: pack.cells.province, pack.provinces - -#### 10. src/engine/modules/routes-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.burg, pack.burgs, pack.cells.h, pack.cells.t -- **PROVIDES**: pack.routes, pack.cells.routes - -#### 11. src/engine/modules/military-generator.js -**Changes made**: Added JSDoc-style requirement comment block at the top of the `generate` function -- **REQUIRES**: pack.cells.state, pack.states, pack.burgs, config.debug -- **PROVIDES**: pack.states[].military - -### Summary: -Implemented **Fix 4** from the Data_Mismatch_Tasks.md plan by adding requirement comments to all 11 major engine modules. Each module now has clear JSDoc-style documentation at the top of its main function showing: - -1. **Self-documenting modules** - Each module clearly states what it requires and provides -2. **No runtime overhead** - Comments are compile-time only and don't affect performance -3. **Clear for developers** - Easy to understand dependencies at a glance -4. **Dependency tracking** - Shows exact relationships between modules - -The comment format follows the task specification: -```javascript -/** - * Module description - * - * REQUIRES: - * - dependency1 (from source module) - * - dependency2 (from source module) - * - * PROVIDES: - * - output1 (what this module adds) - * - output2 (what this module adds) - */ -``` - -This addresses the "Module dependencies" issue by making all dependencies explicit and self-documenting, complementing the runtime property checks from Task 1. \ No newline at end of file diff --git a/procedural/docs/Data_Mismatch_Tasks.md b/procedural/docs/Data_Mismatch_Tasks.md deleted file mode 100644 index d10833d4..00000000 --- a/procedural/docs/Data_Mismatch_Tasks.md +++ /dev/null @@ -1,177 +0,0 @@ - -# Simplified Plan to Fix Data Mismatches in Fantasy Map Generator - -This document outlines a practical plan to address the four common data mismatches identified in the MAP_GENERATION_TRACE.md: - -1. **Modules expecting properties that don't exist yet** -2. **Config sections missing expected fields** -3. **Pack/Grid structure differences** -4. **Module dependencies** - -## Problem Analysis - -### 1. Modules Expecting Properties That Don't Exist Yet - -**Current Issue**: Modules access properties like `cells.culture` before the Cultures module has run, causing undefined references and potential crashes. - -**Root Cause**: Modules don't check if required properties exist before accessing them. - -### 2. Config Sections Missing Expected Fields - -**Current Issue**: Modules expect certain configuration fields that may not be present, even after validation. - -**Root Cause**: The existing config validator may not catch all missing fields that modules expect. - -### 3. Pack/Grid Structure Differences - -**Current Issue**: Pack is a refined version of grid, but modules sometimes confuse which structure they're working with. - -**Root Cause**: Similar naming and structure between pack and grid, but different levels of detail and available properties. - -### 4. Module Dependencies - -**Current Issue**: Some modules require data from other modules (e.g., Rivers needs Lakes, Cultures needs Names) but these dependencies aren't formally tracked. - -**Root Cause**: No explicit dependency management system; modules assume previous modules have run successfully. - -## Fix Plan - -### Fix 1: Add Simple Property Checks to Each Module - -Instead of complex wrappers or validators, add simple checks at the start of each module: - -**Example for burgs-and-states.js:** -```javascript -export const generate = (pack, grid, config, utils) => { - // Check required properties exist - if (!pack.cells.culture) { - throw new Error("BurgsAndStates module requires cells.culture from Cultures module"); - } - if (!pack.cells.s) { - throw new Error("BurgsAndStates module requires cells.s (suitability) from Cell ranking"); - } - - // Continue with existing code... -} -``` - -**Benefits:** -- Clear error messages -- No complex infrastructure -- Easy to add to existing modules -- Fail fast with helpful information - -### Fix 2: Enhance Existing Config Validator - -Update the existing `src/viewer/config-validator.js` to ensure all required fields are present: - -```javascript -// Add to existing validator -const requiredFields = { - 'cultures.culturesInSetNumber': (config) => { - // Ensure this field exists based on culturesSet - const maxCultures = getCultureSetMax(config.cultures.culturesSet); - return maxCultures; - }, - 'rivers.cellsCount': (config) => { - // Ensure this matches the actual cell count - return config.graph.cellsDesired || 10000; - } -}; -``` - -**Benefits:** -- Uses existing infrastructure -- No duplication -- Config is complete before engine runs - -### Fix 3: Document Property Timeline in Existing Docs - -Add a section to the existing `docs/FMG Data Model.md`: - -```markdown -## Property Availability Timeline - -Properties are added to grid/pack progressively: - -### Grid Properties (coarse mesh ~10K cells) -- `cells.h` - Available after: heightmap module -- `cells.t` - Available after: features module -- `cells.temp` - Available after: geography module -- `cells.prec` - Available after: geography module - -### Pack Properties (refined mesh) -- `cells.biome` - Available after: biomes module -- `cells.s` - Available after: cell ranking -- `cells.pop` - Available after: cell ranking -- `cells.culture` - Available after: cultures module -- `cells.state` - Available after: states module -- `cells.burg` - Available after: burgs module -- `cells.religion` - Available after: religions module -- `cells.province` - Available after: provinces module -``` - -+ Add mermaid flow diagram - -**Benefits:** -- Uses existing documentation -- Clear reference for developers -- No new files or folders - -### Fix 4: Add Module Requirements as Comments - -At the top of each module, clearly document what it requires: - -```javascript -// src/engine/modules/burgs-and-states.js -"use strict"; - -/** - * Generates burgs (settlements) and states (political entities) - * - * REQUIRES: - * - pack.cells.culture (from cultures module) - * - pack.cells.s (from cell ranking) - * - pack.cultures (from cultures module) - * - * PROVIDES: - * - pack.burgs - * - pack.states - * - pack.cells.burg - * - pack.cells.state - */ -export const generate = (pack, grid, config, utils) => { - // ... module code -} -``` - -**Benefits:** -- Self-documenting -- No runtime overhead -- Clear for developers - -### Optional: Debug Mode for Dependency Checking - -Add to `src/engine/main.js`: - -```javascript -// Only if debug flag is set -if (config.debug.CHECK_DEPENDENCIES) { - // Simple property existence checks before each module - if (!pack.cells.culture && moduleNeedsCulture) { - console.error("Missing required property: cells.culture"); - } -} -``` - -## Notes for tasks. - -Please log all completed activities in docs/Data_Mismatch_Task_Activity.md. -For each task, log the files you altered witrh the changes made in that file. - -## Implementation Steps - -1. **Task 1**: Add property checks to all modules -2. **Task 2**: Update config validator for missing fields -3. **Task 3**: Update documentation with property timeline, including a mermaid flow diagram -4. **Task 4**: Add requirement comments to all modules diff --git a/procedural/docs/FMG Data Model.md b/procedural/docs/FMG Data Model.md deleted file mode 100644 index b0e3d47f..00000000 --- a/procedural/docs/FMG Data Model.md +++ /dev/null @@ -1,366 +0,0 @@ -**FMG data model** is poorly defined, inconsistent and not documented in the codebase. This page is the first and the only attempt to document it. Once everything is documented, it can be used for building a new consistent model. - -FMG exposes all its data into the global namespace. The global namespace is getting polluted and it can cause conflicts with 3rd party extensions. Meanwhile it simplifies debugging and allows users to run custom JS code in dev console to alter the tool behavior. - -# Basic objects -FMG has two meta-objects storing most of the map data: -* `grid` contains map data before _repacking_ -* `pack` contains map data after _repacking_ - -Repacking is a process of amending an initial [voronoi diagram](https://en.wikipedia.org/wiki/Voronoi_diagram), that is based on a jittered square grid of points, into a voronoi diagram optimized for the current landmass (see [my old blog post](https://azgaar.wordpress.com/2017/10/05/templates) for the details). So the `pack` object is used for most of the data, but data optimized for square grid is available only via the `grid` object. - -## Voronoi data -Both `grid` and `pack` objects include data representing voronoi diagrams and their inner connections. Both initial and repacked voronoi can be build from the initial set of points, so this data is stored in memory only. It does not included into the .map file and getting calculated on map load. - -### Grid object -* `grid.cellsDesired`: `number` - initial count of cells/points requested for map creation. Used to define `spacing` and place points on a jittered square grid, hence the object name. Actual number of cells is defined by the number points able to place on a square grid. Default `cellsDesired` is 10 000, maximum - 100 000, minimal - 1 000 -* `grid.spacing`: `number` - spacing between points before jittering -* `grid.cellsY`: `number` - number of cells in column -* `grid.cellsX`: `number` - number of cells in row -* `grid.points`: `number[][]` - coordinates `[x, y]` based on jittered square grid. Numbers rounded to 2 decimals -* `grid.boundary`: `number[][]` - off-canvas points coordinates used to cut the diagram approximately by canvas edges. Integers -* `grid.cells`: `{}` - cells data object, including voronoi data: -* * `grid.cells.i`: `number[]` - cell indexes `Uint16Array` or `Uint32Array` (depending on cells number) -* * `grid.cells.c`: `number[][]` - indexes of cells adjacent to each cell (neighboring cells) -* * `grid.cells.v`: `number[][]` - indexes of vertices of each cell -* * `grid.cells.b`: `number[]` - indicates if cell borders map edge, 1 if `true`, 0 if `false`. Integers, not Boolean - -* `grid.vertices`: `{}` - vertices data object, contains only voronoi data: -* * `grid.vertices.p`: `number[][]` - vertices coordinates `[x, y]`, integers -* * `grid.vertices.c`: `number[][]` - indexes of cells adjacent to each vertex, each vertex has 3 adjacent cells -* * `grid.vertices.v`: `number[][]` - indexes of vertices adjacent to each vertex. Most vertices have 3 neighboring vertices, bordering vertices has only 2, while the third is still added to the data as `-1` - -### Pack object -* `pack.cells`: `{}` - cells data object, including voronoi data: -* * `pack.cells.i`: `number[]` - cell indexes `Uint16Array` or `Uint32Array` (depending on cells number) -* * `pack.cells.p`: `number[][]` - cells coordinates `[x, y]` after repacking. Numbers rounded to 2 decimals -* * `pack.cells.c`: `number[][]` - indexes of cells adjacent to each cell (neighboring cells) -* * `pack.cells.v`: `number[][]` - indexes of vertices of each cell -* * `pack.cells.b`: `number[]` - indicator whether the cell borders the map edge, 1 if `true`, 0 if `false`. Integers, not Boolean -* * `pack.cells.g`: `number[]` - indexes of a source cell in `grid`. `Uint16Array` or `Uint32Array`. The only way to find correct `grid` cell parent for `pack` cells - -* `pack.vertices`: `{}` - vertices data object, contains only voronoi data: -* * `pack.vertices.p`: `number[][]` - vertices coordinates `[x, y]`, integers -* * `pack.vertices.c`: `number[][]` - indexes of cells adjacent to each vertex, each vertex has 3 adjacent cells -* * `pack.vertices.v`: `number[][]` - indexes of vertices adjacent to each vertex. Most vertices have 3 neighboring vertices, bordering vertices has only 2, while the third is still added to the data as `-1` - -## Features data -Features represent separate locked areas like islands, lakes and oceans. - -### Grid object -* `grid.features`: `object[]` - array containing objects for all enclosed entities of original graph: islands, lakes and oceans. Feature object structure: -* * `i`: `number` - feature id starting from `1` -* * `land`: `boolean` - `true` if feature is land (height >= `20`) -* * `border`: `boolean` - `true` if feature touches map border (used to separate lakes from oceans) -* * `type`: `string` - feature type, can be `ocean`, `island` or `lake - -### Pack object -* `pack.features`: `object[]` - array containing objects for all enclosed entities of repacked graph: islands, lakes and oceans. Note: element 0 has no data. Stored in .map file. Feature object structure: -* * `i`: `number` - feature id starting from `1` -* * `land`: `boolean` - `true` if feature is land (height >= `20`) -* * `border`: `boolean` - `true` if feature touches map border (used to separate lakes from oceans) -* * `type`: `string` - feature type, can be `ocean`, `island` or `lake` -* * `group`: `string`: feature subtype, depends on type. Subtype for ocean is `ocean`; for land it is `continent`, `island`, `isle` or `lake_island`; for lake it is `freshwater`, `salt`, `dry`, `sinkhole` or `lava` -* * `cells`: `number` - number of cells in feature -* * `firstCell`: `number` - index of the first (top left) cell in feature -* * `vertices`: `number[]` - indexes of vertices around the feature (perimetric vertices) -** `name`: `string` - name, available for `lake` type only - -## Specific cells data -World data is mainly stored in typed arrays within `cells` object in both `grid` and `pack`. - -### Grid object -* `grid.cells.h`: `number[]` - cells elevation in `[0, 100]` range, where `20` is the minimal land elevation. `Uint8Array` -* `grid.cells.f`: `number[]` - indexes of feature. `Uint16Array` or `Uint32Array` (depending on cells number) -* `grid.cells.t`: `number[]` - [distance field](https://prideout.net/blog/distance_fields/) from water level. `1, 2, ...` - land cells, `-1, -2, ...` - water cells, `0` - unmarked cell. `Uint8Array` -* `grid.cells.temp`: `number[]` - cells temperature in Celsius. `Uint8Array` -* `grid.cells.prec`: `number[]` - cells precipitation in unspecified scale. `Uint8Array` - -### Pack object -* `pack.cells.h`: `number[]` - cells elevation in `[0, 100]` range, where `20` is the minimal land elevation. `Uint8Array` -* `pack.cells.f`: `number[]` - indexes of feature. `Uint16Array` or `Uint32Array` (depending on cells number) -* `pack.cells.t`: `number[]` - distance field. `1, 2, ...` - land cells, `-1, -2, ...` - water cells, `0` - unmarked cell. `Uint8Array` -* `pack.cells.s`: `number[]` - cells score. Scoring is used to define best cells to place a burg. `Uint16Array` -* `pack.cells.biome`: `number[]` - cells biome index. `Uint8Array` -* `pack.cells.burg`: `number[]` - cells burg index. `Uint16Array` -* `pack.cells.culture`: `number[]` - cells culture index. `Uint16Array` -* `pack.cells.state`: `number[]` - cells state index. `Uint16Array` -* `pack.cells.province`: `number[]` - cells province index. `Uint16Array` -* `pack.cells.religion`: `number[]` - cells religion index. `Uint16Array` -* `pack.cells.area`: `number[]` - cells area in pixels. `Uint16Array` -* `pack.cells.pop`: `number[]` - cells population in population points (1 point = 1000 people by default). `Float32Array`, not rounded to not lose population of high population rate -* `pack.cells.r`: `number[]` - cells river index. `Uint16Array` -* `pack.cells.fl`: `number[]` - cells flux amount. Defines how much water flow through the cell. Use to get rivers data and score cells. `Uint16Array` -* `pack.cells.conf`: `number[]` - cells flux amount in confluences. Confluences are cells where rivers meet each other. `Uint16Array` -* `pack.cells.harbor`: `number[]` - cells harbor score. Shows how many water cells are adjacent to the cell. Used for scoring. `Uint8Array` -* `pack.cells.haven`: `number[]` - cells haven cells index. Each coastal cell has haven cells defined for correct routes building. `Uint16Array` or `Uint32Array` (depending on cells number) -* `pack.cells.routes`: `object` - cells connections via routes. E.g. `pack.cells.routes[8] = {9: 306, 10: 306}` shows that cell `8` has two route connections - with cell `9` via route `306` and with cell `10` by route `306` -* `pack.cells.q`: `object` - quadtree used for fast closest cell detection - -# Secondary data -Secondary data available as a part of the `pack` object. - -## Cultures -Cultures (races, language zones) data is stored as an array of objects with strict element order. Element 0 is reserved by the _wildlands_ culture. If culture is removed, the element is not getting removed, but instead a `removed` attribute is added. Object structure: -* `i`: `number` - culture id, always equal to the array index -* `base`: `number` - _nameBase_ id, name base is used for names generation -* `name`: `string` - culture name -* `origins`: `number[]` - ids of origin cultures. Used to render cultures tree to show cultures evolution. The first array member is main link, other - supporting out-of-tree links -* `shield`: `string` - shield type. Used for emblems rendering -* `center`: `number` - cell id of culture center (initial cell) -* `code`: `string` - culture name abbreviation. Used to render cultures tree -* `color`: `string` - culture color in hex (e.g. `#45ff12`) or link to hatching pattern (e.g. `url(#hatch7)`) -* `expansionism`: `number` - culture growth multiplier. Used mainly during cultures generation to spread cultures not uniformly -* `type`: `string` - culture type, see [culture types](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Culture-types) -* `area`: `number` - culture area in pixels -* `cells`: `number` - number of cells assigned to culture -* `rural`: `number` - rural (non-burg) population of cells assigned to culture. In population points -* `urban`: `number` - urban (burg) population of cells assigned to culture. In population points -* `lock`: `boolean` - `true` if culture is locked (not affected by regeneration) -* `removed`: `boolean` - `true` if culture is removed - -## Burgs -Burgs (settlements) data is stored as an array of objects with strict element order. Element 0 is an empty object. If burg is removed, the element is not getting removed, but instead a `removed` attribute is added. Object structure: -* `i`: `number` - burg id, always equal to the array index -* `name`: `string` - burg name -* `cell`: `number` - burg cell id. One cell can have only one burg -* `x`: `number` - x axis coordinate, rounded to two decimals -* `y`: `number` - y axis coordinate, rounded to two decimals -* `culture`: `number` - burg culture id -* `state`: `number` - burg state id -* `feature`: `number` - burg feature id (id of a landmass) -* `population`: `number` - burg population in population points -* `type`: `string` - burg type, see [culture types](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Culture_types) -* `coa`: `object` - emblem object, data model is the same as in [Armoria](https://github.com/Azgaar/Armoria) and covered in [API documentation](https://github.com/Azgaar/armoria-api#readme). The only additional fields are optional `size`: `number`, `x`: `number` and `y`: `number` that controls the emblem position on the map (if it's not default). If emblem is loaded by user, then the value is `{ custom: true }` and cannot be displayed in Armoria -* `MFCG`: `number` - burg seed in [Medieval Fantasy City Generator](https://watabou.github.io/city-generator) (MFCG). If not provided, seed is combined from map seed and burg id -* `link`: `string` - custom link to burg in MFCG. `MFCG` seed is not used if link is provided -* `capital`: `number` - `1` if burg is a capital, `0` if not (each state has only 1 capital) -* `port`: `number` - if burg is not a port, then `0`, otherwise feature id of the water body the burg stands on -* `citadel`: `number` - `1` if burg has a castle, `0` if not. Used for MFCG -* `plaza`: `number` - `1` if burg has a marketplace, `0` if not. Used for MFCG -* `shanty`: `number` - `1` if burg has a shanty town, `0` if not. Used for MFCG -* `temple`: `number` - `1` if burg has a temple, `0` if not. Used for MFCG -* `walls`: `number` - `1` if burg has walls, `0` if not. Used for MFCG -* `lock`: `boolean` - `true` if burg is locked (not affected by regeneration) -* `removed`: `boolean` - `true` if burg is removed - -## States -States (countries) data is stored as an array of objects with strict element order. Element 0 is reserved for `neutrals`. If state is removed, the element is not getting removed, but instead a `removed` attribute is added. Object structure: -* `i`: `number` - state id, always equal to the array index -* `name`: `string` - short (proper) form of the state name -* `form`: `string` - state form type. Available types are `Monarchy`, `Republic`, `Theocracy`, `Union`, and `Anarchy` -* `formName`: `string` - string form name, used to get state `fullName` -* `fullName`: `string` - full state name. Combination of the proper name and state `formName` -* `color`: `string` - state color in hex (e.g. `#45ff12`) or link to hatching pattern (e.g. `url(#hatch7)`) -* `center`: `number` - cell id of state center (initial cell) -* `pole`: `number[]` - state pole of inaccessibility (visual center) coordinates, see [the concept description](https://blog.mapbox.com/a-new-algorithm-for-finding-a-visual-center-of-a-polygon-7c77e6492fbc?gi=6bd4fcb9ecc1) -* `culture`: `number` - state culture id (equals to initial cell culture) -* `type`: `string` - state type, see [culture types](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Culture types) -* `expansionism`: `number` - state growth multiplier. Used mainly during state generation to spread states not uniformly -* `area`: `number` - state area in pixels -* `burgs`: `number` - number of burgs within the state -* `cells`: `number` - number of cells within the state -* `rural`: `number` - rural (non-burg) population of state cells. In population points -* `urban`: `number` - urban (burg) population of state cells. In population points -* `neighbors`: `number[]` - ids of neighboring (bordering by land) states -* `provinces`: `number[]` - ids of state provinces -* `diplomacy`: `string[]` - diplomatic relations status for all states. 'x' for self and neutrals. Element 0 (neutrals) `diplomacy` is used differently and contains wars story as `string[][]` -* `campaigns`: `object[]` - wars the state participated in. The was is defined as `start`: `number` (year), `end`: `number` (year), `name`: `string` -* `alert`: `number` - state war alert, see [military forces page](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Military-Forces) -* `military`: `Regiment[]` - list of state regiments, see [military forces page](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Military-Forces) -* `coa`: `object` - emblem object, data model is the same as in [Armoria](https://github.com/Azgaar/Armoria) and covered in [API documentation](https://github.com/Azgaar/armoria-api#readme). The only additional fields are optional `size`: `number`, `x`: `number` and `y`: `number` that controls the emblem position on the map (if it's not default). If emblem is loaded by user, then the value is `{ custom: true }` and cannot be displayed in Armoria -* `lock`: `boolean` - `true` if state is locked (not affected by regeneration) -* `removed`: `boolean` - `true` if state is removed - -### Regiment -* `i`: `number` - regiment id, equals to the array index of regiment in the `state[x].military` array. Not unique, as unique string `regimentStateId-regimentId` is used -* `x`: `number` - regiment x coordinate -* `y`: `number` - regiment y coordinate -* `bx`: `number` - regiment base x coordinate -* `by`: `number` - regiment base y coordinate -* `angle`: `number` - regiment rotation angle degree -* `icon`: `number` - Unicode character to serve as an icon -* `cell`: `number` - original regiment cell id -* `state`: `number` - regiment state id -* `name`: `string` - regiment name -* `n`: `number` - `1` if regiment is a separate unit (like naval units), `0` is not -* `u`: `Record` - regiment content object - -## Provinces -Provinces data is stored as an array of objects with strict element order. Element 0 is not used. If religion is removed, the element is not getting removed, but instead a `removed` attribute is added. Object structure: -* `i`: `number` - province id, always equal to the array index -* `name`: `string` - short (proper) form of the province name -* `formName`: `string` - string form name, used to get province `fullName` -* `fullName`: `string` - full state name. Combination of the proper name and province `formName` -* `color`: `string` - province color in hex (e.g. `#45ff12`) or link to hatching pattern (e.g. `url(#hatch7)`) -* `center`: `number` - cell id of province center (initial cell) -* `pole`: `number[]` - province pole of inaccessibility (visual center) coordinates, see [the concept description](https://blog.mapbox.com/a-new-algorithm-for-finding-a-visual-center-of-a-polygon-7c77e6492fbc?gi=6bd4fcb9ecc1) -* `area`: `number` - province area in pixels -* `burg`: `number` - id of province capital burg if any -* `burgs`: `number[]` - id of burgs within the province. Optional (added when Province editor is opened) -* `cells`: `number` - number of cells within the province -* `rural`: `number` - rural (non-burg) population of province cells. In population points -* `urban`: `number` - urban (burg) population of state province. In population points -* `coa`: `object` - emblem object, data model is the same as in [Armoria](https://github.com/Azgaar/Armoria) and covered in [API documentation](https://github.com/Azgaar/armoria-api#readme). The only additional fields are optional `size`: `number`, `x`: `number` and `y`: `number` that controls the emblem position on the map (if it's not default). If emblem is loaded by user, then the value is `{ custom: true }` and cannot be displayed in Armoria -* `lock`: `boolean` - `true` if province is locked (not affected by regeneration) -* `removed`: `boolean` - `true` if province is removed - -## Religions -Religions data is stored as an array of objects with strict element order. Element 0 is reserved for "No religion". If province is removed, the element is not getting removed, but instead a `removed` attribute is added. Object structure: -* `i`: `number` - religion id, always equal to the array index -* `name`: `string` - religion name -* `type`: `string` - religion type. Available types are `Folk`, `Organized`, `Heresy` and `Cult` -* `form`: `string` - religion form -* `deity`: `string` - religion supreme deity if any -* `color`: `string` - religion color in hex (e.g. `#45ff12`) or link to hatching pattern (e.g. `url(#hatch7)`) -* `code`: `string` - religion name abbreviation. Used to render religions tree -* `origins`: `number[]` - ids of ancestor religions. `[0]` if religion doesn't have an ancestor. Used to render religions tree. The first array member is main link, other - supporting out-of-tree links -* `center`: `number` - cell id of religion center (initial cell) -* `culture`: `number` - religion original culture -* `expansionism`: `number` - religion growth multiplier. Used during religion generation to define competitive size -* `expansion`: `string` - religion expansion type. Can be `culture` so that religion grow only within its culture or `global` -* `area`: `number` - religion area in pixels -* `cells`: `number` - number of cells within the religion -* `rural`: `number` - rural (non-burg) population of religion cells. In population points -* `urban`: `number` - urban (burg) population of state religion. In population points -* `lock`: `boolean` - `true` if religion is locked (not affected by regeneration) -* `removed`: `boolean` - `true` if religion is removed - -## Rivers -Rivers data is stored as an unordered array of objects (so element id is _not_ the array index). Object structure: -* `i`: `number` - river id -* `name`: `string` - river name -* `type`: `string` - river type, used to get river full name only -* `source`: `number` - id of cell at river source -* `mouth`: `number` - id of cell at river mouth -* `parent`: `number` - parent river id. If river doesn't have a parent, the value is self id or `0` -* `basin`: `number` - river basin id. Basin id is a river system main stem id. If river doesn't have a parent, the value is self id -* `cells`: `number[]` - if of river points cells. Cells may not be unique. Cell value `-1` means the river flows off-canvas -* `points`: `number[][]` - river points coordinates. Auto-generated rivers don't have points stored and rely on `cells` for rendering -* `discharge`: `number` - river flux in m3/s -* `length`: `number` - river length in km -* `width`: `number` - river mouth width in km -* `sourceWidth`: `number` - additional width added to river source on rendering. Used to make lake outlets start with some width depending on flux. Can be also used to manually create channels - -## Markers -Markers data is stored as an unordered array of objects (so element id is _not_ the array index). Object structure: -* `i`: `number` - marker id. `'marker' + i` is used as svg element id and marker reference in `notes` object -* `icon`: `number` - Unicode character (usually an [emoji](https://emojipedia.org/)) to serve as an icon -* `x`: `number` - marker x coordinate -* `y`: `number` - marker y coordinate -* `cell`: `number` - cell id, used to prevent multiple markers generation in the same cell -* `type`: `string` - marker type. If set, style changes will be applied to all markers of the same type. Optional -* `size`: `number` - marker size in pixels. Optional, default value is `30` (30px) -* `fill`: `string` - marker pin fill color. Optional, default is `#fff` (white) -* `stroke`: `string` - marker pin stroke color. Optional, default is `#000` (black) -* `pin`: `string`: pin element type. Optional, default is `bubble`. Pin is not rendered if value is set to `no` -* `pinned`: `boolean`: if any marker is pinned, then only markers with `pinned = true` will be rendered. Optional -* `dx`: `number` - icon x shift percent. Optional, default is `50` (50%, center) -* `dy`: `number` - icon y shift percent. Optional, default s `50` (50%, center) -* `px`: `number` - icon font-size in pixels. Optional, default is `12` (12px) -* `lock`: `boolean` - `true` if marker is locked (not affected by regeneration). Optional - -## Routes -Routes data is stored as an unordered array of objects (so element id is _not_ the array index). Object structure: -* `i`: `number` - route id. Please note the element with id `0` is a fully valid route, not a placeholder -* `points`: `number[]` - array of control points in format `[x, y, cellId]` -* `feature`: `number` - feature id of the route. Auto-generated routes cannot be place on multiple features -* `group`: `string` - route group. Default groups are: 'roads', 'trails', 'searoutes' -* `length`: `number` - route length in km. Optional -* `name`: `string` - route name. Optional -* `lock`: `boolean` - `true` if route is locked (not affected by regeneration). Optional - -## Zones -Zones data is stored as an array of objects with `i` not necessary equal to the element index, but order of element defines the rendering order and is important. Object structure: -* `i`: `number` - zone id. Please note the element with id `0` is a fully valid zone, not a placeholder -* `name`: `string` - zone description -* `type`: `string` - zone type -* `color`: `string` - link to hatching pattern (e.g. `url(#hatch7)`) or color in hex (e.g. `#45ff12`) -* `cells`: `number[]` - array of zone cells -* `lock`: `boolean` - `true` if zone is locked (not affected by regeneration). Optional -* `hidden`: `boolean` - `true` if zone is hidden (not displayed). Optional - -# Secondary global data -Secondary data exposed to global space. - -## Biomes -Biomes data object is globally available as `biomesData`. It stores a few arrays, making it different from other data. Object structure: -* `i`: `number[]` - biome id -* `name`: `string[]` - biome names -* `color`: `string[]` - biome colors in hex (e.g. `#45ff12`) or link to hatching pattern (e.g. `url(#hatch7)`) -* `biomesMartix`: `number[][]` - 2d matrix used to define cell biome by temperature and moisture. Columns contain temperature data going from > `19` °C to < `-4` °C. Rows contain data for 5 moisture bands from the drier to the wettest one. Each row is a `Uint8Array` -* `cost`: `number[]` - biome movement cost, must be `0` or positive. Extensively used during cultures, states and religions growth phase. `0` means spread to this biome costs nothing. Max value is not defined, but `5000` is the actual max used by default -* `habitability`: `number[]` - biome habitability, must be `0` or positive. `0` means the biome is uninhabitable, max value is not defined, but `100` is the actual max used by default -* `icons`: `string[][]` - non-weighed array of icons for each biome. Used for _relief icons_ rendering. Not-weighed means that random icons from array is selected, so the same icons can be mentioned multiple times -* `iconsDensity`: `number[]` - defines how packed icons can be for the biome. An integer from `0` to `150` - -## Notes -Notes (legends) data is stored in unordered array of objects: `notes`. Object structure is as simple as: -* `i`: `string` - note id -* `name`: `string` - note name, visible in Legend box -* `legend`: `string` - note text in html - -## Name bases -Name generator consumes training sets of real-world town names (with the exception of fantasy name bases) stored in `nameBases` array, that is available globally. Each array element represent a separate base. Base structure is: -* `i`: `number` - base id, always equal to the array index -* `name`: `string` - names base proper name -* `b`: `string` - long string containing comma-separated list of names -* `min`: `number` - recommended minimal length of generated names. Generator will adding new syllables until min length is reached -* `max`: `number` - recommended maximal length of generated names. If max length is reached, generator will stop adding new syllables -* `d`: `string` - letters that are allowed to be duplicated in generated names -* `m`: `number` - if multi-word name is generated, how many of this cases should be transformed into a single word. `0` means multi-word names are not allowed, `1` - all generated multi-word names will stay as they are - -## Property Availability Timeline - -Properties are added to grid/pack progressively during map generation. Understanding when each property becomes available is crucial for module dependencies and avoiding undefined references. - -### Grid Properties (coarse mesh ~10K cells) -- `cells.h` - Available after: heightmap module -- `cells.f` - Available after: features markupGrid module -- `cells.t` - Available after: features markupGrid module -- `cells.temp` - Available after: geography temperature calculation -- `cells.prec` - Available after: geography precipitation generation - -### Pack Properties (refined mesh) -- `cells.h` - Available after: pack generation (reGraph utility) -- `cells.f` - Available after: features markupPack module -- `cells.t` - Available after: features markupPack module -- `cells.fl` - Available after: rivers module -- `cells.r` - Available after: rivers module -- `cells.biome` - Available after: biomes module -- `cells.s` - Available after: cell ranking utility -- `cells.pop` - Available after: cell ranking utility -- `cells.culture` - Available after: cultures module -- `cells.burg` - Available after: burgs-and-states module -- `cells.state` - Available after: burgs-and-states module -- `cells.religion` - Available after: religions module -- `cells.province` - Available after: provinces module - -### Module Execution Flow - -```mermaid -flowchart TD - A[Generate Grid] --> B[Heightmap Module] - B --> |adds cells.h| C[Features markupGrid] - C --> |adds cells.f, cells.t| D[Geography Temperature] - D --> |adds cells.temp| E[Geography Precipitation] - E --> |adds cells.prec| F[Pack Generation - reGraph] - F --> |refines cells.h| G[Features markupPack] - G --> |adds pack cells.f, cells.t| H[Rivers Module] - H --> |adds cells.fl, cells.r| I[Biomes Module] - I --> |adds cells.biome| J[Cell Ranking] - J --> |adds cells.s, cells.pop| K[Cultures Module] - K --> |adds cells.culture, pack.cultures| L[Cultures Expand] - L --> M[BurgsAndStates Module] - M --> |adds cells.burg, cells.state, pack.burgs, pack.states| N[Routes Module] - N --> O[Religions Module] - O --> |adds cells.religion, pack.religions| P[State Forms] - P --> Q[Provinces Module] - Q --> |adds cells.province, pack.provinces| R[Burg Features] - R --> S[Rivers Specify] - S --> T[Features Specify] - T --> U[Military Module] - U --> V[Markers Module] - V --> W[Zones Module] - W --> X[Complete Map Data] -``` diff --git a/procedural/docs/FUCKING_HELL_PLAN_AGENT_A.md b/procedural/docs/FUCKING_HELL_PLAN_AGENT_A.md deleted file mode 100644 index 42eb5f99..00000000 --- a/procedural/docs/FUCKING_HELL_PLAN_AGENT_A.md +++ /dev/null @@ -1,1383 +0,0 @@ -# FUCKING_HELL_PLAN_AGENT_A.md -# Complete Task Breakdown for Agent A - Critical Engine Files - -## Agent A Responsibility: 24 Critical Files -### Focus: Main engine files, critical modules, and core functionality - ---- - -## Task 1 - Agent_A -Read ./cli.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_01_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_01_ACTION.md: │ - │ # TASK_01_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/cli.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 2 - Agent_A -Read ./main.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_02_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_02_ACTION.md: │ - │ # TASK_02_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/main.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 3 - Agent_A -Read ./src/viewer/config-builder.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_03_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_03_ACTION.md: │ - │ # TASK_03_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/config-builder.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 4 - Agent_A -Read ./src/viewer/libs/shorthands.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_04_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_04_ACTION.md: │ - │ # TASK_04_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/libs/shorthands.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 5 - Agent_A -Read ./src/viewer/libs/umami.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_05_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_05_ACTION.md: │ - │ # TASK_05_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/libs/umami.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 6 - Agent_A -Read ./src/viewer/libs/indexedDB.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_06_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_06_ACTION.md: │ - │ # TASK_06_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/libs/indexedDB.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 7 - Agent_A -Read ./src/viewer/main.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_07_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_07_ACTION.md: │ - │ # TASK_07_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/main.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 8 - Agent_A -Read ./src/viewer/config-presets.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_08_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_08_ACTION.md: │ - │ # TASK_08_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/config-presets.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 9 - Agent_A -Read ./src/viewer/config-validator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_09_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_09_ACTION.md: │ - │ # TASK_09_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/viewer/config-validator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 10 - Agent_A - CRITICAL ENGINE MAIN -Read ./src/engine/main.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_10_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ CRITICAL PRIORITY: This file controls all module calls │ - │ - Fix ALL module parameter passing issues │ - │ - Ensure all config sections passed correctly │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_10_ACTION.md: │ - │ # TASK_10_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/main.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 11 - Agent_A - CRITICAL RUNTIME ERROR -Read ./src/engine/modules/cultures-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_11_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ CRITICAL PRIORITY: This file has runtime error │ - │ - Fix selectCultures function missing modules parameter │ - │ - Fix all config.cultures.* paths │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_11_ACTION.md: │ - │ # TASK_11_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/cultures-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 12 - Agent_A -Read ./src/engine/modules/burgs-and-states.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_12_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_12_ACTION.md: │ - │ # TASK_12_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/burgs-and-states.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 13 - Agent_A -Read ./src/engine/modules/features.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_13_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_13_ACTION.md: │ - │ # TASK_13_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/features.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 14 - Agent_A -Read ./src/engine/modules/river-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_14_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_14_ACTION.md: │ - │ # TASK_14_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/river-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 15 - Agent_A -Read ./src/engine/modules/religions-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_15_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_15_ACTION.md: │ - │ # TASK_15_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/religions-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 16 - Agent_A -Read ./src/engine/modules/provinces-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_16_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_16_ACTION.md: │ - │ # TASK_16_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/provinces-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 17 - Agent_A -Read ./src/engine/modules/routes-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_17_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_17_ACTION.md: │ - │ # TASK_17_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/routes-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 18 - Agent_A -Read ./src/engine/modules/markers-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_18_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_18_ACTION.md: │ - │ # TASK_18_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/markers-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 19 - Agent_A -Read ./src/engine/modules/military-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_19_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_19_ACTION.md: │ - │ # TASK_19_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/military-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 20 - Agent_A -Read ./src/engine/modules/coa-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_20_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_20_ACTION.md: │ - │ # TASK_20_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/coa-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 21 - Agent_A -Read ./src/engine/modules/lakes.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_21_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_21_ACTION.md: │ - │ # TASK_21_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/lakes.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 22 - Agent_A -Read ./src/engine/modules/heightmap-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_22_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_22_ACTION.md: │ - │ # TASK_22_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/heightmap-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 23 - Agent_A -Read ./src/engine/modules/biomes.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_23_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_23_ACTION.md: │ - │ # TASK_23_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/biomes.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 24 - Agent_A -Read ./src/engine/modules/zones-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_24_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_24_ACTION.md: │ - │ # TASK_24_ACTION.md │ - │ ## Agent: Agent_A │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/zones-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## AGENT A SUMMARY - -**Total Tasks: 24** -**Focus: Critical Engine Files** - -### Priority Order: -1. **Task 10**: ./src/engine/main.js (CRITICAL - controls all module calls) -2. **Task 11**: ./src/engine/modules/cultures-generator.js (CRITICAL - runtime error) -3. **Task 12**: ./src/engine/modules/burgs-and-states.js (CRITICAL - parameter order issues) -4. **Tasks 1-9**: Viewer and config files -5. **Tasks 13-24**: Remaining engine modules - -### Key Objectives: -- Fix ALL modules parameter passing issues -- Ensure ALL config paths follow CONFIG_STRUCTURE.md -- Eliminate ALL runtime errors -- NO FALSE CLAIMS - verify every change - -### Success Metrics: -- All 24 task action logs completed -- Zero "modules is not defined" errors -- All config paths using correct nested structure -- Complete verification for every file \ No newline at end of file diff --git a/procedural/docs/FUCKING_HELL_PLAN_AGENT_B.md b/procedural/docs/FUCKING_HELL_PLAN_AGENT_B.md deleted file mode 100644 index c4882bdf..00000000 --- a/procedural/docs/FUCKING_HELL_PLAN_AGENT_B.md +++ /dev/null @@ -1,1382 +0,0 @@ -# FUCKING_HELL_PLAN_AGENT_B.md -# Complete Task Breakdown for Agent B - Engine Modules + Utilities - -## Agent B Responsibility: 24 Files -### Focus: Remaining engine modules and critical utilities - ---- - -## Task 25 - Agent_B -Read ./src/engine/modules/names-generator.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_25_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_25_ACTION.md: │ - │ # TASK_25_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/names-generator.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 26 - Agent_B -Read ./src/engine/modules/coa-renderer.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_26_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_26_ACTION.md: │ - │ # TASK_26_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/coa-renderer.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 27 - Agent_B -Read ./src/engine/modules/fonts.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_27_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_27_ACTION.md: │ - │ # TASK_27_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/fonts.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 28 - Agent_B -Read ./src/engine/modules/submap.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_28_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_28_ACTION.md: │ - │ # TASK_28_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/submap.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 29 - Agent_B -Read ./src/engine/modules/ocean-layers.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_29_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_29_ACTION.md: │ - │ # TASK_29_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/ocean-layers.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 30 - Agent_B -Read ./src/engine/modules/voronoi.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_30_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_30_ACTION.md: │ - │ # TASK_30_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/voronoi.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 31 - Agent_B -Read ./src/engine/modules/resample.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_31_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_31_ACTION.md: │ - │ # TASK_31_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/modules/resample.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 32 - Agent_B - CRITICAL UTILITY -Read ./src/engine/utils/stringUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_32_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_32_ACTION.md: │ - │ # TASK_32_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/stringUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 33 - Agent_B - CRITICAL UTILITY -Read ./src/engine/utils/graph.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_33_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_33_ACTION.md: │ - │ # TASK_33_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/graph.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 34 - Agent_B -Read ./src/engine/utils/cell.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_34_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_34_ACTION.md: │ - │ # TASK_34_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/cell.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 35 - Agent_B - CRITICAL UTILITY -Read ./src/engine/utils/commonUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_35_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ CRITICAL PRIORITY: Browser compatibility issues │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_35_ACTION.md: │ - │ # TASK_35_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/commonUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 36 - Agent_B -Read ./src/engine/utils/colorUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_36_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_36_ACTION.md: │ - │ # TASK_36_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/colorUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 37 - Agent_B -Read ./src/engine/utils/polyfills.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_37_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_37_ACTION.md: │ - │ # TASK_37_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/polyfills.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 38 - Agent_B -Read ./src/engine/utils/pathUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_38_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_38_ACTION.md: │ - │ # TASK_38_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/pathUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 39 - Agent_B -Read ./src/engine/utils/simplify.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_39_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_39_ACTION.md: │ - │ # TASK_39_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/simplify.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 40 - Agent_B -Read ./src/engine/utils/unitUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_40_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_40_ACTION.md: │ - │ # TASK_40_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/unitUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 41 - Agent_B - CRITICAL UTILITY -Read ./src/engine/utils/index.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_41_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ CRITICAL PRIORITY: Central utility exports │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_41_ACTION.md: │ - │ # TASK_41_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/index.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 42 - Agent_B - CRITICAL UTILITY -Read ./src/engine/utils/geography.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_42_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ CRITICAL PRIORITY: Config structure violations │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_42_ACTION.md: │ - │ # TASK_42_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/geography.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 43 - Agent_B -Read ./src/engine/utils/functionUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_43_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_43_ACTION.md: │ - │ # TASK_43_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/functionUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 44 - Agent_B -Read ./src/engine/utils/debugUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_44_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_44_ACTION.md: │ - │ # TASK_44_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/debugUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 45 - Agent_B -Read ./src/engine/utils/arrayUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_45_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_45_ACTION.md: │ - │ # TASK_45_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/arrayUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 46 - Agent_B -Read ./src/engine/utils/nodeUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_46_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_46_ACTION.md: │ - │ # TASK_46_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/nodeUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 47 - Agent_B -Read ./src/engine/utils/graphUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_47_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_47_ACTION.md: │ - │ # TASK_47_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/graphUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 48 - Agent_B -Read ./src/engine/utils/numberUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_48_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_48_ACTION.md: │ - │ # TASK_48_ACTION.md │ - │ ## Agent: Agent_B │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/numberUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## AGENT B SUMMARY - -**Total Tasks: 24** -**Focus: Engine Modules + Critical Utilities** - -### Priority Order: -1. **Task 35**: ./src/engine/utils/commonUtils.js (CRITICAL - browser compatibility) -2. **Task 41**: ./src/engine/utils/index.js (CRITICAL - central exports) -3. **Task 42**: ./src/engine/utils/geography.js (CRITICAL - config structure) -4. **Task 32-33**: stringUtils.js, graph.js (CRITICAL utilities) -5. **Tasks 25-31**: Remaining engine modules -6. **Tasks 34-48**: Remaining utilities - -### Key Objectives: -- Fix ALL utility import/export issues -- Ensure ALL config paths follow CONFIG_STRUCTURE.md -- Resolve browser compatibility issues -- NO FALSE CLAIMS - verify every change - -### Success Metrics: -- All 24 task action logs completed -- All utilities properly structured -- All config paths using correct nested structure -- Complete verification for every file \ No newline at end of file diff --git a/procedural/docs/FUCKING_HELL_PLAN_AGENT_C.md b/procedural/docs/FUCKING_HELL_PLAN_AGENT_C.md deleted file mode 100644 index d9f58caf..00000000 --- a/procedural/docs/FUCKING_HELL_PLAN_AGENT_C.md +++ /dev/null @@ -1,683 +0,0 @@ -# FUCKING_HELL_PLAN_AGENT_C.md -# Complete Task Breakdown for Agent C - Remaining Files + Validation - -## Agent C Responsibility: 24 Tasks -### Focus: Remaining utilities + comprehensive validation + oversight - ---- - -## Task 49 - Agent_C -Read ./src/engine/utils/alea.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_49_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_49_ACTION.md: │ - │ # TASK_49_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/alea.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 50 - Agent_C -Read ./src/engine/utils/heightmap-templates.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_50_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_50_ACTION.md: │ - │ # TASK_50_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/heightmap-templates.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 51 - Agent_C -Read ./src/engine/utils/probabilityUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_51_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_51_ACTION.md: │ - │ # TASK_51_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/probabilityUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 52 - Agent_C -Read ./src/engine/utils/languageUtils.js - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read the actual file to understand current state │ - │ 2. Reference CONFIG_STRUCTURE.md for all config path │ - │ validation │ - │ 3. Document ALL issues found in TASK_52_ACTION.md │ - │ 4. Make actual fixes (not false claims) │ - │ 5. Verify changes by re-reading file │ - │ 6. Test for syntax/import errors │ - │ 7. NO FALSE CLAIMS - only mark as fixed after verification │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_52_ACTION.md: │ - │ # TASK_52_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## File: /Users/barrulus/Fantasy-Map-Generator/procedural/src/engine/utils/languageUtils.js │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Pre-Investigation Status: │ - │ [Current status from javascript_files_list.md] │ - │ │ - │ ### Issues Actually Found: │ - │ [Real issues discovered by reading file] │ - │ │ - │ ### Config Structure Violations: │ - │ [Violations against CONFIG_STRUCTURE.md] │ - │ │ - │ ### Modules Parameter Issues: │ - │ [Missing/incorrect modules parameters] │ - │ │ - │ ### Function Signature Mismatches: │ - │ [Signature vs call site issues] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of every change made] │ - │ │ - │ ### Verification Performed: │ - │ [Proof that changes were applied] │ - │ │ - │ ### Final Status: │ - │ [COMPLETE/PARTIAL/FAILED with reasons] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 53 - Agent_C - VALIDATION OVERSIGHT -Verify Agent_A Task Completion and Accuracy - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read ALL Agent_A task action logs (TASK_01 through 24) │ - │ 2. Verify each claimed fix by reading the actual files │ - │ 3. Document ALL discrepancies found in TASK_53_ACTION.md │ - │ 4. Flag any FALSE CLAIMS made by Agent_A │ - │ 5. Create summary of Agent_A's actual completion status │ - │ 6. Test critical files for remaining runtime errors │ - │ 7. NO FALSE CLAIMS - only mark as verified after checking │ - │ │ - │ CRITICAL OVERSIGHT: Agent_A handles most critical files │ - │ - Verify cultures-generator.js runtime error actually fixed │ - │ - Verify main.js module calls actually corrected │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_53_ACTION.md: │ - │ # TASK_53_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Agent_A Validation Oversight │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Agent_A Files Reviewed: │ - │ [List of all 24 files Agent_A was responsible for] │ - │ │ - │ ### Verification Results: │ - │ [File-by-file validation of claimed fixes] │ - │ │ - │ ### False Claims Identified: │ - │ [Any discrepancies between claims and actual state] │ - │ │ - │ ### Remaining Issues Found: │ - │ [Issues that Agent_A missed or didn't actually fix] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of validation performed] │ - │ │ - │ ### Final Status: │ - │ [AGENT_A_VERIFIED/AGENT_A_HAS_ISSUES with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 54 - Agent_C - VALIDATION OVERSIGHT -Verify Agent_B Task Completion and Accuracy - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read ALL Agent_B task action logs (TASK_25 through 48) │ - │ 2. Verify each claimed fix by reading the actual files │ - │ 3. Document ALL discrepancies found in TASK_54_ACTION.md │ - │ 4. Flag any FALSE CLAIMS made by Agent_B │ - │ 5. Create summary of Agent_B's actual completion status │ - │ 6. Test utility imports for remaining errors │ - │ 7. NO FALSE CLAIMS - only mark as verified after checking │ - │ │ - │ CRITICAL OVERSIGHT: Agent_B handles critical utilities │ - │ - Verify commonUtils.js browser issues actually fixed │ - │ - Verify geography.js config paths actually corrected │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_54_ACTION.md: │ - │ # TASK_54_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Agent_B Validation Oversight │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Agent_B Files Reviewed: │ - │ [List of all 24 files Agent_B was responsible for] │ - │ │ - │ ### Verification Results: │ - │ [File-by-file validation of claimed fixes] │ - │ │ - │ ### False Claims Identified: │ - │ [Any discrepancies between claims and actual state] │ - │ │ - │ ### Remaining Issues Found: │ - │ [Issues that Agent_B missed or didn't actually fix] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of validation performed] │ - │ │ - │ ### Final Status: │ - │ [AGENT_B_VERIFIED/AGENT_B_HAS_ISSUES with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 55 - Agent_C - CONFIG STRUCTURE AUDIT -Complete CONFIG_STRUCTURE.md Compliance Validation - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read CONFIG_STRUCTURE.md to understand correct structure │ - │ 2. Systematically check ALL 72 files for config violations │ - │ 3. Document ALL violations found in TASK_55_ACTION.md │ - │ 4. Create comprehensive list of all config path issues │ - │ 5. Verify ALL fixes claimed by Agent_A and Agent_B │ - │ 6. Test config structure compliance end-to-end │ - │ 7. NO FALSE CLAIMS - complete systematic validation │ - │ │ - │ CRITICAL VALIDATION: Config structure compliance │ - │ - Check for config.culturesInput vs config.cultures.culturesInput │ - │ - Check for config.TIME vs config.debug.TIME │ - │ - Check for config.graphWidth vs config.graph.width │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_55_ACTION.md: │ - │ # TASK_55_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Complete CONFIG_STRUCTURE.md Compliance Validation │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Config Structure Violations Found: │ - │ [Complete list of all violations across all files] │ - │ │ - │ ### Files with Violations: │ - │ [File-by-file breakdown of config issues] │ - │ │ - │ ### Agent Fix Claims Validation: │ - │ [Verification of Agent_A and Agent_B config fix claims] │ - │ │ - │ ### Remaining Violations: │ - │ [Config violations that still need to be fixed] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of config validation performed] │ - │ │ - │ ### Final Status: │ - │ [CONFIG_COMPLIANT/CONFIG_HAS_VIOLATIONS with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 56 - Agent_C - MODULES PARAMETER AUDIT -Complete Modules Parameter Implementation Validation - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read ALL 72 files to check modules parameter usage │ - │ 2. Find ALL functions referencing modules without parameter │ - │ 3. Document ALL issues found in TASK_56_ACTION.md │ - │ 4. Verify ALL modules dependency injection is correct │ - │ 5. Test for "modules is not defined" runtime errors │ - │ 6. Cross-reference with Agent_A and Agent_B claims │ - │ 7. NO FALSE CLAIMS - complete systematic validation │ - │ │ - │ CRITICAL VALIDATION: Modules parameter consistency │ - │ - Find functions accessing modules.Names without parameter │ - │ - Find functions accessing modules.Lakes without parameter │ - │ - Find missing modules in function signatures │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_56_ACTION.md: │ - │ # TASK_56_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Complete Modules Parameter Implementation Validation │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Modules Parameter Issues Found: │ - │ [Complete list of all modules parameter violations] │ - │ │ - │ ### Functions Missing Modules Parameter: │ - │ [Function-by-function breakdown of missing parameters] │ - │ │ - │ ### Agent Fix Claims Validation: │ - │ [Verification of Agent_A and Agent_B modules fix claims] │ - │ │ - │ ### Remaining Issues: │ - │ [Modules parameter issues that still need to be fixed] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of modules validation performed] │ - │ │ - │ ### Final Status: │ - │ [MODULES_COMPLIANT/MODULES_HAS_ISSUES with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 57 - Agent_C - RUNTIME ERROR TESTING -Complete End-to-End Runtime Error Validation - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Test the application for ALL runtime errors │ - │ 2. Check browser console for ANY remaining errors │ - │ 3. Document ALL issues found in TASK_57_ACTION.md │ - │ 4. Test critical code paths for function signature errors │ - │ 5. Verify NO "modules is not defined" errors remain │ - │ 6. Test config path access for any violations │ - │ 7. NO FALSE CLAIMS - actual runtime testing required │ - │ │ - │ CRITICAL TESTING: End-to-end runtime validation │ - │ - Test cultures-generator.js execution │ - │ - Test main.js module calls │ - │ - Test all utility imports │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_57_ACTION.md: │ - │ # TASK_57_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Complete End-to-End Runtime Error Validation │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Runtime Errors Found: │ - │ [Complete list of all runtime errors discovered] │ - │ │ - │ ### Browser Console Output: │ - │ [Actual browser console output during testing] │ - │ │ - │ ### Critical Path Testing Results: │ - │ [Results of testing critical code paths] │ - │ │ - │ ### Remaining Issues: │ - │ [Runtime errors that still need to be fixed] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of runtime testing performed] │ - │ │ - │ ### Final Status: │ - │ [RUNTIME_CLEAN/RUNTIME_HAS_ERRORS with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 58 - Agent_C - DOCUMENTATION VALIDATION -Validate javascript_files_list.md Accuracy - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Read current javascript_files_list.md status │ - │ 2. Compare claimed status with actual file state │ - │ 3. Document ALL discrepancies in TASK_58_ACTION.md │ - │ 4. Update javascript_files_list.md with ACTUAL status │ - │ 5. Remove ALL false claims from documentation │ - │ 6. Create accurate final status for each file │ - │ 7. NO FALSE CLAIMS - only accurate status reporting │ - │ │ - │ CRITICAL DOCUMENTATION: Accurate status reporting │ - │ - Remove false ✅ marks for unfixed files │ - │ - Update with actual issues found and fixed │ - │ - Create honest assessment of work completed │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_58_ACTION.md: │ - │ # TASK_58_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Validate javascript_files_list.md Accuracy │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### False Claims Identified: │ - │ [List of inaccurate status claims in current documentation] │ - │ │ - │ ### Actual File Status: │ - │ [File-by-file accurate status assessment] │ - │ │ - │ ### Documentation Updates Made: │ - │ [Changes made to javascript_files_list.md] │ - │ │ - │ ### Final Accurate Status: │ - │ [Honest assessment of actual completion state] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of documentation validation performed] │ - │ │ - │ ### Final Status: │ - │ [DOCUMENTATION_ACCURATE/DOCUMENTATION_UPDATED] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 59 - Agent_C - FINAL COMPREHENSIVE REPORT -Generate Complete Project Status Report - ┌──────────────────────────────────────────────────────────────┐ - │ 1. Compile ALL findings from all agents and validation │ - │ 2. Create comprehensive status report │ - │ 3. Document ALL issues found in TASK_59_ACTION.md │ - │ 4. List ALL remaining work needed │ - │ 5. Provide honest assessment of completion level │ - │ 6. Create actionable next steps │ - │ 7. NO FALSE CLAIMS - complete transparency │ - │ │ - │ CRITICAL REPORTING: Comprehensive final assessment │ - │ - Total files processed vs claimed │ - │ - Total issues found and fixed vs claimed │ - │ - Remaining runtime errors and their fixes │ - │ │ - │ Action Logging Template │ - │ │ - │ Every task logs to TASK_59_ACTION.md: │ - │ # TASK_59_ACTION.md │ - │ ## Agent: Agent_C │ - │ ## Task: Generate Complete Project Status Report │ - │ ## Config Reference: CONFIG_STRUCTURE.md validation │ - │ │ - │ ### Project Completion Summary: │ - │ [High-level summary of actual work completed] │ - │ │ - │ ### Files Successfully Processed: │ - │ [List of files that were actually fixed and verified] │ - │ │ - │ ### Remaining Issues: │ - │ [Complete list of issues that still need resolution] │ - │ │ - │ ### Agent Performance Assessment: │ - │ [Evaluation of Agent_A and Agent_B work quality] │ - │ │ - │ ### Next Steps Required: │ - │ [Actionable steps to complete remaining work] │ - │ │ - │ ### Actions Actually Taken: │ - │ [Detailed log of comprehensive assessment] │ - │ │ - │ ### Final Status: │ - │ [PROJECT_COMPLETE/PROJECT_NEEDS_MORE_WORK with details] │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## Task 60-72 - Agent_C - PLACEHOLDER TASKS -Additional Validation and Follow-up Tasks - ┌──────────────────────────────────────────────────────────────┐ - │ Tasks 60-72 are reserved for additional validation work: │ - │ │ - │ - Task 60: Re-validate critical runtime fixes │ - │ - Task 61: Cross-check Agent_A and Agent_B work │ - │ - Task 62: Additional config structure validation │ - │ - Task 63: Additional modules parameter validation │ - │ - Task 64: Browser compatibility final check │ - │ - Task 65: Import/export consistency validation │ - │ - Task 66: Function signature final validation │ - │ - Task 67: Create final fix summary │ - │ - Task 68: Update all documentation with accurate status │ - │ - Task 69: Create final testing protocol │ - │ - Task 70: Validate all changes are persistent │ - │ - Task 71: Create final handoff documentation │ - │ - Task 72: Final comprehensive verification │ - │ │ - │ Each task follows the same rigorous template as above │ - │ NO FALSE CLAIMS - complete verification for all tasks │ - │ │ - │ Success Criteria │ - │ │ - │ - ZERO runtime errors │ - │ - ALL 72 files systematically investigated │ - │ - ALL config paths comply with CONFIG_STRUCTURE.md │ - │ - ALL modules parameters properly implemented │ - │ - COMPLETE action logs for every file │ - │ - NO FALSE CLAIMS in documentation │ - └──────────────────────────────────────────────────────────────┘ - ---- - -## AGENT C SUMMARY - -**Total Tasks: 24 (Tasks 49-72)** -**Focus: Remaining Files + Comprehensive Validation + Oversight** - -### Priority Order: -1. **Tasks 49-52**: Remaining utility files -2. **Task 53**: Agent_A validation oversight (CRITICAL) -3. **Task 54**: Agent_B validation oversight (CRITICAL) -4. **Task 55**: CONFIG_STRUCTURE.md compliance audit (CRITICAL) -5. **Task 56**: Modules parameter implementation audit (CRITICAL) -6. **Task 57**: End-to-end runtime error testing (CRITICAL) -7. **Task 58**: Documentation accuracy validation (CRITICAL) -8. **Task 59**: Final comprehensive report (CRITICAL) -9. **Tasks 60-72**: Additional validation and follow-up tasks - -### Key Objectives: -- **ZERO TOLERANCE for false claims** -- Validate ALL work done by Agent_A and Agent_B -- Ensure complete CONFIG_STRUCTURE.md compliance -- Ensure complete modules parameter implementation -- Provide comprehensive project oversight -- Create accurate final documentation - -### Success Metrics: -- All 24 task action logs completed -- Complete validation of Agent_A's 24 tasks -- Complete validation of Agent_B's 24 tasks -- Zero runtime errors in final application -- Accurate documentation with no false claims -- Comprehensive final project status report - -### Critical Validation Focus: -- **Agent_A Critical Files**: cultures-generator.js, main.js, burgs-and-states.js -- **Agent_B Critical Files**: commonUtils.js, geography.js, index.js -- **CONFIG_STRUCTURE.md**: Complete compliance across all files -- **Modules Parameters**: Complete implementation across all files -- **Runtime Testing**: End-to-end validation of all fixes \ No newline at end of file diff --git a/procedural/docs/HEIGHTMAP_ASSESSMENT.md b/procedural/docs/HEIGHTMAP_ASSESSMENT.md deleted file mode 100644 index 1588ee7f..00000000 --- a/procedural/docs/HEIGHTMAP_ASSESSMENT.md +++ /dev/null @@ -1,226 +0,0 @@ -# Heightmap Generator Assessment - -## Executive Summary - -The heightmap generator module has undergone significant refactoring during the port from the browser-based version to the headless engine. While the core logic remains intact, several critical deviations have been introduced that impact functionality, architecture consistency, and maintainability. Most critically, the naming convention has become inconsistent and the state management pattern has been fundamentally altered. - -## Key Deviations Identified - -### 1. **Critical Naming Convention Violations** - -**Original Pattern (../modules/heightmap-generator.js):** -- **Input parameter:** `graph` (lines 9, 24, 40, 64, 70) - represents the incoming data structure -- **Internal variable:** `grid` (line 4, 14, 21) - closure variable storing the graph after `setGraph(graph)` call -- **Usage pattern:** Functions receive `graph`, call `setGraph(graph)` to store as `grid`, then use `grid` throughout - -**Current Broken Pattern (src/engine/modules/heightmap-generator.js):** -- **Inconsistent parameter naming:** Mix of `grid` and `graph` parameters -- **Line 178:** `addPit(heights, graph, ...)` - should be `grid` like other functions -- **Line 254, 255:** `findGridCell(startX, startY, graph)` - uses undefined `graph` variable -- **Line 347:** `findGridCell(startX, startY, graph)` - uses undefined `graph` variable -- **Line 444, 445:** `findGridCell(startX, startY, graph)` - uses undefined `graph` variable - -**CRITICAL BUG:** These functions will fail at runtime because `graph` is undefined in their scope. - -### 2. **setGraph/setGrid State Management Deviation** - -**Original setGraph Pattern:** -```javascript -const setGraph = graph => { - const {cellsDesired, cells, points} = graph; - heights = cells.h ? Uint8Array.from(cells.h) : createTypedArray({maxValue: 100, length: points.length}); - blobPower = getBlobPower(cellsDesired); - linePower = getLinePower(cellsDesired); - grid = graph; // Store graph as grid for internal use -}; -``` - -**Current setGrid Pattern:** -```javascript -function setGrid(grid, utils) { - const { createTypedArray } = utils; - const { cellsDesired, cells, points } = grid; - const heights = cells.h ? Uint8Array.from(cells.h) : createTypedArray({ maxValue: 100, length: points.length }); - const blobPower = getBlobPower(cellsDesired); - const linePower = getLinePower(cellsDesired); - return { heights, blobPower, linePower }; // Returns computed values instead of storing state -} -``` - -**Critical Differences:** -1. **State Storage:** Original stores state in closure, current returns computed values -2. **Naming:** Original uses `graph` parameter name, current uses `grid` -3. **Function Name:** `setGraph` vs `setGrid` - breaks the original naming logic -4. **Return Pattern:** Original modifies closure state, current returns data for functional approach - -### 3. **Architectural Pattern Shift Analysis** - -**Original Closure-Based State Management:** -- State variables (`grid`, `heights`, `blobPower`, `linePower`) live in module closure -- `setGraph(graph)` initializes state once per generation cycle -- Helper functions access closure state directly (no parameters needed) -- `clearData()` cleans up state after generation - -**Current Pure Functional Approach:** -- No persistent state - everything passed as parameters -- Each function receives `(heights, grid, blobPower, config, utils, ...args)` -- `setGrid(grid, utils)` computes values and returns them (no state storage) -- Each helper function creates new arrays and returns modified results - -**Impact Analysis:** -- **Positive:** True functional purity enables better testing and no side effects -- **Negative:** Massive parameter bloat (8+ parameters per function vs 0 in original) -- **Performance:** Multiple array allocations vs single state initialization - -### 4. **Parameter Propagation Problems** - -**Missing Parameters:** -- Line 90-92: `modify()` function call missing `power` parameter that's used in implementation -- Line 92: `modify(heights, a3, +a2, 1, utils)` - missing `power` but function expects it - -**Wrong Parameter Order:** -- Functions expect `(heights, grid, ...)` but some calls pass different structures -- Type mismatches between expected `grid` object and passed `graph` references - -### 5. **Return Value Handling Issues** - -**Critical Deviation:** -- Original functions modified global `heights` array in place -- Current functions create new `Uint8Array(heights)` copies but don't always maintain referential consistency -- This could lead to performance issues and memory overhead - -### 6. **Utility Dependencies** - -**Incomplete Migration:** -- Line 51: `fromPrecreated` function is completely stubbed out -- Missing critical browser-to-headless migration for image processing -- DOM dependencies (`document.createElement`, `canvas`, `Image`) not replaced - -## Specific Runtime Failures - -### Bug 1: Undefined Variable References (CRITICAL) -```javascript -// Line 178 - Function parameter name -export function addPit(heights, graph, blobPower, config, utils, count, height, rangeX, rangeY) { -// Line 199 - Internal usage tries to access 'grid' (UNDEFINED) -start = findGridCell(x, y, grid); // ReferenceError: grid is not defined -``` - -### Bug 2: Parameter/Variable Mismatch Pattern -**Broken Functions:** -- `addPit` (line 178): parameter `graph`, usage `grid` (line 199, 209) -- `addRange` (line 221): parameter `grid`, but calls `findGridCell(x, y, graph)` (lines 254-255) -- `addTrough` (line 320): parameter `grid`, but calls `findGridCell(x, y, graph)` (lines 347, 359) -- `addStrait` (line 425): parameter `grid`, but calls `findGridCell(x, y, graph)` (lines 444-445) - -### Bug 3: Missing Parameter in Function Calls -```javascript -// Line 90 - Call site -if (tool === "Add") return modify(heights, a3, +a2, 1, utils); - -// Line 490 - Function signature expects 6 parameters, gets 5 -export function modify(heights, range, add, mult, power, utils) { -// ^^^^^ undefined -``` - -### Bug 4: Inconsistent Array Handling -```javascript -// Every helper function does: -heights = new Uint8Array(heights); // Unnecessary copying if already Uint8Array -// Original pattern: direct mutation of closure variable -``` - -## Performance Impact Assessment - -1. **Memory Overhead:** Each helper function creates new Uint8Array copies -2. **Parameter Bloat:** Functions now take 6-8 parameters instead of accessing closure variables -3. **Reduced Efficiency:** Multiple array allocations per generation step - -## Recommendations - -### Critical Fixes (Must Fix Immediately) - -#### 1. **Restore Original Naming Convention** -**All functions must use the original pattern:** -- **Parameter name:** `graph` (not `grid`) -- **Internal usage:** `grid` (converted from `graph` parameter) -- **Function name:** `setGraph` (not `setGrid`) - -```javascript -// CORRECT pattern matching original: -export function addPit(heights, graph, blobPower, config, utils, count, height, rangeX, rangeY) { - const grid = graph; // Convert parameter to internal variable name - // ... use grid throughout function body -} -``` - -#### 2. **Fix Parameter/Variable Mismatches** -**Every function with graph/grid issues:** -- Line 178: `addPit` - change parameter from `graph` to `grid` OR add `const grid = graph;` -- Lines 254-255, 347, 359, 444-445: Change `graph` to `grid` in `findGridCell` calls -- Line 92: Add missing `power` parameter to `modify()` call - -#### 3. **Standardize Function Signatures** -**All helper functions should follow this pattern:** -```javascript -export function addHill(heights, graph, blobPower, config, utils, ...specificArgs) { - const grid = graph; // Mirror original internal conversion - // ... implementation using grid -} -``` - -### Architecture Decision Points - -#### Option A: Pure Functional (Current Broken Approach) -**Pros:** No side effects, better testability -**Cons:** 8+ parameters per function, performance overhead, complexity -**Fix Required:** Complete parameter standardization - -#### Option B: Hybrid Closure Pattern (Recommended) -**Restore original naming but keep functional returns:** -```javascript -function setGraph(graph, utils) { // Restore original name - const grid = graph; // Original internal conversion - const { cellsDesired, cells, points } = grid; - // ... compute values - return { heights, blobPower, linePower, grid }; // Include grid in return -} -``` - -#### Option C: Context Object Pattern -**Bundle related parameters:** -```javascript -export function addHill(context, count, height, rangeX, rangeY) { - const { heights, graph, blobPower, config, utils } = context; - const grid = graph; // Maintain original pattern - // ... implementation -} -``` - -## Conclusion - -The heightmap generator refactoring represents an **incomplete and broken migration** from the original closure-based pattern. While the functional approach has merit, the implementation violates the original naming convention and introduces multiple runtime failures. The core issue is that the refactoring was performed without understanding the original `graph` → `grid` naming logic. - -**Root Cause:** The original code used `graph` as the input parameter name and `grid` as the internal variable name after calling `setGraph(graph)`. The current version inconsistently mixes these names, creating undefined variable references. - -**Severity:** HIGH - Multiple functions will fail at runtime due to undefined variable access. - -## Priority Actions (In Order) - -### Immediate (Blocking) -1. **Fix undefined variable references** - All `findGridCell(x, y, graph)` calls where `graph` is undefined -2. **Standardize parameter names** - Either all `graph` or all `grid`, but consistently applied -3. **Restore setGraph naming** - Change `setGrid` back to `setGraph` to match original pattern -4. **Fix missing parameters** - Add `power` parameter to `modify()` function calls - -### Short Term -1. **Choose architectural pattern** - Pure functional vs hybrid vs context object -2. **Optimize array handling** - Eliminate unnecessary Uint8Array copying -3. **Complete parameter standardization** - Ensure all functions follow chosen pattern - -### Long Term -1. **Complete fromPrecreated migration** - Implement headless image processing -2. **Performance benchmarking** - Compare against original implementation -3. **Add comprehensive testing** - Prevent regression of these naming issues - -**Recommendation:** Restore the original `graph` parameter → `grid` internal variable pattern throughout the entire module to maintain consistency with the original design intent. \ No newline at end of file diff --git a/procedural/docs/MAP_GENERATION_TRACE.md b/procedural/docs/MAP_GENERATION_TRACE.md deleted file mode 100644 index 9dc444bf..00000000 --- a/procedural/docs/MAP_GENERATION_TRACE.md +++ /dev/null @@ -1,460 +0,0 @@ -# Exhaustive Step-by-Step Trace of Fantasy Map Generator Execution Flow - -## Starting Point: "Generate Map" Button Click - -**File**: `/home/user/Fantasy-Map-Generator/procedural/index.html` -- **Line 262**: `` -- **Line 263**: `` - -## Phase 1: Event Handler Registration and Initialization - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/main.js` - -### Step 1: DOM Content Loaded -- **Line 210**: `window.addEventListener('DOMContentLoaded', () => { ... })` -- **Data Created**: DOM content loaded event listener -- **Function Called**: Anonymous function for initialization - -### Step 2: Button Event Handler Registration -- **Lines 222-225**: Button event handler registration -```javascript -const generateBtn = byId("newMapButton") || byId("generateButton"); -if (generateBtn) { - generateBtn.addEventListener("click", handleGenerateClick); -} -``` -- **Data Created**: Event listener for click event -- **Function Called**: `handleGenerateClick` when clicked - -## Phase 2: Configuration Building and Validation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/main.js` - -### Step 3: Generate Click Handler Starts -- **Line 32**: `async function handleGenerateClick()` starts execution -- **Function Called**: `handleGenerateClick()` - -### Step 4: Build Configuration from UI -- **Line 36**: `const config = buildConfigFromUI();` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/config-builder.js` -- **Function Called**: `buildConfigFromUI()` (Line 8) -- **Data Created**: Complete configuration object with sections: - -### Step 5: Configuration Object Structure Created -- **Lines 9-31** in config-builder.js: Configuration object structure created -```javascript -const config = { - seed: getSeed(), // Line 61: Gets seed from UI or generates new one - graph: buildGraphConfig(), // Line 67: { width, height, cellsDesired } - map: buildMapConfig(), // Line 79: { coordinatesSize, latitude } - heightmap: buildHeightmapConfig(), // Line 86: { templateId } - temperature: buildTemperatureConfig(), // Line 93: { heightExponent, temperatureScale, temperatureBase } - precipitation: buildPrecipitationConfig(), // Line 101: { winds, moisture } - features: {}, - biomes: {}, - lakes: buildLakesConfig(), // Line 110: { lakeElevationLimit, heightExponent } - rivers: buildRiversConfig(), // Line 119: { resolveDepressionsSteps, cellsCount } - oceanLayers: buildOceanLayersConfig(), // Line 129: { outline } - cultures: buildCulturesConfig(), // Line 137: { culturesInput, culturesSet, emblemShape, etc. } - burgs: buildBurgsConfig(), // Line 162: { statesNumber, manorsInput, growthRate, etc. } - religions: buildReligionsConfig(), // Line 178: { religionsNumber, growthRate } - provinces: buildProvincesConfig(), // Line 185: { provincesRatio } - military: buildMilitaryConfig(), // Line 192: { year, eraShort, era } - markers: buildMarkersConfig(), // Line 196: { culturesSet } - zones: buildZonesConfig(), // Line 202: { globalModifier } - debug: buildDebugConfig() // Line 208: { TIME, WARN, INFO, ERROR } -}; -``` - -### Step 6: Configuration Validation -- **Line 39** in main.js: `const { fixed, originalValidation, fixedValidation, wasFixed } = validateAndFix(config);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/config-validator.js` -- **Function Called**: `validateAndFix(config)` -- **Data Created**: Validation results and fixed configuration - -### Step 7: Validation Logging -- **Lines 42-60** in main.js: Validation logging and error handling -- **Objects Received**: `originalValidation`, `fixedValidation`, `wasFixed` boolean -- **Expected**: Validation objects with `errors`, `warnings`, `valid` properties - -### Step 8: Save Configuration to LocalStorage -- **Line 64**: `localStorage.setItem('fmg-last-config', saveConfigToJSON(fixed));` -- **Function Called**: `saveConfigToJSON(fixed)` from config-builder.js -- **Data Created**: JSON string representation of configuration stored in localStorage - -## Phase 3: Engine Generation Call - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/main.js` - -### Step 9: Call Engine Generate Function -- **Line 70**: `const mapData = await generateMapEngine(fixed);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` -- **Function Called**: `generate(config)` (imported as `generateMapEngine`, Line 33) -- **Data Passed**: Validated and fixed configuration object - -## Phase 4: Engine Initialization - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 10: Start Performance Timer -- **Line 34**: `const timeStart = performance.now();` -- **Data Created**: Timestamp for performance measurement - -### Step 11: Extract Debug Flags -- **Line 37**: `const { TIME, WARN, INFO } = config.debug;` -- **Data Extracted**: Debug flags from configuration - -### Step 12: Seed Initialization -- **Line 40**: `const seed = config.seed || Utils.generateSeed();` -- **Function Called**: `Utils.generateSeed()` if no seed provided -- **Data Created**: Final seed value for generation - -### Step 13: Initialize Seeded Random Number Generator -- **Line 41**: `Math.random = Utils.aleaPRNG(seed);` -- **Function Called**: `Utils.aleaPRNG(seed)` from `/home/user/Fantasy-Map-Generator/procedural/src/engine/utils/alea.js` -- **Data Modified**: Global Math.random function replaced with seeded PRNG - -### Step 14: Console Group Start -- **Line 44**: `INFO && console.group("Generating Map with Seed: " + seed);` -- **Action**: Console group started if INFO debug flag is true - -## Phase 5: Grid Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 15: Generate Initial Grid -- **Line 48**: `let grid = Graph.generateGrid(config.graph);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/utils/graph.js` -- **Function Called**: `generateGrid(config)` (Line 13) -- **Data Passed**: `config.graph` object containing `{ width, height, cellsDesired }` -- **Data Created**: Initial grid object - -### Step 16: Grid Generation Process -- **Lines 15-17** in graph.js: Grid generation process -```javascript -const { spacing, cellsDesired, boundary, points, cellsX, cellsY } = placePoints(config); -const { cells, vertices } = calculateVoronoi(points, boundary); -return { spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices }; -``` -- **Functions Called**: `placePoints(config)` (Line 21), `calculateVoronoi(points, boundary)` (Line 34) -- **Data Created**: Voronoi diagram with cells and vertices - -### Step 17: Point Placement -- **Lines 21-31** in graph.js: Point placement -- **Function Called**: `getBoundaryPoints()`, `getJitteredGrid()` -- **Data Created**: Array of points for Voronoi calculation -- **Objects Created**: `{ spacing, cellsDesired, boundary, points, cellsX, cellsY }` - -### Step 18: Voronoi Calculation -- **Lines 34-50** in graph.js: Voronoi calculation -- **External Library**: Delaunator for Delaunay triangulation -- **Function Called**: `new Voronoi(delaunay, allPoints, points.length)` -- **Data Created**: `cells` and `vertices` objects with neighbor relationships - -## Phase 6: Heightmap Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 19: Generate Heightmap -- **Line 51**: `grid.cells.h = await Heightmap.generate(grid, config, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/heightmap-generator.js` -- **Function Called**: `generate(graph, config, utils)` (Line 3) -- **Data Passed**: `grid` object, `config` object, `Utils` module -- **Data Created**: Height values array assigned to `grid.cells.h` - -### Step 20: Heightmap Processing -- **Lines 3-18** in heightmap-generator.js: Heightmap generation -- **Data Extracted**: `templateId` from `config.heightmap` -- **Function Called**: `fromTemplate(graph, templateId, config, utils)` (Line 32) -- **Data Created**: Heights array for all grid cells - -### Step 21: Template Processing -- **Lines 32-48** in heightmap-generator.js: Template processing -- **Data Accessed**: `heightmapTemplates[id].template` string -- **Function Called**: `setGraph(graph, utils)`, `addStep()` for each template step -- **Data Created**: Final heights array with template-based terrain - -## Phase 7: Features Markup - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 22: Markup Grid Features -- **Line 52**: `grid = Features.markupGrid(grid, config, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/features.js` -- **Function Called**: `markupGrid(grid, config, utils)` (Line 28) -- **Data Passed**: Grid with heights, config, utils -- **Data Modified**: Grid object enhanced with feature information - -### Step 23: Grid Markup Process -- **Lines 28-50** in features.js: Grid markup process -- **Data Created**: `distanceField` (Int8Array), `featureIds` (Uint16Array), `features` array -- **Algorithm**: Flood-fill to identify connected land/water regions -- **Data Added to Grid**: Distance fields and feature classifications - -## Phase 8: Geography and Climate - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 24: Define Map Size -- **Line 55**: `const { mapCoordinates } = Geography.defineMapSize(grid, config, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/utils/geography.js` -- **Function Called**: `defineMapSize(grid, config, Utils)` -- **Data Created**: `mapCoordinates` object with geographic bounds - -### Step 25: Add Lakes in Deep Depressions -- **Line 56**: `grid = Geography.addLakesInDeepDepressions(grid, config.lakes, Utils);` -- **Function Called**: `addLakesInDeepDepressions(grid, config.lakes, Utils)` -- **Data Modified**: Grid enhanced with lake information - -### Step 26: Open Near-Sea Lakes -- **Line 57**: `grid = Geography.openNearSeaLakes(grid, config.lakes, Utils);` -- **Function Called**: `openNearSeaLakes(grid, config.lakes, Utils)` -- **Data Modified**: Lake connectivity to ocean processed - -### Step 27: Calculate Temperatures -- **Line 60**: `const { temp } = Geography.calculateTemperatures(grid, mapCoordinates, config.temperature, Utils);` -- **Function Called**: `calculateTemperatures()` -- **Data Created**: Temperature array for all cells -- **Line 61**: `grid.cells.temp = temp;` - Temperature data assigned to grid - -### Step 28: Generate Precipitation -- **Line 62**: `const { prec } = Geography.generatePrecipitation(grid, mapCoordinates, config.precipitation, Utils);` -- **Function Called**: `generatePrecipitation()` -- **Data Created**: Precipitation array for all cells -- **Line 63**: `grid.cells.prec = prec;` - Precipitation data assigned to grid - -## Phase 9: Pack Generation (Refined Mesh) - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 29: Generate Refined Mesh (Pack) -- **Line 66**: `let pack = Graph.reGraph(grid, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/utils/graph.js` -- **Function Called**: `reGraph(grid, Utils)` -- **Data Created**: Refined mesh (`pack`) with higher resolution than grid -- **Purpose**: Creates detailed mesh for final map features - -### Step 30: Markup Pack Features -- **Line 67**: `pack = Features.markupPack(pack, grid, config, Utils, { Lakes });` -- **Function Called**: `Features.markupPack()` -- **Data Passed**: Pack mesh, original grid, config, utils, Lakes module -- **Data Modified**: Pack enhanced with feature information - -## Phase 10: River Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 31: Generate Rivers -- **Line 70**: `const riverResult = Rivers.generate(pack, grid, config.rivers, Utils, { Lakes, Names });` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/river-generator.js` -- **Function Called**: `generate(pack, grid, config.rivers, Utils, { Lakes, Names })` -- **Data Passed**: Pack mesh, grid, river config, utilities, Lakes and Names modules -- **Data Created**: River system data - -### Step 32: Update Pack with Rivers -- **Line 71**: `pack = riverResult.pack;` -- **Data Modified**: Pack object updated with river information - -## Phase 11: Biome Assignment - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 33: Define Biomes -- **Line 74**: `const { biome } = Biomes.define(pack, grid, config.biomes, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/biomes.js` -- **Function Called**: `define(pack, grid, config.biomes, Utils)` -- **Data Created**: Biome classifications for each cell - -### Step 34: Assign Biomes to Pack -- **Line 75**: `pack.cells.biome = biome;` -- **Data Modified**: Biome data assigned to pack cells - -## Phase 12: Cell Ranking and Population - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 35: Rank Cells -- **Line 78**: `const { s, pop } = Cell.rankCells(pack, Utils, { biomesData: Biomes.getDefault() });` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/utils/cell.js` -- **Function Called**: `rankCells(pack, Utils, { biomesData })` -- **Data Passed**: Pack, utilities, default biomes data -- **Data Created**: Cell suitability rankings (`s`) and population values (`pop`) - -### Step 36: Assign Cell Rankings -- **Lines 79-80**: Cell data assignment -```javascript -pack.cells.s = s; -pack.cells.pop = pop; -``` - -## Phase 13: Culture Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 37: Generate Cultures -- **Line 83**: `const culturesResult = Cultures.generate(pack, grid, config.cultures, Utils, { Names });` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/cultures-generator.js` -- **Function Called**: `generate(pack, grid, config.cultures, Utils, { Names })` -- **Data Created**: Cultures data and culture assignments - -### Step 38: Integrate Culture Data -- **Lines 84-85**: Culture data integration -```javascript -let packWithCultures = { ...pack, cultures: culturesResult.cultures }; -packWithCultures.cells.culture = culturesResult.culture; -``` - -### Step 39: Expand Cultures -- **Line 87**: `const expandedCulturesData = Cultures.expand(packWithCultures, config.cultures, Utils, { biomesData: Biomes.getDefault() });` -- **Function Called**: `Cultures.expand()` -- **Data Created**: Expanded culture territories - -### Step 40: Update Pack with Expanded Cultures -- **Line 88**: `pack = { ...packWithCultures, ...expandedCulturesData };` -- **Data Modified**: Pack updated with expanded culture data - -## Phase 14: Burgs and States Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 41: Generate Burgs and States -- **Line 90**: `const burgsAndStatesResult = BurgsAndStates.generate(pack, grid, config.burgs, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/burgs-and-states.js` -- **Function Called**: `generate(pack, grid, config.burgs, Utils)` -- **Data Created**: Settlements (burgs) and political entities (states) - -### Step 42: Integrate Burgs and States Data -- **Lines 91-97**: Burgs and states data integration -```javascript -pack = { - ...pack, - burgs: burgsAndStatesResult.burgs, - states: burgsAndStatesResult.states -}; -pack.cells.burg = burgsAndStatesResult.burg; -pack.cells.state = burgsAndStatesResult.state; -``` - -## Phase 15: Additional Features Generation - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 43: Generate Routes -- **Line 99**: `const routesResult = Routes.generate(pack, grid, Utils, []);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/routes-generator.js` -- **Function Called**: `generate(pack, grid, Utils, [])` -- **Data Created**: Trade and travel routes - -### Step 44: Generate Religions -- **Line 102**: `const religionsResult = Religions.generate(pack, grid, config.religions, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/religions-generator.js` -- **Function Called**: `generate(pack, grid, config.religions, Utils)` -- **Data Created**: Religious systems and distributions - -### Step 45: Define State Forms -- **Line 105**: `const stateFormsResult = BurgsAndStates.defineStateForms(undefined, pack, Utils);` -- **Function Called**: `BurgsAndStates.defineStateForms()` -- **Data Created**: Government forms for states - -### Step 46: Generate Provinces -- **Line 108**: `const provincesResult = Provinces.generate(pack, config.provinces, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/provinces-generator.js` -- **Function Called**: `generate(pack, config.provinces, Utils)` -- **Data Created**: Provincial subdivisions - -### Step 47: Define Burg Features -- **Line 111**: `const burgFeaturesResult = BurgsAndStates.defineBurgFeatures(undefined, pack, Utils);` -- **Function Called**: `BurgsAndStates.defineBurgFeatures()` -- **Data Created**: Detailed settlement features - -### Step 48: Specify Rivers -- **Line 114**: `const specifiedRiversResult = Rivers.specify(pack, { Names }, Utils);` -- **Function Called**: `Rivers.specify()` -- **Data Created**: Named and detailed river information - -### Step 49: Specify Features -- **Line 117**: `const specifiedFeaturesResult = Features.specify(pack, grid, { Lakes });` -- **Function Called**: `Features.specify()` -- **Data Created**: Detailed geographic feature information - -### Step 50: Initialize Notes Array -- **Line 121**: `const notes = [];` -- **Data Created**: Notes array for modules requiring annotation - -### Step 51: Generate Military -- **Line 123**: `const militaryResult = Military.generate(pack, config.military, Utils, notes);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/military-generator.js` -- **Function Called**: `generate(pack, config.military, Utils, notes)` -- **Data Created**: Military units and fortifications - -### Step 52: Generate Markers -- **Line 126**: `const markersResult = Markers.generateMarkers(pack, config.markers, Utils);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/markers-generator.js` -- **Function Called**: `generateMarkers(pack, config.markers, Utils)` -- **Data Created**: Map markers and labels - -### Step 53: Generate Zones -- **Line 129**: `const zonesResult = Zones.generate(pack, notes, Utils, config.zones);` -- **File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/modules/zones-generator.js` -- **Function Called**: `generate(pack, notes, Utils, config.zones)` -- **Data Created**: Special zones and areas - -## Phase 16: Generation Completion - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/engine/main.js` - -### Step 54: Log Performance Timing -- **Line 133**: `WARN && console.warn(\`TOTAL GENERATION TIME: ${Utils.rn((performance.now() - timeStart) / 1000, 2)}s\`);` -- **Action**: Performance timing logged if WARN debug flag is true - -### Step 55: Close Console Group -- **Line 134**: `INFO && console.groupEnd("Generated Map " + seed);` -- **Action**: Console group ended if INFO debug flag is true - -### Step 56: Return Generated Map Data -- **Line 137**: `return { seed, grid, pack, mapCoordinates };` -- **Data Returned**: Complete map data object containing: - - `seed`: Generation seed - - `grid`: Coarse Voronoi mesh with basic geographic data - - `pack`: Refined mesh with all generated features - - `mapCoordinates`: Geographic coordinate system - -## Phase 17: Return to Viewer - -**File**: `/home/user/Fantasy-Map-Generator/procedural/src/viewer/main.js` - -### Step 57: Log Map Generation Complete -- **Line 72**: `console.log("Engine finished. Map data generated:", mapData);` -- **Data Received**: Complete `mapData` object from engine -- **Objects Available**: `{ seed, grid, pack, mapCoordinates }` - -### Step 58: Render Map (Currently Commented Out) -- **Line 74**: `// renderMap(mapData);` (commented out) -- **Expected Next Step**: Rendering system would take the mapData and create visual representation -- **Current State**: Generation complete, awaiting rendering implementation - -## Summary of Data Flow - -### Key Data Transformations: -1. **UI → Configuration**: HTML form values → structured config object -2. **Configuration → Grid**: Config parameters → Voronoi mesh -3. **Grid → Heightmap**: Mesh structure → elevation data -4. **Grid → Features**: Heights → land/water classification -5. **Grid → Pack**: Coarse mesh → refined mesh -6. **Pack → Biomes**: Climate data → biome assignments -7. **Pack → Cultures**: Suitability → cultural territories -8. **Pack → Complete Map**: Sequential module processing → final map data - -### Module Interaction Pattern: -- Each module receives: `(pack/grid, config_section, Utils, dependencies)` -- Each module returns: New data to merge into pack/grid -- Modules are stateless and pure (no side effects beyond returned data) -- Sequential processing builds up complexity from basic terrain to complete civilization - -### Common Data Mismatches: -1. **Modules expecting properties that don't exist yet** (e.g., expecting `cells.culture` before Cultures module runs) -2. **Config sections missing expected fields** (validation tries to fix this) -3. **Pack/Grid structure differences** (pack is refined version of grid) -4. **Module dependencies** (Rivers needs Lakes, Cultures needs Names) - -This trace shows the complete execution flow from button click to final map data generation, with each step clearly identifying the files, functions, data transformations, and object relationships involved in the Fantasy Map Generator's procedural generation process. \ No newline at end of file diff --git a/procedural/docs/Port_Project_Plan.md b/procedural/docs/Port_Project_Plan.md deleted file mode 100644 index 63fcb83c..00000000 --- a/procedural/docs/Port_Project_Plan.md +++ /dev/null @@ -1,277 +0,0 @@ -# Port Project - -This project is decoupling a tightly-integrated browser application into a core procedural engine and a separate presentation layer. The goal of maintaining full cross-compatibility is not only achievable but is the natural outcome of a well-architected port. - -Recommended design for porting the Fantasy Map Generator to a headless system. - -### High-Level Architectural Vision - -The fundamental flaw of the current architecture for headless operation is the **tight coupling of generation logic and presentation (UI/rendering) logic**. Everything relies on the global `window` object, reads configuration from DOM elements, and directly manipulates SVG with libraries like `d3`. - -Our primary goal is to refactor this into two distinct, well-defined components: - -1. **The FMG Core Engine**: A pure, environment-agnostic JavaScript library. It will contain all the procedural generation logic. It will have no knowledge of browsers, DOM, or SVG. Its only job is to take a configuration object and produce a serializable data object representing the map. This engine will be the heart of both the headless system and the refactored web application. - -2. **The FMG Viewer/Client**: The existing web application, refactored to act as a client to the FMG Core Engine. It will handle user input, manage the SVG canvas, and call renderer modules to visualize the data produced by the engine. - -This separation is the key to achieving your cross-compatibility goal. The serialized data object (the `.map` file) becomes the universal "source of truth" that both systems can produce and consume. - ---- - -### Phase 1: Designing the FMG Core Engine - -This is the most critical phase. The engine must be a collection of pure JavaScript modules that can run in any modern JS environment (Node.js, Deno, Bun, or a browser). - -#### 1.1. Input: The Configuration Object - -All functions that currently read from the DOM (`byId("statesNumber").value`, `manorsInput.valueAsNumber`, etc.) must be refactored. The main generation function of the engine will accept a single `config` object. - -**Example `config` object:** - -```javascript -const config = { - seed: "123456789", - graph: { - width: 1920, - height: 1080, - points: 10000 - }, - generation: { - template: "continents", - cultures: 12, - culturesSet: "european", - states: 10, - provincesRatio: 40, - manors: 1000, - neutralRate: 1.2 - }, - display: { - populationRate: 10, - urbanization: 1, - // ... other options from the options panel - } -}; -``` - -#### 1.2. Output: The `MapData` Object - -The engine's primary function, let's call it `generateMap(config)`, will return a single, serializable object. This object will contain everything that is currently stored in global variables like `grid`, `pack`, `notes`, `options`, `seed`, etc. - -**Example `MapData` structure:** - -```javascript -{ - meta: { - version: "1.9", // FMG version for compatibility - generated: new Date().toISOString() - }, - seed: "123456789", - config: { /* the config object used for generation */ }, - grid: { /* grid data */ }, - pack: { /* pack data */ }, - notes: [ /* notes array */ ], - // ... any other top-level state -} -``` - -#### 1.3. Module Refactoring Strategy - -Each of your provided `.js` files will be converted into an ES module within the engine. - -* **Remove IIFE and `window`:** Replace `window.MyModule = (function () { ... })();` with standard `export` statements. -* **Dependency Injection & Pure Functions:** Modules should not rely on or modify a global `pack` or `grid` object. Instead, they should receive the current state of the map data as an argument and return the new data they've generated. - -**Example Refactoring: `biomes.js`** - -**Before (biomes.js):** - -```javascript -"use strict"; -window.Biomes = (function () { - // ... - function define() { - // ... reads from global `pack` and `grid` - pack.cells.biome = new Uint8Array(pack.cells.i.length); // Direct modification - // ... - } - return {getDefault, define, getId}; -})(); -``` - -**After (engine/modules/biomes.js):** - -```javascript -"use strict"; -// No IIFE, no window dependency - -export function getDefaultBiomesData() { - // ... returns default biome data -} - -export function defineBiomes(pack, grid) { - const {fl, r, h, c, g} = pack.cells; - const {temp, prec} = grid.cells; - const biome = new Uint8Array(pack.cells.i.length); - - for (let cellId = 0; cellId < h.length; cellId++) { - // ... calculations ... - biome[cellId] = getId(/* ... */); - } - - // Return only the data this module is responsible for - return { biome }; -} - -// ... other helper functions (getId, etc.) -``` - -#### 1.4. The Orchestrator (`engine/main.js`) - -A new "main" module in the engine will orchestrate the entire generation process, calling the refactored modules in the correct sequence and composing the final `MapData` object. - -```javascript -// engine/main.js -import { generateGrid } from './grid.js'; -import * as Heightmap from './heightmap.js'; -import * as Biomes from './biomes.js'; -// ... import all other engine modules - -export function generateMap(config) { - const seed = setSeed(config.seed); - - let grid = generateGrid(config.graph); - grid.cells.h = Heightmap.generate(grid, config.generation.template); - - // ... other initial grid steps (features, temperatures, etc.) - - let pack = reGraph(grid); // Assume reGraph is refactored - - // Sequentially build the pack object - const { biome } = Biomes.defineBiomes(pack, grid); - pack.cells.biome = biome; - - const { cultures, cellsCulture } = Cultures.generate(pack, grid, config); - pack.cultures = cultures; - pack.cells.culture = cellsCulture; - - // ... continue for states, burgs, rivers, etc. - - return { meta, seed, config, grid, pack, notes: [] }; -} -``` - ---- - -### Phase 2: Refactoring the Web Application (Viewer) - -The web app becomes a consumer of the Core Engine. - -#### 2.1. Separating Renderers - -All modules with rendering logic (`coa-renderer.js`, `ocean-layers.js`, parts of `routes-generator.js`, `markers-generator.js`) must be moved out of the engine and into a `viewer/renderers/` directory. - -* `COArenderer.draw` is already well-designed for this. It takes a `coa` object and renders it. Perfect. -* The `Markers.generate` function should be split. The logic for *selecting candidate cells* (`listVolcanoes`, etc.) goes into the engine. The logic for *drawing the marker icon* (`addMarker` which creates SVG elements) goes into a `viewer/renderers/markers.js` module. -* All `d3.select` and direct SVG manipulation must live exclusively in the Viewer. - -#### 2.2. New Web App Workflow - -1. **UI Interaction:** The user changes options in the UI. -2. **Build Config:** A function gathers all UI settings and builds the `config` object. -3. **Call Engine:** It calls `FMG_Engine.generateMap(config)`. This happens entirely in memory, with no DOM updates. -4. **Receive `MapData`:** It receives the complete `MapData` object. -5. **Render:** It calls a main `renderMap(MapData)` function, which in turn calls all the specific renderers (`renderBiomes`, `renderStates`, `renderRoutes`, etc.) to draw the SVG. - ---- - -### Phase 3: Building the Headless System - -This now becomes incredibly simple. The headless application is just a new entry point that uses the FMG Core Engine. - -#### 3.1. Node.js CLI Application - -A simple command-line tool. - -**`package.json` dependencies:** -`"dependencies": { "d3-quadtree": "...", "d3-polygon": "...", "delaunator": "..." }` -(Note the absence of browser-specific libraries). - -**`generate.js`:** - -```javascript -import { generateMap } from './engine/main.js'; -import fs from 'fs'; - -// Load config from a JSON file passed as an argument -const configFile = process.argv[2]; -const config = JSON.parse(fs.readFileSync(configFile, 'utf8')); - -console.log(`Generating map with seed: ${config.seed}...`); -const mapData = generateMap(config); - -// Save the output -const outputFile = config.output || `map_${config.seed}.map`; -fs.writeFileSync(outputFile, JSON.stringify(mapData)); -console.log(`Map data saved to ${outputFile}`); -``` - -You could run it like: `node generate.js my_config.json` - -#### 3.2. REST API Server - -Similarly, you could wrap the engine in a web server (e.g., using Express.js) to provide map generation as a service. - ---- - -### Data Persistence and Cross-Compatibility - -The `.map` file is the lynchpin. - -* **Format:** It should be a **JSON serialization of the `MapData` object**. This is human-readable, universally compatible, and simple. For large maps, consider compressing it with Gzip (resulting in a `.map.gz` file), which is standard practice. -* **Workflow:** - * **Web App Save:** `const mapFileContent = JSON.stringify(currentMapData);` -> User downloads this content as `my_world.map`. - * **Headless Generation:** The CLI tool saves the `JSON.stringify(mapData)` output. - * **Web App Load:** User uploads a `.map` file -> `const mapData = JSON.parse(fileContent);` -> `renderMap(mapData);`. - -Since both systems use the **exact same FMG Core Engine** to generate the data structure, and they both save/load this structure in the same format (JSON), they are **guaranteed to be cross-compatible**. - -### Proposed Project Structure - -``` -/fmg -├── /src -│ ├── /engine # FMG Core Engine (headless, browser-agnostic) -│ │ ├── main.js # Orchestrator (generateMap function) -│ │ ├── modules/ # Refactored modules (biomes.js, cultures.js, etc.) -│ │ └── utils/ # Agnostic utilities (math, array, etc.) -│ │ -│ ├── /viewer # Web Application (UI and rendering) -│ │ ├── main.js # Main UI logic, event handlers, orchestrates rendering -│ │ ├── renderers/ # SVG rendering modules (mapRenderer.js, coaRenderer.js) -│ │ └── ui/ # UI components, dialogs, etc. -│ │ -│ └── /headless # Headless Application -│ ├── cli.js # Command-line interface entry point -│ └── server.js # (Optional) REST API server entry point -│ -├── /assets # SVGs for charges, icons, etc. -├── index.html -├── package.json -└── ... -``` - -### Step-by-Step Roadmap - -1. **Setup & Scaffolding:** Create the new project structure. Set up a build process (like Vite or Webpack) to handle modules for the browser. -2. **Isolate Utilities:** [x] *DONE* Move all environment-agnostic utility functions (`arrayUtils`, `colorUtils`, `probabilityUtils`, etc.) into `engine/utils`. -3. **Create the `config` Object:** Define the structure of the `config` object and modify the web app to build this object from the UI instead of having modules read from the DOM directly. -4. **Refactor Incrementally:** - * Start with the simplest, most self-contained modules (e.g., `biomes`, `names-generator`). Convert them to the new engine module pattern (take data, return new data). - * Create the `engine/main.js` orchestrator and have it call these first few refactored modules. - * Modify the `viewer/main.js` to call the new `generateMap` function and then manually merge the results back into the global `pack`/`grid` objects for now. - * Separate the corresponding renderer for the refactored module. -5. **Iterate:** Continue this process for all modules, one by one. `BurgsAndStates` and `Routes` will be the most complex due to their interdependencies and mixed logic. -6. **Build Headless Entry Point:** Once the engine is fully decoupled, create the `headless/cli.js` file. It should be trivial at this point. -7. **Finalize Viewer:** Complete the refactoring of the web app to fully rely on the `MapData` object returned by the engine, calling a master `renderMap` function instead of many individual `draw*` functions. - -This phased approach ensures that you can test and validate at each step, maintaining a working application throughout the process while moving methodically toward the final, robust, and portable architecture. \ No newline at end of file diff --git a/procedural/docs/findGridCell.md b/procedural/docs/findGridCell.md deleted file mode 100644 index caff3cc9..00000000 --- a/procedural/docs/findGridCell.md +++ /dev/null @@ -1,52 +0,0 @@ -Looking more carefully at the code flow: - - 1. findGridCell vs Voronoi Cell Indices - - The key insight is that findGridCell in graphUtils.js:100-105 is used for coordinate-to-grid mapping, but the - actual iteration in heightmap-generator.js happens over different indices. - - 2. Actual Iteration Patterns - - In heightmap-generator.js, the problematic iterations are: - - - Line 165: for (const c of grid.cells.c[q]) - but q comes from queue.shift() where queue starts with [start] - - Line 209: grid.cells.c[q].forEach(...) - same pattern - - Line 268: grid.cells.c[cur].forEach(...) - cur is tracked through valid cell traversal - - Line 516: grid.cells.c[i].forEach(...) - i is the map index over heights - - 3. The Critical Constraint - - Looking at voronoi.js:22-27, cells.c[p] is only populated when: - if (p < this.pointsN && !this.cells.c[p]) { - // populate cells.c[p] - } - - 4. Why No Errors Occur - - The code works because: - - 1. heights array length matches valid cells: In the original ../modules/heightmap-generator.js:11, heights is created with length: - points.length, which equals pointsN - ```javascript -const setGraph = graph => { -const {cellsDesired, cells, points} = graph; -heights = cells.h ? Uint8Array.from(cells.h) : createTypedArray({maxValue: 100, length: points.length}); -blobPower = getBlobPower(cellsDesired); -linePower = getLinePower(cellsDesired); -grid = graph; -}; -``` - 1. Queue propagation stays within bounds: When iterating grid.cells.c[q], the q values come from: - - Initial valid starting points from findGridCell - - Subsequent values from grid.cells.c[previous_q] which only contains valid adjacent cell indices - 2. Voronoi adjacency constraint: The cells.c[p] arrays only contain indices of valid neighboring cells (< - pointsN), so the iteration naturally stays within the populated sparse array bounds - 3. Map iteration bounds: In line 468's heights.map((h, i) => ...), i ranges from 0 to heights.length-1, which - equals pointsN-1, so grid.cells.c[i] is always within the populated range. - - The sparse array works because the algorithm's traversal patterns are naturally constrained to only access - indices that were populated during Voronoi construction. - -## TASKS - -1. analyze ../modules heightmap-generator.js and src/engine/modules/heightmap-generator.js to determine why the logic has deviated. Produce docs/HEIGHTMAP_ASSESSMENT.md with findings and recommendations \ No newline at end of file diff --git a/procedural/index.html b/procedural/index.html deleted file mode 100644 index c36696ee..00000000 --- a/procedural/index.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - Fantasy Map Generator - Procedural Engine - - - - -
- -
-

Map Configuration

- - -
- - -
- - -
-

Core Settings

- - - - - - - - - -
- - - - -
-

Heightmap

- - -
- - - - - - - - - - - - - - -
-

Cultures

- - - - - - -
- - -
-

States & Burgs

- - - - -
- - -
-

Religions

- - -
- - - - - - - - -
-

Actions

- - -
- - - -
- - -
- - -
-

Info

-
Ready
-
- - Shortcuts:
- Ctrl+G - Generate Map
- Ctrl+Shift+S - Save Config -
-
-
-
- - - - - - - -
- - - - - - - - - - - - diff --git a/procedural/main.js b/procedural/main.js deleted file mode 100644 index 02b4eeed..00000000 --- a/procedural/main.js +++ /dev/null @@ -1,29 +0,0 @@ -// main.js (Viewer Entry Point) -import './style.css'; -import { generate } from './src/engine/main.js'; - -console.log("FMG Viewer Initialized!"); - - -document.addEventListener('DOMContentLoaded', () => { - const generateButton = document.getElementById('generateButton'); // Assuming you have a button with this ID - - generateButton.addEventListener('click', () => { - console.log("Generating map..."); - - // 1. Build config from UI - const config = { - seed: document.getElementById('optionsSeed').value || '12345', - graph: { width: 1024, height: 768, points: 10000 }, - // ... more config from UI - }; - - // 2. Call the engine - const mapData = generate(config); - - console.log("Map data generated by engine:", mapData); - - // 3. Render the map (this function will be built out later) - // renderMap(mapData); - }); -}); diff --git a/procedural/package-lock.json b/procedural/package-lock.json deleted file mode 100644 index 1edc0f2d..00000000 --- a/procedural/package-lock.json +++ /dev/null @@ -1,1389 +0,0 @@ -{ - "name": "procedural", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "procedural", - "version": "0.0.0", - "dependencies": { - "aleaprng": "^1.0.1", - "d3": "^5.16.0", - "delaunator": "^5.0.1" - }, - "devDependencies": { - "vite": "^7.0.4" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", - "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", - "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", - "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", - "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", - "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", - "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", - "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", - "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", - "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", - "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", - "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", - "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", - "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", - "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", - "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", - "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", - "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", - "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", - "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", - "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/aleaprng": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/aleaprng/-/aleaprng-1.0.1.tgz", - "integrity": "sha512-bbhVl2gniAT4Szfl+UtPM9NXM+yaC73pe4fq7AL2NO+htYT0PSUgxNa4vbKj+AiFAiXrQQrSR6YgjGwExB5Q6Q==", - "license": "BSD-3-Clause" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/d3": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", - "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" - } - }, - "node_modules/d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-brush": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", - "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "node_modules/d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1", - "d3-path": "1" - } - }, - "node_modules/d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "^1.1.1" - } - }, - "node_modules/d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-drag": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", - "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1", - "d3-selection": "1" - } - }, - "node_modules/d3-dsv": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", - "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", - "license": "BSD-3-Clause", - "dependencies": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json", - "csv2tsv": "bin/dsv2dsv", - "dsv2dsv": "bin/dsv2dsv", - "dsv2json": "bin/dsv2json", - "json2csv": "bin/json2dsv", - "json2dsv": "bin/json2dsv", - "json2tsv": "bin/json2dsv", - "tsv2csv": "bin/dsv2dsv", - "tsv2json": "bin/dsv2json" - } - }, - "node_modules/d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-fetch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", - "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dsv": "1" - } - }, - "node_modules/d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" - } - }, - "node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1" - } - }, - "node_modules/d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1" - } - }, - "node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-polygon": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", - "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", - "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1", - "d3-interpolate": "1" - } - }, - "node_modules/d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-time": "1" - } - }, - "node_modules/d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" - } - }, - "node_modules/d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-zoom": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", - "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" - } - }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "license": "Unlicense" - }, - "node_modules/rollup": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", - "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.2", - "@rollup/rollup-android-arm64": "4.46.2", - "@rollup/rollup-darwin-arm64": "4.46.2", - "@rollup/rollup-darwin-x64": "4.46.2", - "@rollup/rollup-freebsd-arm64": "4.46.2", - "@rollup/rollup-freebsd-x64": "4.46.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", - "@rollup/rollup-linux-arm-musleabihf": "4.46.2", - "@rollup/rollup-linux-arm64-gnu": "4.46.2", - "@rollup/rollup-linux-arm64-musl": "4.46.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", - "@rollup/rollup-linux-ppc64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-musl": "4.46.2", - "@rollup/rollup-linux-s390x-gnu": "4.46.2", - "@rollup/rollup-linux-x64-gnu": "4.46.2", - "@rollup/rollup-linux-x64-musl": "4.46.2", - "@rollup/rollup-win32-arm64-msvc": "4.46.2", - "@rollup/rollup-win32-ia32-msvc": "4.46.2", - "@rollup/rollup-win32-x64-msvc": "4.46.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/vite": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", - "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - } - } -} diff --git a/procedural/package.json b/procedural/package.json deleted file mode 100644 index a5ffa4d2..00000000 --- a/procedural/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "procedural", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "devDependencies": { - "vite": "^7.0.4" - }, - "dependencies": { - "aleaprng": "^1.0.1", - "d3": "^5.16.0", - "delaunator": "^5.0.1" - } -} diff --git a/procedural/public/assets/charges/agnusDei.svg b/procedural/public/assets/charges/agnusDei.svg deleted file mode 100644 index 60ea9d87..00000000 --- a/procedural/public/assets/charges/agnusDei.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/anchor.svg b/procedural/public/assets/charges/anchor.svg deleted file mode 100644 index 59678314..00000000 --- a/procedural/public/assets/charges/anchor.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/angel.svg b/procedural/public/assets/charges/angel.svg deleted file mode 100644 index e6f3ba3e..00000000 --- a/procedural/public/assets/charges/angel.svg +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/annulet.svg b/procedural/public/assets/charges/annulet.svg deleted file mode 100644 index b3dad13d..00000000 --- a/procedural/public/assets/charges/annulet.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/anvil.svg b/procedural/public/assets/charges/anvil.svg deleted file mode 100644 index 2b6b0868..00000000 --- a/procedural/public/assets/charges/anvil.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/procedural/public/assets/charges/apple.svg b/procedural/public/assets/charges/apple.svg deleted file mode 100644 index 6679428d..00000000 --- a/procedural/public/assets/charges/apple.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/arbalest.svg b/procedural/public/assets/charges/arbalest.svg deleted file mode 100644 index 38adac50..00000000 --- a/procedural/public/assets/charges/arbalest.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/arbalest2.svg b/procedural/public/assets/charges/arbalest2.svg deleted file mode 100644 index be9b1a6a..00000000 --- a/procedural/public/assets/charges/arbalest2.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/archer.svg b/procedural/public/assets/charges/archer.svg deleted file mode 100644 index 00891c1b..00000000 --- a/procedural/public/assets/charges/archer.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/armEmbowedHoldingSabre.svg b/procedural/public/assets/charges/armEmbowedHoldingSabre.svg deleted file mode 100644 index 2fbae9f6..00000000 --- a/procedural/public/assets/charges/armEmbowedHoldingSabre.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/armEmbowedVambraced.svg b/procedural/public/assets/charges/armEmbowedVambraced.svg deleted file mode 100644 index ac1422b5..00000000 --- a/procedural/public/assets/charges/armEmbowedVambraced.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/armEmbowedVambracedHoldingSword.svg b/procedural/public/assets/charges/armEmbowedVambracedHoldingSword.svg deleted file mode 100644 index d00d8bab..00000000 --- a/procedural/public/assets/charges/armEmbowedVambracedHoldingSword.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/armillarySphere.svg b/procedural/public/assets/charges/armillarySphere.svg deleted file mode 100644 index 45d09b8d..00000000 --- a/procedural/public/assets/charges/armillarySphere.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/arrow.svg b/procedural/public/assets/charges/arrow.svg deleted file mode 100644 index 2135169f..00000000 --- a/procedural/public/assets/charges/arrow.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/arrowsSheaf.svg b/procedural/public/assets/charges/arrowsSheaf.svg deleted file mode 100644 index 4b879e10..00000000 --- a/procedural/public/assets/charges/arrowsSheaf.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/attire.svg b/procedural/public/assets/charges/attire.svg deleted file mode 100644 index 99427a60..00000000 --- a/procedural/public/assets/charges/attire.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/axe.svg b/procedural/public/assets/charges/axe.svg deleted file mode 100644 index e1608145..00000000 --- a/procedural/public/assets/charges/axe.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/badgerStatant.svg b/procedural/public/assets/charges/badgerStatant.svg deleted file mode 100644 index 29cd5f39..00000000 --- a/procedural/public/assets/charges/badgerStatant.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/banner.svg b/procedural/public/assets/charges/banner.svg deleted file mode 100644 index 8ca47b97..00000000 --- a/procedural/public/assets/charges/banner.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - diff --git a/procedural/public/assets/charges/basilisk.svg b/procedural/public/assets/charges/basilisk.svg deleted file mode 100644 index b2755dff..00000000 --- a/procedural/public/assets/charges/basilisk.svg +++ /dev/null @@ -1,220 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bearPassant.svg b/procedural/public/assets/charges/bearPassant.svg deleted file mode 100644 index 847ea7bd..00000000 --- a/procedural/public/assets/charges/bearPassant.svg +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bearRampant.svg b/procedural/public/assets/charges/bearRampant.svg deleted file mode 100644 index 418d6fb3..00000000 --- a/procedural/public/assets/charges/bearRampant.svg +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bee.svg b/procedural/public/assets/charges/bee.svg deleted file mode 100644 index 7f3a0069..00000000 --- a/procedural/public/assets/charges/bee.svg +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bell.svg b/procedural/public/assets/charges/bell.svg deleted file mode 100644 index 307b6493..00000000 --- a/procedural/public/assets/charges/bell.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/billet.svg b/procedural/public/assets/charges/billet.svg deleted file mode 100644 index 9482f5a7..00000000 --- a/procedural/public/assets/charges/billet.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/boarHeadErased.svg b/procedural/public/assets/charges/boarHeadErased.svg deleted file mode 100644 index 08348586..00000000 --- a/procedural/public/assets/charges/boarHeadErased.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/boarRampant.svg b/procedural/public/assets/charges/boarRampant.svg deleted file mode 100644 index 436fb439..00000000 --- a/procedural/public/assets/charges/boarRampant.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/boat.svg b/procedural/public/assets/charges/boat.svg deleted file mode 100644 index b50f6194..00000000 --- a/procedural/public/assets/charges/boat.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/boat2.svg b/procedural/public/assets/charges/boat2.svg deleted file mode 100644 index f3e37a5b..00000000 --- a/procedural/public/assets/charges/boat2.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bone.svg b/procedural/public/assets/charges/bone.svg deleted file mode 100644 index 27a9e410..00000000 --- a/procedural/public/assets/charges/bone.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/bookClosed.svg b/procedural/public/assets/charges/bookClosed.svg deleted file mode 100644 index 0cd74341..00000000 --- a/procedural/public/assets/charges/bookClosed.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bookClosed2.svg b/procedural/public/assets/charges/bookClosed2.svg deleted file mode 100644 index bf8d2519..00000000 --- a/procedural/public/assets/charges/bookClosed2.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bookOpen.svg b/procedural/public/assets/charges/bookOpen.svg deleted file mode 100644 index 62f1c6fc..00000000 --- a/procedural/public/assets/charges/bookOpen.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bow.svg b/procedural/public/assets/charges/bow.svg deleted file mode 100644 index b1f2c8ec..00000000 --- a/procedural/public/assets/charges/bow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/bowWithArrow.svg b/procedural/public/assets/charges/bowWithArrow.svg deleted file mode 100644 index 12d7f9ff..00000000 --- a/procedural/public/assets/charges/bowWithArrow.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bowWithThreeArrows.svg b/procedural/public/assets/charges/bowWithThreeArrows.svg deleted file mode 100644 index 0efc30f2..00000000 --- a/procedural/public/assets/charges/bowWithThreeArrows.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bridge.svg b/procedural/public/assets/charges/bridge.svg deleted file mode 100644 index f5ce588e..00000000 --- a/procedural/public/assets/charges/bridge.svg +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bridge2.svg b/procedural/public/assets/charges/bridge2.svg deleted file mode 100644 index 6d3dfab5..00000000 --- a/procedural/public/assets/charges/bridge2.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bucket.svg b/procedural/public/assets/charges/bucket.svg deleted file mode 100644 index 56c7e83c..00000000 --- a/procedural/public/assets/charges/bucket.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/buckle.svg b/procedural/public/assets/charges/buckle.svg deleted file mode 100644 index b83ba97d..00000000 --- a/procedural/public/assets/charges/buckle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/bugleHorn.svg b/procedural/public/assets/charges/bugleHorn.svg deleted file mode 100644 index a7985ca6..00000000 --- a/procedural/public/assets/charges/bugleHorn.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bugleHorn2.svg b/procedural/public/assets/charges/bugleHorn2.svg deleted file mode 100644 index cf5bcaa7..00000000 --- a/procedural/public/assets/charges/bugleHorn2.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bullHeadCaboshed.svg b/procedural/public/assets/charges/bullHeadCaboshed.svg deleted file mode 100644 index d3ddeccc..00000000 --- a/procedural/public/assets/charges/bullHeadCaboshed.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/bullPassant.svg b/procedural/public/assets/charges/bullPassant.svg deleted file mode 100644 index 0314b64e..00000000 --- a/procedural/public/assets/charges/bullPassant.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/butterfly.svg b/procedural/public/assets/charges/butterfly.svg deleted file mode 100644 index 2c301fcf..00000000 --- a/procedural/public/assets/charges/butterfly.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/camel.svg b/procedural/public/assets/charges/camel.svg deleted file mode 100644 index e2dd8bb9..00000000 --- a/procedural/public/assets/charges/camel.svg +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cancer.svg b/procedural/public/assets/charges/cancer.svg deleted file mode 100644 index a8bf102c..00000000 --- a/procedural/public/assets/charges/cancer.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cannon.svg b/procedural/public/assets/charges/cannon.svg deleted file mode 100644 index 05e88b25..00000000 --- a/procedural/public/assets/charges/cannon.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/caravel.svg b/procedural/public/assets/charges/caravel.svg deleted file mode 100644 index 9eb57671..00000000 --- a/procedural/public/assets/charges/caravel.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/carreau.svg b/procedural/public/assets/charges/carreau.svg deleted file mode 100644 index bfeeb049..00000000 --- a/procedural/public/assets/charges/carreau.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/castle.svg b/procedural/public/assets/charges/castle.svg deleted file mode 100644 index 43a2fa38..00000000 --- a/procedural/public/assets/charges/castle.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/castle2.svg b/procedural/public/assets/charges/castle2.svg deleted file mode 100644 index 5f12a8aa..00000000 --- a/procedural/public/assets/charges/castle2.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/catPassantGuardant.svg b/procedural/public/assets/charges/catPassantGuardant.svg deleted file mode 100644 index b49dc820..00000000 --- a/procedural/public/assets/charges/catPassantGuardant.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cavalier.svg b/procedural/public/assets/charges/cavalier.svg deleted file mode 100644 index 7bfad7ac..00000000 --- a/procedural/public/assets/charges/cavalier.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/centaur.svg b/procedural/public/assets/charges/centaur.svg deleted file mode 100644 index f4ddeb22..00000000 --- a/procedural/public/assets/charges/centaur.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/chain.svg b/procedural/public/assets/charges/chain.svg deleted file mode 100644 index cfe4a3bb..00000000 --- a/procedural/public/assets/charges/chain.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/chalice.svg b/procedural/public/assets/charges/chalice.svg deleted file mode 100644 index 0f4f95f6..00000000 --- a/procedural/public/assets/charges/chalice.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cinquefoil.svg b/procedural/public/assets/charges/cinquefoil.svg deleted file mode 100644 index 49db293e..00000000 --- a/procedural/public/assets/charges/cinquefoil.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cock.svg b/procedural/public/assets/charges/cock.svg deleted file mode 100644 index 4b100034..00000000 --- a/procedural/public/assets/charges/cock.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/column.svg b/procedural/public/assets/charges/column.svg deleted file mode 100644 index 38d8b2a0..00000000 --- a/procedural/public/assets/charges/column.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/comet.svg b/procedural/public/assets/charges/comet.svg deleted file mode 100644 index 096ddb67..00000000 --- a/procedural/public/assets/charges/comet.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/compassRose.svg b/procedural/public/assets/charges/compassRose.svg deleted file mode 100644 index dec495cc..00000000 --- a/procedural/public/assets/charges/compassRose.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/cossack.svg b/procedural/public/assets/charges/cossack.svg deleted file mode 100644 index f5cf75ed..00000000 --- a/procedural/public/assets/charges/cossack.svg +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/cowHorns.svg b/procedural/public/assets/charges/cowHorns.svg deleted file mode 100644 index 74378210..00000000 --- a/procedural/public/assets/charges/cowHorns.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/cowStatant.svg b/procedural/public/assets/charges/cowStatant.svg deleted file mode 100644 index 66b46823..00000000 --- a/procedural/public/assets/charges/cowStatant.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/crescent.svg b/procedural/public/assets/charges/crescent.svg deleted file mode 100644 index 9181995b..00000000 --- a/procedural/public/assets/charges/crescent.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/crocodile.svg b/procedural/public/assets/charges/crocodile.svg deleted file mode 100644 index 442e456d..00000000 --- a/procedural/public/assets/charges/crocodile.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/crosier.svg b/procedural/public/assets/charges/crosier.svg deleted file mode 100644 index 631ef998..00000000 --- a/procedural/public/assets/charges/crosier.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossAnkh.svg b/procedural/public/assets/charges/crossAnkh.svg deleted file mode 100644 index 6bbac70e..00000000 --- a/procedural/public/assets/charges/crossAnkh.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossArrowed.svg b/procedural/public/assets/charges/crossArrowed.svg deleted file mode 100644 index ff4b6c02..00000000 --- a/procedural/public/assets/charges/crossArrowed.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossAvellane.svg b/procedural/public/assets/charges/crossAvellane.svg deleted file mode 100644 index 303e7f72..00000000 --- a/procedural/public/assets/charges/crossAvellane.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossBiparted.svg b/procedural/public/assets/charges/crossBiparted.svg deleted file mode 100644 index 0e6ac5f8..00000000 --- a/procedural/public/assets/charges/crossBiparted.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossBottony.svg b/procedural/public/assets/charges/crossBottony.svg deleted file mode 100644 index 2d246b29..00000000 --- a/procedural/public/assets/charges/crossBottony.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossBurgundy.svg b/procedural/public/assets/charges/crossBurgundy.svg deleted file mode 100644 index cb681714..00000000 --- a/procedural/public/assets/charges/crossBurgundy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossCalvary.svg b/procedural/public/assets/charges/crossCalvary.svg deleted file mode 100644 index dd0447b5..00000000 --- a/procedural/public/assets/charges/crossCalvary.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossCarolingian.svg b/procedural/public/assets/charges/crossCarolingian.svg deleted file mode 100644 index 761464a7..00000000 --- a/procedural/public/assets/charges/crossCarolingian.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/crossCeltic.svg b/procedural/public/assets/charges/crossCeltic.svg deleted file mode 100644 index 6abe10fe..00000000 --- a/procedural/public/assets/charges/crossCeltic.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossCeltic2.svg b/procedural/public/assets/charges/crossCeltic2.svg deleted file mode 100644 index 84628911..00000000 --- a/procedural/public/assets/charges/crossCeltic2.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossCercelee.svg b/procedural/public/assets/charges/crossCercelee.svg deleted file mode 100644 index 2b8bff16..00000000 --- a/procedural/public/assets/charges/crossCercelee.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossClechy.svg b/procedural/public/assets/charges/crossClechy.svg deleted file mode 100644 index c246534e..00000000 --- a/procedural/public/assets/charges/crossClechy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossDouble.svg b/procedural/public/assets/charges/crossDouble.svg deleted file mode 100644 index 1a0e4bc8..00000000 --- a/procedural/public/assets/charges/crossDouble.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossErminee.svg b/procedural/public/assets/charges/crossErminee.svg deleted file mode 100644 index 1def3830..00000000 --- a/procedural/public/assets/charges/crossErminee.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossFitchy.svg b/procedural/public/assets/charges/crossFitchy.svg deleted file mode 100644 index 954b8e70..00000000 --- a/procedural/public/assets/charges/crossFitchy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossFleury.svg b/procedural/public/assets/charges/crossFleury.svg deleted file mode 100644 index d617a15d..00000000 --- a/procedural/public/assets/charges/crossFleury.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossFormee.svg b/procedural/public/assets/charges/crossFormee.svg deleted file mode 100644 index efef99d4..00000000 --- a/procedural/public/assets/charges/crossFormee.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossFormee2.svg b/procedural/public/assets/charges/crossFormee2.svg deleted file mode 100644 index 2309e494..00000000 --- a/procedural/public/assets/charges/crossFormee2.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossFourchy.svg b/procedural/public/assets/charges/crossFourchy.svg deleted file mode 100644 index 9308a143..00000000 --- a/procedural/public/assets/charges/crossFourchy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossGamma.svg b/procedural/public/assets/charges/crossGamma.svg deleted file mode 100644 index d2da6490..00000000 --- a/procedural/public/assets/charges/crossGamma.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossHummetty.svg b/procedural/public/assets/charges/crossHummetty.svg deleted file mode 100644 index e2676ab2..00000000 --- a/procedural/public/assets/charges/crossHummetty.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossJerusalem.svg b/procedural/public/assets/charges/crossJerusalem.svg deleted file mode 100644 index 54ba95dc..00000000 --- a/procedural/public/assets/charges/crossJerusalem.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossLatin.svg b/procedural/public/assets/charges/crossLatin.svg deleted file mode 100644 index 16f60138..00000000 --- a/procedural/public/assets/charges/crossLatin.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossMaltese.svg b/procedural/public/assets/charges/crossMaltese.svg deleted file mode 100644 index 5718dacf..00000000 --- a/procedural/public/assets/charges/crossMaltese.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossMoline.svg b/procedural/public/assets/charges/crossMoline.svg deleted file mode 100644 index 97f3b918..00000000 --- a/procedural/public/assets/charges/crossMoline.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossOccitan.svg b/procedural/public/assets/charges/crossOccitan.svg deleted file mode 100644 index 15fe7587..00000000 --- a/procedural/public/assets/charges/crossOccitan.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossOrthodox.svg b/procedural/public/assets/charges/crossOrthodox.svg deleted file mode 100644 index 9519f84d..00000000 --- a/procedural/public/assets/charges/crossOrthodox.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPatonce.svg b/procedural/public/assets/charges/crossPatonce.svg deleted file mode 100644 index aaaec339..00000000 --- a/procedural/public/assets/charges/crossPatonce.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPatriarchal.svg b/procedural/public/assets/charges/crossPatriarchal.svg deleted file mode 100644 index 12338bc2..00000000 --- a/procedural/public/assets/charges/crossPatriarchal.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPattee.svg b/procedural/public/assets/charges/crossPattee.svg deleted file mode 100644 index 1c3bf761..00000000 --- a/procedural/public/assets/charges/crossPattee.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPatteeAlisee.svg b/procedural/public/assets/charges/crossPatteeAlisee.svg deleted file mode 100644 index 801e7113..00000000 --- a/procedural/public/assets/charges/crossPatteeAlisee.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPommy.svg b/procedural/public/assets/charges/crossPommy.svg deleted file mode 100644 index da002347..00000000 --- a/procedural/public/assets/charges/crossPommy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossPotent.svg b/procedural/public/assets/charges/crossPotent.svg deleted file mode 100644 index cc3bb92f..00000000 --- a/procedural/public/assets/charges/crossPotent.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossSaltire.svg b/procedural/public/assets/charges/crossSaltire.svg deleted file mode 100644 index 8798ba45..00000000 --- a/procedural/public/assets/charges/crossSaltire.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossSantiago.svg b/procedural/public/assets/charges/crossSantiago.svg deleted file mode 100644 index 7e510a90..00000000 --- a/procedural/public/assets/charges/crossSantiago.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/crossTau.svg b/procedural/public/assets/charges/crossTau.svg deleted file mode 100644 index 0789faf5..00000000 --- a/procedural/public/assets/charges/crossTau.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossTemplar.svg b/procedural/public/assets/charges/crossTemplar.svg deleted file mode 100644 index 9c33613c..00000000 --- a/procedural/public/assets/charges/crossTemplar.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossTriquetra.svg b/procedural/public/assets/charges/crossTriquetra.svg deleted file mode 100644 index 024225c1..00000000 --- a/procedural/public/assets/charges/crossTriquetra.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/crossVoided.svg b/procedural/public/assets/charges/crossVoided.svg deleted file mode 100644 index 0fa95bd7..00000000 --- a/procedural/public/assets/charges/crossVoided.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/crossedBones.svg b/procedural/public/assets/charges/crossedBones.svg deleted file mode 100644 index 3b06442f..00000000 --- a/procedural/public/assets/charges/crossedBones.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/crosslet.svg b/procedural/public/assets/charges/crosslet.svg deleted file mode 100644 index 645b68a3..00000000 --- a/procedural/public/assets/charges/crosslet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/crown.svg b/procedural/public/assets/charges/crown.svg deleted file mode 100644 index 00dbbacf..00000000 --- a/procedural/public/assets/charges/crown.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/crown2.svg b/procedural/public/assets/charges/crown2.svg deleted file mode 100644 index f06a106e..00000000 --- a/procedural/public/assets/charges/crown2.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/deerHeadCaboshed.svg b/procedural/public/assets/charges/deerHeadCaboshed.svg deleted file mode 100644 index aca87f99..00000000 --- a/procedural/public/assets/charges/deerHeadCaboshed.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/delf.svg b/procedural/public/assets/charges/delf.svg deleted file mode 100644 index 227bd04f..00000000 --- a/procedural/public/assets/charges/delf.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/dolphin.svg b/procedural/public/assets/charges/dolphin.svg deleted file mode 100644 index be5b00bd..00000000 --- a/procedural/public/assets/charges/dolphin.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/donkeyHeadCaboshed.svg b/procedural/public/assets/charges/donkeyHeadCaboshed.svg deleted file mode 100644 index da5aee0a..00000000 --- a/procedural/public/assets/charges/donkeyHeadCaboshed.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/dove.svg b/procedural/public/assets/charges/dove.svg deleted file mode 100644 index 7406a6ec..00000000 --- a/procedural/public/assets/charges/dove.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/doveDisplayed.svg b/procedural/public/assets/charges/doveDisplayed.svg deleted file mode 100644 index 35bd14d6..00000000 --- a/procedural/public/assets/charges/doveDisplayed.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/dragonPassant.svg b/procedural/public/assets/charges/dragonPassant.svg deleted file mode 100644 index b8fbc58e..00000000 --- a/procedural/public/assets/charges/dragonPassant.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/dragonRampant.svg b/procedural/public/assets/charges/dragonRampant.svg deleted file mode 100644 index 6ff64c2a..00000000 --- a/procedural/public/assets/charges/dragonRampant.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/dragonfly.svg b/procedural/public/assets/charges/dragonfly.svg deleted file mode 100644 index c8b501ca..00000000 --- a/procedural/public/assets/charges/dragonfly.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/drakkar.svg b/procedural/public/assets/charges/drakkar.svg deleted file mode 100644 index f36c3c4d..00000000 --- a/procedural/public/assets/charges/drakkar.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/drawingCompass.svg b/procedural/public/assets/charges/drawingCompass.svg deleted file mode 100644 index 65a252d3..00000000 --- a/procedural/public/assets/charges/drawingCompass.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/procedural/public/assets/charges/drum.svg b/procedural/public/assets/charges/drum.svg deleted file mode 100644 index bd8fd638..00000000 --- a/procedural/public/assets/charges/drum.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/duck.svg b/procedural/public/assets/charges/duck.svg deleted file mode 100644 index 940c3e4d..00000000 --- a/procedural/public/assets/charges/duck.svg +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/eagle.svg b/procedural/public/assets/charges/eagle.svg deleted file mode 100644 index c52fd495..00000000 --- a/procedural/public/assets/charges/eagle.svg +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/eagleTwoHeads.svg b/procedural/public/assets/charges/eagleTwoHeads.svg deleted file mode 100644 index cc8ac124..00000000 --- a/procedural/public/assets/charges/eagleTwoHeads.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/earOfWheat.svg b/procedural/public/assets/charges/earOfWheat.svg deleted file mode 100644 index a1b3072c..00000000 --- a/procedural/public/assets/charges/earOfWheat.svg +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/elephant.svg b/procedural/public/assets/charges/elephant.svg deleted file mode 100644 index fed4c13f..00000000 --- a/procedural/public/assets/charges/elephant.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/elephantHeadErased.svg b/procedural/public/assets/charges/elephantHeadErased.svg deleted file mode 100644 index f521413b..00000000 --- a/procedural/public/assets/charges/elephantHeadErased.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/escallop.svg b/procedural/public/assets/charges/escallop.svg deleted file mode 100644 index 4fda9dda..00000000 --- a/procedural/public/assets/charges/escallop.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/estoile.svg b/procedural/public/assets/charges/estoile.svg deleted file mode 100644 index 6966ccbc..00000000 --- a/procedural/public/assets/charges/estoile.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/falchion.svg b/procedural/public/assets/charges/falchion.svg deleted file mode 100644 index ccb71d3d..00000000 --- a/procedural/public/assets/charges/falchion.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/falcon.svg b/procedural/public/assets/charges/falcon.svg deleted file mode 100644 index 5e4cebd5..00000000 --- a/procedural/public/assets/charges/falcon.svg +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/fan.svg b/procedural/public/assets/charges/fan.svg deleted file mode 100644 index d7504ac6..00000000 --- a/procedural/public/assets/charges/fan.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/fasces.svg b/procedural/public/assets/charges/fasces.svg deleted file mode 100644 index 71e27285..00000000 --- a/procedural/public/assets/charges/fasces.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/feather.svg b/procedural/public/assets/charges/feather.svg deleted file mode 100644 index 0be55bf5..00000000 --- a/procedural/public/assets/charges/feather.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/flamberge.svg b/procedural/public/assets/charges/flamberge.svg deleted file mode 100644 index ab9d2277..00000000 --- a/procedural/public/assets/charges/flamberge.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/flangedMace.svg b/procedural/public/assets/charges/flangedMace.svg deleted file mode 100644 index 901d942f..00000000 --- a/procedural/public/assets/charges/flangedMace.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/fleurDeLis.svg b/procedural/public/assets/charges/fleurDeLis.svg deleted file mode 100644 index 2583e48f..00000000 --- a/procedural/public/assets/charges/fleurDeLis.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/fly.svg b/procedural/public/assets/charges/fly.svg deleted file mode 100644 index 9c880f72..00000000 --- a/procedural/public/assets/charges/fly.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/foot.svg b/procedural/public/assets/charges/foot.svg deleted file mode 100644 index 22963e5b..00000000 --- a/procedural/public/assets/charges/foot.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/fountain.svg b/procedural/public/assets/charges/fountain.svg deleted file mode 100644 index 1ed8ac67..00000000 --- a/procedural/public/assets/charges/fountain.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/procedural/public/assets/charges/frog.svg b/procedural/public/assets/charges/frog.svg deleted file mode 100644 index 392d3d05..00000000 --- a/procedural/public/assets/charges/frog.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/fusil.svg b/procedural/public/assets/charges/fusil.svg deleted file mode 100644 index eac10ed4..00000000 --- a/procedural/public/assets/charges/fusil.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/garb.svg b/procedural/public/assets/charges/garb.svg deleted file mode 100644 index fbefacd8..00000000 --- a/procedural/public/assets/charges/garb.svg +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/gauntlet.svg b/procedural/public/assets/charges/gauntlet.svg deleted file mode 100644 index f00a03eb..00000000 --- a/procedural/public/assets/charges/gauntlet.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/gear.svg b/procedural/public/assets/charges/gear.svg deleted file mode 100644 index 2d128dfb..00000000 --- a/procedural/public/assets/charges/gear.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/goat.svg b/procedural/public/assets/charges/goat.svg deleted file mode 100644 index 721c3f15..00000000 --- a/procedural/public/assets/charges/goat.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/goutte.svg b/procedural/public/assets/charges/goutte.svg deleted file mode 100644 index 40f23183..00000000 --- a/procedural/public/assets/charges/goutte.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/grapeBunch.svg b/procedural/public/assets/charges/grapeBunch.svg deleted file mode 100644 index 38d4693f..00000000 --- a/procedural/public/assets/charges/grapeBunch.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/grapeBunch2.svg b/procedural/public/assets/charges/grapeBunch2.svg deleted file mode 100644 index 0af7b6a6..00000000 --- a/procedural/public/assets/charges/grapeBunch2.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/grenade.svg b/procedural/public/assets/charges/grenade.svg deleted file mode 100644 index 07436784..00000000 --- a/procedural/public/assets/charges/grenade.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/greyhoundCourant.svg b/procedural/public/assets/charges/greyhoundCourant.svg deleted file mode 100644 index 916d70cf..00000000 --- a/procedural/public/assets/charges/greyhoundCourant.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/greyhoundRampant.svg b/procedural/public/assets/charges/greyhoundRampant.svg deleted file mode 100644 index cf607df5..00000000 --- a/procedural/public/assets/charges/greyhoundRampant.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/greyhoundSejant.svg b/procedural/public/assets/charges/greyhoundSejant.svg deleted file mode 100644 index 09efa81c..00000000 --- a/procedural/public/assets/charges/greyhoundSejant.svg +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/griffinPassant.svg b/procedural/public/assets/charges/griffinPassant.svg deleted file mode 100644 index 871b3591..00000000 --- a/procedural/public/assets/charges/griffinPassant.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/griffinRampant.svg b/procedural/public/assets/charges/griffinRampant.svg deleted file mode 100644 index da2cfebe..00000000 --- a/procedural/public/assets/charges/griffinRampant.svg +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/hand.svg b/procedural/public/assets/charges/hand.svg deleted file mode 100644 index 91c279ae..00000000 --- a/procedural/public/assets/charges/hand.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/harp.svg b/procedural/public/assets/charges/harp.svg deleted file mode 100644 index 91e5ee08..00000000 --- a/procedural/public/assets/charges/harp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/hatchet.svg b/procedural/public/assets/charges/hatchet.svg deleted file mode 100644 index 37052556..00000000 --- a/procedural/public/assets/charges/hatchet.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/head.svg b/procedural/public/assets/charges/head.svg deleted file mode 100644 index 08e36060..00000000 --- a/procedural/public/assets/charges/head.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/headWreathed.svg b/procedural/public/assets/charges/headWreathed.svg deleted file mode 100644 index b6dafc64..00000000 --- a/procedural/public/assets/charges/headWreathed.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/heart.svg b/procedural/public/assets/charges/heart.svg deleted file mode 100644 index 56d0940c..00000000 --- a/procedural/public/assets/charges/heart.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/hedgehog.svg b/procedural/public/assets/charges/hedgehog.svg deleted file mode 100644 index c7a84caa..00000000 --- a/procedural/public/assets/charges/hedgehog.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/helmet.svg b/procedural/public/assets/charges/helmet.svg deleted file mode 100644 index 9099c7c3..00000000 --- a/procedural/public/assets/charges/helmet.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/helmetCorinthian.svg b/procedural/public/assets/charges/helmetCorinthian.svg deleted file mode 100644 index 3ccd9cdd..00000000 --- a/procedural/public/assets/charges/helmetCorinthian.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/helmetGreat.svg b/procedural/public/assets/charges/helmetGreat.svg deleted file mode 100644 index b7a7bf49..00000000 --- a/procedural/public/assets/charges/helmetGreat.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/helmetZischagge.svg b/procedural/public/assets/charges/helmetZischagge.svg deleted file mode 100644 index 8985d197..00000000 --- a/procedural/public/assets/charges/helmetZischagge.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/heron.svg b/procedural/public/assets/charges/heron.svg deleted file mode 100644 index 4893b082..00000000 --- a/procedural/public/assets/charges/heron.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/hindStatant.svg b/procedural/public/assets/charges/hindStatant.svg deleted file mode 100644 index 3f9cc429..00000000 --- a/procedural/public/assets/charges/hindStatant.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/hook.svg b/procedural/public/assets/charges/hook.svg deleted file mode 100644 index d5679f64..00000000 --- a/procedural/public/assets/charges/hook.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/horseHeadCouped.svg b/procedural/public/assets/charges/horseHeadCouped.svg deleted file mode 100644 index 8c455c23..00000000 --- a/procedural/public/assets/charges/horseHeadCouped.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/horsePassant.svg b/procedural/public/assets/charges/horsePassant.svg deleted file mode 100644 index b2361b84..00000000 --- a/procedural/public/assets/charges/horsePassant.svg +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/horseRampant.svg b/procedural/public/assets/charges/horseRampant.svg deleted file mode 100644 index be7fee6c..00000000 --- a/procedural/public/assets/charges/horseRampant.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/horseSalient.svg b/procedural/public/assets/charges/horseSalient.svg deleted file mode 100644 index 21b5db48..00000000 --- a/procedural/public/assets/charges/horseSalient.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/horseshoe.svg b/procedural/public/assets/charges/horseshoe.svg deleted file mode 100644 index 3bd51f7a..00000000 --- a/procedural/public/assets/charges/horseshoe.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/hourglass.svg b/procedural/public/assets/charges/hourglass.svg deleted file mode 100644 index 87e7930b..00000000 --- a/procedural/public/assets/charges/hourglass.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/key.svg b/procedural/public/assets/charges/key.svg deleted file mode 100644 index 6fb883ea..00000000 --- a/procedural/public/assets/charges/key.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ladder.svg b/procedural/public/assets/charges/ladder.svg deleted file mode 100644 index fc72f130..00000000 --- a/procedural/public/assets/charges/ladder.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ladder2.svg b/procedural/public/assets/charges/ladder2.svg deleted file mode 100644 index 5596e045..00000000 --- a/procedural/public/assets/charges/ladder2.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ladybird.svg b/procedural/public/assets/charges/ladybird.svg deleted file mode 100644 index 3cc1587e..00000000 --- a/procedural/public/assets/charges/ladybird.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lamb.svg b/procedural/public/assets/charges/lamb.svg deleted file mode 100644 index 1d574b53..00000000 --- a/procedural/public/assets/charges/lamb.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lambPassantReguardant.svg b/procedural/public/assets/charges/lambPassantReguardant.svg deleted file mode 100644 index 90884eb7..00000000 --- a/procedural/public/assets/charges/lambPassantReguardant.svg +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lanceHead.svg b/procedural/public/assets/charges/lanceHead.svg deleted file mode 100644 index 255492db..00000000 --- a/procedural/public/assets/charges/lanceHead.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/lanceWithBanner.svg b/procedural/public/assets/charges/lanceWithBanner.svg deleted file mode 100644 index 556e5ade..00000000 --- a/procedural/public/assets/charges/lanceWithBanner.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/laurelWreath.svg b/procedural/public/assets/charges/laurelWreath.svg deleted file mode 100644 index 989e00c4..00000000 --- a/procedural/public/assets/charges/laurelWreath.svg +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/laurelWreath2.svg b/procedural/public/assets/charges/laurelWreath2.svg deleted file mode 100644 index 6dfb373e..00000000 --- a/procedural/public/assets/charges/laurelWreath2.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lighthouse.svg b/procedural/public/assets/charges/lighthouse.svg deleted file mode 100644 index b66938f5..00000000 --- a/procedural/public/assets/charges/lighthouse.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lionHeadCaboshed.svg b/procedural/public/assets/charges/lionHeadCaboshed.svg deleted file mode 100644 index 63022fc0..00000000 --- a/procedural/public/assets/charges/lionHeadCaboshed.svg +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lionHeadErased.svg b/procedural/public/assets/charges/lionHeadErased.svg deleted file mode 100644 index 7ed39c67..00000000 --- a/procedural/public/assets/charges/lionHeadErased.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lionPassant.svg b/procedural/public/assets/charges/lionPassant.svg deleted file mode 100644 index 7dd6b396..00000000 --- a/procedural/public/assets/charges/lionPassant.svg +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lionPassantGuardant.svg b/procedural/public/assets/charges/lionPassantGuardant.svg deleted file mode 100644 index 584e8835..00000000 --- a/procedural/public/assets/charges/lionPassantGuardant.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/lionRampant.svg b/procedural/public/assets/charges/lionRampant.svg deleted file mode 100644 index 8aab2b09..00000000 --- a/procedural/public/assets/charges/lionRampant.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/lionSejant.svg b/procedural/public/assets/charges/lionSejant.svg deleted file mode 100644 index 23ca98db..00000000 --- a/procedural/public/assets/charges/lionSejant.svg +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lizard.svg b/procedural/public/assets/charges/lizard.svg deleted file mode 100644 index dc8dbf9d..00000000 --- a/procedural/public/assets/charges/lizard.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lochaberAxe.svg b/procedural/public/assets/charges/lochaberAxe.svg deleted file mode 100644 index cf09c61e..00000000 --- a/procedural/public/assets/charges/lochaberAxe.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/log.svg b/procedural/public/assets/charges/log.svg deleted file mode 100644 index 387fad44..00000000 --- a/procedural/public/assets/charges/log.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/lozenge.svg b/procedural/public/assets/charges/lozenge.svg deleted file mode 100644 index 4cfb22c9..00000000 --- a/procedural/public/assets/charges/lozenge.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/lozengeFaceted.svg b/procedural/public/assets/charges/lozengeFaceted.svg deleted file mode 100644 index 63a575e3..00000000 --- a/procedural/public/assets/charges/lozengeFaceted.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lozengePloye.svg b/procedural/public/assets/charges/lozengePloye.svg deleted file mode 100644 index 0187c681..00000000 --- a/procedural/public/assets/charges/lozengePloye.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/lute.svg b/procedural/public/assets/charges/lute.svg deleted file mode 100644 index c88cc397..00000000 --- a/procedural/public/assets/charges/lute.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/lymphad.svg b/procedural/public/assets/charges/lymphad.svg deleted file mode 100644 index 457f10d7..00000000 --- a/procedural/public/assets/charges/lymphad.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/lyre.svg b/procedural/public/assets/charges/lyre.svg deleted file mode 100644 index d89b8550..00000000 --- a/procedural/public/assets/charges/lyre.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mace.svg b/procedural/public/assets/charges/mace.svg deleted file mode 100644 index a3f0e074..00000000 --- a/procedural/public/assets/charges/mace.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/maces.svg b/procedural/public/assets/charges/maces.svg deleted file mode 100644 index 93988b80..00000000 --- a/procedural/public/assets/charges/maces.svg +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mallet.svg b/procedural/public/assets/charges/mallet.svg deleted file mode 100644 index ecb418f9..00000000 --- a/procedural/public/assets/charges/mallet.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/mantle.svg b/procedural/public/assets/charges/mantle.svg deleted file mode 100644 index 5493c30b..00000000 --- a/procedural/public/assets/charges/mantle.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mapleLeaf.svg b/procedural/public/assets/charges/mapleLeaf.svg deleted file mode 100644 index 93e63767..00000000 --- a/procedural/public/assets/charges/mapleLeaf.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/martenCourant.svg b/procedural/public/assets/charges/martenCourant.svg deleted file mode 100644 index 1760d038..00000000 --- a/procedural/public/assets/charges/martenCourant.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mascle.svg b/procedural/public/assets/charges/mascle.svg deleted file mode 100644 index b867e47a..00000000 --- a/procedural/public/assets/charges/mascle.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mastiffStatant.svg b/procedural/public/assets/charges/mastiffStatant.svg deleted file mode 100644 index 14feb266..00000000 --- a/procedural/public/assets/charges/mastiffStatant.svg +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/millstone.svg b/procedural/public/assets/charges/millstone.svg deleted file mode 100644 index f8d523ca..00000000 --- a/procedural/public/assets/charges/millstone.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mitre.svg b/procedural/public/assets/charges/mitre.svg deleted file mode 100644 index 1a20e002..00000000 --- a/procedural/public/assets/charges/mitre.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/monk.svg b/procedural/public/assets/charges/monk.svg deleted file mode 100644 index 8ede0359..00000000 --- a/procedural/public/assets/charges/monk.svg +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/moonInCrescent.svg b/procedural/public/assets/charges/moonInCrescent.svg deleted file mode 100644 index 27fdb512..00000000 --- a/procedural/public/assets/charges/moonInCrescent.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/mullet.svg b/procedural/public/assets/charges/mullet.svg deleted file mode 100644 index 2658e971..00000000 --- a/procedural/public/assets/charges/mullet.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet10.svg b/procedural/public/assets/charges/mullet10.svg deleted file mode 100644 index 60ed608a..00000000 --- a/procedural/public/assets/charges/mullet10.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet4.svg b/procedural/public/assets/charges/mullet4.svg deleted file mode 100644 index 37242f99..00000000 --- a/procedural/public/assets/charges/mullet4.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet6.svg b/procedural/public/assets/charges/mullet6.svg deleted file mode 100644 index 1a1972aa..00000000 --- a/procedural/public/assets/charges/mullet6.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet6Faceted.svg b/procedural/public/assets/charges/mullet6Faceted.svg deleted file mode 100644 index 1ca0c335..00000000 --- a/procedural/public/assets/charges/mullet6Faceted.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/mullet6Pierced.svg b/procedural/public/assets/charges/mullet6Pierced.svg deleted file mode 100644 index 96d6f279..00000000 --- a/procedural/public/assets/charges/mullet6Pierced.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet7.svg b/procedural/public/assets/charges/mullet7.svg deleted file mode 100644 index 53321978..00000000 --- a/procedural/public/assets/charges/mullet7.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mullet8.svg b/procedural/public/assets/charges/mullet8.svg deleted file mode 100644 index 0239a883..00000000 --- a/procedural/public/assets/charges/mullet8.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/mulletFaceted.svg b/procedural/public/assets/charges/mulletFaceted.svg deleted file mode 100644 index a43fe26e..00000000 --- a/procedural/public/assets/charges/mulletFaceted.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/mulletPierced.svg b/procedural/public/assets/charges/mulletPierced.svg deleted file mode 100644 index b469bf0c..00000000 --- a/procedural/public/assets/charges/mulletPierced.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/oak.svg b/procedural/public/assets/charges/oak.svg deleted file mode 100644 index b01da9b4..00000000 --- a/procedural/public/assets/charges/oak.svg +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/orb.svg b/procedural/public/assets/charges/orb.svg deleted file mode 100644 index 2d7354f5..00000000 --- a/procedural/public/assets/charges/orb.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ouroboros.svg b/procedural/public/assets/charges/ouroboros.svg deleted file mode 100644 index f0f39146..00000000 --- a/procedural/public/assets/charges/ouroboros.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/owl.svg b/procedural/public/assets/charges/owl.svg deleted file mode 100644 index 015a7f81..00000000 --- a/procedural/public/assets/charges/owl.svg +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/owlDisplayed.svg b/procedural/public/assets/charges/owlDisplayed.svg deleted file mode 100644 index 353fe5e7..00000000 --- a/procedural/public/assets/charges/owlDisplayed.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/palace.svg b/procedural/public/assets/charges/palace.svg deleted file mode 100644 index 9e03dd4b..00000000 --- a/procedural/public/assets/charges/palace.svg +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/palmTree.svg b/procedural/public/assets/charges/palmTree.svg deleted file mode 100644 index 590d14a3..00000000 --- a/procedural/public/assets/charges/palmTree.svg +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/parrot.svg b/procedural/public/assets/charges/parrot.svg deleted file mode 100644 index 92d7dd10..00000000 --- a/procedural/public/assets/charges/parrot.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/peacock.svg b/procedural/public/assets/charges/peacock.svg deleted file mode 100644 index 064831f4..00000000 --- a/procedural/public/assets/charges/peacock.svg +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/peacockInPride.svg b/procedural/public/assets/charges/peacockInPride.svg deleted file mode 100644 index b8b4f8c7..00000000 --- a/procedural/public/assets/charges/peacockInPride.svg +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pear.svg b/procedural/public/assets/charges/pear.svg deleted file mode 100644 index d421e208..00000000 --- a/procedural/public/assets/charges/pear.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pegasus.svg b/procedural/public/assets/charges/pegasus.svg deleted file mode 100644 index 5335022d..00000000 --- a/procedural/public/assets/charges/pegasus.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pike.svg b/procedural/public/assets/charges/pike.svg deleted file mode 100644 index 812002b7..00000000 --- a/procedural/public/assets/charges/pike.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pillar.svg b/procedural/public/assets/charges/pillar.svg deleted file mode 100644 index d9aa943f..00000000 --- a/procedural/public/assets/charges/pillar.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pincers.svg b/procedural/public/assets/charges/pincers.svg deleted file mode 100644 index 2e91f728..00000000 --- a/procedural/public/assets/charges/pincers.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/pineCone.svg b/procedural/public/assets/charges/pineCone.svg deleted file mode 100644 index c326f2a2..00000000 --- a/procedural/public/assets/charges/pineCone.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pineTree.svg b/procedural/public/assets/charges/pineTree.svg deleted file mode 100644 index 2abebe11..00000000 --- a/procedural/public/assets/charges/pineTree.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pique.svg b/procedural/public/assets/charges/pique.svg deleted file mode 100644 index 06c5836e..00000000 --- a/procedural/public/assets/charges/pique.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/plaice.svg b/procedural/public/assets/charges/plaice.svg deleted file mode 100644 index 8325d1ca..00000000 --- a/procedural/public/assets/charges/plaice.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/plough.svg b/procedural/public/assets/charges/plough.svg deleted file mode 100644 index 7c1f42fc..00000000 --- a/procedural/public/assets/charges/plough.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ploughshare.svg b/procedural/public/assets/charges/ploughshare.svg deleted file mode 100644 index 45928f03..00000000 --- a/procedural/public/assets/charges/ploughshare.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/porcupine.svg b/procedural/public/assets/charges/porcupine.svg deleted file mode 100644 index bdc53081..00000000 --- a/procedural/public/assets/charges/porcupine.svg +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/portcullis.svg b/procedural/public/assets/charges/portcullis.svg deleted file mode 100644 index de154326..00000000 --- a/procedural/public/assets/charges/portcullis.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/pot.svg b/procedural/public/assets/charges/pot.svg deleted file mode 100644 index 64cc0ff6..00000000 --- a/procedural/public/assets/charges/pot.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/quatrefoil.svg b/procedural/public/assets/charges/quatrefoil.svg deleted file mode 100644 index ff33f762..00000000 --- a/procedural/public/assets/charges/quatrefoil.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/rabbitSejant.svg b/procedural/public/assets/charges/rabbitSejant.svg deleted file mode 100644 index c61e0a9b..00000000 --- a/procedural/public/assets/charges/rabbitSejant.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/raft.svg b/procedural/public/assets/charges/raft.svg deleted file mode 100644 index d5b728b4..00000000 --- a/procedural/public/assets/charges/raft.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/rake.svg b/procedural/public/assets/charges/rake.svg deleted file mode 100644 index 6feed725..00000000 --- a/procedural/public/assets/charges/rake.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ramHeadErased.svg b/procedural/public/assets/charges/ramHeadErased.svg deleted file mode 100644 index c971f7b4..00000000 --- a/procedural/public/assets/charges/ramHeadErased.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ramPassant.svg b/procedural/public/assets/charges/ramPassant.svg deleted file mode 100644 index 421e416c..00000000 --- a/procedural/public/assets/charges/ramPassant.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ramsHorn.svg b/procedural/public/assets/charges/ramsHorn.svg deleted file mode 100644 index 26585cc9..00000000 --- a/procedural/public/assets/charges/ramsHorn.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/rapier.svg b/procedural/public/assets/charges/rapier.svg deleted file mode 100644 index fed83920..00000000 --- a/procedural/public/assets/charges/rapier.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ratRampant.svg b/procedural/public/assets/charges/ratRampant.svg deleted file mode 100644 index 13c6746a..00000000 --- a/procedural/public/assets/charges/ratRampant.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/raven.svg b/procedural/public/assets/charges/raven.svg deleted file mode 100644 index 6e3c4eca..00000000 --- a/procedural/public/assets/charges/raven.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/rhinoceros.svg b/procedural/public/assets/charges/rhinoceros.svg deleted file mode 100644 index 1d43c4f4..00000000 --- a/procedural/public/assets/charges/rhinoceros.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon1.svg b/procedural/public/assets/charges/ribbon1.svg deleted file mode 100644 index 03bf3515..00000000 --- a/procedural/public/assets/charges/ribbon1.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon2.svg b/procedural/public/assets/charges/ribbon2.svg deleted file mode 100644 index 0f15bb04..00000000 --- a/procedural/public/assets/charges/ribbon2.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon3.svg b/procedural/public/assets/charges/ribbon3.svg deleted file mode 100644 index dd168991..00000000 --- a/procedural/public/assets/charges/ribbon3.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon4.svg b/procedural/public/assets/charges/ribbon4.svg deleted file mode 100644 index bab35959..00000000 --- a/procedural/public/assets/charges/ribbon4.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon5.svg b/procedural/public/assets/charges/ribbon5.svg deleted file mode 100644 index 3c718bce..00000000 --- a/procedural/public/assets/charges/ribbon5.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon6.svg b/procedural/public/assets/charges/ribbon6.svg deleted file mode 100644 index a32eede7..00000000 --- a/procedural/public/assets/charges/ribbon6.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon7.svg b/procedural/public/assets/charges/ribbon7.svg deleted file mode 100644 index 2d9a1e21..00000000 --- a/procedural/public/assets/charges/ribbon7.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/ribbon8.svg b/procedural/public/assets/charges/ribbon8.svg deleted file mode 100644 index f7c672a7..00000000 --- a/procedural/public/assets/charges/ribbon8.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/rose.svg b/procedural/public/assets/charges/rose.svg deleted file mode 100644 index 7ce11665..00000000 --- a/procedural/public/assets/charges/rose.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/roundel.svg b/procedural/public/assets/charges/roundel.svg deleted file mode 100644 index 344991ed..00000000 --- a/procedural/public/assets/charges/roundel.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/roundel2.svg b/procedural/public/assets/charges/roundel2.svg deleted file mode 100644 index fffa7ad4..00000000 --- a/procedural/public/assets/charges/roundel2.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/public/assets/charges/rustre.svg b/procedural/public/assets/charges/rustre.svg deleted file mode 100644 index 60c253a1..00000000 --- a/procedural/public/assets/charges/rustre.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/sabre.svg b/procedural/public/assets/charges/sabre.svg deleted file mode 100644 index 0f1f002d..00000000 --- a/procedural/public/assets/charges/sabre.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sabre2.svg b/procedural/public/assets/charges/sabre2.svg deleted file mode 100644 index 2466a761..00000000 --- a/procedural/public/assets/charges/sabre2.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sabresCrossed.svg b/procedural/public/assets/charges/sabresCrossed.svg deleted file mode 100644 index e9cec5f4..00000000 --- a/procedural/public/assets/charges/sabresCrossed.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sagittarius.svg b/procedural/public/assets/charges/sagittarius.svg deleted file mode 100644 index ac5eaeeb..00000000 --- a/procedural/public/assets/charges/sagittarius.svg +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/salmon.svg b/procedural/public/assets/charges/salmon.svg deleted file mode 100644 index bda9bf49..00000000 --- a/procedural/public/assets/charges/salmon.svg +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/saw.svg b/procedural/public/assets/charges/saw.svg deleted file mode 100644 index c356263f..00000000 --- a/procedural/public/assets/charges/saw.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scale.svg b/procedural/public/assets/charges/scale.svg deleted file mode 100644 index b2da96ea..00000000 --- a/procedural/public/assets/charges/scale.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scaleImbalanced.svg b/procedural/public/assets/charges/scaleImbalanced.svg deleted file mode 100644 index ea151649..00000000 --- a/procedural/public/assets/charges/scaleImbalanced.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scalesHanging.svg b/procedural/public/assets/charges/scalesHanging.svg deleted file mode 100644 index 1065211b..00000000 --- a/procedural/public/assets/charges/scalesHanging.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sceptre.svg b/procedural/public/assets/charges/sceptre.svg deleted file mode 100644 index 57869cbf..00000000 --- a/procedural/public/assets/charges/sceptre.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scissors.svg b/procedural/public/assets/charges/scissors.svg deleted file mode 100644 index a7437f1e..00000000 --- a/procedural/public/assets/charges/scissors.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/procedural/public/assets/charges/scissors2.svg b/procedural/public/assets/charges/scissors2.svg deleted file mode 100644 index 109d8238..00000000 --- a/procedural/public/assets/charges/scissors2.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/scorpion.svg b/procedural/public/assets/charges/scorpion.svg deleted file mode 100644 index c132511d..00000000 --- a/procedural/public/assets/charges/scorpion.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scrollClosed.svg b/procedural/public/assets/charges/scrollClosed.svg deleted file mode 100644 index 1f7d8034..00000000 --- a/procedural/public/assets/charges/scrollClosed.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scythe.svg b/procedural/public/assets/charges/scythe.svg deleted file mode 100644 index b8d0a04d..00000000 --- a/procedural/public/assets/charges/scythe.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/scythe2.svg b/procedural/public/assets/charges/scythe2.svg deleted file mode 100644 index 7fa9de5d..00000000 --- a/procedural/public/assets/charges/scythe2.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/serpent.svg b/procedural/public/assets/charges/serpent.svg deleted file mode 100644 index e12c9570..00000000 --- a/procedural/public/assets/charges/serpent.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sextifoil.svg b/procedural/public/assets/charges/sextifoil.svg deleted file mode 100644 index ad6e3303..00000000 --- a/procedural/public/assets/charges/sextifoil.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/shears.svg b/procedural/public/assets/charges/shears.svg deleted file mode 100644 index 3b3daeb4..00000000 --- a/procedural/public/assets/charges/shears.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/shield.svg b/procedural/public/assets/charges/shield.svg deleted file mode 100644 index 89ad41de..00000000 --- a/procedural/public/assets/charges/shield.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/shipWheel.svg b/procedural/public/assets/charges/shipWheel.svg deleted file mode 100644 index fa23136c..00000000 --- a/procedural/public/assets/charges/shipWheel.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sickle.svg b/procedural/public/assets/charges/sickle.svg deleted file mode 100644 index 753eeeaa..00000000 --- a/procedural/public/assets/charges/sickle.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/skeleton.svg b/procedural/public/assets/charges/skeleton.svg deleted file mode 100644 index cde28dae..00000000 --- a/procedural/public/assets/charges/skeleton.svg +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/skull.svg b/procedural/public/assets/charges/skull.svg deleted file mode 100644 index c7dc7044..00000000 --- a/procedural/public/assets/charges/skull.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/skull2.svg b/procedural/public/assets/charges/skull2.svg deleted file mode 100644 index a580afd7..00000000 --- a/procedural/public/assets/charges/skull2.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/snail.svg b/procedural/public/assets/charges/snail.svg deleted file mode 100644 index 387dc48a..00000000 --- a/procedural/public/assets/charges/snail.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/snake.svg b/procedural/public/assets/charges/snake.svg deleted file mode 100644 index 532f2c0c..00000000 --- a/procedural/public/assets/charges/snake.svg +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/snowflake.svg b/procedural/public/assets/charges/snowflake.svg deleted file mode 100644 index d85d067b..00000000 --- a/procedural/public/assets/charges/snowflake.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/spear.svg b/procedural/public/assets/charges/spear.svg deleted file mode 100644 index 9944f9d5..00000000 --- a/procedural/public/assets/charges/spear.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/spiral.svg b/procedural/public/assets/charges/spiral.svg deleted file mode 100644 index 05736127..00000000 --- a/procedural/public/assets/charges/spiral.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/squirrel.svg b/procedural/public/assets/charges/squirrel.svg deleted file mode 100644 index bf2cc68b..00000000 --- a/procedural/public/assets/charges/squirrel.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/stagLodgedRegardant.svg b/procedural/public/assets/charges/stagLodgedRegardant.svg deleted file mode 100644 index 024f648c..00000000 --- a/procedural/public/assets/charges/stagLodgedRegardant.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/stagPassant.svg b/procedural/public/assets/charges/stagPassant.svg deleted file mode 100644 index 745ed25e..00000000 --- a/procedural/public/assets/charges/stagPassant.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/stagsAttires.svg b/procedural/public/assets/charges/stagsAttires.svg deleted file mode 100644 index c8b3692b..00000000 --- a/procedural/public/assets/charges/stagsAttires.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/stirrup.svg b/procedural/public/assets/charges/stirrup.svg deleted file mode 100644 index 4350ad26..00000000 --- a/procedural/public/assets/charges/stirrup.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sun.svg b/procedural/public/assets/charges/sun.svg deleted file mode 100644 index 6d58a55c..00000000 --- a/procedural/public/assets/charges/sun.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/sunInSplendour.svg b/procedural/public/assets/charges/sunInSplendour.svg deleted file mode 100644 index 4de9c571..00000000 --- a/procedural/public/assets/charges/sunInSplendour.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/sunInSplendour2.svg b/procedural/public/assets/charges/sunInSplendour2.svg deleted file mode 100644 index d56c221d..00000000 --- a/procedural/public/assets/charges/sunInSplendour2.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/swallow.svg b/procedural/public/assets/charges/swallow.svg deleted file mode 100644 index bf363a15..00000000 --- a/procedural/public/assets/charges/swallow.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/swan.svg b/procedural/public/assets/charges/swan.svg deleted file mode 100644 index 26a345f7..00000000 --- a/procedural/public/assets/charges/swan.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/swanErased.svg b/procedural/public/assets/charges/swanErased.svg deleted file mode 100644 index 510c380a..00000000 --- a/procedural/public/assets/charges/swanErased.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/procedural/public/assets/charges/sword.svg b/procedural/public/assets/charges/sword.svg deleted file mode 100644 index cdf8853b..00000000 --- a/procedural/public/assets/charges/sword.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/talbotPassant.svg b/procedural/public/assets/charges/talbotPassant.svg deleted file mode 100644 index 121e6ba4..00000000 --- a/procedural/public/assets/charges/talbotPassant.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/talbotSejant.svg b/procedural/public/assets/charges/talbotSejant.svg deleted file mode 100644 index e89d4b90..00000000 --- a/procedural/public/assets/charges/talbotSejant.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/template.svg b/procedural/public/assets/charges/template.svg deleted file mode 100644 index cad51295..00000000 --- a/procedural/public/assets/charges/template.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/thistle.svg b/procedural/public/assets/charges/thistle.svg deleted file mode 100644 index 09cff52a..00000000 --- a/procedural/public/assets/charges/thistle.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/tower.svg b/procedural/public/assets/charges/tower.svg deleted file mode 100644 index 9754803a..00000000 --- a/procedural/public/assets/charges/tower.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/tree.svg b/procedural/public/assets/charges/tree.svg deleted file mode 100644 index 0e2854fc..00000000 --- a/procedural/public/assets/charges/tree.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/procedural/public/assets/charges/trefle.svg b/procedural/public/assets/charges/trefle.svg deleted file mode 100644 index 8180526b..00000000 --- a/procedural/public/assets/charges/trefle.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/trefoil.svg b/procedural/public/assets/charges/trefoil.svg deleted file mode 100644 index 438c993e..00000000 --- a/procedural/public/assets/charges/trefoil.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/procedural/public/assets/charges/triangle.svg b/procedural/public/assets/charges/triangle.svg deleted file mode 100644 index 0a06d67c..00000000 --- a/procedural/public/assets/charges/triangle.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/trianglePierced.svg b/procedural/public/assets/charges/trianglePierced.svg deleted file mode 100644 index 6bbe2fc2..00000000 --- a/procedural/public/assets/charges/trianglePierced.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/procedural/public/assets/charges/trowel.svg b/procedural/public/assets/charges/trowel.svg deleted file mode 100644 index b9533474..00000000 --- a/procedural/public/assets/charges/trowel.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/unicornRampant.svg b/procedural/public/assets/charges/unicornRampant.svg deleted file mode 100644 index a3102dbc..00000000 --- a/procedural/public/assets/charges/unicornRampant.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wasp.svg b/procedural/public/assets/charges/wasp.svg deleted file mode 100644 index 9d54306c..00000000 --- a/procedural/public/assets/charges/wasp.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wheatStalk.svg b/procedural/public/assets/charges/wheatStalk.svg deleted file mode 100644 index c6113ccd..00000000 --- a/procedural/public/assets/charges/wheatStalk.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wheel.svg b/procedural/public/assets/charges/wheel.svg deleted file mode 100644 index 6133eee9..00000000 --- a/procedural/public/assets/charges/wheel.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/windmill.svg b/procedural/public/assets/charges/windmill.svg deleted file mode 100644 index b814ef1e..00000000 --- a/procedural/public/assets/charges/windmill.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wing.svg b/procedural/public/assets/charges/wing.svg deleted file mode 100644 index d38622f2..00000000 --- a/procedural/public/assets/charges/wing.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wingSword.svg b/procedural/public/assets/charges/wingSword.svg deleted file mode 100644 index 75c2f80e..00000000 --- a/procedural/public/assets/charges/wingSword.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wolfHeadErased.svg b/procedural/public/assets/charges/wolfHeadErased.svg deleted file mode 100644 index 518ea83f..00000000 --- a/procedural/public/assets/charges/wolfHeadErased.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wolfPassant.svg b/procedural/public/assets/charges/wolfPassant.svg deleted file mode 100644 index 06f8761a..00000000 --- a/procedural/public/assets/charges/wolfPassant.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wolfRampant.svg b/procedural/public/assets/charges/wolfRampant.svg deleted file mode 100644 index f5c7f57c..00000000 --- a/procedural/public/assets/charges/wolfRampant.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wolfStatant.svg b/procedural/public/assets/charges/wolfStatant.svg deleted file mode 100644 index d592b9ef..00000000 --- a/procedural/public/assets/charges/wolfStatant.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wyvern.svg b/procedural/public/assets/charges/wyvern.svg deleted file mode 100644 index f09b821e..00000000 --- a/procedural/public/assets/charges/wyvern.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/charges/wyvernWithWingsDisplayed.svg b/procedural/public/assets/charges/wyvernWithWingsDisplayed.svg deleted file mode 100644 index d3aef9b4..00000000 --- a/procedural/public/assets/charges/wyvernWithWingsDisplayed.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/procedural/public/assets/heightmaps/africa-centric.png b/procedural/public/assets/heightmaps/africa-centric.png deleted file mode 100644 index 02e4a311..00000000 Binary files a/procedural/public/assets/heightmaps/africa-centric.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/arabia.png b/procedural/public/assets/heightmaps/arabia.png deleted file mode 100644 index 27946711..00000000 Binary files a/procedural/public/assets/heightmaps/arabia.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/atlantics.png b/procedural/public/assets/heightmaps/atlantics.png deleted file mode 100644 index be123705..00000000 Binary files a/procedural/public/assets/heightmaps/atlantics.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/britain.png b/procedural/public/assets/heightmaps/britain.png deleted file mode 100644 index 60e08da4..00000000 Binary files a/procedural/public/assets/heightmaps/britain.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/caribbean.png b/procedural/public/assets/heightmaps/caribbean.png deleted file mode 100644 index e4a8ed05..00000000 Binary files a/procedural/public/assets/heightmaps/caribbean.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/east-asia.png b/procedural/public/assets/heightmaps/east-asia.png deleted file mode 100644 index 41144db5..00000000 Binary files a/procedural/public/assets/heightmaps/east-asia.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/eurasia.png b/procedural/public/assets/heightmaps/eurasia.png deleted file mode 100644 index bdbdb4d7..00000000 Binary files a/procedural/public/assets/heightmaps/eurasia.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/europe-accented.png b/procedural/public/assets/heightmaps/europe-accented.png deleted file mode 100644 index 9be9480e..00000000 Binary files a/procedural/public/assets/heightmaps/europe-accented.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/europe-and-central-asia.png b/procedural/public/assets/heightmaps/europe-and-central-asia.png deleted file mode 100644 index c23e97ed..00000000 Binary files a/procedural/public/assets/heightmaps/europe-and-central-asia.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/europe-central.png b/procedural/public/assets/heightmaps/europe-central.png deleted file mode 100644 index b220f546..00000000 Binary files a/procedural/public/assets/heightmaps/europe-central.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/europe-north.png b/procedural/public/assets/heightmaps/europe-north.png deleted file mode 100644 index 1bb49184..00000000 Binary files a/procedural/public/assets/heightmaps/europe-north.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/europe.png b/procedural/public/assets/heightmaps/europe.png deleted file mode 100644 index 59dfdfea..00000000 Binary files a/procedural/public/assets/heightmaps/europe.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/greenland.png b/procedural/public/assets/heightmaps/greenland.png deleted file mode 100644 index 3136c539..00000000 Binary files a/procedural/public/assets/heightmaps/greenland.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/hellenica.png b/procedural/public/assets/heightmaps/hellenica.png deleted file mode 100644 index 2681d6ec..00000000 Binary files a/procedural/public/assets/heightmaps/hellenica.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/iceland.png b/procedural/public/assets/heightmaps/iceland.png deleted file mode 100644 index 88463158..00000000 Binary files a/procedural/public/assets/heightmaps/iceland.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/import-rules.txt b/procedural/public/assets/heightmaps/import-rules.txt deleted file mode 100644 index 69499114..00000000 --- a/procedural/public/assets/heightmaps/import-rules.txt +++ /dev/null @@ -1,8 +0,0 @@ -To get heightmap with correct height scale: -1. Open https://tangrams.github.io/heightmapper -2. Toggle off auto-exposure -3. Set max elevation to 2000 -4. Set min elevation to -500 -5. Find region you like -6. Render image -7. Optionally rescale image to a smaller size (e.g. 500x300px) as high resolution is not used diff --git a/procedural/public/assets/heightmaps/indian-ocean.png b/procedural/public/assets/heightmaps/indian-ocean.png deleted file mode 100644 index 860ca952..00000000 Binary files a/procedural/public/assets/heightmaps/indian-ocean.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/mediterranean-sea.png b/procedural/public/assets/heightmaps/mediterranean-sea.png deleted file mode 100644 index 6a7c8bb3..00000000 Binary files a/procedural/public/assets/heightmaps/mediterranean-sea.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/middle-east.png b/procedural/public/assets/heightmaps/middle-east.png deleted file mode 100644 index bfcc55bb..00000000 Binary files a/procedural/public/assets/heightmaps/middle-east.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/north-america.png b/procedural/public/assets/heightmaps/north-america.png deleted file mode 100644 index 1c1f1ad5..00000000 Binary files a/procedural/public/assets/heightmaps/north-america.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/us-centric.png b/procedural/public/assets/heightmaps/us-centric.png deleted file mode 100644 index 7094df6a..00000000 Binary files a/procedural/public/assets/heightmaps/us-centric.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/us-mainland.png b/procedural/public/assets/heightmaps/us-mainland.png deleted file mode 100644 index 3b1984e7..00000000 Binary files a/procedural/public/assets/heightmaps/us-mainland.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/world-from-pacific.png b/procedural/public/assets/heightmaps/world-from-pacific.png deleted file mode 100644 index 02043165..00000000 Binary files a/procedural/public/assets/heightmaps/world-from-pacific.png and /dev/null differ diff --git a/procedural/public/assets/heightmaps/world.png b/procedural/public/assets/heightmaps/world.png deleted file mode 100644 index 22a79298..00000000 Binary files a/procedural/public/assets/heightmaps/world.png and /dev/null differ diff --git a/procedural/public/assets/images/Discord.png b/procedural/public/assets/images/Discord.png deleted file mode 100644 index 78dab317..00000000 Binary files a/procedural/public/assets/images/Discord.png and /dev/null differ diff --git a/procedural/public/assets/images/Facebook.png b/procedural/public/assets/images/Facebook.png deleted file mode 100644 index 3d249fd9..00000000 Binary files a/procedural/public/assets/images/Facebook.png and /dev/null differ diff --git a/procedural/public/assets/images/Pinterest.png b/procedural/public/assets/images/Pinterest.png deleted file mode 100644 index fcc85914..00000000 Binary files a/procedural/public/assets/images/Pinterest.png and /dev/null differ diff --git a/procedural/public/assets/images/Reddit.png b/procedural/public/assets/images/Reddit.png deleted file mode 100644 index 4637f3a4..00000000 Binary files a/procedural/public/assets/images/Reddit.png and /dev/null differ diff --git a/procedural/public/assets/images/Twitter.png b/procedural/public/assets/images/Twitter.png deleted file mode 100644 index 05e0c2c2..00000000 Binary files a/procedural/public/assets/images/Twitter.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/favicon-16x16.png b/procedural/public/assets/images/icons/favicon-16x16.png deleted file mode 100644 index ddd75b4a..00000000 Binary files a/procedural/public/assets/images/icons/favicon-16x16.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/favicon-32x32.png b/procedural/public/assets/images/icons/favicon-32x32.png deleted file mode 100644 index 13e5179d..00000000 Binary files a/procedural/public/assets/images/icons/favicon-32x32.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/icon_x512.png b/procedural/public/assets/images/icons/icon_x512.png deleted file mode 100644 index f1f8c9aa..00000000 Binary files a/procedural/public/assets/images/icons/icon_x512.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/maskable_icon_x128.png b/procedural/public/assets/images/icons/maskable_icon_x128.png deleted file mode 100644 index fa877d1b..00000000 Binary files a/procedural/public/assets/images/icons/maskable_icon_x128.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/maskable_icon_x192.png b/procedural/public/assets/images/icons/maskable_icon_x192.png deleted file mode 100644 index 3322eab3..00000000 Binary files a/procedural/public/assets/images/icons/maskable_icon_x192.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/maskable_icon_x384.png b/procedural/public/assets/images/icons/maskable_icon_x384.png deleted file mode 100644 index c7e7e705..00000000 Binary files a/procedural/public/assets/images/icons/maskable_icon_x384.png and /dev/null differ diff --git a/procedural/public/assets/images/icons/maskable_icon_x512.png b/procedural/public/assets/images/icons/maskable_icon_x512.png deleted file mode 100644 index 5b2361fd..00000000 Binary files a/procedural/public/assets/images/icons/maskable_icon_x512.png and /dev/null differ diff --git a/procedural/public/assets/images/kiwiroo.png b/procedural/public/assets/images/kiwiroo.png deleted file mode 100644 index 4f34ae7e..00000000 Binary files a/procedural/public/assets/images/kiwiroo.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern1.png b/procedural/public/assets/images/pattern1.png deleted file mode 100644 index 59375796..00000000 Binary files a/procedural/public/assets/images/pattern1.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern2.png b/procedural/public/assets/images/pattern2.png deleted file mode 100644 index e96f68fa..00000000 Binary files a/procedural/public/assets/images/pattern2.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern3.png b/procedural/public/assets/images/pattern3.png deleted file mode 100644 index 636fde6f..00000000 Binary files a/procedural/public/assets/images/pattern3.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern4.png b/procedural/public/assets/images/pattern4.png deleted file mode 100644 index d96aa18a..00000000 Binary files a/procedural/public/assets/images/pattern4.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern5.png b/procedural/public/assets/images/pattern5.png deleted file mode 100644 index 82a2af7c..00000000 Binary files a/procedural/public/assets/images/pattern5.png and /dev/null differ diff --git a/procedural/public/assets/images/pattern6.png b/procedural/public/assets/images/pattern6.png deleted file mode 100644 index dc9271ef..00000000 Binary files a/procedural/public/assets/images/pattern6.png and /dev/null differ diff --git a/procedural/public/assets/images/preview.png b/procedural/public/assets/images/preview.png deleted file mode 100644 index 2b150732..00000000 Binary files a/procedural/public/assets/images/preview.png and /dev/null differ diff --git a/procedural/public/assets/images/textures/antique-big.jpg b/procedural/public/assets/images/textures/antique-big.jpg deleted file mode 100644 index 711b1681..00000000 Binary files a/procedural/public/assets/images/textures/antique-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/antique-small.jpg b/procedural/public/assets/images/textures/antique-small.jpg deleted file mode 100644 index 851b5d07..00000000 Binary files a/procedural/public/assets/images/textures/antique-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/folded-paper-big.jpg b/procedural/public/assets/images/textures/folded-paper-big.jpg deleted file mode 100644 index c2c4d761..00000000 Binary files a/procedural/public/assets/images/textures/folded-paper-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/folded-paper-small.jpg b/procedural/public/assets/images/textures/folded-paper-small.jpg deleted file mode 100644 index 88418a13..00000000 Binary files a/procedural/public/assets/images/textures/folded-paper-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/gray-paper.jpg b/procedural/public/assets/images/textures/gray-paper.jpg deleted file mode 100644 index 238d6e4c..00000000 Binary files a/procedural/public/assets/images/textures/gray-paper.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/iran-small.jpg b/procedural/public/assets/images/textures/iran-small.jpg deleted file mode 100644 index 39f34512..00000000 Binary files a/procedural/public/assets/images/textures/iran-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/marble-big.jpg b/procedural/public/assets/images/textures/marble-big.jpg deleted file mode 100644 index c1d2a6d4..00000000 Binary files a/procedural/public/assets/images/textures/marble-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/marble-blue-big.jpg b/procedural/public/assets/images/textures/marble-blue-big.jpg deleted file mode 100644 index dbfc0975..00000000 Binary files a/procedural/public/assets/images/textures/marble-blue-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/marble-blue-small.jpg b/procedural/public/assets/images/textures/marble-blue-small.jpg deleted file mode 100644 index 2e95fdcb..00000000 Binary files a/procedural/public/assets/images/textures/marble-blue-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/marble-small.jpg b/procedural/public/assets/images/textures/marble-small.jpg deleted file mode 100644 index 10d1a9ab..00000000 Binary files a/procedural/public/assets/images/textures/marble-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/mars-big.jpg b/procedural/public/assets/images/textures/mars-big.jpg deleted file mode 100644 index 3fd39dae..00000000 Binary files a/procedural/public/assets/images/textures/mars-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/mars-small.jpg b/procedural/public/assets/images/textures/mars-small.jpg deleted file mode 100644 index 75de8dd3..00000000 Binary files a/procedural/public/assets/images/textures/mars-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/mauritania-small.jpg b/procedural/public/assets/images/textures/mauritania-small.jpg deleted file mode 100644 index 22d9cecf..00000000 Binary files a/procedural/public/assets/images/textures/mauritania-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/mercury-big.jpg b/procedural/public/assets/images/textures/mercury-big.jpg deleted file mode 100644 index 7e06f0ee..00000000 Binary files a/procedural/public/assets/images/textures/mercury-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/mercury-small.jpg b/procedural/public/assets/images/textures/mercury-small.jpg deleted file mode 100644 index 53f31ee3..00000000 Binary files a/procedural/public/assets/images/textures/mercury-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/ocean.jpg b/procedural/public/assets/images/textures/ocean.jpg deleted file mode 100644 index 981366ca..00000000 Binary files a/procedural/public/assets/images/textures/ocean.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/pergamena-small.jpg b/procedural/public/assets/images/textures/pergamena-small.jpg deleted file mode 100644 index 951f9eda..00000000 Binary files a/procedural/public/assets/images/textures/pergamena-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/plaster.jpg b/procedural/public/assets/images/textures/plaster.jpg deleted file mode 100644 index 8ec85c81..00000000 Binary files a/procedural/public/assets/images/textures/plaster.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/soiled-paper-vertical.png b/procedural/public/assets/images/textures/soiled-paper-vertical.png deleted file mode 100644 index f8bb720e..00000000 Binary files a/procedural/public/assets/images/textures/soiled-paper-vertical.png and /dev/null differ diff --git a/procedural/public/assets/images/textures/soiled-paper.jpg b/procedural/public/assets/images/textures/soiled-paper.jpg deleted file mode 100644 index 00333992..00000000 Binary files a/procedural/public/assets/images/textures/soiled-paper.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/spain-small.jpg b/procedural/public/assets/images/textures/spain-small.jpg deleted file mode 100644 index a413f508..00000000 Binary files a/procedural/public/assets/images/textures/spain-small.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/timbercut-big.jpg b/procedural/public/assets/images/textures/timbercut-big.jpg deleted file mode 100644 index 7dc9b656..00000000 Binary files a/procedural/public/assets/images/textures/timbercut-big.jpg and /dev/null differ diff --git a/procedural/public/assets/images/textures/timbercut-small.jpg b/procedural/public/assets/images/textures/timbercut-small.jpg deleted file mode 100644 index a73e47be..00000000 Binary files a/procedural/public/assets/images/textures/timbercut-small.jpg and /dev/null differ diff --git a/procedural/public/vite.svg b/procedural/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/procedural/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/procedural/src/default_prompt.md b/procedural/src/default_prompt.md deleted file mode 100644 index 29225109..00000000 --- a/procedural/src/default_prompt.md +++ /dev/null @@ -1,84 +0,0 @@ -# cultures-generator.js - -**You are an expert senior JavaScript developer specializing in refactoring legacy code into modern, modular, and environment-agnostic libraries. You have a deep understanding of design patterns like dependency injection and the separation of concerns.** - -**Your Goal:** - -Your task is to refactor a single JavaScript module from a legacy Fantasy Map Generator application. The goal is to migrate it from its old, browser-dependent format into a pure, headless-first ES module that will be part of a core generation engine. This engine must be able to run in any JavaScript environment, including Node.js, without any dependencies on a browser or DOM. - -**Architectural Context:** - -* **Old Architecture:** The original code is wrapped in an IIFE and attaches its exports to the global `window` object. It directly reads from and mutates global state variables like `pack` and `grid`, and directly accesses the DOM via `byId()`. -* **New Architecture (Target):** - 1. **Core Engine:** A collection of pure ES modules. It receives all necessary data (`pack`, `grid`) and configuration as function arguments. It performs its logic and returns the newly generated data. It has **zero** knowledge of the browser. - 2. **Viewer/Client:** The application responsible for all DOM interaction, UI, and rendering SVG based on the data object produced by the engine. - -**The Golden Rules of Refactoring for the Core Engine:** - -1. **No Globals:** Remove the IIFE and the attachment to the `window` object. -2. **Use ES Modules:** All exported functions and data must use the `export` keyword. -3. **Dependency Injection:** Functions must not read from or mutate global state. All data they need (`pack`, `grid`) must be passed in as arguments. -4. **Introduce a `config` Object:** - * **When you find code that reads a value from the DOM (e.g., `byId("statesNumber").value`), this is a configuration parameter.** - * **You must replace this DOM call with a property from a `config` object (e.g., `config.statesNumber`).** - * Add this `config` object as a new argument to the function's signature. -5. **Return New Data:** Instead of modifying an object in place (e.g., `pack.cells.biome = ...`), functions should create the new data and return it. The calling function will be responsible for merging this data into the main state object. -6. **Pure functions:** Functions should not have side effects. They should either return a new state object or a specific piece of data. -7. **Strict Separation of Concerns (Crucial):** - * **UI Input Reading:** As per Rule #4, these `byId()` calls are your guide to what properties the `config` object needs. - * **Rendering Logic:** Any code that **writes to the DOM or SVG** (e.g., `d3.select`, `document.getElementById(...).innerHTML = ...`, creating `` elements, etc.) is considered rendering logic. - * **You must REMOVE all rendering logic** from the engine module. -8. **Maintain Style:** Preserve the original code style, comments, and variable names as much as possible for consistency. -9. **Efficient Destructuring:** When passing a utils object, only destructure the specific properties needed within the scope of the function that uses them, rather than destructuring the entire object at the top of every function. This improves clarity and reduces code repetition. - ---- - -**Concrete Example of Refactoring:** - -**BEFORE (Legacy `burgs-and-states.js`):** - -```javascript -// ... -function placeCapitals() { - // Direct DOM read - THIS IS A CONFIGURATION VALUE - let count = +byId("statesNumber").value; - // ... -} -// ... -``` - -**AFTER (Refactored `engine/modules/burgsAndStates.js`):** - -```javascript -// ... -// Dependencies, including the new `config` object, are injected. -export function placeCapitals(cells, graphWidth, graphHeight, config) { - // DOM read is replaced by a property from the `config` object. - let count = config.statesNumber; - // ... - // Returns the generated data - return { burgs, states }; -} -// ... -``` - ---- - -**Your Specific Task:** - -Now, please apply these principles to refactor the following module: `cultures-generator.js`. - -**File Content:** -```javascript - -``` - -**Instructions:** - -Provide a response in three parts: - -1. **Refactored Code:** The complete JavaScript code for the new ES module in ./cultures-generator.js -2. **Engine Dependencies:** - * List the external modules the refactored code will need to `import` (e.g., `Names`, `COA`) in ./cultures-generator_external.md - * **List the new `config` properties you identified and used** (e.g., `statesNumber`, `growthRate`) in cultures-generator_config.md This is essential. -3. **Removed Rendering/UI Logic:** List all the code blocks related to DOM manipulation or SVG rendering that you have **removed** so they can be moved to the Viewer application into cultures-generator_render.md diff --git a/procedural/src/engine/main.js b/procedural/src/engine/main.js deleted file mode 100644 index dbca63d7..00000000 --- a/procedural/src/engine/main.js +++ /dev/null @@ -1,139 +0,0 @@ -// src/engine/main.js - -// Import all the refactored engine modules -import * as Biomes from "./modules/biomes.js"; -import * as BurgsAndStates from "./modules/burgs-and-states.js"; -import * as COA from "./modules/coa-generator.js"; -import * as COArenderer from "./modules/coa-renderer.js"; -import * as Cultures from "./modules/cultures-generator.js"; -import * as Features from "./modules/features.js"; -import * as Heightmap from "./modules/heightmap-generator.js"; -import * as Lakes from "./modules/lakes.js"; -import * as Markers from "./modules/markers-generator.js"; -import * as Military from "./modules/military-generator.js"; -import * as Names from "./modules/names-generator.js"; -import * as Provinces from "./modules/provinces-generator.js"; -import * as Religions from "./modules/religions-generator.js"; -import * as Rivers from "./modules/river-generator.js"; -import * as Routes from "./modules/routes-generator.js"; -import * as Zones from "./modules/zones-generator.js"; -import * as voronoi from "./modules/voronoi.js"; -import * as Utils from "./utils/index.js"; -import * as graphUtils from "./utils/graphUtils.js" - -// Import the new utility modules -import * as Graph from "./utils/graph.js"; -import * as Geography from "./utils/geography.js"; -import * as Cell from "./utils/cell.js"; - -/** - * The main entry point for the headless map generation engine. - * @param {object} config - A comprehensive configuration object. - * @returns {object} An object containing the complete generated map data { grid, pack, notes, etc. }. - */ -export async function generate(config) { - const timeStart = performance.now(); - - // CORRECT: Get debug flags (values) from the config object. - const { TIME, WARN, INFO } = config.debug; - - // CORRECT: Call utility functions directly from the Utils object. - const seed = config.seed || Utils.generateSeed(); - Math.random = Utils.aleaPRNG(seed); - - // This now works, because INFO is a boolean from config.debug - INFO && console.group("Generating Map with Seed: " + seed); - - - // 2. Pass the entire config to generateGrid (it needs graph and debug sections) - let grid = graphUtils.generateGrid(config); - - // --- Heightmap and Features (assumed to be already modular) --- - grid.cells.h = await Heightmap.generate(grid, config, Utils); - grid = Features.markupGrid(grid, config, Utils); - - // 3. Pass 'map' and 'lakes' configs to the new geography utilities - const { mapCoordinates } = Geography.defineMapSize(grid, config, Utils); - grid = Geography.addLakesInDeepDepressions(grid, config.lakes, Utils); - grid = Geography.openNearSeaLakes(grid, config.lakes, Utils); - - // 4. Pass specific config sections for temperature and precipitation - const { temp } = Geography.calculateTemperatures(grid, mapCoordinates, config.temperature, Utils); - grid.cells.temp = temp; - const { prec } = Geography.generatePrecipitation(grid, mapCoordinates, config.precipitation, Utils); - grid.cells.prec = prec; - - // --- Pack Generation --- - let pack = Graph.reGraph(grid, Utils); - pack = Features.markupPack(pack, grid, config, Utils, { Lakes }); - - // --- River Generation --- - const riverResult = Rivers.generate(pack, grid, config, Utils, { Lakes, Names }); - pack = riverResult.pack; - - // --- Biome and Population --- - const { biome } = Biomes.define(pack, grid, config, Utils); - pack.cells.biome = biome; - - // 5. Call the new cell ranking utility - const { s, pop } = Cell.rankCells(pack, grid, config, Utils, { biomesData: Biomes.getDefault() }); - pack.cells.s = s; - pack.cells.pop = pop; - - // 6. Cultures, States, and Burgs - const culturesResult = Cultures.generate(pack, grid, config, Utils, { Names }); - let packWithCultures = { ...pack, cultures: culturesResult.cultures }; - packWithCultures.cells.culture = culturesResult.culture; - - const expandedCulturesData = Cultures.expand(packWithCultures, config.cultures, Utils, { biomesData: Biomes.getDefault() }); - pack = { ...packWithCultures, ...expandedCulturesData }; // Assumes expand returns an object with updated pack properties - - const burgsAndStatesResult = BurgsAndStates.generate(pack, grid, config.burgs, Utils); - pack = { - ...pack, - burgs: burgsAndStatesResult.burgs, - states: burgsAndStatesResult.states - }; - pack.cells.burg = burgsAndStatesResult.burg; - pack.cells.state = burgsAndStatesResult.state; - - const routesResult = Routes.generate(pack, grid, Utils, []); - pack = { ...pack, ...routesResult }; // Merge new routes data - - const religionsResult = Religions.generate(pack, grid, config.religions, Utils); - pack = { ...pack, ...religionsResult }; // Merge new religions data - - const stateFormsResult = BurgsAndStates.defineStateForms(undefined, pack, Utils); - pack = { ...pack, ...stateFormsResult }; // Merge updated state forms - - const provincesResult = Provinces.generate(pack, config.provinces, Utils); - pack = { ...pack, ...provincesResult }; // Merge new provinces data - - const burgFeaturesResult = BurgsAndStates.defineBurgFeatures(undefined, pack, Utils); - pack = { ...pack, ...burgFeaturesResult }; // Merge updated burg features - - const specifiedRiversResult = Rivers.specify(pack, { Names }, Utils); - pack = { ...pack, ...specifiedRiversResult }; // Merge specified river data - - const specifiedFeaturesResult = Features.specify(pack, grid, { Lakes }); - pack = { ...pack, ...specifiedFeaturesResult }; // Merge specified feature data - - // Initialize notes array for modules that require it - const notes = []; - - const militaryResult = Military.generate(pack, config.military, Utils, notes); - pack = { ...pack, ...militaryResult }; // Merge new military data - - const markersResult = Markers.generateMarkers(pack, config.markers, Utils); - pack = { ...pack, ...markersResult }; // Merge new markers data - - const zonesResult = Zones.generate(pack, notes, Utils, config.zones); - pack = { ...pack, ...zonesResult }; // Merge new zones data - - - WARN && console.warn(`TOTAL GENERATION TIME: ${Utils.rn((performance.now() - timeStart) / 1000, 2)}s`); - INFO && console.groupEnd("Generated Map " + seed); - - // Return all the generated data - return { seed, grid, pack, mapCoordinates }; -} diff --git a/procedural/src/engine/modules/biomes.js b/procedural/src/engine/modules/biomes.js deleted file mode 100644 index 8377ddf1..00000000 --- a/procedural/src/engine/modules/biomes.js +++ /dev/null @@ -1,156 +0,0 @@ -"use strict"; - -const MIN_LAND_HEIGHT = 20; - -export const getDefault = () => { - const name = [ - "Marine", - "Hot desert", - "Cold desert", - "Savanna", - "Grassland", - "Tropical seasonal forest", - "Temperate deciduous forest", - "Tropical rainforest", - "Temperate rainforest", - "Taiga", - "Tundra", - "Glacier", - "Wetland" - ]; - - const color = [ - "#466eab", - "#fbe79f", - "#b5b887", - "#d2d082", - "#c8d68f", - "#b6d95d", - "#29bc56", - "#7dcb35", - "#409c43", - "#4b6b32", - "#96784b", - "#d5e7eb", - "#0b9131" - ]; - const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12]; - const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 250]; - const icons = [ - {}, - {dune: 3, cactus: 6, deadTree: 1}, - {dune: 9, deadTree: 1}, - {acacia: 1, grass: 9}, - {grass: 1}, - {acacia: 8, palm: 1}, - {deciduous: 1}, - {acacia: 5, palm: 3, deciduous: 1, swamp: 1}, - {deciduous: 6, swamp: 1}, - {conifer: 1}, - {grass: 1}, - {}, - {swamp: 1} - ]; - const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost - const biomesMartix = [ - // hot � cold [>19�C; <-4�C]; dry � wet - new Uint8Array([1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10]), - new Uint8Array([3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9, 9, 9, 10, 10, 10]), - new Uint8Array([5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 9, 9, 9, 9, 10, 10, 10]), - new Uint8Array([5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10]), - new Uint8Array([7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10]) - ]; - - // parse icons weighted array into a simple array - for (let i = 0; i < icons.length; i++) { - const parsed = []; - for (const icon in icons[i]) { - for (let j = 0; j < icons[i][icon]; j++) { - parsed.push(icon); - } - } - icons[i] = parsed; - } - - return {i: Array.from({length: name.length}, (_, i) => i), name, color, biomesMartix, habitability, iconsDensity, icons, cost}; -}; - -/** - * Assign biome id for each cell - * - * REQUIRES: - * - pack.cells.h (heights from heightmap processing) - * - grid.cells.temp (temperature from geography module) - * - grid.cells.prec (precipitation from geography module) - * - pack.cells.g (grid reference from pack generation) - * - config.debug (debug configuration) - * - * PROVIDES: - * - pack.cells.biome (biome assignments for each cell) - */ -export function define(pack, grid, config, utils) { - // Check required properties exist - if (!pack.cells.h) { - throw new Error("Biomes module requires pack.cells.h (heights) from heightmap processing"); - } - if (!grid.cells.temp || !grid.cells.prec) { - throw new Error("Biomes module requires grid.cells.temp and grid.cells.prec from geography module"); - } - if (!pack.cells.g) { - throw new Error("Biomes module requires pack.cells.g (grid reference) from pack generation"); - } - if (!config.debug) { - throw new Error("Biomes module requires config.debug section"); - } - - const { d3, rn} = utils; - const { TIME } = config.debug; - TIME && console.time("defineBiomes"); - - const {fl: flux, r: riverIds, h: heights, c: neighbors, g: gridReference} = pack.cells; - const {temp, prec} = grid.cells; - const biome = new Uint8Array(pack.cells.i.length); // biomes array - const biomesData = getDefault(); - - for (let cellId = 0; cellId < heights.length; cellId++) { - const height = heights[cellId]; - const moisture = height < MIN_LAND_HEIGHT ? 0 : calculateMoisture(cellId); - const temperature = temp[gridReference[cellId]]; - biome[cellId] = getId(moisture, temperature, height, Boolean(riverIds[cellId]), biomesData); - } - - function calculateMoisture(cellId) { - let moisture = prec[gridReference[cellId]]; - if (riverIds[cellId]) moisture += Math.max(flux[cellId] / 10, 2); - - const moistAround = neighbors[cellId] - .filter(neibCellId => heights[neibCellId] >= MIN_LAND_HEIGHT) - .map(c => prec[gridReference[c]]) - .concat([moisture]); - return rn(4 + d3.mean(moistAround)); - } - - TIME && console.timeEnd("defineBiomes"); - return {biome}; -} - -export function getId(moisture, temperature, height, hasRiver, biomesData = null) { - const data = biomesData || getDefault(); - - if (height < 20) return 0; // all water cells: marine biome - if (temperature < -5) return 11; // too cold: permafrost biome - if (temperature >= 25 && !hasRiver && moisture < 8) return 1; // too hot and dry: hot desert biome - if (isWetland(moisture, temperature, height)) return 12; // too wet: wetland biome - - // in other cases use biome matrix - const moistureBand = Math.min((moisture / 5) | 0, 4); // [0-4] - const temperatureBand = Math.min(Math.max(20 - temperature, 0), 25); // [0-25] - return data.biomesMartix[moistureBand][temperatureBand]; -} - -function isWetland(moisture, temperature, height) { - if (temperature <= -2) return false; // too cold - if (moisture > 40 && height < 25) return true; // near coast - if (moisture > 24 && height > 24 && height < 60) return true; // off coast - return false; -} diff --git a/procedural/src/engine/modules/burgs-and-states.js b/procedural/src/engine/modules/burgs-and-states.js deleted file mode 100644 index b5f40dc6..00000000 --- a/procedural/src/engine/modules/burgs-and-states.js +++ /dev/null @@ -1,922 +0,0 @@ -"use strict"; - -/** - * Generates burgs (settlements) and states (political entities) - * - * REQUIRES: - * - pack.cells.culture (from cultures module) - * - pack.cells.s (from cell ranking) - * - pack.cultures (from cultures module) - * - config.statesNumber (number of states to generate) - * - * PROVIDES: - * - pack.burgs (burgs array) - * - pack.states (states array) - * - pack.cells.burg (burg assignments) - * - pack.cells.state (state assignments) - */ -export const generate = (pack, grid, config, utils) => { - // Check required properties exist - if (!pack.cells.culture) { - throw new Error("BurgsAndStates module requires cells.culture from Cultures module"); - } - if (!pack.cells.s) { - throw new Error("BurgsAndStates module requires cells.s (suitability) from Cell ranking"); - } - if (!pack.cultures) { - throw new Error("BurgsAndStates module requires pack.cultures from Cultures module"); - } - if (!config.statesNumber) { - throw new Error("BurgsAndStates module requires config.statesNumber"); - } - - const {cells, cultures} = pack; - const n = cells.i.length; - - const newCells = {...cells, burg: new Uint16Array(n)}; - const newPack = {...pack, cells: newCells}; - - const burgs = placeCapitals(newPack, grid, config, utils); - const states = createStates(newPack, burgs, config, utils); - - placeTowns(newPack, burgs, grid, config, utils); - expandStates(newPack, grid, config, utils); - normalizeStates(newPack, utils); - getPoles(newPack, utils); - - specifyBurgs(newPack, grid, utils); - - collectStatistics(newPack); - assignColors(newPack, utils); - - generateCampaigns(newPack, utils); - generateDiplomacy(newPack, utils); - - return { - burgs: newPack.burgs, - states: newPack.states, - cells: { - ...pack.cells, - burg: newPack.cells.burg, - state: newPack.cells.state - } - }; -}; - -function placeCapitals(pack, grid, config, utils) { - const { WARN, d3, graphWidth, graphHeight} = utils; - const { TIME } = config.debug; - TIME && console.time("placeCapitals"); - let count = config.statesNumber; - let burgs = [0]; - - const {cells} = pack; - const rand = () => 0.5 + Math.random() * 0.5; - const score = new Int16Array(cells.s.map(s => s * rand())); // cell score for capitals placement - const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes - - if (sorted.length < count * 10) { - count = Math.floor(sorted.length / 10); - if (!count) { - WARN && console.warn("There is no populated cells. Cannot generate states"); - return burgs; - } else { - WARN && console.warn(`Not enough populated cells (${sorted.length}). Will generate only ${count} states`); - } - } - - let burgsTree = d3.quadtree(); - let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals - - for (let i = 0; burgs.length <= count; i++) { - const cell = sorted[i]; - const [x, y] = cells.p[cell]; - - if (burgsTree.find(x, y, spacing) === undefined) { - burgs.push({cell, x, y}); - burgsTree.add([x, y]); - } - - if (i === sorted.length - 1) { - WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing"); - burgsTree = d3.quadtree(); - i = -1; - burgs = [0]; - spacing /= 1.2; - } - } - - burgs[0] = burgsTree; - TIME && console.timeEnd("placeCapitals"); - return burgs; -} - -// For each capital create a state -function createStates(pack, burgs, config, utils) { - const {TIME, rn, each, Names, COA, getColors} = utils; - TIME && console.time("createStates"); - const {cells, cultures} = pack; - const states = [{i: 0, name: "Neutrals"}]; - const colors = getColors(burgs.length - 1); - const each5th = each(5); - - burgs.forEach((b, i) => { - if (!i) return; // skip first element - - // burgs data - b.i = b.state = i; - b.culture = cells.culture[b.cell]; - b.name = Names.getCultureShort(b.culture); - b.feature = cells.f[b.cell]; - b.capital = 1; - - // states data - const expansionism = rn(Math.random() * config.sizeVariety + 1, 1); - const basename = b.name.length < 9 && each5th(b.cell) ? b.name : Names.getCultureShort(b.culture); - const name = Names.getState(basename, b.culture); - const type = cultures[b.culture].type; - - const coa = COA.generate(null, null, null, type); - coa.shield = COA.getShield(b.culture, null); - states.push({ - i, - color: colors[i - 1], - name, - expansionism, - capital: i, - type, - center: b.cell, - culture: b.culture, - coa - }); - cells.burg[b.cell] = i; - }); - - TIME && console.timeEnd("createStates"); - return states; -} - -// place secondary settlements based on geo and economical evaluation -function placeTowns(pack, burgs, grid, config, utils) { - const {TIME, ERROR, rn, gauss, Names, graphWidth, graphHeight} = utils; - TIME && console.time("placeTowns"); - const {cells} = pack; - const score = new Int16Array(cells.s.map(s => s * gauss(1, 3, 0, 20, 3))); // a bit randomized cell score for towns placement - const sorted = cells.i - .filter(i => !cells.burg[i] && score[i] > 0 && cells.culture[i]) - .sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes - - const desiredNumber = - config.manorsInput == 1000 - ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) - : config.manorsInput; - const burgsNumber = Math.min(desiredNumber, sorted.length); // towns to generate - let burgsAdded = 0; - - const burgsTree = burgs[0]; - let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** 0.7 / 66); // min distance between towns - - while (burgsAdded < burgsNumber && spacing > 1) { - for (let i = 0; burgsAdded < burgsNumber && i < sorted.length; i++) { - if (cells.burg[sorted[i]]) continue; - const cell = sorted[i]; - const [x, y] = cells.p[cell]; - const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform - if (burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg - const burg = burgs.length; - const culture = cells.culture[cell]; - const name = Names.getCulture(culture); - burgs.push({cell, x, y, state: 0, i: burg, culture, name, capital: 0, feature: cells.f[cell]}); - burgsTree.add([x, y]); - cells.burg[cell] = burg; - burgsAdded++; - } - spacing *= 0.5; - } - - if (config.manorsInput != 1000 && burgsAdded < desiredNumber) { - ERROR && console.error(`Cannot place all burgs. Requested ${desiredNumber}, placed ${burgsAdded}`); - } - - burgs[0] = {name: undefined}; // do not store burgsTree anymore - TIME && console.timeEnd("placeTowns"); -} - -// define burg coordinates, coa, port status and define details -export const specifyBurgs = (pack, grid, utils) => { - const {TIME, rn, gauss, P, COA} = utils; - TIME && console.time("specifyBurgs"); - const {cells, features} = pack; - const temp = grid.cells.temp; - - for (const b of pack.burgs) { - if (!b.i || b.lock) continue; - const i = b.cell; - - // asign port status to some coastline burgs with temp > 0 °C - const haven = cells.haven[i]; - if (haven && temp[cells.g[i]] > 0) { - const f = cells.f[haven]; // water body id - // port is a capital with any harbor OR town with good harbor - const port = features[f].cells > 1 && ((b.capital && cells.harbor[i]) || cells.harbor[i] === 1); - b.port = port ? f : 0; // port is defined by water body id it lays on - } else b.port = 0; - - // define burg population (keep urbanization at about 10% rate) - b.population = rn(Math.max(cells.s[i] / 8 + b.i / 1000 + (i % 100) / 1000, 0.1), 3); - if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population - - if (b.port) { - b.population = b.population * 1.3; // increase port population - const [x, y] = getCloseToEdgePoint(i, haven, pack, utils); - b.x = x; - b.y = y; - } - - // add random factor - b.population = rn(b.population * gauss(2, 3, 0.6, 20, 3), 3); - - // shift burgs on rivers semi-randomly and just a bit - if (!b.port && cells.r[i]) { - const shift = Math.min(cells.fl[i] / 150, 1); - if (i % 2) b.x = rn(b.x + shift, 2); - else b.x = rn(b.x - shift, 2); - if (cells.r[i] % 2) b.y = rn(b.y + shift, 2); - else b.y = rn(b.y - shift, 2); - } - - // define emblem - const state = pack.states[b.state]; - const stateCOA = state.coa; - let kinship = 0.25; - if (b.capital) kinship += 0.1; - else if (b.port) kinship -= 0.1; - if (b.culture !== state.culture) kinship -= 0.25; - b.type = getType(i, b.port, pack, utils); - const type = b.capital && P(0.2) ? "Capital" : b.type === "Generic" ? "City" : b.type; - b.coa = COA.generate(stateCOA, kinship, null, type); - b.coa.shield = COA.getShield(b.culture, b.state); - } - - // de-assign port status if it's the only one on feature - const ports = pack.burgs.filter(b => !b.removed && b.port > 0); - for (const f of features) { - if (!f.i || f.land || f.border) continue; - const featurePorts = ports.filter(b => b.port === f.i); - if (featurePorts.length === 1) featurePorts[0].port = 0; - } - - TIME && console.timeEnd("specifyBurgs"); -}; - -export function getCloseToEdgePoint(cell1, cell2, pack, utils) { - const {cells, vertices} = pack; - const {rn} = utils; - - const [x0, y0] = cells.p[cell1]; - - const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2)); - const [x1, y1] = vertices.p[commonVertices[0]]; - const [x2, y2] = vertices.p[commonVertices[1]]; - const xEdge = (x1 + x2) / 2; - const yEdge = (y1 + y2) / 2; - - const x = rn(x0 + 0.95 * (xEdge - x0), 2); - const y = rn(y0 + 0.95 * (yEdge - y0), 2); - - return [x, y]; -} - -export const getType = (cellId, port, pack, utils) => { - const {cells, features, burgs} = pack; - - if (port) return "Naval"; - - const haven = cells.haven[cellId]; - if (haven !== undefined && features[cells.f[haven]].type === "lake") return "Lake"; - - if (cells.h[cellId] > 60) return "Highland"; - - if (cells.r[cellId] && cells.fl[cellId] >= 100) return "River"; - - const biome = cells.biome[cellId]; - const population = cells.pop[cellId]; - if (!cells.burg[cellId] || population <= 5) { - if (population < 5 && [1, 2, 3, 4].includes(biome)) return "Nomadic"; - if (biome > 4 && biome < 10) return "Hunting"; - } - - return "Generic"; -}; - -export const defineBurgFeatures = (burg, pack, utils) => { - const {P} = utils; - const {cells} = pack; - - pack.burgs - .filter(b => (burg ? b.i == burg.i : b.i && !b.removed && !b.lock)) - .forEach(b => { - const pop = b.population; - b.citadel = Number(b.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1)); - b.plaza = Number(pop > 20 || (pop > 10 && P(0.8)) || (pop > 4 && P(0.7)) || P(0.6)); - b.walls = Number(b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1)); - b.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4))); - const religion = cells.religion[b.cell]; - const theocracy = pack.states[b.state].form === "Theocracy"; - b.temple = Number( - (religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) - ); - }); -}; - -// expand cultures across the map (Dijkstra-like algorithm) -export const expandStates = (pack, grid, config, utils) => { - const {TIME, FlatQueue, minmax, biomesData} = utils; - TIME && console.time("expandStates"); - const {cells, states, cultures, burgs} = pack; - - cells.state = cells.state || new Uint16Array(cells.i.length); - - const queue = new FlatQueue(); - const cost = []; - - const globalGrowthRate = config.growthRate || 1; - const statesGrowthRate = config.statesGrowthRate || 1; - const growthRate = (cells.i.length / 2) * globalGrowthRate * statesGrowthRate; // limit cost for state growth - - // remove state from all cells except of locked - for (const cellId of cells.i) { - const state = states[cells.state[cellId]]; - if (state.lock) continue; - cells.state[cellId] = 0; - } - - for (const state of states) { - if (!state.i || state.removed) continue; - - const capitalCell = burgs[state.capital].cell; - cells.state[capitalCell] = state.i; - const cultureCenter = cultures[state.culture].center; - const b = cells.biome[cultureCenter]; // state native biome - queue.push({e: state.center, p: 0, s: state.i, b}, 0); - cost[state.center] = 1; - } - - while (queue.length) { - const next = queue.pop(); - - const {e, p, s, b} = next; - const {type, culture} = states[s]; - - cells.c[e].forEach(e => { - const state = states[cells.state[e]]; - if (state.lock) return; // do not overwrite cell of locked states - if (cells.state[e] && e === state.center) return; // do not overwrite capital cells - - const cultureCost = culture === cells.culture[e] ? -9 : 100; - const populationCost = cells.h[e] < 20 ? 0 : cells.s[e] ? Math.max(20 - cells.s[e], 0) : 5000; - const biomeCost = getBiomeCost(b, cells.biome[e], type); - const heightCost = getHeightCost(pack.features[cells.f[e]], cells.h[e], type); - const riverCost = getRiverCost(cells.r[e], e, type); - const typeCost = getTypeCost(cells.t[e], type); - const cellCost = Math.max(cultureCost + populationCost + biomeCost + heightCost + riverCost + typeCost, 0); - const totalCost = p + 10 + cellCost / states[s].expansionism; - - if (totalCost > growthRate) return; - - if (!cost[e] || totalCost < cost[e]) { - if (cells.h[e] >= 20) cells.state[e] = s; // assign state to cell - cost[e] = totalCost; - queue.push({e, p: totalCost, s, b}, totalCost); - } - }); - } - - burgs.filter(b => b.i && !b.removed).forEach(b => (b.state = cells.state[b.cell])); // assign state to burgs - - function getBiomeCost(b, biome, type) { - if (b === biome) return 10; // tiny penalty for native biome - if (type === "Hunting") return biomesData.cost[biome] * 2; // non-native biome penalty for hunters - if (type === "Nomadic" && biome > 4 && biome < 10) return biomesData.cost[biome] * 3; // forest biome penalty for nomads - return biomesData.cost[biome]; // general non-native biome penalty - } - - function getHeightCost(f, h, type) { - if (type === "Lake" && f.type === "lake") return 10; // low lake crossing penalty for Lake cultures - if (type === "Naval" && h < 20) return 300; // low sea crossing penalty for Navals - if (type === "Nomadic" && h < 20) return 10000; // giant sea crossing penalty for Nomads - if (h < 20) return 1000; // general sea crossing penalty - if (type === "Highland" && h < 62) return 1100; // penalty for highlanders on lowlands - if (type === "Highland") return 0; // no penalty for highlanders on highlands - if (h >= 67) return 2200; // general mountains crossing penalty - if (h >= 44) return 300; // general hills crossing penalty - return 0; - } - - function getRiverCost(r, i, type) { - if (type === "River") return r ? 0 : 100; // penalty for river cultures - if (!r) return 0; // no penalty for others if there is no river - return minmax(cells.fl[i] / 10, 20, 100); // river penalty from 20 to 100 based on flux - } - - function getTypeCost(t, type) { - if (t === 1) return type === "Naval" || type === "Lake" ? 0 : type === "Nomadic" ? 60 : 20; // penalty for coastline - if (t === 2) return type === "Naval" || type === "Nomadic" ? 30 : 0; // low penalty for land level 2 for Navals and nomads - if (t !== -1) return type === "Naval" || type === "Lake" ? 100 : 0; // penalty for mainland for navals - return 0; - } - - TIME && console.timeEnd("expandStates"); -}; - -export const normalizeStates = (pack, utils) => { - const {TIME} = utils; - TIME && console.time("normalizeStates"); - const {cells, burgs} = pack; - - for (const i of cells.i) { - if (cells.h[i] < 20 || cells.burg[i]) continue; // do not overwrite burgs - if (pack.states[cells.state[i]]?.lock) continue; // do not overwrite cells of locks states - if (cells.c[i].some(c => burgs[cells.burg[c]].capital)) continue; // do not overwrite near capital - const neibs = cells.c[i].filter(c => cells.h[c] >= 20); - const adversaries = neibs.filter(c => !pack.states[cells.state[c]]?.lock && cells.state[c] !== cells.state[i]); - if (adversaries.length < 2) continue; - const buddies = neibs.filter(c => !pack.states[cells.state[c]]?.lock && cells.state[c] === cells.state[i]); - if (buddies.length > 2) continue; - if (adversaries.length <= buddies.length) continue; - cells.state[i] = cells.state[adversaries[0]]; - } - TIME && console.timeEnd("normalizeStates"); -}; - -// calculate pole of inaccessibility for each state -export const getPoles = (pack, utils) => { - const {getPolesOfInaccessibility} = utils; - const getType = cellId => pack.cells.state[cellId]; - const poles = getPolesOfInaccessibility(pack, getType); - - pack.states.forEach(s => { - if (!s.i || s.removed) return; - s.pole = poles[s.i] || [0, 0]; - }); -}; - -// Resets the cultures of all burgs and states to their cell or center cell's (respectively) culture -export const updateCultures = (pack, utils) => { - const {TIME} = utils; - TIME && console.time("updateCulturesForBurgsAndStates"); - - // Assign the culture associated with the burgs cell - pack.burgs = pack.burgs.map((burg, index) => { - if (index === 0) return burg; - return {...burg, culture: pack.cells.culture[burg.cell]}; - }); - - // Assign the culture associated with the states' center cell - pack.states = pack.states.map((state, index) => { - if (index === 0) return state; - return {...state, culture: pack.cells.culture[state.center]}; - }); - - TIME && console.timeEnd("updateCulturesForBurgsAndStates"); -}; - -// calculate states data like area, population etc. -export const collectStatistics = (pack) => { - const {cells, states} = pack; - - states.forEach(s => { - if (s.removed) return; - s.cells = s.area = s.burgs = s.rural = s.urban = 0; - s.neighbors = new Set(); - }); - - for (const i of cells.i) { - if (cells.h[i] < 20) continue; - const s = cells.state[i]; - - // check for neighboring states - cells.c[i] - .filter(c => cells.h[c] >= 20 && cells.state[c] !== s) - .forEach(c => states[s].neighbors.add(cells.state[c])); - - // collect stats - states[s].cells += 1; - states[s].area += cells.area[i]; - states[s].rural += cells.pop[i]; - if (cells.burg[i]) { - states[s].urban += pack.burgs[cells.burg[i]].population; - states[s].burgs++; - } - } - - // convert neighbors Set object into array - states.forEach(s => { - if (!s.neighbors) return; - s.neighbors = Array.from(s.neighbors); - }); -}; - -export const assignColors = (pack, utils) => { - const {TIME, getRandomColor, getMixedColor} = utils; - TIME && console.time("assignColors"); - const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2; - - // assign basic color using greedy coloring algorithm - pack.states.forEach(s => { - if (!s.i || s.removed || s.lock) return; - const neibs = s.neighbors; - s.color = colors.find(c => neibs.every(n => pack.states[n].color !== c)); - if (!s.color) s.color = getRandomColor(); - colors.push(colors.shift()); - }); - - // randomize each already used color a bit - colors.forEach(c => { - const sameColored = pack.states.filter(s => s.color === c && !s.lock); - sameColored.forEach((s, d) => { - if (!d) return; - s.color = getMixedColor(s.color); - }); - }); - - TIME && console.timeEnd("assignColors"); -}; - -const wars = { - War: 6, - Conflict: 2, - Campaign: 4, - Invasion: 2, - Rebellion: 2, - Conquest: 2, - Intervention: 1, - Expedition: 1, - Crusade: 1 -}; - -export const generateCampaign = (state, pack, utils) => { - const {P, gauss, rw, getAdjective, Names, options} = utils; - const neighbors = state.neighbors.length ? state.neighbors : [0]; - return neighbors - .map(i => { - const name = i && P(0.8) ? pack.states[i].name : Names.getCultureShort(state.culture); - const start = gauss(options.year - 100, 150, 1, options.year - 6); - const end = start + gauss(4, 5, 1, options.year - start - 1); - return {name: getAdjective(name) + " " + rw(wars), start, end}; - }) - .sort((a, b) => a.start - b.start); -}; - -// generate historical conflicts of each state -export const generateCampaigns = (pack, utils) => { - pack.states.forEach(s => { - if (!s.i || s.removed) return; - s.campaigns = generateCampaign(s, pack, utils); - }); -}; - -// generate Diplomatic Relationships -export const generateDiplomacy = (pack, utils) => { - const {TIME, d3, P, ra, gauss, rw, trimVowels, options} = utils; - TIME && console.time("generateDiplomacy"); - const {cells, states} = pack; - const chronicle = (states[0].diplomacy = []); - const valid = states.filter(s => s.i && !states.removed); - - const neibs = {Ally: 1, Friendly: 2, Neutral: 1, Suspicion: 10, Rival: 9}; // relations to neighbors - const neibsOfNeibs = {Ally: 10, Friendly: 8, Neutral: 5, Suspicion: 1}; // relations to neighbors of neighbors - const far = {Friendly: 1, Neutral: 12, Suspicion: 2, Unknown: 6}; // relations to other - const navals = {Neutral: 1, Suspicion: 2, Rival: 1, Unknown: 1}; // relations of naval powers - - valid.forEach(s => (s.diplomacy = new Array(states.length).fill("x"))); // clear all relationships - if (valid.length < 2) return; // no states to renerate relations with - const areaMean = d3.mean(valid.map(s => s.area)); // average state area - - // generic relations - for (let f = 1; f < states.length; f++) { - if (states[f].removed) continue; - - if (states[f].diplomacy.includes("Vassal")) { - // Vassals copy relations from their Suzerains - const suzerain = states[f].diplomacy.indexOf("Vassal"); - - for (let i = 1; i < states.length; i++) { - if (i === f || i === suzerain) continue; - states[f].diplomacy[i] = states[suzerain].diplomacy[i]; - if (states[suzerain].diplomacy[i] === "Suzerain") states[f].diplomacy[i] = "Ally"; - for (let e = 1; e < states.length; e++) { - if (e === f || e === suzerain) continue; - if (states[e].diplomacy[suzerain] === "Suzerain" || states[e].diplomacy[suzerain] === "Vassal") continue; - states[e].diplomacy[f] = states[e].diplomacy[suzerain]; - } - } - continue; - } - - for (let t = f + 1; t < states.length; t++) { - if (states[t].removed) continue; - - if (states[t].diplomacy.includes("Vassal")) { - const suzerain = states[t].diplomacy.indexOf("Vassal"); - states[f].diplomacy[t] = states[f].diplomacy[suzerain]; - continue; - } - - const naval = - states[f].type === "Naval" && - states[t].type === "Naval" && - cells.f[states[f].center] !== cells.f[states[t].center]; - const neib = naval ? false : states[f].neighbors.includes(t); - const neibOfNeib = - naval || neib - ? false - : states[f].neighbors - .map(n => states[n].neighbors) - .join("") - .includes(t); - - let status = naval ? rw(navals) : neib ? rw(neibs) : neibOfNeib ? rw(neibsOfNeibs) : rw(far); - - // add Vassal - if ( - neib && - P(0.8) && - states[f].area > areaMean && - states[t].area < areaMean && - states[f].area / states[t].area > 2 - ) - status = "Vassal"; - states[f].diplomacy[t] = status === "Vassal" ? "Suzerain" : status; - states[t].diplomacy[f] = status; - } - } - - // declare wars - for (let attacker = 1; attacker < states.length; attacker++) { - const ad = states[attacker].diplomacy; // attacker relations; - if (states[attacker].removed) continue; - if (!ad.includes("Rival")) continue; // no rivals to attack - if (ad.includes("Vassal")) continue; // not independent - if (ad.includes("Enemy")) continue; // already at war - - // random independent rival - const defender = ra( - ad.map((r, d) => (r === "Rival" && !states[d].diplomacy.includes("Vassal") ? d : 0)).filter(d => d) - ); - let ap = states[attacker].area * states[attacker].expansionism; - let dp = states[defender].area * states[defender].expansionism; - if (ap < dp * gauss(1.6, 0.8, 0, 10, 2)) continue; // defender is too strong - - const an = states[attacker].name; - const dn = states[defender].name; // names - const attackers = [attacker]; - const defenders = [defender]; // attackers and defenders array - const dd = states[defender].diplomacy; // defender relations; - - // start an ongoing war - const name = `${an}-${trimVowels(dn)}ian War`; - const start = options.year - gauss(2, 3, 0, 10); - const war = [name, `${an} declared a war on its rival ${dn}`]; - const campaign = {name, start, attacker, defender}; - states[attacker].campaigns.push(campaign); - states[defender].campaigns.push(campaign); - - // attacker vassals join the war - ad.forEach((r, d) => { - if (r === "Suzerain") { - attackers.push(d); - war.push(`${an}'s vassal ${states[d].name} joined the war on attackers side`); - } - }); - - // defender vassals join the war - dd.forEach((r, d) => { - if (r === "Suzerain") { - defenders.push(d); - war.push(`${dn}'s vassal ${states[d].name} joined the war on defenders side`); - } - }); - - ap = d3.sum(attackers.map(a => states[a].area * states[a].expansionism)); // attackers joined power - dp = d3.sum(defenders.map(d => states[d].area * states[d].expansionism)); // defender joined power - - // defender allies join - dd.forEach((r, d) => { - if (r !== "Ally" || states[d].diplomacy.includes("Vassal")) return; - if (states[d].diplomacy[attacker] !== "Rival" && ap / dp > 2 * gauss(1.6, 0.8, 0, 10, 2)) { - const reason = states[d].diplomacy.includes("Enemy") ? "Being already at war," : `Frightened by ${an},`; - war.push(`${reason} ${states[d].name} severed the defense pact with ${dn}`); - dd[d] = states[d].diplomacy[defender] = "Suspicion"; - return; - } - defenders.push(d); - dp += states[d].area * states[d].expansionism; - war.push(`${dn}'s ally ${states[d].name} joined the war on defenders side`); - - // ally vassals join - states[d].diplomacy - .map((r, d) => (r === "Suzerain" ? d : 0)) - .filter(d => d) - .forEach(v => { - defenders.push(v); - dp += states[v].area * states[v].expansionism; - war.push(`${states[d].name}'s vassal ${states[v].name} joined the war on defenders side`); - }); - }); - - // attacker allies join if the defender is their rival or joined power > defenders power and defender is not an ally - ad.forEach((r, d) => { - if (r !== "Ally" || states[d].diplomacy.includes("Vassal") || defenders.includes(d)) return; - const name = states[d].name; - if (states[d].diplomacy[defender] !== "Rival" && (P(0.2) || ap <= dp * 1.2)) { - war.push(`${an}'s ally ${name} avoided entering the war`); - return; - } - const allies = states[d].diplomacy.map((r, d) => (r === "Ally" ? d : 0)).filter(d => d); - if (allies.some(ally => defenders.includes(ally))) { - war.push(`${an}'s ally ${name} did not join the war as its allies are in war on both sides`); - return; - } - - attackers.push(d); - ap += states[d].area * states[d].expansionism; - war.push(`${an}'s ally ${name} joined the war on attackers side`); - - // ally vassals join - states[d].diplomacy - .map((r, d) => (r === "Suzerain" ? d : 0)) - .filter(d => d) - .forEach(v => { - attackers.push(v); - dp += states[v].area * states[v].expansionism; - war.push(`${states[d].name}'s vassal ${states[v].name} joined the war on attackers side`); - }); - }); - - // change relations to Enemy for all participants - attackers.forEach(a => defenders.forEach(d => (states[a].diplomacy[d] = states[d].diplomacy[a] = "Enemy"))); - chronicle.push(war); // add a record to diplomatical history - } - - TIME && console.timeEnd("generateDiplomacy"); -}; - -// select a forms for listed or all valid states -export const defineStateForms = (list, pack, utils) => { - const {TIME, d3, P, rw, rand, trimVowels, getAdjective} = utils; - TIME && console.time("defineStateForms"); - const states = pack.states.filter(s => s.i && !s.removed && !s.lock); - if (states.length < 1) return; - - const generic = {Monarchy: 25, Republic: 2, Union: 1}; - const naval = {Monarchy: 25, Republic: 8, Union: 3}; - - const median = d3.median(pack.states.map(s => s.area)); - const empireMin = states.map(s => s.area).sort((a, b) => b - a)[Math.max(Math.ceil(states.length ** 0.4) - 2, 0)]; - const expTiers = pack.states.map(s => { - let tier = Math.min(Math.floor((s.area / median) * 2.6), 4); - if (tier === 4 && s.area < empireMin) tier = 3; - return tier; - }); - - const monarchy = ["Duchy", "Grand Duchy", "Principality", "Kingdom", "Empire"]; // per expansionism tier - const republic = { - Republic: 75, - Federation: 4, - "Trade Company": 4, - "Most Serene Republic": 2, - Oligarchy: 2, - Tetrarchy: 1, - Triumvirate: 1, - Diarchy: 1, - Junta: 1 - }; // weighted random - const union = { - Union: 3, - League: 4, - Confederation: 1, - "United Kingdom": 1, - "United Republic": 1, - "United Provinces": 2, - Commonwealth: 1, - Heptarchy: 1 - }; // weighted random - const theocracy = {Theocracy: 20, Brotherhood: 1, Thearchy: 2, See: 1, "Holy State": 1}; - const anarchy = {"Free Territory": 2, Council: 3, Commune: 1, Community: 1}; - - for (const s of states) { - if (list && !list.includes(s.i)) continue; - const tier = expTiers[s.i]; - - const religion = pack.cells.religion[s.center]; - const isTheocracy = - (religion && pack.religions[religion].expansion === "state") || - (P(0.1) && ["Organized", "Cult"].includes(pack.religions[religion].type)); - const isAnarchy = P(0.01 - tier / 500); - - if (isTheocracy) s.form = "Theocracy"; - else if (isAnarchy) s.form = "Anarchy"; - else s.form = s.type === "Naval" ? rw(naval) : rw(generic); - s.formName = selectForm(s, tier, pack, utils); - s.fullName = getFullName(s, utils); - } - - function selectForm(s, tier, pack, utils) { - const {P, rand, rw, trimVowels} = utils; - const base = pack.cultures[s.culture].base; - - if (s.form === "Monarchy") { - const form = monarchy[tier]; - // Default name depends on exponent tier, some culture bases have special names for tiers - if (s.diplomacy) { - if ( - form === "Duchy" && - s.neighbors.length > 1 && - rand(6) < s.neighbors.length && - s.diplomacy.includes("Vassal") - ) - return "Marches"; // some vassal duchies on borderland - if (base === 1 && P(0.3) && s.diplomacy.includes("Vassal")) return "Dominion"; // English vassals - if (P(0.3) && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals - } - - if (base === 31 && (form === "Empire" || form === "Kingdom")) return "Khanate"; // Mongolian - if (base === 16 && form === "Principality") return "Beylik"; // Turkic - if (base === 5 && (form === "Empire" || form === "Kingdom")) return "Tsardom"; // Ruthenian - if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic - if (base === 12 && (form === "Kingdom" || form === "Grand Duchy")) return "Shogunate"; // Japanese - if ([18, 17].includes(base) && form === "Empire") return "Caliphate"; // Arabic, Berber - if (base === 18 && (form === "Grand Duchy" || form === "Duchy")) return "Emirate"; // Arabic - if (base === 7 && (form === "Grand Duchy" || form === "Duchy")) return "Despotate"; // Greek - if (base === 31 && (form === "Grand Duchy" || form === "Duchy")) return "Ulus"; // Mongolian - if (base === 16 && (form === "Grand Duchy" || form === "Duchy")) return "Horde"; // Turkic - if (base === 24 && (form === "Grand Duchy" || form === "Duchy")) return "Satrapy"; // Iranian - return form; - } - - if (s.form === "Republic") { - // Default name is from weighted array, special case for small states with only 1 burg - if (tier < 2 && s.burgs === 1) { - if (trimVowels(s.name) === trimVowels(pack.burgs[s.capital].name)) { - s.name = pack.burgs[s.capital].name; - return "Free City"; - } - if (P(0.3)) return "City-state"; - } - return rw(republic); - } - - if (s.form === "Union") return rw(union); - if (s.form === "Anarchy") return rw(anarchy); - - if (s.form === "Theocracy") { - // European - if ([0, 1, 2, 3, 4, 6, 8, 9, 13, 15, 20].includes(base)) { - if (P(0.1)) return "Divine " + monarchy[tier]; - if (tier < 2 && P(0.5)) return "Diocese"; - if (tier < 2 && P(0.5)) return "Bishopric"; - } - if (P(0.9) && [7, 5].includes(base)) { - // Greek, Ruthenian - if (tier < 2) return "Eparchy"; - if (tier === 2) return "Exarchate"; - if (tier > 2) return "Patriarchate"; - } - if (P(0.9) && [21, 16].includes(base)) return "Imamah"; // Nigerian, Turkish - if (tier > 2 && P(0.8) && [18, 17, 28].includes(base)) return "Caliphate"; // Arabic, Berber, Swahili - return rw(theocracy); - } - } - - TIME && console.timeEnd("defineStateForms"); -}; - -// state forms requiring Adjective + Name, all other forms use scheme Form + Of + Name -const adjForms = [ - "Empire", - "Sultanate", - "Khaganate", - "Shogunate", - "Caliphate", - "Despotate", - "Theocracy", - "Oligarchy", - "Union", - "Confederation", - "Trade Company", - "League", - "Tetrarchy", - "Triumvirate", - "Diarchy", - "Horde", - "Marches" -]; - -export const getFullName = (state, utils) => { - const {getAdjective} = utils; - if (!state.formName) return state.name; - if (!state.name && state.formName) return "The " + state.formName; - const adjName = adjForms.includes(state.formName) && !/-| /.test(state.name); - return adjName ? `${getAdjective(state.name)} ${state.formName}` : `${state.formName} of ${state.name}`; -}; diff --git a/procedural/src/engine/modules/coa-generator.js b/procedural/src/engine/modules/coa-generator.js deleted file mode 100644 index 814e5da2..00000000 --- a/procedural/src/engine/modules/coa-generator.js +++ /dev/null @@ -1,2218 +0,0 @@ -"use strict"; - -// Import required utility functions -import { P } from "../utils/probabilityUtils.js"; - -const tinctures = { - field: {metals: 3, colours: 4, stains: +P(0.03), patterns: 1}, - division: {metals: 5, colours: 8, stains: +P(0.03), patterns: 1}, - charge: {metals: 2, colours: 3, stains: +P(0.05), patterns: 0}, - metals: {argent: 3, or: 2}, - colours: {gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2}, - stains: {murrey: 1, sanguine: 1, tenné: 1}, - patterns: { - semy: 8, - ermine: 6, - vair: 4, - counterVair: 1, - vairInPale: 1, - vairEnPointe: 2, - vairAncien: 2, - potent: 2, - counterPotent: 1, - potentInPale: 1, - potentEnPointe: 1, - chequy: 8, - lozengy: 5, - fusily: 2, - pally: 8, - barry: 10, - gemelles: 1, - bendy: 8, - bendySinister: 4, - palyBendy: 2, - barryBendy: 1, - pappellony: 2, - pappellony2: 3, - scaly: 1, - plumetty: 1, - masoned: 6, - fretty: 3, - grillage: 1, - chainy: 1, - maily: 2, - honeycombed: 1 - } -}; - -const chargeData = { - agnusDei: { - colors: 2, - sinister: true - }, - angel: { - colors: 2, - positions: {e: 1} - }, - anvil: { - sinister: true - }, - apple: { - colors: 2 - }, - arbalest: { - colors: 3, - reversed: true - }, - archer: { - colors: 3, - sinister: true - }, - armEmbowedHoldingSabre: { - colors: 3, - sinister: true - }, - armEmbowedVambraced: { - sinister: true - }, - armEmbowedVambracedHoldingSword: { - colors: 3, - sinister: true - }, - armillarySphere: { - positions: {e: 1} - }, - arrow: { - colors: 3, - reversed: true - }, - arrowsSheaf: { - colors: 3, - reversed: true - }, - axe: { - colors: 2, - sinister: true - }, - badgerStatant: { - colors: 2, - sinister: true - }, - banner: { - colors: 2 - }, - basilisk: { - colors: 3, - sinister: true - }, - bearPassant: { - colors: 3, - sinister: true - }, - bearRampant: { - colors: 3, - sinister: true - }, - bee: { - colors: 3, - reversed: true - }, - bell: { - colors: 2 - }, - boarHeadErased: { - colors: 3, - sinister: true - }, - boarRampant: { - colors: 3, - sinister: true, - positions: {e: 12, beh: 1, kn: 1, jln: 2} - }, - boat: { - colors: 2 - }, - bookClosed: { - colors: 3, - sinister: true - }, - bookClosed2: { - sinister: true - }, - bookOpen: { - colors: 3 - }, - bow: { - sinister: true - }, - bowWithArrow: { - colors: 3, - reversed: true - }, - bowWithThreeArrows: { - colors: 3 - }, - bucket: { - colors: 2 - }, - bugleHorn: { - colors: 2 - }, - bugleHorn2: { - colors: 2 - }, - bullHeadCaboshed: { - colors: 2 - }, - bullPassant: { - colors: 3, - sinister: true - }, - butterfly: { - colors: 3, - reversed: true - }, - camel: { - colors: 2, - sinister: true - }, - cancer: { - reversed: true - }, - cannon: { - colors: 2, - sinister: true - }, - caravel: { - colors: 3, - sinister: true - }, - castle: { - colors: 2 - }, - castle2: { - colors: 3 - }, - catPassantGuardant: { - colors: 2, - sinister: true - }, - cavalier: { - colors: 3, - sinister: true, - positions: {e: 1} - }, - centaur: { - colors: 3, - sinister: true - }, - chalice: { - colors: 2 - }, - cinquefoil: { - reversed: true - }, - cock: { - colors: 3, - sinister: true - }, - comet: { - reversed: true - }, - cowStatant: { - colors: 3, - sinister: true - }, - cossack: { - colors: 3, - sinister: true - }, - crescent: { - reversed: true - }, - crocodile: { - colors: 2, - sinister: true - }, - crosier: { - sinister: true - }, - crossbow: { - colors: 3, - sinister: true - }, - crossGamma: { - sinister: true - }, - crossLatin: { - reversed: true - }, - crossTau: { - reversed: true - }, - crossTriquetra: { - reversed: true - }, - crown: { - colors: 2, - positions: { - e: 10, - abcdefgzi: 1, - beh: 3, - behdf: 2, - acegi: 1, - kn: 1, - pq: 2, - abc: 1, - jln: 4, - jleh: 1, - def: 2, - abcpqh: 3 - } - }, - crown2: { - colors: 3, - positions: { - e: 10, - abcdefgzi: 1, - beh: 3, - behdf: 2, - acegi: 1, - kn: 1, - pq: 2, - abc: 1, - jln: 4, - jleh: 1, - def: 2, - abcpqh: 3 - } - }, - deerHeadCaboshed: { - colors: 2 - }, - dolphin: { - colors: 2, - sinister: true - }, - donkeyHeadCaboshed: { - colors: 2 - }, - dove: { - colors: 2, - natural: "argent", - sinister: true - }, - doveDisplayed: { - colors: 2, - natural: "argent", - sinister: true - }, - dragonfly: { - colors: 2, - reversed: true - }, - dragonPassant: { - colors: 3, - sinister: true - }, - dragonRampant: { - colors: 3, - sinister: true - }, - drakkar: { - colors: 3, - sinister: true - }, - drawingCompass: { - sinister: true - }, - drum: { - colors: 3 - }, - duck: { - colors: 3, - sinister: true - }, - eagle: { - colors: 3, - sinister: true, - positions: {e: 15, beh: 1, kn: 1, abc: 1, jlh: 2, def: 2, pq: 1} - }, - eagleTwoHeads: { - colors: 3 - }, - elephant: { - colors: 2, - sinister: true - }, - elephantHeadErased: { - colors: 2, - sinister: true - }, - falchion: { - colors: 2, - reversed: true - }, - falcon: { - colors: 3, - sinister: true - }, - fan: { - colors: 2, - reversed: true - }, - fasces: { - colors: 3, - sinister: true - }, - feather: { - sinister: true - }, - flamberge: { - colors: 2, - reversed: true - }, - flangedMace: { - reversed: true - }, - fly: { - colors: 3, - reversed: true - }, - foot: { - sinister: true - }, - fountain: { - natural: "azure" - }, - frog: { - reversed: true - }, - garb: { - colors: 2, - natural: "or", - positions: {e: 1, def: 3, abc: 2, beh: 1, kn: 1, jln: 3, jleh: 1, abcpqh: 1, joe: 1, lme: 1} - }, - gauntlet: { - sinister: true, - reversed: true - }, - goat: { - colors: 3, - sinister: true - }, - goutte: { - reversed: true - }, - grapeBunch: { - colors: 3, - sinister: true - }, - grapeBunch2: { - colors: 3, - sinister: true - }, - grenade: { - colors: 2 - }, - greyhoundCourant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1} - }, - greyhoundRampant: { - colors: 2, - sinister: true, - positions: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1} - }, - greyhoundSejant: { - colors: 3, - sinister: true - }, - griffinPassant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1} - }, - griffinRampant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1} - }, - hand: { - sinister: true, - reversed: true, - positions: {e: 10, jln: 2, kn: 1, jeo: 1, abc: 2, pqe: 1} - }, - harp: { - colors: 2, - sinister: true - }, - hatchet: { - colors: 2, - sinister: true - }, - head: { - colors: 2, - sinister: true, - positions: {e: 1} - }, - headWreathed: { - colors: 3, - sinister: true, - positions: {e: 1} - }, - hedgehog: { - colors: 3, - sinister: true - }, - helmet: { - sinister: true - }, - helmetCorinthian: { - colors: 3, - sinister: true - }, - helmetGreat: { - sinister: true - }, - helmetZischagge: { - sinister: true - }, - heron: { - colors: 2, - sinister: true - }, - hindStatant: { - colors: 2, - sinister: true - }, - hook: { - sinister: true - }, - horseHeadCouped: { - sinister: true - }, - horsePassant: { - colors: 2, - sinister: true - }, - horseRampant: { - colors: 3, - sinister: true - }, - horseSalient: { - colors: 2, - sinister: true - }, - horseshoe: { - reversed: true - }, - hourglass: { - colors: 3 - }, - ladybird: { - colors: 3, - reversed: true - }, - lamb: { - colors: 2, - sinister: true - }, - lambPassantReguardant: { - colors: 2, - sinister: true - }, - lanceWithBanner: { - colors: 3, - sinister: true - }, - laurelWreath: { - colors: 2 - }, - lighthouse: { - colors: 3 - }, - lionHeadCaboshed: { - colors: 2 - }, - lionHeadErased: { - colors: 2, - sinister: true - }, - lionPassant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1} - }, - lionPassantGuardant: { - colors: 3, - sinister: true - }, - lionRampant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1} - }, - lionSejant: { - colors: 3, - sinister: true - }, - lizard: { - reversed: true - }, - lochaberAxe: { - colors: 2, - sinister: true - }, - log: { - sinister: true - }, - lute: { - colors: 2, - sinister: true - }, - lymphad: { - colors: 3, - sinister: true, - positions: {e: 1} - }, - mace: { - colors: 2 - }, - maces: { - colors: 2 - }, - mallet: { - colors: 2 - }, - mantle: { - colors: 3 - }, - martenCourant: { - colors: 3, - sinister: true - }, - mascle: { - positions: { - e: 15, - abcdefgzi: 3, - beh: 3, - bdefh: 4, - acegi: 1, - kn: 3, - joe: 2, - abc: 3, - jlh: 8, - jleh: 1, - df: 3, - abcpqh: 4, - pqe: 3, - eknpq: 3 - } - }, - mastiffStatant: { - colors: 3, - sinister: true - }, - mitre: { - colors: 3 - }, - monk: { - sinister: true - }, - moonInCrescent: { - sinister: true - }, - mullet: { - reversed: true - }, - mullet7: { - reversed: true - }, - oak: { - colors: 3 - }, - orb: { - colors: 3 - }, - ouroboros: { - sinister: true - }, - owl: { - colors: 2, - sinister: true - }, - owlDisplayed: { - colors: 2 - }, - palmTree: { - colors: 3 - }, - parrot: { - colors: 2, - sinister: true - }, - peacock: { - colors: 3, - sinister: true - }, - peacockInPride: { - colors: 3, - sinister: true - }, - pear: { - colors: 2 - }, - pegasus: { - colors: 3, - sinister: true - }, - pike: { - colors: 2, - sinister: true - }, - pineTree: { - colors: 2 - }, - plaice: { - colors: 2, - sinister: true - }, - plough: { - colors: 2, - sinister: true - }, - ploughshare: { - sinister: true - }, - porcupine: { - colors: 2, - sinister: true - }, - portcullis: { - colors: 2 - }, - rabbitSejant: { - colors: 2, - sinister: true - }, - rake: { - reversed: true - }, - rapier: { - colors: 2, - sinister: true, - reversed: true - }, - ramHeadErased: { - colors: 3, - sinister: true - }, - ramPassant: { - colors: 3, - sinister: true - }, - ratRampant: { - colors: 2, - sinister: true - }, - raven: { - colors: 2, - natural: "sable", - sinister: true, - positions: {e: 15, beh: 1, kn: 1, jeo: 1, abc: 3, jln: 3, def: 1} - }, - rhinoceros: { - colors: 2, - sinister: true - }, - rose: { - colors: 3 - }, - sabre: { - colors: 2, - sinister: true - }, - sabre2: { - colors: 2, - sinister: true, - reversed: true - }, - sabresCrossed: { - colors: 2, - reversed: true - }, - sagittarius: { - colors: 3, - sinister: true - }, - salmon: { - colors: 2, - sinister: true - }, - saw: { - colors: 2 - }, - scale: { - colors: 2 - }, - scaleImbalanced: { - colors: 2, - sinister: true - }, - scissors: { - reversed: true - }, - scorpion: { - reversed: true - }, - scrollClosed: { - colors: 2, - sinister: true - }, - scythe: { - colors: 2, - sinister: true, - reversed: true - }, - scythe2: { - sinister: true - }, - serpent: { - colors: 2, - sinister: true - }, - shield: { - colors: 2, - sinister: true - }, - sickle: { - colors: 2, - sinister: true, - reversed: true - }, - snail: { - colors: 2, - sinister: true - }, - snake: { - colors: 2, - sinister: true - }, - spear: { - colors: 2, - reversed: true - }, - spiral: { - sinister: true, - reversed: true - }, - squirrel: { - sinister: true - }, - stagLodgedRegardant: { - colors: 3, - sinister: true - }, - stagPassant: { - colors: 2, - sinister: true - }, - stirrup: { - colors: 2 - }, - swallow: { - colors: 2, - sinister: true - }, - swan: { - colors: 3, - sinister: true - }, - swanErased: { - colors: 3, - sinister: true - }, - sword: { - colors: 2, - reversed: true - }, - talbotPassant: { - colors: 3, - sinister: true - }, - talbotSejant: { - colors: 3, - sinister: true - }, - tower: { - colors: 2 - }, - tree: { - positions: {e: 1} - }, - trefoil: { - reversed: true - }, - trowel: { - colors: 2, - sinister: true, - reversed: true - }, - unicornRampant: { - colors: 3, - sinister: true - }, - wasp: { - colors: 3, - reversed: true - }, - wheatStalk: { - colors: 2 - }, - windmill: { - colors: 3, - sinister: true - }, - wing: { - sinister: true - }, - wingSword: { - colors: 3, - sinister: true - }, - wolfHeadErased: { - colors: 2, - sinister: true - }, - wolfPassant: { - colors: 3, - sinister: true, - positions: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1} - }, - wolfRampant: { - colors: 3, - sinister: true - }, - wolfStatant: { - colors: 3, - sinister: true - }, - wyvern: { - colors: 3, - sinister: true, - positions: {e: 10, jln: 1} - }, - wyvernWithWingsDisplayed: { - colors: 3, - sinister: true - } -}; - -const charges = { - types: { - conventional: 33, // 40 charges - crosses: 13, // 30 charges - beasts: 7, // 41 charges - beastHeads: 3, // 10 charges - birds: 3, // 16 charges - reptiles: 2, // 5 charges - bugs: 2, // 8 charges - fishes: 1, // 3 charges - molluscs: 1, // 2 charges - plants: 3, // 18 charges - fantastic: 5, // 14 charges - agriculture: 2, // 8 charges - arms: 5, // 32 charges - bodyparts: 2, // 12 charges - people: 2, // 4 charges - architecture: 3, // 11 charges - seafaring: 3, // 9 charges - tools: 3, // 15 charges - miscellaneous: 5, // 30 charges - inescutcheon: 3, // 43 charges - ornaments: 0, // 9 charges - uploaded: 0 - }, - single: { - conventional: 10, - crosses: 8, - beasts: 7, - beastHeads: 3, - birds: 3, - reptiles: 2, - bugs: 2, - fishes: 1, - molluscs: 1, - plants: 3, - fantastic: 5, - agriculture: 2, - arms: 5, - bodyparts: 2, - people: 2, - architecture: 3, - seafaring: 3, - tools: 3, - miscellaneous: 5, - inescutcheon: 1 - }, - semy: { - conventional: 4, - crosses: 1 - }, - conventional: { - annulet: 4, - billet: 5, - carreau: 1, - comet: 1, - compassRose: 1, - crescent: 5, - delf: 0, - estoile: 1, - fleurDeLis: 6, - fountain: 1, - fusil: 4, - gear: 1, - goutte: 4, - heart: 4, - lozenge: 2, - lozengeFaceted: 3, - lozengePloye: 1, - mascle: 4, - moonInCrescent: 1, - mullet: 5, - mullet10: 1, - mullet4: 3, - mullet6: 4, - mullet6Faceted: 1, - mullet6Pierced: 1, - mullet7: 1, - mullet8: 1, - mulletFaceted: 1, - mulletPierced: 1, - pique: 2, - roundel: 4, - roundel2: 3, - rustre: 2, - spiral: 1, - sun: 3, - sunInSplendour: 1, - sunInSplendour2: 1, - trefle: 2, - triangle: 3, - trianglePierced: 1 - }, - crosses: { - crossHummetty: 15, - crossVoided: 1, - crossPattee: 2, - crossPatteeAlisee: 1, - crossFormee: 1, - crossFormee2: 2, - crossPotent: 2, - crossJerusalem: 1, - crosslet: 1, - crossClechy: 3, - crossBottony: 1, - crossFleury: 3, - crossPatonce: 1, - crossPommy: 1, - crossGamma: 1, - crossArrowed: 1, - crossFitchy: 1, - crossCercelee: 1, - crossMoline: 2, - crossFourchy: 1, - crossAvellane: 1, - crossErminee: 1, - crossBiparted: 1, - crossMaltese: 3, - crossTemplar: 2, - crossCeltic: 1, - crossCeltic2: 1, - crossTriquetra: 1, - crossCarolingian: 1, - crossOccitan: 1, - crossSaltire: 3, - crossBurgundy: 1, - crossLatin: 3, - crossPatriarchal: 1, - crossOrthodox: 1, - crossCalvary: 1, - crossDouble: 1, - crossTau: 1, - crossSantiago: 1, - crossAnkh: 1 - }, - beasts: { - agnusDei: 1, - badgerStatant: 1, - bearPassant: 1, - bearRampant: 3, - boarRampant: 1, - bullPassant: 1, - camel: 1, - catPassantGuardant: 1, - cowStatant: 1, - dolphin: 1, - elephant: 1, - goat: 1, - greyhoundCourant: 1, - greyhoundRampant: 1, - greyhoundSejant: 1, - hedgehog: 1, - hindStatant: 1, - horsePassant: 1, - horseRampant: 2, - horseSalient: 1, - lamb: 1, - lambPassantReguardant: 1, - lionPassant: 3, - lionPassantGuardant: 2, - lionRampant: 7, - lionSejant: 2, - martenCourant: 1, - mastiffStatant: 1, - porcupine: 1, - rabbitSejant: 1, - ramPassant: 1, - ratRampant: 1, - rhinoceros: 1, - squirrel: 1, - stagLodgedRegardant: 1, - stagPassant: 1, - talbotPassant: 1, - talbotSejant: 1, - wolfPassant: 1, - wolfRampant: 1, - wolfStatant: 1 - }, - beastHeads: { - boarHeadErased: 1, - bullHeadCaboshed: 1, - deerHeadCaboshed: 1, - donkeyHeadCaboshed: 1, - elephantHeadErased: 1, - horseHeadCouped: 1, - lionHeadCaboshed: 2, - lionHeadErased: 2, - ramHeadErased: 1, - wolfHeadErased: 2 - }, - birds: { - cock: 3, - dove: 2, - doveDisplayed: 1, - duck: 1, - eagle: 9, - falcon: 2, - heron: 1, - owl: 1, - owlDisplayed: 1, - parrot: 1, - peacock: 1, - peacockInPride: 1, - raven: 2, - swallow: 1, - swan: 2, - swanErased: 1 - }, - reptiles: { - crocodile: 1, - frog: 1, - lizard: 1, - ouroboros: 1, - snake: 1 - }, - bugs: { - bee: 1, - butterfly: 1, - cancer: 1, - dragonfly: 1, - fly: 1, - ladybird: 1, - scorpion: 1, - wasp: 1 - }, - fishes: { - pike: 1, - plaice: 1, - salmon: 1 - }, - molluscs: { - escallop: 4, - snail: 1 - }, - plants: { - apple: 1, - cinquefoil: 1, - earOfWheat: 1, - grapeBunch: 1, - grapeBunch2: 1, - mapleLeaf: 1, - oak: 1, - palmTree: 1, - pear: 1, - pineCone: 1, - pineTree: 1, - quatrefoil: 1, - rose: 1, - sextifoil: 1, - thistle: 1, - tree: 1, - trefoil: 1, - wheatStalk: 1 - }, - fantastic: { - angel: 3, - basilisk: 1, - centaur: 1, - dragonPassant: 3, - dragonRampant: 2, - eagleTwoHeads: 2, - griffinPassant: 1, - griffinRampant: 2, - pegasus: 1, - sagittarius: 1, - serpent: 1, - unicornRampant: 1, - wyvern: 1, - wyvernWithWingsDisplayed: 1 - }, - agriculture: { - garb: 2, - millstone: 1, - plough: 1, - ploughshare: 1, - rake: 1, - scythe: 1, - scythe2: 1, - sickle: 1 - }, - arms: { - arbalest: 1, - arbalest2: 1, - arrow: 1, - arrowsSheaf: 1, - axe: 3, - bow: 1, - bowWithArrow: 2, - bowWithThreeArrows: 1, - cannon: 1, - falchion: 1, - flamberge: 1, - flangedMace: 1, - gauntlet: 1, - grenade: 1, - hatchet: 3, - helmet: 2, - helmetCorinthian: 1, - helmetGreat: 2, - helmetZischagge: 1, - lanceHead: 1, - lanceWithBanner: 1, - lochaberAxe: 1, - mace: 1, - maces: 1, - mallet: 1, - rapier: 1, - sabre: 1, - sabre2: 1, - sabresCrossed: 1, - shield: 1, - spear: 1, - sword: 4 - }, - bodyparts: { - armEmbowedHoldingSabre: 1, - armEmbowedVambraced: 1, - armEmbowedVambracedHoldingSword: 1, - bone: 1, - crossedBones: 2, - foot: 1, - hand: 4, - head: 1, - headWreathed: 1, - skeleton: 2, - skull: 2, - skull2: 1 - }, - people: { - archer: 1, - cavalier: 3, - cossack: 1, - monk: 1 - }, - architecture: { - bridge: 1, - bridge2: 1, - castle: 2, - castle2: 1, - column: 1, - lighthouse: 1, - palace: 1, - pillar: 1, - portcullis: 1, - tower: 2, - windmill: 1 - }, - seafaring: { - anchor: 6, - armillarySphere: 1, - boat: 2, - boat2: 1, - caravel: 1, - drakkar: 1, - lymphad: 2, - raft: 1, - shipWheel: 1 - }, - tools: { - anvil: 2, - drawingCompass: 2, - fan: 1, - hook: 1, - ladder: 1, - ladder2: 1, - pincers: 1, - saw: 1, - scale: 1, - scaleImbalanced: 1, - scalesHanging: 1, - scissors: 1, - scissors2: 1, - shears: 1, - trowel: 1 - }, - miscellaneous: { - attire: 2, - banner: 2, - bell: 3, - bookClosed: 1, - bookClosed2: 1, - bookOpen: 1, - bucket: 1, - buckle: 1, - bugleHorn: 2, - bugleHorn2: 1, - chain: 2, - chalice: 2, - cowHorns: 3, - crosier: 1, - crown: 3, - crown2: 2, - drum: 1, - fasces: 1, - feather: 3, - harp: 2, - horseshoe: 3, - hourglass: 2, - key: 3, - laurelWreath: 2, - laurelWreath2: 1, - log: 1, - lute: 2, - lyre: 1, - mitre: 1, - orb: 1, - pot: 2, - ramsHorn: 1, - sceptre: 1, - scrollClosed: 1, - snowflake: 1, - stagsAttires: 1, - stirrup: 2, - wheel: 3, - wing: 2, - wingSword: 1 - }, - inescutcheon: { - inescutcheonHeater: 1, - inescutcheonSpanish: 1, - inescutcheonFrench: 1, - inescutcheonHorsehead: 1, - inescutcheonHorsehead2: 1, - inescutcheonPolish: 1, - inescutcheonHessen: 1, - inescutcheonSwiss: 1, - inescutcheonBoeotian: 1, - inescutcheonRoman: 1, - inescutcheonKite: 1, - inescutcheonOldFrench: 1, - inescutcheonRenaissance: 1, - inescutcheonBaroque: 1, - inescutcheonTarge: 1, - inescutcheonTarge2: 1, - inescutcheonPavise: 1, - inescutcheonWedged: 1, - inescutcheonFlag: 1, - inescutcheonPennon: 1, - inescutcheonGuidon: 1, - inescutcheonBanner: 1, - inescutcheonDovetail: 1, - inescutcheonGonfalon: 1, - inescutcheonPennant: 1, - inescutcheonRound: 1, - inescutcheonOval: 1, - inescutcheonVesicaPiscis: 1, - inescutcheonSquare: 1, - inescutcheonDiamond: 1, - inescutcheonNo: 1, - inescutcheonFantasy1: 1, - inescutcheonFantasy2: 1, - inescutcheonFantasy3: 1, - inescutcheonFantasy4: 1, - inescutcheonFantasy5: 1, - inescutcheonNoldor: 1, - inescutcheonGondor: 1, - inescutcheonEasterling: 1, - inescutcheonErebor: 1, - inescutcheonIronHills: 1, - inescutcheonUrukHai: 1, - inescutcheonMoriaOrc: 1 - }, - ornaments: { - mantle: 0, - ribbon1: 3, - ribbon2: 2, - ribbon3: 1, - ribbon4: 1, - ribbon5: 1, - ribbon6: 1, - ribbon7: 1, - ribbon8: 1 - }, - data: chargeData -}; - -// charges specific to culture or burg type (FMG-only config, not coming from Armoria) -const typeMapping = { - Naval: { - anchor: 3, - drakkar: 1, - lymphad: 2, - caravel: 1, - shipWheel: 1, - armillarySphere: 1, - escallop: 1, - dolphin: 1, - plaice: 1 - }, - Highland: {tower: 1, raven: 1, wolfHeadErased: 1, wolfPassant: 1, goat: 1, axe: 1}, - River: { - garb: 1, - rake: 1, - raft: 1, - boat: 2, - drakkar: 2, - hook: 2, - pike: 2, - bullHeadCaboshed: 1, - apple: 1, - pear: 1, - plough: 1, - earOfWheat: 1, - salmon: 1, - cancer: 1, - bridge: 1, - bridge2: 2, - sickle: 1, - scythe: 1, - grapeBunch: 1, - wheatStalk: 1, - windmill: 1, - crocodile: 1 - }, - Lake: { - hook: 3, - cancer: 2, - escallop: 1, - pike: 2, - heron: 1, - boat: 1, - boat2: 2, - salmon: 1, - cancer: 1, - sickle: 1, - windmill: 1, - swanErased: 1, - swan: 1, - frog: 1, - wasp: 1 - }, - Nomadic: { - pot: 1, - buckle: 1, - wheel: 2, - sabre: 2, - sabresCrossed: 1, - bow: 2, - arrow: 1, - horseRampant: 1, - horseSalient: 1, - crescent: 1, - camel: 3, - scorpion: 1, - falcon: 1 - }, - Hunting: { - bugleHorn: 2, - bugleHorn2: 1, - stagsAttires: 2, - attire: 2, - hatchet: 1, - bowWithArrow: 2, - arrowsSheaf: 1, - lanceHead: 1, - saw: 1, - deerHeadCaboshed: 1, - wolfStatant: 1, - oak: 1, - pineCone: 1, - pineTree: 1, - oak: 1, - owl: 1, - falcon: 1, - peacock: 1, - boarHeadErased: 2, - horseHeadCouped: 1, - rabbitSejant: 1, - wolfRampant: 1, - wolfPassant: 1, - wolfStatant: 1, - greyhoundCourant: 1, - greyhoundRampant: 1, - greyhoundSejant: 1, - mastiffStatant: 1, - talbotPassant: 1, - talbotSejant: 1, - stagPassant: 21 - }, - // selection based on type - City: { - key: 4, - bell: 3, - lute: 1, - tower: 1, - pillar: 1, - castle: 1, - castle2: 1, - portcullis: 1, - mallet: 1, - cannon: 1, - anvil: 1, - buckle: 1, - horseshoe: 1, - stirrup: 1, - lanceWithBanner: 1, - bookClosed: 1, - scissors: 1, - scissors2: 1, - shears: 1, - pincers: 1, - bridge: 2, - archer: 1, - cannon: 1, - shield: 1, - arbalest: 1, - arbalest2: 1, - bowWithThreeArrows: 1, - spear: 1, - lochaberAxe: 1, - armEmbowedHoldingSabre: 1, - grenade: 1, - maces: 1, - grapeBunch: 1, - cock: 1, - ramHeadErased: 1, - ratRampant: 1, - hourglass: 1, - scale: 1, - scrollClosed: 1 - }, - Capital: { - crown: 2, - crown2: 2, - laurelWreath: 1, - orb: 1, - lute: 1, - lyre: 1, - banner: 1, - castle: 1, - castle2: 1, - palace: 1, - crown2: 2, - column: 1, - lionRampant: 1, - stagLodgedRegardant: 1, - drawingCompass: 1, - rapier: 1, - scaleImbalanced: 1, - scalesHanging: 1 - }, - Сathedra: { - crossHummetty: 3, - mitre: 3, - chalice: 1, - orb: 1, - crosier: 2, - lamb: 1, - monk: 2, - angel: 3, - crossLatin: 2, - crossPatriarchal: 1, - crossOrthodox: 1, - crossCalvary: 1, - agnusDei: 3, - bookOpen: 1, - sceptre: 1, - bone: 1, - skull: 1 - } -}; - -const positions = { - conventional: { - e: 20, - abcdefgzi: 3, - beh: 3, - behdf: 2, - acegi: 1, - kn: 3, - bhdf: 1, - jeo: 1, - abc: 3, - jln: 6, - jlh: 3, - kmo: 2, - jleh: 1, - def: 3, - abcpqh: 4, - ABCDEFGHIJKL: 1 - }, - complex: {e: 40, beh: 1, kn: 1, jeo: 1, abc: 2, jln: 7, jlh: 2, def: 1, abcpqh: 1}, - divisions: { - perPale: {e: 15, pq: 5, jo: 2, jl: 2, ABCDEFGHIJKL: 1}, - perFess: {e: 12, kn: 4, jkl: 2, gizgiz: 1, jlh: 3, kmo: 1, ABCDEFGHIJKL: 1}, - perBend: {e: 5, lm: 5, bcfdgh: 1}, - perBendSinister: {e: 1, jo: 1}, - perCross: {e: 4, jlmo: 1, j: 1, jo: 2, jl: 1}, - perChevron: {e: 1, jlh: 1, dfk: 1, dfbh: 2, bdefh: 1}, - perChevronReversed: {e: 1, mok: 2, dfh: 2, dfbh: 1, bdefh: 1}, - perSaltire: {bhdf: 8, e: 3, abcdefgzi: 1, bh: 1, df: 1, ABCDEFGHIJKL: 1}, - perPile: {ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 1} - }, - inescutcheon: {e: 4, jln: 1} -}; - -const lines = { - straight: 50, - wavy: 8, - engrailed: 4, - invecked: 3, - rayonne: 3, - embattled: 1, - raguly: 1, - urdy: 1, - dancetty: 1, - indented: 2, - dentilly: 1, - bevilled: 1, - angled: 1, - flechy: 1, - barby: 1, - enclavy: 1, - escartely: 1, - arched: 2, - archedReversed: 1, - nowy: 1, - nowyReversed: 1, - embattledGhibellin: 1, - embattledNotched: 1, - embattledGrady: 1, - dovetailedIndented: 1, - dovetailed: 1, - potenty: 1, - potentyDexter: 1, - potentySinister: 1, - nebuly: 2, - seaWaves: 1, - dragonTeeth: 1, - firTrees: 1 -}; - -const divisions = { - variants: { - perPale: 5, - perFess: 5, - perBend: 2, - perBendSinister: 1, - perChevron: 1, - perChevronReversed: 1, - perCross: 5, - perPile: 1, - perSaltire: 1, - gyronny: 1, - chevronny: 1 - }, - perPale: lines, - perFess: lines, - perBend: lines, - perBendSinister: lines, - perChevron: lines, - perChevronReversed: lines, - perCross: { - straight: 20, - wavy: 5, - engrailed: 4, - invecked: 3, - rayonne: 1, - embattled: 1, - raguly: 1, - urdy: 1, - indented: 2, - dentilly: 1, - bevilled: 1, - angled: 1, - embattledGhibellin: 1, - embattledGrady: 1, - dovetailedIndented: 1, - dovetailed: 1, - potenty: 1, - potentyDexter: 1, - potentySinister: 1, - nebuly: 1 - }, - perPile: lines -}; - -const ordinaries = { - lined: { - pale: 7, - fess: 5, - bend: 3, - bendSinister: 2, - chief: 5, - bar: 2, - gemelle: 1, - fessCotissed: 1, - fessDoubleCotissed: 1, - bendlet: 2, - bendletSinister: 1, - terrace: 3, - cross: 6, - crossParted: 1, - saltire: 2, - saltireParted: 1 - }, - straight: { - bordure: 8, - orle: 4, - mount: 1, - point: 2, - flaunches: 1, - gore: 1, - gyron: 1, - quarter: 1, - canton: 2, - pall: 3, - pallReversed: 2, - chevron: 4, - chevronReversed: 3, - pile: 2, - pileInBend: 2, - pileInBendSinister: 1, - piles: 1, - pilesInPoint: 2, - label: 1 - }, - data: { - bar: { - positionsOn: {defdefdef: 1}, - positionsOff: {abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1} - }, - bend: { - positionsOn: {ee: 2, jo: 1, joe: 1}, - positionsOff: {ccg: 2, ccc: 1} - }, - bendSinister: { - positionsOn: {ee: 1, lm: 1, lem: 4}, - positionsOff: {aai: 2, aaa: 1} - }, - bendlet: { - positionsOn: {joejoejoe: 1}, - positionsOff: {ccg: 2, ccc: 1} - }, - bendletSinister: { - positionsOn: {lemlemlem: 1}, - positionsOff: {aai: 2, aaa: 1} - }, - bordure: { - positionsOn: {ABCDEFGHIJKL: 1}, - positionsOff: {e: 4, jleh: 2, kenken: 1, peqpeq: 1} - }, - canton: { - positionsOn: {yyyy: 1}, - positionsOff: {e: 5, beh: 1, def: 1, bdefh: 1, kn: 1} - }, - chevron: { - positionsOn: {ach: 3, hhh: 1} - }, - chevronReversed: { - positionsOff: {bbb: 1} - }, - chief: { - positionsOn: {abc: 5, bbb: 1}, - positionsOff: {emo: 2, emoz: 1, ez: 2} - }, - cross: { - positionsOn: {eeee: 1, behdfbehdf: 3, behbehbeh: 2}, - positionsOff: {acgi: 1} - }, - crossParted: { - positionsOn: {e: 5, ee: 1} - }, - fess: { - positionsOn: {ee: 1, def: 3}, - positionsOff: {abc: 3, abcz: 1} - }, - fessCotissed: { - positionsOn: {ee: 1, def: 3} - }, - fessDoubleCotissed: { - positionsOn: {ee: 1, defdef: 3} - }, - flaunches: { - positionsOff: {e: 3, kn: 1, beh: 3} - }, - gemelle: { - positionsOff: {abc: 1} - }, - gyron: { - positionsOff: {bh: 1} - }, - label: { - positionsOff: {defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5} - }, - mount: { - positionsOff: {e: 5, def: 1, bdf: 3} - }, - orle: { - positionsOff: {e: 4, jleh: 1, kenken: 1, peqpeq: 1} - }, - pale: { - positionsOn: {ee: 12, beh: 10, kn: 3, bb: 1}, - positionsOff: {yyy: 1} - }, - pall: { - positionsOn: {ee: 1, jleh: 5, jlhh: 3}, - positionsOff: {BCKFEILGJbdmfo: 1} - }, - pallReversed: { - positionsOn: {ee: 1, bemo: 5}, - positionsOff: {aczac: 1} - }, - pile: { - positionsOn: {bbb: 1}, - positionsOff: {acdfgi: 1, acac: 1} - }, - pileInBend: { - positionsOn: {eeee: 1, eeoo: 1}, - positionsOff: {cg: 1} - }, - pileInBendSinister: { - positionsOn: {eeee: 1, eemm: 1}, - positionsOff: {ai: 1} - }, - point: { - positionsOff: {e: 2, def: 1, bdf: 3, acbdef: 1} - }, - quarter: { - positionsOn: {jjj: 1}, - positionsOff: {e: 1} - }, - saltire: { - positionsOn: {ee: 5, jlemo: 1} - }, - saltireParted: { - positionsOn: {e: 5, ee: 1} - }, - terrace: { - positionsOff: {e: 5, def: 1, bdf: 3} - } - } -}; - -const shields = { - types: {basic: 10, regional: 2, historical: 1, specific: 1, banner: 1, simple: 2, fantasy: 1, middleEarth: 0}, - basic: {heater: 12, spanish: 6, french: 1}, - regional: {horsehead: 1, horsehead2: 1, polish: 1, hessen: 1, swiss: 1}, - historical: {boeotian: 1, roman: 2, kite: 1, oldFrench: 5, renaissance: 2, baroque: 2}, - specific: {targe: 1, targe2: 0, pavise: 5, wedged: 10}, - banner: {flag: 1, pennon: 0, guidon: 0, banner: 0, dovetail: 1, gonfalon: 5, pennant: 0}, - simple: {round: 12, oval: 6, vesicaPiscis: 1, square: 1, diamond: 2, no: 0}, - fantasy: {fantasy1: 2, fantasy2: 2, fantasy3: 1, fantasy4: 1, fantasy5: 3}, - middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1} -}; - -export function generate(pack, parent, kinship, dominion, type, utils) { - const {P, rw} = utils; - - if (!parent || parent.custom) { - parent = null; - kinship = 0; - dominion = 0; - } - - let usedPattern = null; - let usedTinctures = []; - - const t1 = P(kinship) ? parent.t1 : getTincture("field"); - if (t1.includes("-")) usedPattern = t1; - const coa = {t1}; - - const addCharge = P(usedPattern ? 0.5 : 0.93); // 80% for charge - const linedOrdinary = - (addCharge && P(0.3)) || P(0.5) - ? parent?.ordinaries && P(kinship) - ? parent.ordinaries[0].ordinary - : rw(ordinaries.lined) - : null; - - const ordinary = - (!addCharge && P(0.65)) || P(0.3) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for ordinary - - const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary); - - const divisioned = (() => { - if (rareDivided) return P(0.03); - if (addCharge && ordinary) return P(0.03); - if (addCharge) return P(0.3); - if (ordinary) return P(0.7); - return P(0.995); - })(); - - const division = (() => { - if (divisioned) { - if (parent?.division && P(kinship - 0.1)) return parent.division.division; - return rw(divisions.variants); - } - return null; - })(); - - if (division) { - const t = getTincture("division", usedTinctures, P(0.98) ? coa.t1 : null); - coa.division = {division, t}; - if (divisions[division]) - coa.division.line = usedPattern || (ordinary && P(0.7)) ? "straight" : rw(divisions[division]); - } - - if (ordinary) { - coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}]; - if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines); - if (division && !addCharge && !usedPattern && P(0.5) && ordinary !== "bordure" && ordinary !== "orle") { - if (P(0.8)) coa.ordinaries[0].divided = "counter"; - // 40% - else if (P(0.6)) coa.ordinaries[0].divided = "field"; - // 6% - else coa.ordinaries[0].divided = "division"; // 4% - } - } - - if (addCharge) { - const charge = (() => { - if (parent?.charges && P(kinship - 0.1)) return parent.charges[0].charge; - if (type && type !== "Generic" && P(0.3)) return rw(typeMapping[type]); - return selectCharge(ordinary || divisioned ? charges.types : charges.single); - })(); - const chargeData = charges.data[charge] || {}; - - let p = "e"; - let t = "gules"; - - const ordinaryData = ordinaries.data[ordinary]; - const tOrdinary = coa.ordinaries ? coa.ordinaries[0].t : null; - - if (ordinaryData?.positionsOn && P(0.8)) { - // place charge over ordinary (use tincture of field type) - p = rw(ordinaryData.positionsOn); - t = !usedPattern && P(0.3) ? coa.t1 : getTincture("charge", [], tOrdinary); - } else if (ordinaryData?.positionsOff && P(0.95)) { - // place charge out of ordinary (use tincture of ordinary type) - p = rw(ordinaryData.positionsOff); - t = !usedPattern && P(0.3) ? tOrdinary : getTincture("charge", usedTinctures, coa.t1); - } else if (positions.divisions[division]) { - // place charge in fields made by division - p = rw(positions.divisions[division]); - t = getTincture("charge", tOrdinary ? usedTinctures.concat(tOrdinary) : usedTinctures, coa.t1); - } else if (chargeData.positions) { - // place charge-suitable position - p = rw(chargeData.positions); - t = getTincture("charge", usedTinctures, coa.t1); - } else { - // place in standard position (use new tincture) - p = usedPattern ? "e" : charges.conventional[charge] ? rw(positions.conventional) : rw(positions.complex); - t = getTincture("charge", usedTinctures.concat(tOrdinary), coa.t1); - } - - if (chargeData.natural && chargeData.natural !== t && chargeData.natural !== tOrdinary) t = chargeData.natural; - - const item = {charge: charge, t, p}; - const colors = chargeData.colors || 1; - if (colors > 1) item.t2 = P(0.25) ? getTincture("charge", usedTinctures, coa.t1) : t; - if (colors > 2 && item.t2) item.t3 = P(0.5) ? getTincture("charge", usedTinctures, coa.t1) : t; - coa.charges = [item]; - - if (p === "ABCDEFGHIKL" && P(0.95)) { - // add central charge if charge is in bordure - coa.charges[0].charge = rw(charges.conventional); - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.t1); - coa.charges.push({charge, t, p: "e"}); - } else if (P(0.8) && charge === "inescutcheon") { - // add charge to inescutcheon - const charge = selectCharge(charges.types); - const t2 = getTincture("charge", [], t); - coa.charges.push({charge, t: t2, p, size: 0.5}); - } else if (division && !ordinary) { - const allowCounter = !usedPattern && (!coa.line || coa.line === "straight"); - - // dimidiation: second charge at division basic positons - if (P(0.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") { - coa.charges[0].divided = "field"; - if (P(0.95)) { - const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]); - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.division.t); - coa.charges.push({charge, t, p: p2, divided: "division"}); - } - } else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter"; - // counterchanged, 40% - else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(0.8)) { - // place 2 charges in division standard positions - const [p1, p2] = - division === "perPale" - ? ["p", "q"] - : division === "perFess" - ? ["k", "n"] - : division === "perBend" - ? ["l", "m"] - : ["j", "o"]; // perBendSinister - coa.charges[0].p = p1; - - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.division.t); - coa.charges.push({charge, t, p: p2}); - } else if (["perCross", "perSaltire"].includes(division) && P(0.5)) { - // place 4 charges in division standard positions - const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"]; - coa.charges[0].p = p1; - - const c2 = selectCharge(charges.single); - const t2 = getTincture("charge", [], coa.division.t); - - const c3 = selectCharge(charges.single); - const t3 = getTincture("charge", [], coa.division.t); - - const c4 = selectCharge(charges.single); - const t4 = getTincture("charge", [], coa.t1); - coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4}); - } else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40% - } - - coa.charges.forEach(c => defineChargeAttributes(ordinary, division, c)); - } - - // dominions have canton with parent coa - if (P(dominion) && parent.charges) { - const invert = isSameType(parent.t1, coa.t1); - const t = invert ? getTincture("division", usedTinctures, coa.t1) : parent.t1; - const canton = {ordinary: "canton", t}; - - coa.charges?.forEach((charge, i) => { - if (charge.size === 1.5) charge.size = 1.4; - charge.p = charge.p.replaceAll(/[ajy]/g, ""); - if (!charge.p) coa.charges.splice(i, 1); - }); - - let charge = parent.charges[0].charge; - if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge; - - let t2 = invert ? parent.t1 : parent.charges[0].t; - if (isSameType(t, t2)) t2 = getTincture("charge", usedTinctures, t); - - if (!coa.charges) coa.charges = []; - coa.charges.push({charge, t: t2, p: "y", size: 0.5}); - - coa.ordinaries ? coa.ordinaries.push(canton) : (coa.ordinaries = [canton]); - } - - function selectCharge(set) { - const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types) : rw(charges.single); - return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]); - } - - // select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT - function getTincture(element, fields = [], RoT) { - const base = RoT ? (RoT.includes("-") ? RoT.split("-")[1] : RoT) : null; - - let type = rw(tinctures[element]); // metals, colours, stains, patterns - if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT - if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse - let tincture = rw(tinctures[type]); - - while (tincture === base || fields.includes(tincture)) { - tincture = rw(tinctures[type]); - } // follow RoT - - if (type !== "patterns" && element !== "charge") usedTinctures.push(tincture); // add field tincture - - if (type === "patterns") { - usedPattern = tincture; - tincture = definePattern(tincture, element); - } - - return tincture; - } - - function defineChargeAttributes(ordinary, division, c) { - // define size - c.size = (c.size || 1) * getSize(c.p, ordinary, division); - - // clean-up position - c.p = [...new Set(c.p)].join(""); - - // define orientation - if (P(0.02) && charges.data[c.charge]?.sinister) c.sinister = 1; - if (P(0.02) && charges.data[c.charge]?.reversed) c.reversed = 1; - } - - function getType(t) { - const tincture = t.includes("-") ? t.split("-")[1] : t; - if (Object.keys(tinctures.metals).includes(tincture)) return "metals"; - if (Object.keys(tinctures.colours).includes(tincture)) return "colours"; - if (Object.keys(tinctures.stains).includes(tincture)) return "stains"; - } - - function isSameType(t1, t2) { - return type(t1) === type(t2); - - function type(tincture) { - if (Object.keys(tinctures.metals).includes(tincture)) return "metals"; - if (Object.keys(tinctures.colours).includes(tincture)) return "colours"; - if (Object.keys(tinctures.stains).includes(tincture)) return "stains"; - else return "pattern"; - } - } - - function definePattern(pattern, element, size = "") { - let t1 = null, - t2 = null; - if (P(0.1)) size = "-small"; - else if (P(0.1)) size = "-smaller"; - else if (P(0.01)) size = "-big"; - else if (P(0.005)) size = "-smallest"; - - // apply standard tinctures - if (P(0.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) { - t1 = "azure"; - t2 = "argent"; - } else if (P(0.8) && pattern === "ermine") { - t1 = "argent"; - t2 = "sable"; - } else if (pattern === "pappellony") { - if (P(0.2)) { - t1 = "gules"; - t2 = "or"; - } else if (P(0.2)) { - t1 = "argent"; - t2 = "sable"; - } else if (P(0.2)) { - t1 = "azure"; - t2 = "argent"; - } - } else if (pattern === "masoned") { - if (P(0.3)) { - t1 = "gules"; - t2 = "argent"; - } else if (P(0.3)) { - t1 = "argent"; - t2 = "sable"; - } else if (P(0.1)) { - t1 = "or"; - t2 = "sable"; - } - } else if (pattern === "fretty") { - if (t2 === "sable" || P(0.35)) { - t1 = "argent"; - t2 = "gules"; - } else if (P(0.25)) { - t1 = "sable"; - t2 = "or"; - } else if (P(0.15)) { - t1 = "gules"; - t2 = "argent"; - } - } else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy); - - if (!t1 || !t2) { - const startWithMetal = P(0.7); - t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours); - t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals); - } - - // division should not be the same tincture as base field - if (element === "division") { - if (usedTinctures.includes(t1)) t1 = replaceTincture(t1); - if (usedTinctures.includes(t2)) t2 = replaceTincture(t2); - } - - usedTinctures.push(t1, t2); - return `${pattern}-${t1}-${t2}${size}`; - } - - function replaceTincture(t, n) { - const type = getType(t); - while (!n || n === t) { - n = rw(tinctures[type]); - } - return n; - } - - function getSize(p, o = null, d = null) { - if (p === "e" && (o === "bordure" || o === "orle")) return 1.1; - if (p === "e") return 1.5; - if (p === "jln" || p === "jlh") return 0.7; - if (p === "abcpqh" || p === "ez" || p === "be") return 0.5; - if (["a", "b", "c", "d", "f", "g", "h", "i", "bh", "df"].includes(p)) return 0.5; - if (["j", "l", "m", "o", "jlmo"].includes(p) && d === "perCross") return 0.6; - if (p.length > 10) return 0.18; // >10 (bordure) - if (p.length > 7) return 0.3; // 8, 9, 10 - if (p.length > 4) return 0.4; // 5, 6, 7 - if (p.length > 2) return 0.5; // 3, 4 - return 0.7; // 1, 2 - } - - return coa; -} - -export function getShield(pack, culture, state, config) { - const {emblemShape, emblemShapeGroup} = config; - - if (emblemShapeGroup !== "Diversiform") return emblemShape; - - if (emblemShape === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield; - if (pack.cultures[culture].shield) return pack.cultures[culture].shield; - return "heater"; -} - -export function toString(coa) { - return JSON.stringify(coa).replaceAll("#", "%23"); -} - -export function copy(coa) { - return JSON.parse(JSON.stringify(coa)); -} - -export {shields}; \ No newline at end of file diff --git a/procedural/src/engine/modules/coa-renderer.js b/procedural/src/engine/modules/coa-renderer.js deleted file mode 100644 index f9a6ca0a..00000000 --- a/procedural/src/engine/modules/coa-renderer.js +++ /dev/null @@ -1,346 +0,0 @@ -"use strict"; - -// Data constants (business logic of the module) -const colors = { - argent: "#fafafa", - or: "#ffe066", - gules: "#d7374a", - sable: "#333333", - azure: "#377cd7", - vert: "#26c061", - purpure: "#522d5b", - murrey: "#85185b", - sanguine: "#b63a3a", - tenné: "#cc7f19" -}; - -const shieldPositions = { - // shield-specific position: [x, y] (relative to center) - heater: { - a: [-43.75, -50], - b: [0, -50], - c: [43.75, -50], - d: [-43.75, 0], - e: [0, 0], - f: [43.75, 0], - g: [-32.25, 37.5], - h: [0, 50], - i: [32.25, 37.5], - y: [-50, -50], - z: [0, 62.5], - j: [-37.5, -37.5], - k: [0, -37.5], - l: [37.5, -37.5], - m: [-30, 30], - n: [0, 42.5], - o: [30, 30], - p: [-37.5, 0], - q: [37.5, 0], - A: [-66.2, -66.6], - B: [-22, -66.6], - C: [22, -66.6], - D: [66.2, -66.6], - K: [-66.2, -20], - E: [66.2, -20], - J: [-55.5, 26], - F: [55.5, 26], - I: [-33, 62], - G: [33, 62], - H: [0, 89.5] - }, - spanish: { - a: [-43.75, -50], - b: [0, -50], - c: [43.75, -50], - d: [-43.75, 0], - e: [0, 0], - f: [43.75, 0], - g: [-43.75, 50], - h: [0, 50], - i: [43.75, 50], - y: [-50, -50], - z: [0, 50], - j: [-37.5, -37.5], - k: [0, -37.5], - l: [37.5, -37.5], - m: [-37.5, 37.5], - n: [0, 37.5], - o: [37.5, 37.5], - p: [-37.5, 0], - q: [37.5, 0], - A: [-66.2, -66.6], - B: [-22, -66.6], - C: [22, -66.6], - D: [66.2, -66.6], - K: [-66.4, -20], - E: [66.4, -20], - J: [-66.4, 26], - F: [66.4, 26], - I: [-49, 70], - G: [49, 70], - H: [0, 92] - }, - // ... all other shieldPositions data from original ... - moriaOrc: { - a: [-37.5, -37.5], - b: [0, -37.5], - c: [37.5, -37.5], - d: [-37.5, 0], - e: [0, 0], - f: [37.5, 0], - g: [-37.5, 37.5], - h: [0, 37.5], - i: [37.5, 37.5], - y: [-50, -50], - z: [0, 40], - j: [-30, -30], - k: [0, -30], - l: [30, -30], - m: [-30, 30], - n: [0, 30], - o: [30, 30], - p: [-30, 0], - q: [30, 0], - A: [-48, -48], - B: [-16, -50], - C: [16, -46], - D: [39, -61], - K: [-52, -19], - E: [52, -26], - J: [-42, 9], - F: [52, 9], - I: [-31, 40], - G: [40, 43], - H: [4, 47] - } -}; - -const shieldSize = { - // ... all shieldSize data from original ... - moriaOrc: 0.7 -}; - -const shieldBox = { - // ... all shieldBox data from original ... - moriaOrc: "0 0 200 200" -}; - -export const shieldPaths = { - // ... all shieldPaths data from original ... - moriaOrc: - "M45 35c5 3 7 10 13 9h19c4-2 7-4 9-9 6 1 9 9 16 11 7-2 14 0 21 0 6-3 6-10 10-15 2-5 1-10-2-15-2-4-5-14-4-16 3 6 7 11 12 14 7 3 3 12 7 16 3 6 4 12 9 18 2 4 6 8 5 14 0 6-1 12 3 18-3 6-2 13-1 20 1 6-2 12-1 18 0 6-3 13 0 18 8 4 0 8-5 7-4 3-9 3-13 9-5 5-5 13-8 19 0 6 0 15-7 16-1 6-7 6-10 12-1-6 0-6-2-9l2-19c2-4 5-12-3-12-4-5-11-5-15 1l-13-18c-3-4-2 9-3 12 2 2-4-6-7-5-8-2-8 7-11 11-2 4-5 10-8 9 3-10 3-16 1-23-1-4 2-9-4-11 0-6 1-13-2-19-4-2-9-6-13-7V91c4-7-5-13 0-19-3-7 2-11 2-18-1-6 1-12 3-17v-1z" -}; - -const lines = { - // ... all lines data from original ... - archedReversed: "m 0,85 c 0,0 60,20.2 100,20 40,-0.2 100,-20 100,-20 v 30 H 0 Z" -}; - -const templates = { - // ... all templates data from original ... - saltirePartedLined: line => - `` -}; - -const patterns = { - // ... all patterns data from original ... - honeycombed: (p, c1, c2, size) => - `` -}; - -/** - * Generates an SVG string for a given Coat of Arms definition. - * @param {object} coa The Coat of Arms definition object. - * @param {string} id A unique ID to be used for SVG elements like clipPaths and patterns. - * @param {object} chargesData An object mapping charge names to their raw SVG element strings. - * @returns {string} The complete SVG string for the Coat of Arms. - */ -export function render(coa, id, chargesData) { - const {shield = "heater", division, ordinaries = [], charges = []} = coa; - - const ordinariesRegular = ordinaries.filter(o => !o.above); - const ordinariesAboveCharges = ordinaries.filter(o => o.above); - const shieldPath = shieldPaths[shield] || shieldPaths.heater; - const tDiv = division ? (division.t.includes("-") ? division.t.split("-")[1] : division.t) : null; - const positions = shieldPositions[shield]; - const sizeModifier = shieldSize[shield] || 1; - const viewBox = shieldBox[shield] || "0 0 200 200"; - - const shieldClip = ``; - const divisionClip = division - ? `${getTemplate(division.division, division.line)}` - : ""; - const loadedCharges = getCharges(coa, id, chargesData, shieldPath); - const loadedPatterns = getPatterns(coa, id); - const blacklight = ``; - const field = ``; - const style = ``; - - const divisionGroup = division ? templateDivision() : ""; - const overlay = ``; - - const svg = ` - ${shieldClip}${divisionClip}${loadedCharges}${loadedPatterns}${blacklight}${style} - ${field}${divisionGroup}${templateAboveAll()} - ${overlay}`; - - return svg; - - function templateDivision() { - let svg = ""; - - // In field part - for (const ordinary of ordinariesRegular) { - if (ordinary.divided === "field") svg += templateOrdinary(ordinary, ordinary.t); - else if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, tDiv); - } - for (const charge of charges) { - if (charge.divided === "field") svg += templateCharge(charge, charge.t); - else if (charge.divided === "counter") svg += templateCharge(charge, tDiv); - } - for (const ordinary of ordinariesAboveCharges) { - if (ordinary.divided === "field") svg += templateOrdinary(ordinary, ordinary.t); - else if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, tDiv); - } - - // In division part - svg += ``; - for (const ordinary of ordinariesRegular) { - if (ordinary.divided === "division") svg += templateOrdinary(ordinary, ordinary.t); - else if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, coa.t1); - } - for (const charge of charges) { - if (charge.divided === "division") svg += templateCharge(charge, charge.t); - else if (charge.divided === "counter") svg += templateCharge(charge, coa.t1); - } - for (const ordinary of ordinariesAboveCharges) { - if (ordinary.divided === "division") svg += templateOrdinary(ordinary, ordinary.t); - else if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, coa.t1); - } - return (svg += ``); - } - - function templateAboveAll() { - let svg = ""; - ordinariesRegular.filter(o => !o.divided).forEach(ordinary => (svg += templateOrdinary(ordinary, ordinary.t))); - charges.filter(o => !o.divided || !division).forEach(charge => (svg += templateCharge(charge, charge.t))); - ordinariesAboveCharges - .filter(o => !o.divided) - .forEach(ordinary => (svg += templateOrdinary(ordinary, ordinary.t))); - return svg; - } - - function templateOrdinary(ordinary, tincture) { - const fill = clr(tincture); - let svg = ``; - if (ordinary.ordinary === "bordure") - svg += ``; - else if (ordinary.ordinary === "orle") - svg += ``; - else svg += getTemplate(ordinary.ordinary, ordinary.line); - return svg + ``; - } - - function templateCharge(charge, tincture, secondaryTincture, tertiaryTincture) { - const primary = clr(tincture); - const secondary = clr(secondaryTincture || tincture); - const tertiary = clr(tertiaryTincture || tincture); - const stroke = charge.stroke || "#000"; - - const chargePositions = [...new Set(charge.p)].filter(position => positions[position]); - let svg = ``; - for (const p of chargePositions) { - const transform = getElTransform(charge, p); - svg += ``; - } - return svg + ""; - - function getElTransform(c, p) { - const s = (c.size || 1) * sizeModifier; - const sx = c.sinister ? -s : s; - const sy = c.reversed ? -s : s; - let [x, y] = positions[p]; - x = x - 100 * (sx - 1); - y = y - 100 * (sy - 1); - const scale = c.sinister || c.reversed ? `${sx} ${sy}` : s; - return `translate(${x} ${y}) scale(${scale})`; - } - } -} - -// Helpers -function getCharges(coa, id, chargesData, shieldPath) { - let chargesToLoad = coa.charges ? coa.charges.map(charge => charge.charge) : []; - if (semy(coa.t1)) chargesToLoad.push(semy(coa.t1)); - if (semy(coa.division?.t)) chargesToLoad.push(semy(coa.division.t)); - - const uniqueCharges = [...new Set(chargesToLoad)]; - return uniqueCharges - .map(charge => { - if (charge === "inescutcheon") { - return ``; - } - const chargeSVG = chargesData[charge]; - if (!chargeSVG) { - console.error(`Charge data for "${charge}" not provided.`); - return ""; - } - // Inject the unique ID into the provided tag - return chargeSVG.replace(/ string.includes("-"); - let patternsToAdd = []; - if (coa.t1.includes("-")) patternsToAdd.push(coa.t1); - if (coa.division && isPattern(coa.division.t)) patternsToAdd.push(coa.division.t); - if (coa.ordinaries) { - coa.ordinaries.filter(ordinary => isPattern(ordinary.t)).forEach(ordinary => patternsToAdd.push(ordinary.t)); - } - if (coa.charges) { - coa.charges.filter(charge => isPattern(charge.t)).forEach(charge => patternsToAdd.push(charge.t)); - } - if (!patternsToAdd.length) return ""; - - return [...new Set(patternsToAdd)] - .map(patternString => { - const [pattern, t1, t2, size] = patternString.split("-"); - const charge = semy(patternString); - if (charge) return patterns.semy(patternString, clr(t1), clr(t2), getSizeMod(size), charge + "_" + id); - return patterns[pattern](patternString, clr(t1), clr(t2), getSizeMod(size), charge); - }) - .join(""); -} - -function getSizeMod(size) { - if (size === "small") return 0.8; - if (size === "smaller") return 0.5; - if (size === "smallest") return 0.25; - if (size === "big") return 1.6; - return 1; -} - -function getTemplate(id, line) { - const linedId = id + "Lined"; - if (!line || line === "straight" || !templates[linedId]) return templates[id]; - const linePath = lines[line]; - return templates[linedId](linePath); -} - -function clr(tincture) { - if (colors[tincture]) return colors[tincture]; - return `url(#${tincture})`; -} - -function semy(string) { - const isSemy = /^semy/.test(string); - if (!isSemy) return false; - const match = string.match(/semy_of_(.*?)-/); - return match ? match[1] : false; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/cultures-generator.js b/procedural/src/engine/modules/cultures-generator.js deleted file mode 100644 index 84b7c2ea..00000000 --- a/procedural/src/engine/modules/cultures-generator.js +++ /dev/null @@ -1,649 +0,0 @@ -"use strict"; - -/** - * Generates cultures (races, language zones) for the map - * - * REQUIRES: - * - pack.cells.s (suitability from cell ranking) - * - config.culturesInput (number of cultures to generate) - * - config.culturesInSetNumber (max cultures for culture set) - * - * PROVIDES: - * - pack.cells.culture (culture assignments for each cell) - * - pack.cultures (cultures array) - */ -export const generate = function (pack, grid, config, utils, modules) { - // Check required properties exist - if (!pack.cells.s) { - throw new Error("Cultures module requires cells.s (suitability) from Cell ranking"); - } - if (!config.cultures || !config.cultures.culturesInput || !config.cultures.culturesInSetNumber) { - throw new Error("Cultures module requires config.cultures.culturesInput and config.cultures.culturesInSetNumber"); - } - - const { WARN, ERROR, rand, rn, P, minmax, biased, rw, abbreviate } = utils; - const { TIME } = config.debug; - const { Names } = modules; - - TIME && console.time("generateCultures"); - const cells = pack.cells; - - const cultureIds = new Uint16Array(cells.i.length); // cell cultures - - const culturesInputNumber = config.cultures.culturesInput; - const culturesInSetNumber = config.cultures.culturesInSetNumber; - let count = Math.min(culturesInputNumber, culturesInSetNumber); - - const populated = cells.i.filter(i => cells.s[i]); // populated cells - if (populated.length < count * 25) { - count = Math.floor(populated.length / 50); - if (!count) { - WARN && console.warn(`There are no populated cells. Cannot generate cultures`); - const cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round"}]; - cells.culture = cultureIds; - - // Return data structure with error info for UI handling - return { - cultures, - cells: { culture: cultureIds }, - error: { - type: "extreme_climate", - message: "The climate is harsh and people cannot live in this world. No cultures, states and burgs will be created.", - populated: populated.length - } - }; - } else { - WARN && console.warn(`Not enough populated cells (${populated.length}). Will generate only ${count} cultures`); - // Return warning info for UI handling but continue generation - } - } - - const cultures = selectCultures(count, config, pack, grid, utils); - const centers = utils.d3.quadtree(); - const colors = getColors(count, utils); - const emblemShape = config.emblemShape; - - const codes = []; - - cultures.forEach(function (c, i) { - const newId = i + 1; - - if (c.lock) { - codes.push(c.code); - centers.add(c.center); - - for (const i of cells.i) { - if (cells.culture[i] === c.i) cultureIds[i] = newId; - } - - c.i = newId; - return; - } - - const sortingFn = c.sort ? c.sort : i => cells.s[i]; - const center = placeCenter(sortingFn, populated, cultureIds, centers, cells, config, utils); - - centers.add(cells.p[center]); - c.center = center; - c.i = newId; - delete c.odd; - delete c.sort; - c.color = colors[i]; - c.type = defineCultureType(center, cells, pack, utils); - c.expansionism = defineCultureExpansionism(c.type, config, utils); - c.origins = [0]; - c.code = abbreviate(c.name, codes); - codes.push(c.code); - cultureIds[center] = newId; - if (emblemShape === "random") c.shield = getRandomShield(utils); - }); - - cells.culture = cultureIds; - - // the first culture with id 0 is for wildlands - cultures.unshift({name: "Wildlands", i: 0, base: 1, origins: [null], shield: "round"}); - - // make sure all bases exist in nameBases - if (!utils.nameBases || !utils.nameBases.length) { - ERROR && console.error("Name base is empty, default nameBases will be applied"); - utils.nameBases = Names.getNameBases(); - } - - cultures.forEach(c => (c.base = c.base % utils.nameBases.length)); - - TIME && console.timeEnd("generateCultures"); - - return { - cultures, - cells: { culture: cultureIds } - }; -}; - -function placeCenter(sortingFn, populated, cultureIds, centers, cells, config, utils) { - const graphWidth = config.graphWidth; - const graphHeight = config.graphHeight; - const count = populated.length; - - let spacing = (graphWidth + graphHeight) / 2 / count; - const MAX_ATTEMPTS = 100; - - const sorted = [...populated].sort((a, b) => sortingFn(b) - sortingFn(a)); - const max = Math.floor(sorted.length / 2); - - let cellId = 0; - for (let i = 0; i < MAX_ATTEMPTS; i++) { - cellId = sorted[utils.biased(0, max, 5)]; - spacing *= 0.9; - if (!cultureIds[cellId] && !centers.find(cells.p[cellId][0], cells.p[cellId][1], spacing)) break; - } - - return cellId; -} - -function selectCultures(culturesNumber, config, pack, grid, utils) { - let defaultCultures = getDefault(culturesNumber, config, pack, grid, utils); - const cultures = []; - - pack.cultures?.forEach(function (culture) { - if (culture.lock && !culture.removed) cultures.push(culture); - }); - - if (!cultures.length) { - if (culturesNumber === defaultCultures.length) return defaultCultures; - if (defaultCultures.every(d => d.odd === 1)) return defaultCultures.splice(0, culturesNumber); - } - - for (let culture, rnd, i = 0; cultures.length < culturesNumber && defaultCultures.length > 0; ) { - do { - rnd = utils.rand(defaultCultures.length - 1); - culture = defaultCultures[rnd]; - i++; - } while (i < 200 && !utils.P(culture.odd)); - cultures.push(culture); - defaultCultures.splice(rnd, 1); - } - return cultures; -} - -// set culture type based on culture center position -function defineCultureType(i, cells, pack, utils) { - if (cells.h[i] < 70 && [1, 2, 4].includes(cells.biome[i])) return "Nomadic"; // high penalty in forest biomes and near coastline - if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations - const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature - if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline - if ( - (cells.harbor[i] && f.type !== "lake" && utils.P(0.1)) || - (cells.harbor[i] === 1 && utils.P(0.6)) || - (pack.features[cells.f[i]].group === "isle" && utils.P(0.4)) - ) - return "Naval"; // low water cross penalty and high for non-along-coastline growth - if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth - if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes - return "Generic"; -} - -function defineCultureExpansionism(type, config, utils) { - let base = 1; // Generic - if (type === "Lake") base = 0.8; - else if (type === "Naval") base = 1.5; - else if (type === "River") base = 0.9; - else if (type === "Nomadic") base = 1.5; - else if (type === "Hunting") base = 0.7; - else if (type === "Highland") base = 1.2; - return utils.rn(((Math.random() * config.sizeVariety) / 2 + 1) * base, 1); -} - -export const add = function (center, pack, config, utils) { - const defaultCultures = getDefault(null, config, pack, utils); - let culture, base, name; - - if (pack.cultures.length < defaultCultures.length) { - // add one of the default cultures - culture = pack.cultures.length; - base = defaultCultures[culture].base; - name = defaultCultures[culture].name; - } else { - // add random culture besed on one of the current ones - culture = utils.rand(pack.cultures.length - 1); - name = utils.Names.getCulture(culture, 5, 8, ""); - base = pack.cultures[culture].base; - } - - const code = utils.abbreviate( - name, - pack.cultures.map(c => c.code) - ); - const i = pack.cultures.length; - const color = getRandomColor(utils); - - // define emblem shape - let shield = culture.shield; - const emblemShape = config.emblemShape; - if (emblemShape === "random") shield = getRandomShield(utils); - - const newCulture = { - name, - color, - base, - center, - i, - expansionism: 1, - type: "Generic", - cells: 0, - area: 0, - rural: 0, - urban: 0, - origins: [pack.cells.culture[center]], - code, - shield - }; - - return newCulture; -}; - -export const getDefault = function (count, config, pack, grid, utils) { - // generic sorting functions - const cells = pack.cells, - s = cells.s, - sMax = utils.d3.max(s), - t = cells.t, - h = cells.h, - temp = grid.cells.temp; - const n = cell => Math.ceil((s[cell] / sMax) * 3); // normalized cell score - const td = (cell, goal) => { - const d = Math.abs(temp[cells.g[cell]] - goal); - return d ? d + 1 : 1; - }; // temperature difference fee - const bd = (cell, biomes, fee = 4) => (biomes.includes(cells.biome[cell]) ? 1 : fee); // biome difference fee - const sf = (cell, fee = 4) => - cells.haven[cell] && pack.features[cells.f[cells.haven[cell]]].type !== "lake" ? 1 : fee; // not on sea coast fee - - if (config.culturesSet === "european") { - return [ - {name: "Shwazen", base: 0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "swiss"}, - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "wedged"}, - {name: "Luari", base: 2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "french"}, - {name: "Tallian", base: 3, odd: 1, sort: i => n(i) / td(i, 15), shield: "horsehead"}, - {name: "Astellian", base: 4, odd: 1, sort: i => n(i) / td(i, 16), shield: "spanish"}, - {name: "Slovan", base: 5, odd: 1, sort: i => (n(i) / td(i, 6)) * t[i], shield: "polish"}, - {name: "Norse", base: 6, odd: 1, sort: i => n(i) / td(i, 5), shield: "heater"}, - {name: "Elladan", base: 7, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, - {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 15) / t[i], shield: "roman"}, - {name: "Soumi", base: 9, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, - {name: "Portuzian", base: 13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield: "renaissance"}, - {name: "Vengrian", base: 15, odd: 1, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "horsehead2"}, - {name: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round"}, - {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench"}, - {name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "oval"} - ]; - } - - if (config.culturesSet === "oriental") { - return [ - {name: "Koryo", base: 10, odd: 1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"}, - { - name: "Berberan", - base: 17, - odd: 0.2, - sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], - shield: "oval" - }, - {name: "Eurabic", base: 18, odd: 1, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "oval"}, - {name: "Efratic", base: 23, odd: 0.1, sort: i => (n(i) / td(i, 22)) * t[i], shield: "round"}, - {name: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, - {name: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis"}, - {name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round"}, - {name: "Vietic", base: 29, odd: 0.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, - {name: "Guantzu", base: 30, odd: 0.5, sort: i => n(i) / td(i, 17), shield: "banner"}, - {name: "Ulus", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"} - ]; - } - - if (config.culturesSet === "english") { - const getName = () => utils.Names.getBase(1, 5, 9, "", 0); - return [ - {name: getName(), base: 1, odd: 1, shield: "heater"}, - {name: getName(), base: 1, odd: 1, shield: "wedged"}, - {name: getName(), base: 1, odd: 1, shield: "swiss"}, - {name: getName(), base: 1, odd: 1, shield: "oldFrench"}, - {name: getName(), base: 1, odd: 1, shield: "swiss"}, - {name: getName(), base: 1, odd: 1, shield: "spanish"}, - {name: getName(), base: 1, odd: 1, shield: "hessen"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy5"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy4"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy1"} - ]; - } - - if (config.culturesSet === "antique") { - return [ - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 15) / sf(i), shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 16) / sf(i), shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 17) / t[i], shield: "roman"}, // Roman - {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, // Greek - {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 19) / sf(i)) * h[i], shield: "boeotian"}, // Greek - {name: "Macedonian", base: 7, odd: 0.5, sort: i => (n(i) / td(i, 12)) * h[i], shield: "round"}, // Greek - {name: "Celtic", base: 22, odd: 1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "round"}, - {name: "Germanic", base: 0, odd: 1, sort: i => n(i) / td(i, 10) ** 0.5 / bd(i, [6, 8]), shield: "round"}, - {name: "Persian", base: 24, odd: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval"}, // Iranian - {name: "Scythian", base: 24, odd: 0.5, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [4]), shield: "round"}, // Iranian - {name: "Cantabrian", base: 20, odd: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval"}, // Basque - {name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise"}, // Finnic - {name: "Carthaginian", base: 42, odd: 0.3, sort: i => n(i) / td(i, 20) / sf(i), shield: "oval"}, // Levantine - {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 19)) * sf(i), shield: "oval"}, // Levantine - {name: "Mesopotamian", base: 23, odd: 0.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield: "oval"} // Mesopotamian - ]; - } - - if (config.culturesSet === "highFantasy") { - return [ - // fantasy races - { - name: "Quenian (Elfish)", - base: 33, - odd: 1, - sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], - shield: "gondor" - }, // Elves - { - name: "Eldar (Elfish)", - base: 33, - odd: 1, - sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], - shield: "noldor" - }, // Elves - { - name: "Trow (Dark Elfish)", - base: 34, - odd: 0.9, - sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], - shield: "hessen" - }, // Dark Elves - { - name: "Lothian (Dark Elfish)", - base: 34, - odd: 0.3, - sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], - shield: "wedged" - }, // Dark Elves - {name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills"}, // Dwarfs - {name: "Khazadur (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarfs - {name: "Kobold (Goblin)", base: 36, odd: 1, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin - {name: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // Orc - { - name: "Ugluk (Orkish)", - base: 37, - odd: 0.5, - sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), - shield: "moriaOrc" - }, // Orc - {name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant - {name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2"}, // Draconic - {name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid - {name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"}, // Serpents - // fantasy human - {name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"}, - {name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"}, - {name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"}, - { - name: "Dulandir (Human)", - base: 31, - odd: 1, - sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], - shield: "easterling" - } - ]; - } - - if (config.culturesSet === "darkFantasy") { - return [ - // common real-world English - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, - {name: "Enlandic", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, - {name: "Westen", base: 1, odd: 1, sort: i => n(i) / td(i, 10), shield: "heater"}, - {name: "Nortumbic", base: 1, odd: 1, sort: i => n(i) / td(i, 7), shield: "heater"}, - {name: "Mercian", base: 1, odd: 1, sort: i => n(i) / td(i, 9), shield: "heater"}, - {name: "Kentian", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, - // rare real-world western - {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench"}, - {name: "Schwarzen", base: 0, odd: 0.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "gonfalon"}, - {name: "Luarian", base: 2, odd: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, - {name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"}, - {name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"}, - // rare real-world exotic - { - name: "Kiswaili", - base: 28, - odd: 0.05, - sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), - shield: "vesicaPiscis" - }, - {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"}, - {name: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner"}, - { - name: "Ulus", - base: 31, - odd: 0.05, - sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], - shield: "banner" - }, - {name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round"}, - { - name: "Berberan", - base: 17, - odd: 0.05, - sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], - shield: "round" - }, - { - name: "Eurabic", - base: 18, - odd: 0.05, - sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], - shield: "round" - }, - {name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"}, - { - name: "Keltan", - base: 22, - odd: 0.1, - sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), - shield: "vesicaPiscis" - }, - {name: "Elladan", base: 7, odd: 0.2, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, - {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, - // fantasy races - {name: "Eldar", base: 33, odd: 0.5, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "fantasy5"}, // Elves - {name: "Trow", base: 34, odd: 0.8, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves - {name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarven - {name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin - {name: "Uruk", base: 37, odd: 0.8, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "urukHai"}, // Orc - {name: "Yotunn", base: 38, odd: 0.8, sort: i => td(i, -10), shield: "pavise"}, // Giant - {name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2"}, // Draconic - {name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid - {name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"} // Serpents - ]; - } - - if (config.culturesSet === "random") { - return utils.d3.range(count).map(function () { - const rnd = utils.rand(utils.nameBases.length - 1); - const name = utils.Names.getBaseShort(rnd); - return {name, base: rnd, odd: 1, shield: getRandomShield(utils)}; - }); - } - - // all-world - return [ - {name: "Shwazen", base: 0, odd: 0.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "hessen"}, - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, - {name: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, - {name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2"}, - {name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish"}, - {name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"}, - {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater"}, - {name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, - {name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman"}, - {name: "Soumi", base: 9, odd: 0.3, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, - {name: "Koryo", base: 10, odd: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish"}, - {name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"}, - {name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"}, - {name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"}, - { - name: "Berberan", - base: 17, - odd: 0.1, - sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], - shield: "round" - }, - {name: "Eurabic", base: 18, odd: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"}, - {name: "Inuk", base: 19, odd: 0.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield: "square"}, - {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish"}, - {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"}, - { - name: "Keltan", - base: 22, - odd: 0.05, - sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], - shield: "vesicaPiscis" - }, - {name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond"}, - {name: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, - {name: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round"}, - {name: "Carnatic", base: 26, odd: 0.05, sort: i => n(i) / td(i, 26), shield: "round"}, - {name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square"}, - {name: "Kiswaili", base: 28, odd: 0.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis"}, - {name: "Vietic", base: 29, odd: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, - {name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), shield: "banner"}, - {name: "Ulus", base: 31, odd: 0.1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"}, - {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 18)) * sf(i), shield: "oval"} // Levantine - ]; -}; - -// expand cultures across the map (Dijkstra-like algorithm) -export const expand = function (pack, config, utils) { - const { TIME, minmax } = utils; - TIME && console.time("expandCultures"); - const {cells, cultures} = pack; - - const queue = new utils.FlatQueue(); - const cost = []; - - const neutralRate = config.neutralRate || 1; - const maxExpansionCost = cells.i.length * 0.6 * neutralRate; // limit cost for culture growth - - // remove culture from all cells except of locked - const hasLocked = cultures.some(c => !c.removed && c.lock); - if (hasLocked) { - for (const cellId of cells.i) { - const culture = cultures[cells.culture[cellId]]; - if (culture.lock) continue; - cells.culture[cellId] = 0; - } - } else { - cells.culture = new Uint16Array(cells.i.length); - } - - for (const culture of cultures) { - if (!culture.i || culture.removed || culture.lock) continue; - queue.push({cellId: culture.center, cultureId: culture.i, priority: 0}, 0); - } - - while (queue.length) { - const {cellId, priority, cultureId} = queue.pop(); - const {type, expansionism} = cultures[cultureId]; - - cells.c[cellId].forEach(neibCellId => { - if (hasLocked) { - const neibCultureId = cells.culture[neibCellId]; - if (neibCultureId && cultures[neibCultureId].lock) return; // do not overwrite cell of locked culture - } - - const biome = cells.biome[neibCellId]; - const biomeCost = getBiomeCost(cultureId, biome, type, cells, cultures, pack, utils); - const biomeChangeCost = biome === cells.biome[neibCellId] ? 0 : 20; // penalty on biome change - const heightCost = getHeightCost(neibCellId, cells.h[neibCellId], type, cells, pack, utils); - const riverCost = getRiverCost(cells.r[neibCellId], neibCellId, type, cells, utils); - const typeCost = getTypeCost(cells.t[neibCellId], type); - - const cellCost = (biomeCost + biomeChangeCost + heightCost + riverCost + typeCost) / expansionism; - const totalCost = priority + cellCost; - - if (totalCost > maxExpansionCost) return; - - if (!cost[neibCellId] || totalCost < cost[neibCellId]) { - if (cells.pop[neibCellId] > 0) cells.culture[neibCellId] = cultureId; // assign culture to populated cell - cost[neibCellId] = totalCost; - queue.push({cellId: neibCellId, cultureId, priority: totalCost}, totalCost); - } - }); - } - - TIME && console.timeEnd("expandCultures"); - - return { - cells: { culture: cells.culture } - }; -}; - -function getBiomeCost(c, biome, type, cells, cultures, pack, utils) { - if (cells.biome[cultures[c].center] === biome) return 10; // tiny penalty for native biome - if (type === "Hunting") return utils.biomesData.cost[biome] * 5; // non-native biome penalty for hunters - if (type === "Nomadic" && biome > 4 && biome < 10) return utils.biomesData.cost[biome] * 10; // forest biome penalty for nomads - return utils.biomesData.cost[biome] * 2; // general non-native biome penalty -} - -function getHeightCost(i, h, type, cells, pack, utils) { - const f = pack.features[cells.f[i]], - a = cells.area[i]; - if (type === "Lake" && f.type === "lake") return 10; // no lake crossing penalty for Lake cultures - if (type === "Naval" && h < 20) return a * 2; // low sea/lake crossing penalty for Naval cultures - if (type === "Nomadic" && h < 20) return a * 50; // giant sea/lake crossing penalty for Nomads - if (h < 20) return a * 6; // general sea/lake crossing penalty - if (type === "Highland" && h < 44) return 3000; // giant penalty for highlanders on lowlands - if (type === "Highland" && h < 62) return 200; // giant penalty for highlanders on lowhills - if (type === "Highland") return 0; // no penalty for highlanders on highlands - if (h >= 67) return 200; // general mountains crossing penalty - if (h >= 44) return 30; // general hills crossing penalty - return 0; -} - -function getRiverCost(riverId, cellId, type, cells, utils) { - if (type === "River") return riverId ? 0 : 100; // penalty for river cultures - if (!riverId) return 0; // no penalty for others if there is no river - return utils.minmax(cells.fl[cellId] / 10, 20, 100); // river penalty from 20 to 100 based on flux -} - -function getTypeCost(t, type) { - if (t === 1) return type === "Naval" || type === "Lake" ? 0 : type === "Nomadic" ? 60 : 20; // penalty for coastline - if (t === 2) return type === "Naval" || type === "Nomadic" ? 30 : 0; // low penalty for land level 2 for Navals and nomads - if (t !== -1) return type === "Naval" || type === "Lake" ? 100 : 0; // penalty for mainland for navals - return 0; -} - -export const getRandomShield = function (utils) { - const type = utils.rw(utils.COA.shields.types); - return utils.rw(utils.COA.shields[type]); -}; - -function getColors(count, utils) { - // This function needs to be implemented based on the original getColors logic - // For now, returning a placeholder - return utils.d3.range(count).map(() => utils.getRandomColor ? utils.getRandomColor() : "#000000"); -} - -function getRandomColor(utils) { - // This function needs to be implemented based on the original getRandomColor logic - // For now, returning a placeholder - return utils.getRandomColor ? utils.getRandomColor() : "#000000"; -} diff --git a/procedural/src/engine/modules/features.js b/procedural/src/engine/modules/features.js deleted file mode 100644 index 8b0a5d87..00000000 --- a/procedural/src/engine/modules/features.js +++ /dev/null @@ -1,347 +0,0 @@ -"use strict"; - -const DEEPER_LAND = 3; -const LANDLOCKED = 2; -const LAND_COAST = 1; -const UNMARKED = 0; -const WATER_COAST = -1; -const DEEP_WATER = -2; - -// calculate distance to coast for every cell -function markup({distanceField, neighbors, start, increment, limit = 127}) { - for (let distance = start, marked = Infinity; marked > 0 && distance !== limit; distance += increment) { - marked = 0; - const prevDistance = distance - increment; - for (let cellId = 0; cellId < neighbors.length; cellId++) { - if (distanceField[cellId] !== prevDistance) continue; - - for (const neighborId of neighbors[cellId]) { - if (distanceField[neighborId] !== UNMARKED) continue; - distanceField[neighborId] = distance; - marked++; - } - } - } -} - -/** - * Mark Grid features (ocean, lakes, islands) and calculate distance field - * - * REQUIRES: - * - grid.cells.h (heights from heightmap generation) - * - grid.cells.c (cell neighbors from grid generation) - * - config.debug (debug configuration) - * - * PROVIDES: - * - grid.cells.f (feature assignments) - * - grid.cells.t (distance field) - * - grid.features (features array) - */ -export function markupGrid(grid, config, utils) { - // Check required properties exist - if (!grid.cells.h) { - throw new Error("Features module requires grid.cells.h (heights) from heightmap generation"); - } - if (!grid.cells.c) { - throw new Error("Features module requires grid.cells.c (neighbors) from grid generation"); - } - if (!config.debug) { - throw new Error("Features module requires config.debug section"); - } - - const {rn} = utils; - const { TIME } = config.debug; - - TIME && console.time("markupGrid"); - - const {h: heights, c: neighbors, b: borderCells, i} = grid.cells; - const cellsNumber = i.length; - const distanceField = new Int8Array(cellsNumber); // gird.cells.t - const featureIds = new Uint16Array(cellsNumber); // gird.cells.f - const features = [0]; - - const queue = [0]; - for (let featureId = 1; queue[0] !== -1; featureId++) { - const firstCell = queue[0]; - featureIds[firstCell] = featureId; - - const land = heights[firstCell] >= 20; - let border = false; // set true if feature touches map edge - - while (queue.length) { - const cellId = queue.pop(); - if (!border && borderCells[cellId]) border = true; - - for (const neighborId of neighbors[cellId]) { - const isNeibLand = heights[neighborId] >= 20; - - if (land === isNeibLand && featureIds[neighborId] === UNMARKED) { - featureIds[neighborId] = featureId; - queue.push(neighborId); - } else if (land && !isNeibLand) { - distanceField[cellId] = LAND_COAST; - distanceField[neighborId] = WATER_COAST; - } - } - } - - const type = land ? "island" : border ? "ocean" : "lake"; - features.push({i: featureId, land, border, type}); - - queue[0] = featureIds.findIndex(f => f === UNMARKED); // find unmarked cell - } - - // markup deep ocean cells - markup({distanceField, neighbors, start: DEEP_WATER, increment: -1, limit: -10}); - - const updatedGrid = { - ...grid, - cells: { - ...grid.cells, - t: distanceField, - f: featureIds - }, - features - }; - - TIME && console.timeEnd("markupGrid"); - - return updatedGrid; -} - -/** - * Mark Pack features (ocean, lakes, islands), calculate distance field and add properties - * - * REQUIRES: - * - pack.cells.h (heights from pack generation) - * - pack.cells.c (cell neighbors from pack generation) - * - grid.features (features from grid markup) - * - * PROVIDES: - * - pack.cells.f (feature assignments) - * - pack.cells.t (distance field) - * - pack.features (features array) - */ -export function markupPack(pack, grid, config, utils, modules) { - // Check required properties exist - if (!pack.cells.h) { - throw new Error("Features markupPack requires pack.cells.h (heights) from pack generation"); - } - if (!pack.cells.c) { - throw new Error("Features markupPack requires pack.cells.c (neighbors) from pack generation"); - } - if (!grid.features) { - throw new Error("Features markupPack requires grid.features from grid markup"); - } - - const {TIME} = config; - const {isLand, isWater, dist2, rn, clipPoly, unique, createTypedArray, connectVertices} = utils; - const {Lakes} = modules; - const {d3} = utils; - - TIME && console.time("markupPack"); - - const {cells, vertices} = pack; - const {c: neighbors, b: borderCells, i} = cells; - const packCellsNumber = i.length; - if (!packCellsNumber) return pack; // no cells -> there is nothing to do - - const distanceField = new Int8Array(packCellsNumber); // pack.cells.t - const featureIds = new Uint16Array(packCellsNumber); // pack.cells.f - const haven = createTypedArray({maxValue: packCellsNumber, length: packCellsNumber}); // haven: opposite water cell - const harbor = new Uint8Array(packCellsNumber); // harbor: number of adjacent water cells - const features = [0]; - - const queue = [0]; - for (let featureId = 1; queue[0] !== -1; featureId++) { - const firstCell = queue[0]; - featureIds[firstCell] = featureId; - - const land = isLand(firstCell, pack); - let border = Boolean(borderCells[firstCell]); // true if feature touches map border - let totalCells = 1; // count cells in a feature - - while (queue.length) { - const cellId = queue.pop(); - if (borderCells[cellId]) border = true; - if (!border && borderCells[cellId]) border = true; - - for (const neighborId of neighbors[cellId]) { - const isNeibLand = isLand(neighborId, pack); - - if (land && !isNeibLand) { - distanceField[cellId] = LAND_COAST; - distanceField[neighborId] = WATER_COAST; - if (!haven[cellId]) defineHaven(cellId); - } else if (land && isNeibLand) { - if (distanceField[neighborId] === UNMARKED && distanceField[cellId] === LAND_COAST) - distanceField[neighborId] = LANDLOCKED; - else if (distanceField[cellId] === UNMARKED && distanceField[neighborId] === LAND_COAST) - distanceField[cellId] = LANDLOCKED; - } - - if (!featureIds[neighborId] && land === isNeibLand) { - queue.push(neighborId); - featureIds[neighborId] = featureId; - totalCells++; - } - } - } - - features.push(addFeature({firstCell, land, border, featureId, totalCells})); - queue[0] = featureIds.findIndex(f => f === UNMARKED); // find unmarked cell - } - - markup({distanceField, neighbors, start: DEEPER_LAND, increment: 1}); // markup pack land - markup({distanceField, neighbors, start: DEEP_WATER, increment: -1, limit: -10}); // markup pack water - - const updatedPack = { - ...pack, - cells: { - ...pack.cells, - t: distanceField, - f: featureIds, - haven, - harbor - }, - features - }; - - TIME && console.timeEnd("markupPack"); - - return updatedPack; - - function defineHaven(cellId) { - const waterCells = neighbors[cellId].filter(i => isWater(i, pack)); - const distances = waterCells.map(neibCellId => dist2(cells.p[cellId], cells.p[neibCellId])); - const closest = distances.indexOf(Math.min.apply(Math, distances)); - - haven[cellId] = waterCells[closest]; - harbor[cellId] = waterCells.length; - } - - function addFeature({firstCell, land, border, featureId, totalCells}) { - const type = land ? "island" : border ? "ocean" : "lake"; - const [startCell, featureVertices] = getCellsData(type, firstCell); - const points = clipPoly(featureVertices.map(vertex => vertices.p[vertex]), config); - const area = d3.polygonArea(points); // feature perimiter area - const absArea = Math.abs(rn(area)); - - const feature = { - i: featureId, - type, - land, - border, - cells: totalCells, - firstCell: startCell, - vertices: featureVertices, - area: absArea - }; - - if (type === "lake") { - if (area > 0) feature.vertices = feature.vertices.reverse(); - feature.shoreline = unique(feature.vertices.map(vertex => vertices.c[vertex].filter(i => isLand(i, pack))).flat()); - feature.height = Lakes.getHeight(feature, pack, utils); - } - - return feature; - - function getCellsData(featureType, firstCell) { - if (featureType === "ocean") return [firstCell, []]; - - const getType = cellId => featureIds[cellId]; - const type = getType(firstCell); - const ofSameType = cellId => getType(cellId) === type; - const ofDifferentType = cellId => getType(cellId) !== type; - - const startCell = findOnBorderCell(firstCell); - const featureVertices = getFeatureVertices(startCell); - return [startCell, featureVertices]; - - function findOnBorderCell(firstCell) { - const isOnBorder = cellId => borderCells[cellId] || neighbors[cellId].some(ofDifferentType); - if (isOnBorder(firstCell)) return firstCell; - - const startCell = cells.i.filter(ofSameType).find(isOnBorder); - if (startCell === undefined) - throw new Error(`Markup: firstCell ${firstCell} is not on the feature or map border`); - - return startCell; - } - - function getFeatureVertices(startCell) { - const startingVertex = cells.v[startCell].find(v => vertices.c[v].some(ofDifferentType)); - if (startingVertex === undefined) - throw new Error(`Markup: startingVertex for cell ${startCell} is not found`); - - return connectVertices({vertices, startingVertex, ofSameType, closeRing: false}); - } - } - } -} - -// add properties to pack features -export function specify(pack, grid, modules) { - const {Lakes} = modules; - const gridCellsNumber = grid.cells.i.length; - const OCEAN_MIN_SIZE = gridCellsNumber / 25; - const SEA_MIN_SIZE = gridCellsNumber / 1000; - const CONTINENT_MIN_SIZE = gridCellsNumber / 10; - const ISLAND_MIN_SIZE = gridCellsNumber / 1000; - - const updatedFeatures = pack.features.map(feature => { - if (!feature || feature.type === "ocean") return feature; - - const updatedFeature = { - ...feature, - group: defineGroup(feature) - }; - - if (feature.type === "lake") { - updatedFeature.height = Lakes.getHeight(feature); - updatedFeature.name = Lakes.getName(feature); - } - - return updatedFeature; - }); - - return { - ...pack, - features: updatedFeatures - }; - - function defineGroup(feature) { - if (feature.type === "island") return defineIslandGroup(feature); - if (feature.type === "ocean") return defineOceanGroup(feature); - if (feature.type === "lake") return defineLakeGroup(feature); - throw new Error(`Markup: unknown feature type ${feature.type}`); - } - - function defineOceanGroup(feature) { - if (feature.cells > OCEAN_MIN_SIZE) return "ocean"; - if (feature.cells > SEA_MIN_SIZE) return "sea"; - return "gulf"; - } - - function defineIslandGroup(feature) { - const prevFeature = pack.features[pack.cells.f[feature.firstCell - 1]]; - if (prevFeature && prevFeature.type === "lake") return "lake_island"; - if (feature.cells > CONTINENT_MIN_SIZE) return "continent"; - if (feature.cells > ISLAND_MIN_SIZE) return "island"; - return "isle"; - } - - function defineLakeGroup(feature) { - if (feature.temp < -3) return "frozen"; - if (feature.height > 60 && feature.cells < 10 && feature.firstCell % 10 === 0) return "lava"; - - if (!feature.inlets && !feature.outlet) { - if (feature.evaporation > feature.flux * 4) return "dry"; - if (feature.cells < 3 && feature.firstCell % 10 === 0) return "sinkhole"; - } - - if (!feature.outlet && feature.evaporation > feature.flux) return "salt"; - - return "freshwater"; - } -} diff --git a/procedural/src/engine/modules/fonts.js b/procedural/src/engine/modules/fonts.js deleted file mode 100644 index b743419a..00000000 --- a/procedural/src/engine/modules/fonts.js +++ /dev/null @@ -1,346 +0,0 @@ -"use strict"; - -const fonts = [ - {family: "Arial"}, - {family: "Brush Script MT"}, - {family: "Century Gothic"}, - {family: "Comic Sans MS"}, - {family: "Copperplate"}, - {family: "Courier New"}, - {family: "Garamond"}, - {family: "Georgia"}, - {family: "Herculanum"}, - {family: "Impact"}, - {family: "Papyrus"}, - {family: "Party LET"}, - {family: "Times New Roman"}, - {family: "Verdana"}, - { - family: "Almendra SC", - src: "url(https://fonts.gstatic.com/s/almendrasc/v13/Iure6Yx284eebowr7hbyTaZOrLQ.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Amarante", - src: "url(https://fonts.gstatic.com/s/amarante/v22/xMQXuF1KTa6EvGx9bp-wAXs.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Amatic SC", - src: "url(https://fonts.gstatic.com/s/amaticsc/v11/TUZ3zwprpvBS1izr_vOMscGKfrUC.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Arima Madurai", - src: "url(https://fonts.gstatic.com/s/arimamadurai/v14/t5tmIRoeKYORG0WNMgnC3seB3T7Prw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Architects Daughter", - src: "url(https://fonts.gstatic.com/s/architectsdaughter/v8/RXTgOOQ9AAtaVOHxx0IUBM3t7GjCYufj5TXV5VnA2p8.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Bitter", - src: "url(https://fonts.gstatic.com/s/bitter/v12/zfs6I-5mjWQ3nxqccMoL2A.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Caesar Dressing", - src: "url(https://fonts.gstatic.com/s/caesardressing/v6/yYLx0hLa3vawqtwdswbotmK4vrRHdrz7.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Cinzel", - src: "url(https://fonts.gstatic.com/s/cinzel/v7/zOdksD_UUTk1LJF9z4tURA.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Dancing Script", - src: "url(https://fonts.gstatic.com/s/dancingscript/v9/KGBfwabt0ZRLA5W1ywjowUHdOuSHeh0r6jGTOGdAKHA.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Eagle Lake", - src: "url(https://fonts.gstatic.com/s/eaglelake/v24/ptRMTiqbbuNJDOiKj9wG1On4KCFtpe4.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Faster One", - src: "url(https://fonts.gstatic.com/s/fasterone/v17/H4ciBXCHmdfClFb-vWhf-LyYhw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Forum", - src: "url(https://fonts.gstatic.com/s/forum/v16/6aey4Ky-Vb8Ew8IROpI.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Fredericka the Great", - src: "url(https://fonts.gstatic.com/s/frederickathegreat/v6/9Bt33CxNwt7aOctW2xjbCstzwVKsIBVV--Sjxbc.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Gloria Hallelujah", - src: "url(https://fonts.gstatic.com/s/gloriahallelujah/v9/CA1k7SlXcY5kvI81M_R28cNDay8z-hHR7F16xrcXsJw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Great Vibes", - src: "url(https://fonts.gstatic.com/s/greatvibes/v5/6q1c0ofG6NKsEhAc2eh-3Y4P5ICox8Kq3LLUNMylGO4.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Henny Penny", - src: "url(https://fonts.gstatic.com/s/hennypenny/v17/wXKvE3UZookzsxz_kjGSfPQtvXI.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "IM Fell English", - src: "url(https://fonts.gstatic.com/s/imfellenglish/v7/xwIisCqGFi8pff-oa9uSVAkYLEKE0CJQa8tfZYc_plY.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Kelly Slab", - src: "url(https://fonts.gstatic.com/s/kellyslab/v15/-W_7XJX0Rz3cxUnJC5t6fkQLfg.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Kranky", - src: "url(https://fonts.gstatic.com/s/kranky/v24/hESw6XVgJzlPsFn8oR2F.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Lobster Two", - src: "url(https://fonts.gstatic.com/s/lobstertwo/v18/BngMUXZGTXPUvIoyV6yN5-fN5qU.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Lugrasimo", - src: "url(https://fonts.gstatic.com/s/lugrasimo/v4/qkBXXvoF_s_eT9c7Y7au455KsgbLMA.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Kaushan Script", - src: "url(https://fonts.gstatic.com/s/kaushanscript/v6/qx1LSqts-NtiKcLw4N03IEd0sm1ffa_JvZxsF_BEwQk.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Macondo", - src: "url(https://fonts.gstatic.com/s/macondo/v21/RrQQboN9-iB1IXmOe2LE0Q.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "MedievalSharp", - src: "url(https://fonts.gstatic.com/s/medievalsharp/v9/EvOJzAlL3oU5AQl2mP5KdgptMqhwMg.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Metal Mania", - src: "url(https://fonts.gstatic.com/s/metalmania/v22/RWmMoKWb4e8kqMfBUdPFJdXFiaQ.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Metamorphous", - src: "url(https://fonts.gstatic.com/s/metamorphous/v7/Wnz8HA03aAXcC39ZEX5y133EOyqs.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Montez", - src: "url(https://fonts.gstatic.com/s/montez/v8/aq8el3-0osHIcFK6bXAPkw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Nova Script", - src: "url(https://fonts.gstatic.com/s/novascript/v10/7Au7p_IpkSWSTWaFWkumvlQKGFw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Orbitron", - src: "url(https://fonts.gstatic.com/s/orbitron/v9/HmnHiRzvcnQr8CjBje6GQvesZW2xOQ-xsNqO47m55DA.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Oregano", - src: "url(https://fonts.gstatic.com/s/oregano/v13/If2IXTPxciS3H4S2oZDVPg.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Pirata One", - src: "url(https://fonts.gstatic.com/s/pirataone/v22/I_urMpiDvgLdLh0fAtofhi-Org.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Sail", - src: "url(https://fonts.gstatic.com/s/sail/v16/DPEjYwiBxwYJJBPJAQ.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Satisfy", - src: "url(https://fonts.gstatic.com/s/satisfy/v8/2OzALGYfHwQjkPYWELy-cw.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Shadows Into Light", - src: "url(https://fonts.gstatic.com/s/shadowsintolight/v7/clhLqOv7MXn459PTh0gXYFK2TSYBz0eNcHnp4YqE4Ts.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - }, - { - family: "Tapestry", - src: "url(https://fonts.gstatic.com/s/macondo/v21/RrQQboN9-iB1IXmOe2LE0Q.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Uncial Antiqua", - src: "url(https://fonts.gstatic.com/s/uncialantiqua/v5/N0bM2S5WOex4OUbESzoESK-i-MfWQZQ.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Underdog", - src: "url(https://fonts.gstatic.com/s/underdog/v6/CHygV-jCElj7diMroWSlWV8.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "UnifrakturMaguntia", - src: "url(https://fonts.gstatic.com/s/unifrakturmaguntia/v16/WWXPlieVYwiGNomYU-ciRLRvEmK7oaVemGZM.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" - }, - { - family: "Yellowtail", - src: "url(https://fonts.gstatic.com/s/yellowtail/v8/GcIHC9QEwVkrA19LJU1qlPk_vArhqVIZ0nv9q090hN8.woff2)", - unicodeRange: - "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215" - } -]; - -export function getAvailableFonts() { - return [...fonts]; -} - -export function getUsedFonts(svgData) { - const usedFontFamilies = new Set(); - - // Check label groups for font-family attributes - if (svgData.labels) { - for (const labelGroup of svgData.labels) { - if (labelGroup.fontFamily) { - usedFontFamilies.add(labelGroup.fontFamily); - } - } - } - - // Check provinces font - if (svgData.provinces && svgData.provinces.fontFamily) { - usedFontFamilies.add(svgData.provinces.fontFamily); - } - - // Check legend font - if (svgData.legend && svgData.legend.fontFamily) { - usedFontFamilies.add(svgData.legend.fontFamily); - } - - const usedFonts = fonts.filter(font => usedFontFamilies.has(font.family)); - return usedFonts; -} - -export async function fetchGoogleFont(family) { - const url = `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, "+")}`; - try { - const resp = await fetch(url); - const text = await resp.text(); - - const fontFaceRules = text.match(/font-face\s*{[^}]+}/g); - const fontData = fontFaceRules.map(fontFace => { - const srcURL = fontFace.match(/url\(['"]?(.+?)['"]?\)/)[1]; - const src = `url(${srcURL})`; - const unicodeRange = fontFace.match(/unicode-range: (.*?);/)?.[1]; - const variant = fontFace.match(/font-style: (.*?);/)?.[1]; - - const font = {family, src}; - if (unicodeRange) font.unicodeRange = unicodeRange; - if (variant && variant !== "normal") font.variant = variant; - return font; - }); - - return fontData; - } catch (err) { - console.error(err); - return null; - } -} - -function readBlobAsDataURL(blob) { - return new Promise(function (resolve, reject) { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result); - reader.onerror = reject; - reader.readAsDataURL(blob); - }); -} - -export async function loadFontsAsDataURI(fontList) { - const promises = fontList.map(async font => { - const url = font.src.match(/url\(['"]?(.+?)['"]?\)/)[1]; - const resp = await fetch(url); - const blob = await resp.blob(); - const dataURL = await readBlobAsDataURL(blob); - - return {...font, src: `url('${dataURL}')`}; - }); - - return await Promise.all(promises); -} - -export function createFontDefinition(font) { - const {family, src, ...rest} = font; - - if (!src) { - return {family, src: `local(${family})`, ...rest}; - } - - return {family, src, ...rest}; -} - -export function addCustomFont(family, src = null) { - const fontDefinition = src ? {family, src} : {family}; - fonts.push(fontDefinition); - return fontDefinition; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/heightmap-generator.js b/procedural/src/engine/modules/heightmap-generator.js deleted file mode 100644 index 78553041..00000000 --- a/procedural/src/engine/modules/heightmap-generator.js +++ /dev/null @@ -1,554 +0,0 @@ -"use strict"; - -export function fromTemplate(grid, templateId, config, utils) { - const { heightmapTemplates, aleaPRNG } = utils; - const templateString = heightmapTemplates[templateId]?.template || ""; - const steps = templateString.split("\n"); - - if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${templateId}. Steps: ${steps}`); - - const { cellsDesired, cells, points } = grid; - let heights = cells.h ? Uint8Array.from(cells.h) : createTypedArray({ maxValue: 100, length: points.length }); - const blobPower = getBlobPower(cellsDesired); - const linePower = getLinePower(cellsDesired); - - // Set up PRNG if seed is provided - if (config.seed !== undefined) { - Math.random = aleaPRNG(config.seed); - } - - for (const step of steps) { - const elements = step.trim().split(" "); - if (elements.length < 2) throw new Error(`Heightmap template: steps < 2. Template: ${templateId}. Step: ${elements}`); - heights = addStep(heights, grid, blobPower, linePower, utils, ...elements); - } - - return heights; -} - -export async function fromPrecreated(grid, imageId, config, utils) { - // This function requires browser-specific Canvas API and Image loading - // It should be handled by the viewer layer, not the headless engine - throw new Error("fromPrecreated requires browser environment - should be handled by viewer layer"); -} - -export async function generate(grid, config, utils) { - const { TIME, aleaPRNG } = utils; - TIME && console.time("defineHeightmap"); - - const templateId = config.heightmap.templateId; - - // Set up PRNG if seed is provided - if (config.seed !== undefined) { - Math.random = aleaPRNG(config.seed); - } - - const { heightmapTemplates } = utils; - const isTemplate = templateId in heightmapTemplates; - - if (!isTemplate) { - throw new Error(`Template "${templateId}" not found. Available templates: ${Object.keys(heightmapTemplates).join(', ')}`); - } - - const heights = fromTemplate(grid, templateId, config, utils); - TIME && console.timeEnd("defineHeightmap"); - - return heights; -} - -function addStep(heights, grid, blobPower, linePower, utils, tool, a2, a3, a4, a5) { - if (tool === "Hill") return addHill(heights, grid, blobPower, utils, a2, a3, a4, a5); - if (tool === "Pit") return addPit(heights, grid, blobPower, utils, a2, a3, a4, a5); - if (tool === "Range") return addRange(heights, grid, linePower, utils, a2, a3, a4, a5); - if (tool === "Trough") return addTrough(heights, grid, linePower, utils, a2, a3, a4, a5); - if (tool === "Strait") return addStrait(heights, grid, utils, a2, a3); - if (tool === "Mask") return mask(heights, grid, a2); - if (tool === "Invert") return invert(heights, grid, a2, a3); - if (tool === "Add") return modify(heights, a3, +a2, 1); - if (tool === "Multiply") return modify(heights, a3, 0, +a2); - if (tool === "Smooth") return smooth(heights, grid, utils, a2); - - return heights; -} - -function getBlobPower(cells) { - const blobPowerMap = { - 1000: 0.93, - 2000: 0.95, - 5000: 0.97, - 10000: 0.98, - 20000: 0.99, - 30000: 0.991, - 40000: 0.993, - 50000: 0.994, - 60000: 0.995, - 70000: 0.9955, - 80000: 0.996, - 90000: 0.9964, - 100000: 0.9973 - }; - return blobPowerMap[cells] || 0.98; -} - -function getLinePower(cells) { - const linePowerMap = { - 1000: 0.75, - 2000: 0.77, - 5000: 0.79, - 10000: 0.81, - 20000: 0.82, - 30000: 0.83, - 40000: 0.84, - 50000: 0.86, - 60000: 0.87, - 70000: 0.88, - 80000: 0.91, - 90000: 0.92, - 100000: 0.93 - }; - - return linePowerMap[cells] || 0.81; -} - -function addHill(heights, grid, blobPower, utils, count, height, rangeX, rangeY) { - const { getNumberInRange, findGridCell, lim } = utils; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - - count = getNumberInRange(count); - let newHeights = new Uint8Array(heights); - - while (count > 0) { - addOneHill(); - count--; - } - - function addOneHill() { - const change = new Uint8Array(heights.length); - let limit = 0; - let start; - let h = lim(getNumberInRange(height)); - - do { - const x = getPointInRange(rangeX, graphWidth, utils); - const y = getPointInRange(rangeY, graphHeight, utils); - start = findGridCell(x, y, grid); - limit++; - } while (newHeights[start] + h > 90 && limit < 50); - - change[start] = h; - const queue = [start]; - while (queue.length) { - const q = queue.shift(); - - for (const c of grid.cells.c[q]) { - if (change[c]) continue; - change[c] = change[q] ** blobPower * (Math.random() * 0.2 + 0.9); - if (change[c] > 1) queue.push(c); - } - } - - newHeights = newHeights.map((h, i) => lim(h + change[i])); - } - - return newHeights; -} - -function addPit(heights, grid, blobPower, utils, count, height, rangeX, rangeY) { - const { getNumberInRange, findGridCell, lim } = utils; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - - count = getNumberInRange(count); - let newHeights = new Uint8Array(heights); - - while (count > 0) { - addOnePit(); - count--; - } - - function addOnePit() { - const used = new Uint8Array(heights.length); - let limit = 0, start; - let h = lim(getNumberInRange(height)); - - do { - const x = getPointInRange(rangeX, graphWidth, utils); - const y = getPointInRange(rangeY, graphHeight, utils); - start = findGridCell(x, y, grid); - limit++; - } while (newHeights[start] < 20 && limit < 50); - - const queue = [start]; - while (queue.length) { - const q = queue.shift(); - h = h ** blobPower * (Math.random() * 0.2 + 0.9); - if (h < 1) return; - - grid.cells.c[q].forEach(function (c, i) { - if (used[c]) return; - newHeights[c] = lim(newHeights[c] - h * (Math.random() * 0.2 + 0.9)); - used[c] = 1; - queue.push(c); - }); - } - } - - return newHeights; -} - -// fromCell, toCell are options cell ids -function addRange(heights, grid, linePower, utils, count, height, rangeX, rangeY, startCell, endCell) { - const { getNumberInRange, findGridCell, lim, d3 } = utils; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - - count = getNumberInRange(count); - let newHeights = new Uint8Array(heights); - - while (count > 0) { - addOneRange(); - count--; - } - - function addOneRange() { - const used = new Uint8Array(heights.length); - let h = lim(getNumberInRange(height)); - - if (rangeX && rangeY) { - // find start and end points - const startX = getPointInRange(rangeX, graphWidth, utils); - const startY = getPointInRange(rangeY, graphHeight, utils); - - let dist = 0, limit = 0, endX, endY; - - do { - endX = Math.random() * graphWidth * 0.8 + graphWidth * 0.1; - endY = Math.random() * graphHeight * 0.7 + graphHeight * 0.15; - dist = Math.abs(endY - startY) + Math.abs(endX - startX); - limit++; - } while ((dist < graphWidth / 8 || dist > graphWidth / 3) && limit < 50); - - startCell = findGridCell(startX, startY, grid); - endCell = findGridCell(endX, endY, grid); - } - - let range = getRange(startCell, endCell); - - // get main ridge - function getRange(cur, end) { - const range = [cur]; - const p = grid.points; - used[cur] = 1; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - if (used[e]) return; - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.85) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - if (min === Infinity) return range; - range.push(cur); - used[cur] = 1; - } - - return range; - } - - // add height to ridge and cells around - let queue = range.slice(), i = 0; - while (queue.length) { - const frontier = queue.slice(); - (queue = []), i++; - frontier.forEach(i => { - newHeights[i] = lim(newHeights[i] + h * (Math.random() * 0.3 + 0.85)); - }); - h = h ** linePower - 1; - if (h < 2) break; - frontier.forEach(f => { - grid.cells.c[f].forEach(i => { - if (!used[i]) { - queue.push(i); - used[i] = 1; - } - }); - }); - } - - // generate prominences - range.forEach((cur, d) => { - if (d % 6 !== 0) return; - for (const l of d3.range(i)) { - const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => newHeights[a] - newHeights[b])]; // downhill cell - newHeights[min] = (newHeights[cur] * 2 + newHeights[min]) / 3; - cur = min; - } - }); - } - - return newHeights; -} - -function addTrough(heights, grid, linePower, utils, count, height, rangeX, rangeY, startCell, endCell) { - const { getNumberInRange, findGridCell, lim, d3 } = utils; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - - count = getNumberInRange(count); - let newHeights = new Uint8Array(heights); - - while (count > 0) { - addOneTrough(); - count--; - } - - function addOneTrough() { - const used = new Uint8Array(heights.length); - let h = lim(getNumberInRange(height)); - - if (rangeX && rangeY) { - // find start and end points - let limit = 0, startX, startY, dist = 0, endX, endY; - do { - startX = getPointInRange(rangeX, graphWidth, utils); - startY = getPointInRange(rangeY, graphHeight, utils); - startCell = findGridCell(startX, startY, grid); - limit++; - } while (newHeights[startCell] < 20 && limit < 50); - - limit = 0; - do { - endX = Math.random() * graphWidth * 0.8 + graphWidth * 0.1; - endY = Math.random() * graphHeight * 0.7 + graphHeight * 0.15; - dist = Math.abs(endY - startY) + Math.abs(endX - startX); - limit++; - } while ((dist < graphWidth / 8 || dist > graphWidth / 2) && limit < 50); - - endCell = findGridCell(endX, endY, grid); - } - - let range = getRange(startCell, endCell); - - // get main ridge - function getRange(cur, end) { - const range = [cur]; - const p = grid.points; - used[cur] = 1; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - if (used[e]) return; - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.8) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - if (min === Infinity) return range; - range.push(cur); - used[cur] = 1; - } - - return range; - } - - // add height to ridge and cells around - let queue = range.slice(), i = 0; - while (queue.length) { - const frontier = queue.slice(); - (queue = []), i++; - frontier.forEach(i => { - newHeights[i] = lim(newHeights[i] - h * (Math.random() * 0.3 + 0.85)); - }); - h = h ** linePower - 1; - if (h < 2) break; - frontier.forEach(f => { - grid.cells.c[f].forEach(i => { - if (!used[i]) { - queue.push(i); - used[i] = 1; - } - }); - }); - } - - // generate prominences - range.forEach((cur, d) => { - if (d % 6 !== 0) return; - for (const l of d3.range(i)) { - const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => newHeights[a] - newHeights[b])]; // downhill cell - newHeights[min] = (newHeights[cur] * 2 + newHeights[min]) / 3; - cur = min; - } - }); - } - - return newHeights; -} - -function addStrait(heights, grid, utils, width, direction = "vertical") { - const { getNumberInRange, findGridCell, P } = utils; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - - width = Math.min(getNumberInRange(width), grid.cellsX / 3); - if (width < 1 && P(width)) return heights; - - let newHeights = new Uint8Array(heights); - const used = new Uint8Array(heights.length); - const vert = direction === "vertical"; - const startX = vert ? Math.floor(Math.random() * graphWidth * 0.4 + graphWidth * 0.3) : 5; - const startY = vert ? 5 : Math.floor(Math.random() * graphHeight * 0.4 + graphHeight * 0.3); - const endX = vert - ? Math.floor(graphWidth - startX - graphWidth * 0.1 + Math.random() * graphWidth * 0.2) - : graphWidth - 5; - const endY = vert - ? graphHeight - 5 - : Math.floor(graphHeight - startY - graphHeight * 0.1 + Math.random() * graphHeight * 0.2); - - const start = findGridCell(startX, startY, grid); - const end = findGridCell(endX, endY, grid); - let range = getRange(start, end); - const query = []; - - function getRange(cur, end) { - const range = []; - const p = grid.points; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.8) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - range.push(cur); - } - - return range; - } - - const step = 0.1 / width; - - while (width > 0) { - const exp = 0.9 - step * width; - range.forEach(function (r) { - grid.cells.c[r].forEach(function (e) { - if (used[e]) return; - used[e] = 1; - query.push(e); - newHeights[e] **= exp; - if (newHeights[e] > 100) newHeights[e] = 5; - }); - }); - range = query.slice(); - - width--; - } - - return newHeights; -} - -function modify(heights, range, add, mult, power) { - const { lim } = { lim: val => Math.max(0, Math.min(100, val)) }; - const min = range === "land" ? 20 : range === "all" ? 0 : +range.split("-")[0]; - const max = range === "land" || range === "all" ? 100 : +range.split("-")[1]; - const isLand = min === 20; - - return heights.map(h => { - if (h < min || h > max) return h; - - if (add) h = isLand ? Math.max(h + add, 20) : h + add; - if (mult !== 1) h = isLand ? (h - 20) * mult + 20 : h * mult; - if (power) h = isLand ? (h - 20) ** power + 20 : h ** power; - return lim(h); - }); -} - -function smooth(heights, grid, utils, fr = 2, add = 0) { - const { d3, lim } = utils; - - return heights.map((h, i) => { - const a = [h]; - grid.cells.c[i].forEach(c => a.push(heights[c])); - if (fr === 1) return d3.mean(a) + add; - return lim((h * (fr - 1) + d3.mean(a) + add) / fr); - }); -} - -function mask(heights, grid, power = 1) { - const { lim } = { lim: val => Math.max(0, Math.min(100, val)) }; - const graphWidth = grid.cellsX; - const graphHeight = grid.cellsY; - const fr = power ? Math.abs(power) : 1; - - return heights.map((h, i) => { - const [x, y] = grid.points[i]; - const nx = (2 * x) / graphWidth - 1; // [-1, 1], 0 is center - const ny = (2 * y) / graphHeight - 1; // [-1, 1], 0 is center - let distance = (1 - nx ** 2) * (1 - ny ** 2); // 1 is center, 0 is edge - if (power < 0) distance = 1 - distance; // inverted, 0 is center, 1 is edge - const masked = h * distance; - return lim((h * (fr - 1) + masked) / fr); - }); -} - -function invert(heights, grid, count, axes) { - const { P } = { P: probability => Math.random() < probability }; - if (!P(count)) return heights; - - const invertX = axes !== "y"; - const invertY = axes !== "x"; - const { cellsX, cellsY } = grid; - - const inverted = heights.map((h, i) => { - const x = i % cellsX; - const y = Math.floor(i / cellsX); - - const nx = invertX ? cellsX - x - 1 : x; - const ny = invertY ? cellsY - y - 1 : y; - const invertedI = nx + ny * cellsX; - return heights[invertedI]; - }); - - return inverted; -} - -function getPointInRange(range, length, utils) { - const { ERROR, rand } = utils; - if (typeof range !== "string") { - ERROR && console.error("Range should be a string"); - return; - } - - const min = range.split("-")[0] / 100 || 0; - const max = range.split("-")[1] / 100 || min; - return rand(min * length, max * length); -} - -function createTypedArray({ maxValue, length }) { - return new Uint8Array(length); -} - -// Export utility functions for standalone use -export { - addHill, - addPit, - addRange, - addTrough, - addStrait, - smooth, - modify, - mask, - invert -}; diff --git a/procedural/src/engine/modules/heightmap-generator_broken.js b/procedural/src/engine/modules/heightmap-generator_broken.js deleted file mode 100644 index 611c4ad6..00000000 --- a/procedural/src/engine/modules/heightmap-generator_broken.js +++ /dev/null @@ -1,578 +0,0 @@ -"use strict"; - -/** - * Generates heightmap data for the grid - * - * REQUIRES: - * - grid.cells (from grid generation) - * - config.heightmap.templateId (heightmap configuration) - * - config.debug (debug configuration) - * - * PROVIDES: - * - grid.cells.h (height values for each cell) - */ -export async function generate(grid, config, utils) { - // Check required properties exist - if (!grid || !grid.cells) { - throw new Error("Heightmap module requires grid with cells structure"); - } - if (!config.heightmap || !config.heightmap.templateId) { - throw new Error("Heightmap module requires config.heightmap.templateId"); - } - if (!config.debug) { - throw new Error("Heightmap module requires config.debug section"); - } - - const { aleaPRNG, heightmapTemplates } = utils; - const { TIME } = config.debug; - const { templateId } = config.heightmap; - - TIME && console.time("defineHeightmap"); - - const isTemplate = templateId in heightmapTemplates; - const heights = isTemplate - ? fromTemplate(grid, templateId, config, utils) - : await fromPrecreated(grid, templateId, config, utils); - - TIME && console.timeEnd("defineHeightmap"); - - return heights; -} - -// Placeholder function for processing precreated heightmaps -// This will need further refactoring to work headlessly (see heightmap-generator_render.md) -export async function fromPrecreated(grid, id, config, utils) { - // TODO: Implement headless image processing - // This function currently requires DOM/Canvas which was removed - // Future implementation will need: - // - utils.loadImage() function to load PNG files headlessly - // - Image processing library (e.g., canvas package for Node.js) - // - getHeightsFromImageData() refactored for headless operation - throw new Error(`fromPrecreated not yet implemented for headless operation. Template ID: ${id}`); -} - -export function fromTemplate(grid, id, config, utils) { - const { heightmapTemplates } = utils; - const templateString = heightmapTemplates[id]?.template || ""; - const steps = templateString.split("\n"); - - if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${id}. Steps: ${steps}`); - - let { heights, blobPower, linePower } = setGrid(grid, utils); - - for (const step of steps) { - const elements = step.trim().split(" "); - if (elements.length < 2) throw new Error(`Heightmap template: steps < 2. Template: ${id}. Step: ${elements}`); - heights = addStep(heights, grid, blobPower, linePower, config, utils, ...elements); - } - - return heights; -} - -function setGrid(grid, utils) { - const { createTypedArray } = utils; - const { cellsDesired, cells, points } = grid; - const heights = cells.h ? Uint8Array.from(cells.h) : createTypedArray({ maxValue: 100, length: points.length }); - const blobPower = getBlobPower(cellsDesired); - const linePower = getLinePower(cellsDesired); - - return { heights, blobPower, linePower }; -} - -function addStep(heights, grid, blobPower, linePower, config, utils, tool, a2, a3, a4, a5) { - if (tool === "Hill") return addHill(heights, grid, blobPower, config, utils, a2, a3, a4, a5); - if (tool === "Pit") return addPit(heights, grid, blobPower, config, utils, a2, a3, a4, a5); - if (tool === "Range") return addRange(heights, grid, linePower, config, utils, a2, a3, a4, a5); - if (tool === "Trough") return addTrough(heights, grid, linePower, config, utils, a2, a3, a4, a5); - if (tool === "Strait") return addStrait(heights, grid, config, utils, a2, a3); - if (tool === "Mask") return mask(heights, grid, config, utils, a2); - if (tool === "Invert") return invert(heights, grid, config, utils, a2, a3); - if (tool === "Add") return modify(heights, a3, +a2, 1, utils); - if (tool === "Multiply") return modify(heights, a3, 0, +a2, utils); - if (tool === "Smooth") return smooth(heights, grid, utils, a2); - return heights; -} - -function getBlobPower(cells) { - const blobPowerMap = { - 1000: 0.93, - 2000: 0.95, - 5000: 0.97, - 10000: 0.98, - 20000: 0.99, - 30000: 0.991, - 40000: 0.993, - 50000: 0.994, - 60000: 0.995, - 70000: 0.9955, - 80000: 0.996, - 90000: 0.9964, - 100000: 0.9973 - }; - return blobPowerMap[cells] || 0.98; -} - -function getLinePower(cells) { - const linePowerMap = { - 1000: 0.75, - 2000: 0.77, - 5000: 0.79, - 10000: 0.81, - 20000: 0.82, - 30000: 0.83, - 40000: 0.84, - 50000: 0.86, - 60000: 0.87, - 70000: 0.88, - 80000: 0.91, - 90000: 0.92, - 100000: 0.93 - }; - - return linePowerMap[cells] || 0.81; -} - -export function addHill(heights, grid, blobPower, config, utils, count, height, rangeX, rangeY) { - const { getNumberInRange, lim, findGridCell } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - count = getNumberInRange(count); - - while (count > 0) { - addOneHill(); - count--; - } - - function addOneHill() { - const change = new Uint8Array(heights.length); - let limit = 0; - let start; - let h = lim(getNumberInRange(height)); - - do { - const x = getPointInRange(rangeX, graphWidth, utils); - const y = getPointInRange(rangeY, graphHeight, utils); - start = findGridCell(x, y, grid); - limit++; - } while (heights[start] + h > 90 && limit < 50); - - change[start] = h; - const queue = [start]; - while (queue.length) { - const q = queue.shift(); - - for (const c of grid.cells.c[q]) { - if (change[c]) continue; - change[c] = change[q] ** blobPower * (Math.random() * 0.2 + 0.9); - if (change[c] > 1) queue.push(c); - } - } - - heights = heights.map((h, i) => lim(h + change[i])); - } - - return heights; -} - -export function addPit(heights, graph, blobPower, config, utils, count, height, rangeX, rangeY) { - const { getNumberInRange, lim, findGridCell } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - count = getNumberInRange(count); - - while (count > 0) { - addOnePit(); - count--; - } - - function addOnePit() { - const used = new Uint8Array(heights.length); - let limit = 0, - start; - let h = lim(getNumberInRange(height)); - - do { - const x = getPointInRange(rangeX, graphWidth, utils); - const y = getPointInRange(rangeY, graphHeight, utils); - start = findGridCell(x, y, grid); - limit++; - } while (heights[start] < 20 && limit < 50); - - const queue = [start]; - while (queue.length) { - const q = queue.shift(); - h = h ** blobPower * (Math.random() * 0.2 + 0.9); - if (h < 1) return; - - grid.cells.c[q].forEach(function (c, i) { - if (used[c]) return; - heights[c] = lim(heights[c] - h * (Math.random() * 0.2 + 0.9)); - used[c] = 1; - queue.push(c); - }); - } - } - - return heights; -} - -export function addRange(heights, grid, linePower, config, utils, count, height, rangeX, rangeY, startCell, endCell) { - const { getNumberInRange, lim, findGridCell, d3 } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - count = getNumberInRange(count); - - while (count > 0) { - addOneRange(); - count--; - } - - function addOneRange() { - const used = new Uint8Array(heights.length); - let h = lim(getNumberInRange(height)); - - if (rangeX && rangeY) { - // find start and end points - const startX = getPointInRange(rangeX, graphWidth, utils); - const startY = getPointInRange(rangeY, graphHeight, utils); - - let dist = 0, - limit = 0, - endX, - endY; - - do { - endX = Math.random() * graphWidth * 0.8 + graphWidth * 0.1; - endY = Math.random() * graphHeight * 0.7 + graphHeight * 0.15; - dist = Math.abs(endY - startY) + Math.abs(endX - startX); - limit++; - } while ((dist < graphWidth / 8 || dist > graphWidth / 3) && limit < 50); - - startCell = findGridCell(startX, startY, graph); - endCell = findGridCell(endX, endY, graph); - } - - let range = getRange(startCell, endCell); - - // get main ridge - function getRange(cur, end) { - const range = [cur]; - const p = grid.points; - used[cur] = 1; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - if (used[e]) return; - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.85) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - if (min === Infinity) return range; - range.push(cur); - used[cur] = 1; - } - - return range; - } - - // add height to ridge and cells around - let queue = range.slice(), - i = 0; - while (queue.length) { - const frontier = queue.slice(); - (queue = []), i++; - frontier.forEach(i => { - heights[i] = lim(heights[i] + h * (Math.random() * 0.3 + 0.85)); - }); - h = h ** linePower - 1; - if (h < 2) break; - frontier.forEach(f => { - grid.cells.c[f].forEach(i => { - if (!used[i]) { - queue.push(i); - used[i] = 1; - } - }); - }); - } - - // generate prominences - range.forEach((cur, d) => { - if (d % 6 !== 0) return; - for (const l of d3.range(i)) { - const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell - heights[min] = (heights[cur] * 2 + heights[min]) / 3; - cur = min; - } - }); - } - - return heights; -} - -export function addTrough(heights, grid, linePower, config, utils, count, height, rangeX, rangeY, startCell, endCell) { - const { getNumberInRange, lim, findGridCell, d3 } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - count = getNumberInRange(count); - - while (count > 0) { - addOneTrough(); - count--; - } - - function addOneTrough() { - const used = new Uint8Array(heights.length); - let h = lim(getNumberInRange(height)); - - if (rangeX && rangeY) { - // find start and end points - let limit = 0, - startX, - startY, - dist = 0, - endX, - endY; - do { - startX = getPointInRange(rangeX, graphWidth, utils); - startY = getPointInRange(rangeY, graphHeight, utils); - startCell = findGridCell(startX, startY, graph); - limit++; - } while (heights[startCell] < 20 && limit < 50); - - limit = 0; - do { - endX = Math.random() * graphWidth * 0.8 + graphWidth * 0.1; - endY = Math.random() * graphHeight * 0.7 + graphHeight * 0.15; - dist = Math.abs(endY - startY) + Math.abs(endX - startX); - limit++; - } while ((dist < graphWidth / 8 || dist > graphWidth / 2) && limit < 50); - - endCell = findGridCell(endX, endY, graph); - } - - let range = getRange(startCell, endCell); - - // get main ridge - function getRange(cur, end) { - const range = [cur]; - const p = grid.points; - used[cur] = 1; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - if (used[e]) return; - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.8) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - if (min === Infinity) return range; - range.push(cur); - used[cur] = 1; - } - - return range; - } - - // add height to ridge and cells around - let queue = range.slice(), - i = 0; - while (queue.length) { - const frontier = queue.slice(); - (queue = []), i++; - frontier.forEach(i => { - heights[i] = lim(heights[i] - h * (Math.random() * 0.3 + 0.85)); - }); - h = h ** linePower - 1; - if (h < 2) break; - frontier.forEach(f => { - grid.cells.c[f].forEach(i => { - if (!used[i]) { - queue.push(i); - used[i] = 1; - } - }); - }); - } - - // generate prominences - range.forEach((cur, d) => { - if (d % 6 !== 0) return; - for (const l of d3.range(i)) { - const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell - //debug.append("circle").attr("cx", p[min][0]).attr("cy", p[min][1]).attr("r", 1); - heights[min] = (heights[cur] * 2 + heights[min]) / 3; - cur = min; - } - }); - } - - return heights; -} - -export function addStrait(heights, grid, config, utils, width, direction = "vertical") { - const { getNumberInRange, findGridCell, P } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - width = Math.min(getNumberInRange(width), grid.cellsX / 3); - if (width < 1 && P(width)) return heights; - - const used = new Uint8Array(heights.length); - const vert = direction === "vertical"; - const startX = vert ? Math.floor(Math.random() * graphWidth * 0.4 + graphWidth * 0.3) : 5; - const startY = vert ? 5 : Math.floor(Math.random() * graphHeight * 0.4 + graphHeight * 0.3); - const endX = vert - ? Math.floor(graphWidth - startX - graphWidth * 0.1 + Math.random() * graphWidth * 0.2) - : graphWidth - 5; - const endY = vert - ? graphHeight - 5 - : Math.floor(graphHeight - startY - graphHeight * 0.1 + Math.random() * graphHeight * 0.2); - - const start = findGridCell(startX, startY, graph); - const end = findGridCell(endX, endY, graph); - let range = getRange(start, end); - const query = []; - - function getRange(cur, end) { - const range = []; - const p = grid.points; - - while (cur !== end) { - let min = Infinity; - grid.cells.c[cur].forEach(function (e) { - let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; - if (Math.random() > 0.8) diff = diff / 2; - if (diff < min) { - min = diff; - cur = e; - } - }); - range.push(cur); - } - - return range; - } - - const step = 0.1 / width; - - while (width > 0) { - const exp = 0.9 - step * width; - range.forEach(function (r) { - grid.cells.c[r].forEach(function (e) { - if (used[e]) return; - used[e] = 1; - query.push(e); - heights[e] **= exp; - if (heights[e] > 100) heights[e] = 5; - }); - }); - range = query.slice(); - - width--; - } - - return heights; -} - -export function modify(heights, range, add, mult, power, utils) { - const { lim } = utils; - - heights = new Uint8Array(heights); - const min = range === "land" ? 20 : range === "all" ? 0 : +range.split("-")[0]; - const max = range === "land" || range === "all" ? 100 : +range.split("-")[1]; - const isLand = min === 20; - - heights = heights.map(h => { - if (h < min || h > max) return h; - - if (add) h = isLand ? Math.max(h + add, 20) : h + add; - if (mult !== 1) h = isLand ? (h - 20) * mult + 20 : h * mult; - if (power) h = isLand ? (h - 20) ** power + 20 : h ** power; - return lim(h); - }); - - return heights; -} - -export function smooth(heights, grid, utils, fr = 2, add = 0) { - const { lim, d3 } = utils; - - heights = new Uint8Array(heights); - heights = heights.map((h, i) => { - const a = [h]; - grid.cells.c[i].forEach(c => a.push(heights[c])); - if (fr === 1) return d3.mean(a) + add; - return lim((h * (fr - 1) + d3.mean(a) + add) / fr); - }); - - return heights; -} - -export function mask(heights, grid, config, utils, power = 1) { - const { lim } = utils; - const { graphWidth, graphHeight } = config; - - heights = new Uint8Array(heights); - const fr = power ? Math.abs(power) : 1; - - heights = heights.map((h, i) => { - const [x, y] = grid.points[i]; - const nx = (2 * x) / graphWidth - 1; // [-1, 1], 0 is center - const ny = (2 * y) / graphHeight - 1; // [-1, 1], 0 is center - let distance = (1 - nx ** 2) * (1 - ny ** 2); // 1 is center, 0 is edge - if (power < 0) distance = 1 - distance; // inverted, 0 is center, 1 is edge - const masked = h * distance; - return lim((h * (fr - 1) + masked) / fr); - }); - - return heights; -} - -export function invert(heights, grid, config, utils, count, axes) { - const { P } = utils; - - if (!P(count)) return heights; - - heights = new Uint8Array(heights); - const invertX = axes !== "y"; - const invertY = axes !== "x"; - const { cellsX, cellsY } = grid; - - const inverted = heights.map((h, i) => { - const x = i % cellsX; - const y = Math.floor(i / cellsX); - - const nx = invertX ? cellsX - x - 1 : x; - const ny = invertY ? cellsY - y - 1 : y; - const invertedI = nx + ny * cellsX; - return heights[invertedI]; - }); - - return inverted; -} - -function getPointInRange(range, length, utils) { - const { rand } = utils; - - if (typeof range !== "string") { - console.error("Range should be a string"); - return; - } - - const min = range.split("-")[0] / 100 || 0; - const max = range.split("-")[1] / 100 || min; - return rand(min * length, max * length); -} diff --git a/procedural/src/engine/modules/heightmap-generator_config.md b/procedural/src/engine/modules/heightmap-generator_config.md deleted file mode 100644 index 8045301e..00000000 --- a/procedural/src/engine/modules/heightmap-generator_config.md +++ /dev/null @@ -1,35 +0,0 @@ -# Config Properties for heightmap-generator.js - -The refactored heightmap generator module requires the following config properties: - -## Required Config Properties - -### `heightmap.templateId` (string) -- **Original DOM source**: `byId("templateInput").value` -- **Purpose**: Specifies which heightmap template to use for generation -- **Example values**: `"continents"`, `"archipelago"`, `"volcano"`, `"atoll"` -- **Usage**: Determines the template key to look up in `heightmapTemplates[templateId]` - -### `seed` (string|number, optional) -- **Original DOM source**: Global `seed` variable -- **Purpose**: Seed for the pseudorandom number generator to ensure reproducible heightmaps -- **Example values**: `"myseed123"`, `42`, `"continent_seed"` -- **Usage**: Passed to `aleaPRNG()` to initialize deterministic random generation - -## Config Object Structure - -```javascript -const config = { - heightmap: { - templateId: "continents" // Template ID to use - }, - seed: "reproducible_seed", // Optional: PRNG seed for reproducibility - // ... other config properties for other modules -}; -``` - -## Notes - -- The `heightmap.templateId` property replaces the direct DOM access `byId("templateInput").value` -- The `seed` property ensures reproducible generation when the same seed is used -- Both properties should be validated by the calling code before passing to the generator \ No newline at end of file diff --git a/procedural/src/engine/modules/heightmap-generator_external.md b/procedural/src/engine/modules/heightmap-generator_external.md deleted file mode 100644 index 2f133f59..00000000 --- a/procedural/src/engine/modules/heightmap-generator_external.md +++ /dev/null @@ -1,35 +0,0 @@ -# External Dependencies for heightmap-generator.js - -The refactored heightmap generator module requires the following external imports: - -## From Utils Package -- `heightmapTemplates` - Object containing heightmap template definitions -- `aleaPRNG` - Pseudorandom number generator for reproducible results -- `getNumberInRange` - Utility to parse and generate numbers from range strings -- `findGridCell` - Function to find grid cell at given coordinates -- `lim` - Function to limit values to valid range (0-100) -- `d3` - D3.js utilities (specifically `d3.mean`, `d3.range`, `d3.scan`) -- `P` - Probability function (returns true/false based on probability) -- `rand` - Random number generator within range -- `ERROR` - Error logging flag -- `TIME` - Time logging flag -- `createTypedArray` - Factory function for creating typed arrays -- `minmax` - Utility to clamp values between min and max - -## Internal Functions -- `getPointInRange` - Utility to generate coordinates within specified ranges (defined within the module) - -## Template System -The module expects `heightmapTemplates` to be an object where each key is a template ID and each value has a `template` property containing newline-separated steps. - -Example structure: -```javascript -const heightmapTemplates = { - "default": { - template: "Hill 5 50 10-30 10-30\nSmooth 2\nMask 1" - }, - "archipelago": { - template: "Hill 20 30 0-100 0-100\nMask -0.5" - } -}; -``` \ No newline at end of file diff --git a/procedural/src/engine/modules/lakes.js b/procedural/src/engine/modules/lakes.js deleted file mode 100644 index 869a3c57..00000000 --- a/procedural/src/engine/modules/lakes.js +++ /dev/null @@ -1,165 +0,0 @@ -"use strict"; - -const LAKE_ELEVATION_DELTA = 0.1; - -/** - * Check if lake can be potentially open (not in deep depression) - * - * REQUIRES: - * - pack.cells (pack cells data structure) - * - pack.features (features array) - * - pack.cells.c (cell neighbors) - * - pack.cells.f (cell features) - * - heights array (cell height values) - * - * PROVIDES: - * - Updated pack.features with closed property - */ -export function detectCloseLakes(pack, grid, heights, config) { - // Check required properties exist - if (!pack || !pack.cells || !pack.features) { - throw new Error("Lakes module requires pack with cells and features structures"); - } - if (!pack.cells.c || !pack.cells.f) { - throw new Error("Lakes module requires pack.cells.c (neighbors) and pack.cells.f (features)"); - } - if (!heights || !Array.isArray(heights)) { - throw new Error("Lakes module requires heights array"); - } - - const {cells, features} = pack; - const ELEVATION_LIMIT = config.lakeElevationLimit; - - const updatedFeatures = features.map(feature => { - if (feature.type !== "lake") return feature; - - const updatedFeature = {...feature}; - delete updatedFeature.closed; - - const MAX_ELEVATION = feature.height + ELEVATION_LIMIT; - if (MAX_ELEVATION > 99) { - updatedFeature.closed = false; - return updatedFeature; - } - - let isDeep = true; - const lowestShorelineCell = feature.shoreline.sort((a, b) => heights[a] - heights[b])[0]; - const queue = [lowestShorelineCell]; - const checked = []; - checked[lowestShorelineCell] = true; - - while (queue.length && isDeep) { - const cellId = queue.pop(); - - for (const neibCellId of cells.c[cellId]) { - if (checked[neibCellId]) continue; - if (heights[neibCellId] >= MAX_ELEVATION) continue; - - if (heights[neibCellId] < 20) { - const nFeature = features[cells.f[neibCellId]]; - if (nFeature.type === "ocean" || feature.height > nFeature.height) isDeep = false; - } - - checked[neibCellId] = true; - queue.push(neibCellId); - } - } - - updatedFeature.closed = isDeep; - return updatedFeature; - }); - - return { - ...pack, - features: updatedFeatures - }; -} - -export function defineClimateData(pack, grid, heights, config, utils) { - const {d3, rn} = utils; - const {cells, features} = pack; - const lakeOutCells = new Uint16Array(cells.i.length); - - const updatedFeatures = features.map(feature => { - if (feature.type !== "lake") return feature; - - const updatedFeature = {...feature}; - updatedFeature.flux = getFlux(feature); - updatedFeature.temp = getLakeTemp(feature); - updatedFeature.evaporation = getLakeEvaporation(feature); - - if (feature.closed) return updatedFeature; // no outlet for lakes in depressed areas - - updatedFeature.outCell = getLowestShoreCell(feature); - lakeOutCells[updatedFeature.outCell] = feature.i; - - return updatedFeature; - }); - - function getFlux(lake) { - return lake.shoreline.reduce((acc, c) => acc + grid.cells.prec[cells.g[c]], 0); - } - - function getLakeTemp(lake) { - if (lake.cells < 6) return grid.cells.temp[cells.g[lake.firstCell]]; - return rn(d3.mean(lake.shoreline.map(c => grid.cells.temp[cells.g[c]])), 1); - } - - function getLakeEvaporation(lake) { - const height = (lake.height - 18) ** config.heightExponent; // height in meters - const evaporation = ((700 * (lake.temp + 0.006 * height)) / 50 + 75) / (80 - lake.temp); // based on Penman formula, [1-11] - return rn(evaporation * lake.cells); - } - - function getLowestShoreCell(lake) { - return lake.shoreline.sort((a, b) => heights[a] - heights[b])[0]; - } - - return { - pack: { - ...pack, - features: updatedFeatures - }, - lakeOutCells - }; -} - -export function cleanupLakeData(pack) { - const updatedFeatures = pack.features.map(feature => { - if (feature.type !== "lake") return feature; - - const updatedFeature = {...feature}; - delete updatedFeature.river; - delete updatedFeature.enteringFlux; - delete updatedFeature.outCell; - delete updatedFeature.closed; - updatedFeature.height = Math.round(feature.height * 1000) / 1000; // rn(feature.height, 3) - - const inlets = feature.inlets?.filter(r => pack.rivers.find(river => river.i === r)); - if (!inlets || !inlets.length) delete updatedFeature.inlets; - else updatedFeature.inlets = inlets; - - const outlet = feature.outlet && pack.rivers.find(river => river.i === feature.outlet); - if (!outlet) delete updatedFeature.outlet; - - return updatedFeature; - }); - - return { - ...pack, - features: updatedFeatures - }; -} - -export function getHeight(feature, pack, utils) { - const {d3, rn} = utils; - const heights = pack.cells.h; - const minShoreHeight = d3.min(feature.shoreline.map(cellId => heights[cellId])) || 20; - return rn(minShoreHeight - LAKE_ELEVATION_DELTA, 2); -} - -export function getName(feature, pack, Names) { - const landCell = pack.cells.c[feature.firstCell].find(c => pack.cells.h[c] >= 20); - const culture = pack.cells.culture[landCell]; - return Names.getCulture(culture); -} \ No newline at end of file diff --git a/procedural/src/engine/modules/markers-generator.js b/procedural/src/engine/modules/markers-generator.js deleted file mode 100644 index 255bf015..00000000 --- a/procedural/src/engine/modules/markers-generator.js +++ /dev/null @@ -1,967 +0,0 @@ -"use strict"; - -export function getDefaultMarkersConfig(config, utils) { - const {Names} = utils; - const culturesSet = config.culturesSet; - const isFantasy = culturesSet.includes("Fantasy"); - - /* - Default markers config: - type - short description (snake-case) - icon - unicode character or url to image - dx: icon offset in x direction, in pixels - dy: icon offset in y direction, in pixels - min: minimum number of candidates to add at least 1 marker - each: how many of the candidates should be added as markers - multiplier: multiply markers quantity to add - list: function to select candidates - add: function to add marker legend - */ - // prettier-ignore - return [ - {type: "volcanoes", icon: "🌋", dx: 52, px: 13, min: 10, each: 500, multiplier: 1, list: listVolcanoes, add: addVolcano}, - {type: "hot-springs", icon: "♨️", dy: 52, min: 30, each: 1200, multiplier: 1, list: listHotSprings, add: addHotSpring}, - {type: "water-sources", icon: "💧", min: 1, each: 1000, multiplier: 1, list: listWaterSources, add: addWaterSource}, - {type: "mines", icon: "⛏️", dx: 48, px: 13, min: 1, each: 15, multiplier: 1, list: listMines, add: addMine}, - {type: "bridges", icon: "🌉", px: 14, min: 1, each: 5, multiplier: 1, list: listBridges, add: addBridge}, - {type: "inns", icon: "🍻", px: 14, min: 1, each: 10, multiplier: 1, list: listInns, add: addInn}, - {type: "lighthouses", icon: "🚨", px: 14, min: 1, each: 2, multiplier: 1, list: listLighthouses, add: addLighthouse}, - {type: "waterfalls", icon: "⟱", dy: 54, px: 16, min: 1, each: 5, multiplier: 1, list: listWaterfalls, add: addWaterfall}, - {type: "battlefields", icon: "⚔️", dy: 52, min: 50, each: 700, multiplier: 1, list: listBattlefields, add: addBattlefield}, - {type: "dungeons", icon: "🗝️", dy: 51, px: 13, min: 30, each: 200, multiplier: 1, list: listDungeons, add: addDungeon}, - {type: "lake-monsters", icon: "🐉", dy: 48, min: 2, each: 10, multiplier: 1, list: listLakeMonsters, add: addLakeMonster}, - {type: "sea-monsters", icon: "🦑", min: 50, each: 700, multiplier: 1, list: listSeaMonsters, add: addSeaMonster}, - {type: "hill-monsters", icon: "👹", dy: 54, px: 13, min: 30, each: 600, multiplier: 1, list: listHillMonsters, add: addHillMonster}, - {type: "sacred-mountains", icon: "🗻", dy: 48, min: 1, each: 5, multiplier: 1, list: listSacredMountains, add: addSacredMountain}, - {type: "sacred-forests", icon: "🌳", min: 30, each: 1000, multiplier: 1, list: listSacredForests, add: addSacredForest}, - {type: "sacred-pineries", icon: "🌲", px: 13, min: 30, each: 800, multiplier: 1, list: listSacredPineries, add: addSacredPinery}, - {type: "sacred-palm-groves", icon: "🌴", px: 13, min: 1, each: 100, multiplier: 1, list: listSacredPalmGroves, add: addSacredPalmGrove}, - {type: "brigands", icon: "💰", px: 13, min: 50, each: 100, multiplier: 1, list: listBrigands, add: addBrigands}, - {type: "pirates", icon: "🏴‍☠️", dx: 51, min: 40, each: 300, multiplier: 1, list: listPirates, add: addPirates}, - {type: "statues", icon: "🗿", min: 80, each: 1200, multiplier: 1, list: listStatues, add: addStatue}, - {type: "ruins", icon: "🏺", min: 80, each: 1200, multiplier: 1, list: listRuins, add: addRuins}, - {type: "libraries", icon: "📚", min: 10, each: 1200, multiplier: 1, list: listLibraries, add: addLibrary}, - {type: "circuses", icon: "🎪", min: 80, each: 1000, multiplier: 1, list: listCircuses, add: addCircuse}, - {type: "jousts", icon: "🤺", dx: 48, min: 5, each: 500, multiplier: 1, list: listJousts, add: addJoust}, - {type: "fairs", icon: "🎠", min: 50, each: 1000, multiplier: 1, list: listFairs, add: addFair}, - {type: "canoes", icon: "🛶", min: 500, each: 2000, multiplier: 1, list: listCanoes, add: addCanoe}, - {type: "migration", icon: "🐗", min: 20, each: 1000, multiplier: 1, list: listMigrations, add: addMigration}, - {type: "dances", icon: "💃🏽", min: 50, each: 1000, multiplier: 1, list: listDances, add: addDances}, - {type: "mirage", icon: "💦", min: 10, each: 400, multiplier: 1, list: listMirage, add: addMirage}, - {type: "caves", icon:"🦇", min: 60, each: 1000, multiplier: 1, list: listCaves, add: addCave}, - {type: "portals", icon: "🌀", px: 14, min: 16, each: 8, multiplier: +isFantasy, list: listPortals, add: addPortal}, - {type: "rifts", icon: "🎆", min: 5, each: 3000, multiplier: +isFantasy, list: listRifts, add: addRift}, - {type: "disturbed-burials", icon: "💀", min: 20, each: 3000, multiplier: +isFantasy, list: listDisturbedBurial, add: addDisturbedBurial}, - {type: "necropolises", icon: "🪦", min: 20, each: 1000, multiplier: 1, list: listNecropolis, add: addNecropolis}, - {type: "encounters", icon: "🧙", min: 10, each: 600, multiplier: 1, list: listEncounters, add: addEncounter}, - ]; -} - -export function generateMarkers(pack, config, utils) { - const { TIME } = config.debug; - const markersConfig = getDefaultMarkersConfig(config, utils); - const markers = []; - const notes = []; - const occupied = []; - - TIME && console.time("addMarkers"); - - markersConfig.forEach(({type, icon, dx, dy, px, min, each, multiplier, list, add}) => { - if (multiplier === 0) return; - - let candidates = Array.from(list(pack, utils)); - let quantity = getQuantity(candidates, min, each, multiplier); - // uncomment for debugging: - // console.info(`${icon} ${type}: each ${each} of ${candidates.length}, min ${min} candidates. Got ${quantity}`); - - while (quantity && candidates.length) { - const [cell] = extractAnyElement(candidates); - const marker = addMarker({icon, type, dx, dy, px}, {cell}, pack, occupied, utils); - if (!marker) continue; - markers.push(marker); - add("marker" + marker.i, cell, pack, notes, utils); - quantity--; - } - }); - - TIME && console.timeEnd("addMarkers"); - - return { markers, notes }; -} - -export function regenerateMarkers(pack, existingMarkers, config, utils) { - const occupied = []; - const filteredMarkers = existingMarkers.filter(({i, lock, cell}) => { - if (lock) { - occupied[cell] = true; - return true; - } - return false; - }); - - const { markers: newMarkers, notes } = generateMarkers(pack, config, utils); - - return { - markers: [...filteredMarkers, ...newMarkers], - notes, - removedMarkerIds: existingMarkers.filter(m => !m.lock).map(m => "marker" + m.i) - }; -} - -export function addSingleMarker(marker, pack, config, utils) { - const markersConfig = getDefaultMarkersConfig(config, utils); - const base = markersConfig.find(c => c.type === marker.type); - const notes = []; - - if (base) { - const {icon, type, dx, dy, px} = base; - const newMarker = addMarker({icon, type, dx, dy, px}, marker, pack, [], utils); - if (newMarker) { - base.add("marker" + newMarker.i, newMarker.cell, pack, notes, utils); - return { marker: newMarker, notes }; - } - } - - const i = 0; // Will be set by caller - const finalMarker = {...marker, i}; - return { marker: finalMarker, notes }; -} - -function getQuantity(array, min, each, multiplier) { - if (!array.length || array.length < min / multiplier) return 0; - const requestQty = Math.ceil((array.length / each) * multiplier); - return array.length < requestQty ? array.length : requestQty; -} - -function extractAnyElement(array) { - const index = Math.floor(Math.random() * array.length); - return array.splice(index, 1); -} - -function getMarkerCoordinates(cell, pack) { - const {cells, burgs} = pack; - const burgId = cells.burg[cell]; - - if (burgId) { - const {x, y} = burgs[burgId]; - return [x, y]; - } - - return cells.p[cell]; -} - -function addMarker(base, marker, pack, occupied, utils) { - if (marker.cell === undefined) return; - const {last} = utils; - const i = last(pack.markers)?.i + 1 || 0; - const [x, y] = getMarkerCoordinates(marker.cell, pack); - const finalMarker = {...base, x, y, ...marker, i}; - occupied[marker.cell] = true; - return finalMarker; -} - -function listVolcanoes({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 70); -} - -function addVolcano(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, P, getFriendlyHeight} = utils; - - const proper = Names.getCulture(cells.culture[cell]); - const name = P(0.3) ? "Mount " + proper : P(0.7) ? proper + " Volcano" : proper; - const status = P(0.6) ? "Dormant" : P(0.4) ? "Active" : "Erupting"; - notes.push({id, name, legend: `${status} volcano. Height: ${getFriendlyHeight(cells.p[cell])}.`}); -} - -function listHotSprings({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] > 50 && cells.culture[i]); -} - -function addHotSpring(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, P, gauss, convertTemperature} = utils; - - const proper = Names.getCulture(cells.culture[cell]); - const temp = convertTemperature(gauss(35, 15, 20, 100)); - const name = P(0.3) ? "Hot Springs of " + proper : P(0.7) ? proper + " Hot Springs" : proper; - const legend = `A geothermal springs with naturally heated water that provide relaxation and medicinal benefits. Average temperature is ${temp}.`; - - notes.push({id, name, legend}); -} - -function listWaterSources({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] > 30 && cells.r[i]); -} - -function addWaterSource(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, rw} = utils; - - const type = rw({ - "Healing Spring": 5, - "Purifying Well": 2, - "Enchanted Reservoir": 1, - "Creek of Luck": 1, - "Fountain of Youth": 1, - "Wisdom Spring": 1, - "Spring of Life": 1, - "Spring of Youth": 1, - "Healing Stream": 1 - }); - - const proper = Names.getCulture(cells.culture[cell]); - const name = `${proper} ${type}`; - const legend = - "This legendary water source is whispered about in ancient tales and believed to possess mystical properties. The spring emanates crystal-clear water, shimmering with an otherworldly iridescence that sparkles even in the dimmest light."; - - notes.push({id, name, legend}); -} - -function listMines({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] > 47 && cells.burg[i]); -} - -function addMine(id, cell, pack, notes, utils) { - const {cells} = pack; - const {rw, rn, populationRate, urbanization} = utils; - - const resources = {salt: 5, gold: 2, silver: 4, copper: 2, iron: 3, lead: 1, tin: 1}; - const resource = rw(resources); - const burg = pack.burgs[cells.burg[cell]]; - const name = `${burg.name} — ${resource} mining town`; - const population = rn(burg.population * populationRate * urbanization); - const legend = `${burg.name} is a mining town of ${population} people just nearby the ${resource} mine.`; - notes.push({id, name, legend}); -} - -function listBridges({cells, burgs}, utils) { - const {d3} = utils; - const occupied = []; - const meanFlux = d3.mean(cells.fl.filter(fl => fl)); - return cells.i.filter( - i => - !occupied[i] && - cells.burg[i] && - cells.t[i] !== 1 && - burgs[cells.burg[i]].population > 20 && - cells.r[i] && - cells.fl[i] > meanFlux - ); -} - -function addBridge(id, cell, pack, notes, utils) { - const {cells} = pack; - const {P, rw, ra} = utils; - - const burg = pack.burgs[cells.burg[cell]]; - const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); - const riverName = river ? `${river.name} ${river.type}` : "river"; - const name = river && P(0.2) ? `${river.name} Bridge` : `${burg.name} Bridge`; - const weightedAdjectives = { - stone: 10, - wooden: 1, - lengthy: 2, - formidable: 2, - rickety: 1, - beaten: 1, - weathered: 1 - }; - const barriers = [ - "its collapse during the flood", - "being rumoured to attract trolls", - "the drying up of local trade", - "banditry infested the area", - "the old waypoints crumbled" - ]; - const legend = P(0.7) - ? `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}.` - : `An old crossing of the ${riverName}, rarely used since ${ra(barriers)}.`; - - notes.push({id, name, legend}); -} - -function listInns({cells}, utils) { - const {Routes} = utils; - const occupied = []; - const crossRoads = cells.i.filter(i => !occupied[i] && cells.pop[i] > 5 && Routes.isCrossroad(i)); - return crossRoads; -} - -function addInn(id, cell, pack, notes, utils) { - const {P, ra, capitalize} = utils; - - const colors = [ - "Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", - "Green", "Yellow", "Amber", "Orange", "Brown", "Grey" - ]; - const animals = [ - "Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Crane", - "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", - "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", - "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", - "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Wolf", - "Wolverine", "Camel", "Falcon", "Hound", "Ox" - ]; - const adjectives = [ - "New", "Good", "High", "Old", "Great", "Big", "Major", "Happy", "Main", "Huge", "Far", - "Beautiful", "Fair", "Prime", "Ancient", "Golden", "Proud", "Lucky", "Fat", "Honest", - "Giant", "Distant", "Friendly", "Loud", "Hungry", "Magical", "Superior", "Peaceful", - "Frozen", "Divine", "Favorable", "Brave", "Sunny", "Flying" - ]; - const methods = [ - "Boiled", "Grilled", "Roasted", "Spit-roasted", "Stewed", "Stuffed", "Jugged", "Mashed", - "Baked", "Braised", "Poached", "Marinated", "Pickled", "Smoked", "Dried", "Dry-aged", - "Corned", "Fried", "Pan-fried", "Deep-fried", "Dressed", "Steamed", "Cured", "Syrupped", - "Flame-Broiled" - ]; - const courses = [ - "beef", "pork", "bacon", "chicken", "lamb", "chevon", "hare", "rabbit", "hart", "deer", - "antlers", "bear", "buffalo", "badger", "beaver", "turkey", "pheasant", "duck", "goose", - "teal", "quail", "pigeon", "seal", "carp", "bass", "pike", "catfish", "sturgeon", - "escallop", "pie", "cake", "pottage", "pudding", "onions", "carrot", "potato", "beet", - "garlic", "cabbage", "eggplant", "eggs", "broccoli", "zucchini", "pepper", "olives", - "pumpkin", "spinach", "peas", "chickpea", "beans", "rice", "pasta", "bread", "apples", - "peaches", "pears", "melon", "oranges", "mango", "tomatoes", "cheese", "corn", "rat tails", - "pig ears" - ]; - const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"]; - const drinks = [ - "wine", "brandy", "gin", "whisky", "rom", "beer", "cider", "mead", "liquor", "spirits", - "vodka", "tequila", "absinthe", "nectar", "milk", "kvass", "kumis", "tea", "water", "juice", "sap" - ]; - - const typeName = P(0.3) ? "inn" : "tavern"; - const isAnimalThemed = P(0.7); - const animal = ra(animals); - const name = isAnimalThemed - ? P(0.6) - ? ra(colors) + " " + animal - : ra(adjectives) + " " + animal - : ra(adjectives) + " " + capitalize(typeName); - const meal = isAnimalThemed && P(0.3) ? animal : ra(courses); - const course = `${ra(methods)} ${meal}`.toLowerCase(); - const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(drinks)}`.toLowerCase(); - const legend = `A big and famous roadside ${typeName}. Delicious ${course} with ${drink} is served here.`; - notes.push({id, name: "The " + name, legend}); -} - -function listLighthouses({cells}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter( - i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && Routes.isConnected(c)) - ); -} - -function addLighthouse(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, getAdjective} = utils; - - const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({ - id, - name: getAdjective(proper) + " Lighthouse", - legend: `A lighthouse to serve as a beacon for ships in the open sea.` - }); -} - -function listWaterfalls({cells}, utils) { - const occupied = []; - return cells.i.filter( - i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c]) - ); -} - -function addWaterfall(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, ra, getAdjective} = utils; - - const descriptions = [ - "A gorgeous waterfall flows here.", - "The rapids of an exceptionally beautiful waterfall.", - "An impressive waterfall has cut through the land.", - "The cascades of a stunning waterfall.", - "A river drops down from a great height forming a wonderous waterfall.", - "A breathtaking waterfall cuts through the landscape." - ]; - - const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({id, name: getAdjective(proper) + " Waterfall", legend: `${ra(descriptions)}`}); -} - -function listBattlefields({cells}, utils) { - const occupied = []; - return cells.i.filter( - i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25 - ); -} - -function addBattlefield(id, cell, pack, notes, utils) { - const {cells, states} = pack; - const {Names, BurgsAndStates, ra, generateDate, options} = utils; - - const state = states[cells.state[cell]]; - if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state); - const campaign = ra(state.campaigns); - const date = generateDate(campaign.start, campaign.end); - const name = Names.getCulture(cells.culture[cell]) + " Battlefield"; - const legend = `A historical battle of the ${campaign.name}. \\r\\nDate: ${date} ${options.era}.`; - notes.push({id, name, legend}); -} - -function listDungeons({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3); -} - -function addDungeon(id, cell, pack, notes, utils) { - const {seed} = utils; - const dungeonSeed = `${seed}${cell}`; - const name = "Dungeon"; - const legend = `
Undiscovered dungeon. See One page dungeon
`; - notes.push({id, name, legend}); -} - -function listLakeMonsters({features}, utils) { - const occupied = []; - return features - .filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell]) - .map(feature => feature.firstCell); -} - -function addLakeMonster(id, cell, pack, notes, utils) { - const {gauss, ra, heightUnit} = utils; - const lake = pack.features[pack.cells.f[cell]]; - - // Check that the feature is a lake in case the user clicked on a wrong square - if (lake.type !== "lake") return; - - const name = `${lake.name} Monster`; - const length = gauss(10, 5, 5, 100); - const subjects = [ - "Locals", "Elders", "Inscriptions", "Tipplers", "Legends", "Whispers", "Rumors", "Journeying folk", "Tales" - ]; - const legend = `${ra(subjects)} say a relic monster of ${length} ${heightUnit.value} long inhabits ${ - lake.name - } Lake. Truth or lie, folks are afraid to fish in the lake.`; - notes.push({id, name, legend}); -} - -function listSeaMonsters({cells, features}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter( - i => !occupied[i] && cells.h[i] < 20 && Routes.isConnected(i) && features[cells.f[i]].type === "ocean" - ); -} - -function addSeaMonster(id, cell, pack, notes, utils) { - const {Names, gauss, heightUnit} = utils; - const name = `${Names.getCultureShort(0)} Monster`; - const length = gauss(25, 10, 10, 100); - const legend = `Old sailors tell stories of a gigantic sea monster inhabiting these dangerous waters. Rumors say it can be ${length} ${heightUnit.value} long.`; - notes.push({id, name, legend}); -} - -function listHillMonsters({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i]); -} - -function addHillMonster(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, ra} = utils; - - const adjectives = [ - "great", "big", "huge", "prime", "golden", "proud", "lucky", "fat", "giant", "hungry", - "magical", "superior", "terrifying", "horrifying", "feared" - ]; - const subjects = [ - "Locals", "Elders", "Inscriptions", "Tipplers", "Legends", "Whispers", "Rumors", "Journeying folk", "Tales" - ]; - const species = [ - "Ogre", "Troll", "Cyclops", "Giant", "Monster", "Beast", "Dragon", "Undead", "Ghoul", - "Vampire", "Hag", "Banshee", "Bearded Devil", "Roc", "Hydra", "Warg" - ]; - const modusOperandi = [ - "steals cattle at night", "prefers eating children", "doesn't mind human flesh", "keeps the region at bay", - "eats kids whole", "abducts young women", "terrorizes the region", "harasses travelers in the area", - "snatches people from homes", "attacks anyone who dares to approach its lair", "attacks unsuspecting victims" - ]; - - const monster = ra(species); - const toponym = Names.getCulture(cells.culture[cell]); - const name = `${toponym} ${monster}`; - const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra( - modusOperandi - )}.`; - notes.push({id, name, legend}); -} - -// Sacred mountains spawn on lonely mountains -function listSacredMountains({cells}, utils) { - const occupied = []; - return cells.i.filter( - i => - !occupied[i] && - cells.h[i] >= 70 && - cells.c[i].some(c => cells.culture[c]) && - cells.c[i].every(c => cells.h[c] < 60) - ); -} - -function addSacredMountain(id, cell, pack, notes, utils) { - const {cells, religions} = pack; - const {Names, getFriendlyHeight} = utils; - - const culture = cells.c[cell].map(c => cells.culture[c]).find(c => c); - const religion = cells.religion[cell]; - const name = `${Names.getCulture(culture)} Mountain`; - const height = getFriendlyHeight(cells.p[cell]); - const legend = `A sacred mountain of ${religions[religion].name}. Height: ${height}.`; - notes.push({id, name, legend}); -} - -// Sacred forests spawn on temperate forests -function listSacredForests({cells}, utils) { - const occupied = []; - return cells.i.filter( - i => !occupied[i] && cells.culture[i] && cells.religion[i] && [6, 8].includes(cells.biome[i]) - ); -} - -function addSacredForest(id, cell, pack, notes, utils) { - const {cells, religions} = pack; - const {Names} = utils; - - const culture = cells.culture[cell]; - const religion = cells.religion[cell]; - const name = `${Names.getCulture(culture)} Forest`; - const legend = `A forest sacred to local ${religions[religion].name}.`; - notes.push({id, name, legend}); -} - -// Sacred pineries spawn on boreal forests -function listSacredPineries({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.religion[i] && cells.biome[i] === 9); -} - -function addSacredPinery(id, cell, pack, notes, utils) { - const {cells, religions} = pack; - const {Names} = utils; - - const culture = cells.culture[cell]; - const religion = cells.religion[cell]; - const name = `${Names.getCulture(culture)} Pinery`; - const legend = `A pinery sacred to local ${religions[religion].name}.`; - notes.push({id, name, legend}); -} - -// Sacred palm groves spawn on oasises -function listSacredPalmGroves({cells}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter( - i => - !occupied[i] && - cells.culture[i] && - cells.religion[i] && - cells.biome[i] === 1 && - cells.pop[i] > 1 && - Routes.isConnected(i) - ); -} - -function addSacredPalmGrove(id, cell, pack, notes, utils) { - const {cells, religions} = pack; - const {Names} = utils; - - const culture = cells.culture[cell]; - const religion = cells.religion[cell]; - const name = `${Names.getCulture(culture)} Palm Grove`; - const legend = `A palm grove sacred to local ${religions[religion].name}.`; - notes.push({id, name, legend}); -} - -function listBrigands({cells}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.culture[i] && Routes.hasRoad(i)); -} - -function addBrigands(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, ra, rw} = utils; - - const animals = [ - "Apes", "Badgers", "Bears", "Beavers", "Bisons", "Boars", "Cats", "Crows", "Dogs", "Foxes", - "Hares", "Hawks", "Hyenas", "Jackals", "Jaguars", "Leopards", "Lions", "Owls", "Panthers", - "Rats", "Ravens", "Rooks", "Scorpions", "Sharks", "Snakes", "Spiders", "Tigers", "Wolfs", - "Wolverines", "Falcons" - ]; - const types = {brigands: 4, bandits: 3, robbers: 1, highwaymen: 1}; - - const culture = cells.culture[cell]; - const biome = cells.biome[cell]; - const height = cells.p[cell]; - - const locality = ((height, biome) => { - if (height >= 70) return "highlander"; - if ([1, 2].includes(biome)) return "desert"; - if ([3, 4].includes(biome)) return "mounted"; - if ([5, 6, 7, 8, 9].includes(biome)) return "forest"; - if (biome === 12) return "swamp"; - return "angry"; - })(height, biome); - - const name = `${Names.getCulture(culture)} ${ra(animals)}`; - const legend = `A gang of ${locality} ${rw(types)}.`; - notes.push({id, name, legend}); -} - -// Pirates spawn on sea routes -function listPirates({cells}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && Routes.isConnected(i)); -} - -function addPirates(id, cell, pack, notes, utils) { - const name = "Pirates"; - const legend = "Pirate ships have been spotted in these waters."; - notes.push({id, name, legend}); -} - -function listStatues({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40); -} - -function addStatue(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, ra, rand} = utils; - - const variants = [ - "Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone", "Sculpture", "Effigy", "Idol" - ]; - const scripts = { - cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ", - geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ", - coptic: "ⲲⲴⲶⲸⲺⲼⲾⳀⳁⳂⳃⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢⳤ⳥⳧⳩⳪ⳫⳬⳭⳲ⳹⳾ ", - tibetan: "ༀ༁༂༃༄༅༆༇༈༉༊་༌༐༑༒༓༔༕༖༗༘༙༚༛༜༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿", - mongolian: "᠀᠐᠑᠒ᠠᠡᠦᠧᠨᠩᠪᠭᠮᠯᠰᠱᠲᠳᠵᠻᠼᠽᠾᠿᡀᡁᡆᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡙᡜᡝᡞᡟᡠᡡᡭᡮᡯᡰᡱᡲᡳᡴᢀᢁᢂᢋᢏᢐᢑᢒᢓᢛᢜᢞᢟᢠᢡᢢᢤᢥᢦ" - }; - - const culture = cells.culture[cell]; - - const variant = ra(variants); - const name = `${Names.getCulture(culture)} ${variant}`; - const script = scripts[ra(Object.keys(scripts))]; - const inscription = Array(rand(40, 100)) - .fill(null) - .map(() => ra(script)) - .join(""); - const legend = `An ancient ${variant.toLowerCase()}. It has an inscription, but no one can translate it: -
${inscription}
`; - notes.push({id, name, legend}); -} - -function listRuins({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && cells.h[i] < 60); -} - -function addRuins(id, cell, pack, notes, utils) { - const {ra} = utils; - const types = [ - "City", "Town", "Settlement", "Pyramid", "Fort", "Stronghold", "Temple", "Sacred site", - "Mausoleum", "Outpost", "Fortification", "Fortress", "Castle" - ]; - - const ruinType = ra(types); - const name = `Ruined ${ruinType}`; - const legend = `Ruins of an ancient ${ruinType.toLowerCase()}. Untold riches may lie within.`; - notes.push({id, name, legend}); -} - -function listLibraries({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.burg[i] && cells.pop[i] > 10); -} - -function addLibrary(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, rw} = utils; - - const type = rw({Library: 3, Archive: 1, Collection: 1}); - const name = `${Names.getCulture(cells.culture[cell])} ${type}`; - const legend = "A vast collection of knowledge, including many rare and ancient tomes."; - - notes.push({id, name, legend}); -} - -function listCircuses({cells}, utils) { - const {Routes} = utils; - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && Routes.isConnected(i)); -} - -function addCircuse(id, cell, pack, notes, utils) { - const {ra} = utils; - const adjectives = [ - "Fantastical", "Wonderous", "Incomprehensible", "Magical", "Extraordinary", "Unmissable", "World-famous", "Breathtaking" - ]; - - const adjective = ra(adjectives); - const name = `Travelling ${adjective} Circus`; - const legend = `Roll up, roll up, this ${adjective.toLowerCase()} circus is here for a limited time only.`; - notes.push({id, name, legend}); -} - -function listJousts({cells, burgs}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.burg[i] && burgs[cells.burg[i]].population > 20); -} - -function addJoust(id, cell, pack, notes, utils) { - const {cells, burgs} = pack; - const {ra} = utils; - const types = ["Joust", "Competition", "Melee", "Tournament", "Contest"]; - const virtues = ["cunning", "might", "speed", "the greats", "acumen", "brutality"]; - - if (!cells.burg[cell]) return; - const burgName = burgs[cells.burg[cell]].name; - const type = ra(types); - const virtue = ra(virtues); - - const name = `${burgName} ${type}`; - const legend = `Warriors from around the land gather for a ${type.toLowerCase()} of ${virtue} in ${burgName}, with fame, fortune and favour on offer to the victor.`; - notes.push({id, name, legend}); -} - -function listFairs({cells, burgs}, utils) { - const occupied = []; - return cells.i.filter( - i => !occupied[i] && cells.burg[i] && burgs[cells.burg[i]].population < 20 && burgs[cells.burg[i]].population < 5 - ); -} - -function addFair(id, cell, pack, notes, utils) { - const {cells, burgs} = pack; - if (!cells.burg[cell]) return; - - const burgName = burgs[cells.burg[cell]].name; - const type = "Fair"; - - const name = `${burgName} ${type}`; - const legend = `A fair is being held in ${burgName}, with all manner of local and foreign goods and services on offer.`; - notes.push({id, name, legend}); -} - -function listCanoes({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.r[i]); -} - -function addCanoe(id, cell, pack, notes, utils) { - const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); - - const name = `Minor Jetty`; - const riverName = river ? `${river.name} ${river.type}` : "river"; - const legend = `A small location along the ${riverName} to launch boats from sits here, along with a weary looking owner, willing to sell passage along the river.`; - notes.push({id, name, legend}); -} - -function listMigrations({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.pop[i] <= 2); -} - -function addMigration(id, cell, pack, notes, utils) { - const {ra} = utils; - const animals = [ - "Antelopes", "Apes", "Badgers", "Bears", "Beavers", "Bisons", "Boars", "Buffalo", "Cats", "Cranes", - "Crocodiles", "Crows", "Deer", "Dogs", "Eagles", "Elk", "Foxes", "Goats", "Geese", "Hares", - "Hawks", "Herons", "Horses", "Hyenas", "Ibises", "Jackals", "Jaguars", "Larks", "Leopards", "Lions", - "Mantises", "Martens", "Mooses", "Mules", "Owls", "Panthers", "Rats", "Ravens", "Rooks", "Scorpions", - "Sharks", "Sheep", "Snakes", "Spiders", "Tigers", "Wolves", "Wolverines", "Camels", "Falcons", "Hounds", "Oxen" - ]; - const animalChoice = ra(animals); - - const name = `${animalChoice} migration`; - const legend = `A huge group of ${animalChoice.toLowerCase()} are migrating, whether part of their annual routine, or something more extraordinary.`; - notes.push({id, name, legend}); -} - -function listDances({cells, burgs}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.burg[i] && burgs[cells.burg[i]].population > 15); -} - -function addDances(id, cell, pack, notes, utils) { - const {cells, burgs} = pack; - const {ra} = utils; - const burgName = burgs[cells.burg[cell]].name; - const socialTypes = [ - "gala", "dance", "performance", "ball", "soiree", "jamboree", "exhibition", "carnival", - "festival", "jubilee", "celebration", "gathering", "fete" - ]; - const people = [ - "great and the good", "nobility", "local elders", "foreign dignitaries", "spiritual leaders", "suspected revolutionaries" - ]; - const socialType = ra(socialTypes); - - const name = `${burgName} ${socialType}`; - const legend = `A ${socialType} has been organised at ${burgName} as a chance to gather the ${ra( - people - )} of the area together to be merry, make alliances and scheme around the crisis.`; - notes.push({id, name, legend}); -} - -function listMirage({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.biome[i] === 1); -} - -function addMirage(id, cell, pack, notes, utils) { - const {ra} = utils; - const adjectives = ["Entrancing", "Diaphanous", "Illusory", "Distant", "Perculiar"]; - - const mirageAdjective = ra(adjectives); - const name = `${mirageAdjective} mirage`; - const legend = `This ${mirageAdjective.toLowerCase()} mirage has been luring travellers out of their way for eons.`; - notes.push({id, name, legend}); -} - -function listCaves({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i]); -} - -function addCave(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, rw} = utils; - - const formations = { - Cave: 10, Cavern: 8, Chasm: 6, Ravine: 6, Fracture: 5, Grotto: 4, Pit: 4, Sinkhole: 2, Hole: 2 - }; - const status = { - "a good spot to hid treasure": 5, - "the home of strange monsters": 5, - "totally empty": 4, - "endlessly deep and unexplored": 4, - "completely flooded": 2, - "slowly filling with lava": 1 - }; - - let formation = rw(formations); - const toponym = Names.getCulture(cells.culture[cell]); - if (cells.biome[cell] === 11) { - formation = "Glacial " + formation; - } - const name = `${toponym} ${formation}`; - const legend = `The ${name}. Locals claim that it is ${rw(status)}.`; - notes.push({id, name, legend}); -} - -function listPortals({burgs}, utils) { - const occupied = []; - return burgs - .slice(1, Math.ceil(burgs.length / 10) + 1) - .filter(({cell}) => !occupied[cell]) - .map(burg => burg.cell); -} - -function addPortal(id, cell, pack, notes, utils) { - const {cells, burgs} = pack; - - if (!cells.burg[cell]) return; - const burgName = burgs[cells.burg[cell]].name; - - const name = `${burgName} Portal`; - const legend = `An element of the magic portal system connecting major cities. The portals were installed centuries ago, but still work fine.`; - notes.push({id, name, legend}); -} - -function listRifts({cells}, utils) { - const {biomesData} = utils; - const occupied = []; - return cells.i.filter(i => !occupied[i] && pack.cells.pop[i] <= 3 && biomesData.habitability[pack.cells.biome[i]]); -} - -function addRift(id, cell, pack, notes, utils) { - const {ra} = utils; - const types = ["Demonic", "Interdimensional", "Abyssal", "Cosmic", "Cataclysmic", "Subterranean", "Ancient"]; - - const descriptions = [ - "all known nearby beings to flee in terror", - "cracks in reality itself to form", - "swarms of foes to spill forth", - "nearby plants to wither and decay", - "an emmissary to step through with an all-powerful relic" - ]; - - const riftType = ra(types); - const name = `${riftType} Rift`; - const legend = `A rumoured ${riftType.toLowerCase()} rift in this area is causing ${ra(descriptions)}.`; - notes.push({id, name, legend}); -} - -function listDisturbedBurial({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.pop[i] > 2); -} -function addDisturbedBurial(id, cell, pack, notes, utils) { - const name = "Disturbed Burial"; - const legend = "A burial site has been disturbed in this area, causing the dead to rise and attack the living."; - notes.push({id, name, legend}); -} - -function listNecropolis({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.pop[i] < 2); -} - -function addNecropolis(id, cell, pack, notes, utils) { - const {cells} = pack; - const {Names, rw, ra} = utils; - - const toponym = Names.getCulture(cells.culture[cell]); - const type = rw({ - Necropolis: 5, Crypt: 2, Tomb: 2, Graveyard: 1, Cemetery: 2, Mausoleum: 1, Sepulchre: 1 - }); - - const name = `${toponym} ${type}`; - const legend = ra([ - "A foreboding necropolis shrouded in perpetual darkness, where eerie whispers echo through the winding corridors and spectral guardians stand watch over the tombs of long-forgotten souls.", - "A towering necropolis adorned with macabre sculptures and guarded by formidable undead sentinels. Its ancient halls house the remains of fallen heroes, entombed alongside their cherished relics.", - "This ethereal necropolis seems suspended between the realms of the living and the dead. Wisps of mist dance around the tombstones, while haunting melodies linger in the air, commemorating the departed.", - "Rising from the desolate landscape, this sinister necropolis is a testament to necromantic power. Its skeletal spires cast ominous shadows, concealing forbidden knowledge and arcane secrets.", - "An eerie necropolis where nature intertwines with death. Overgrown tombstones are entwined by thorny vines, and mournful spirits wander among the fading petals of once-vibrant flowers.", - "A labyrinthine necropolis where each step echoes with haunting murmurs. The walls are adorned with ancient runes, and restless spirits guide or hinder those who dare to delve into its depths.", - "This cursed necropolis is veiled in perpetual twilight, perpetuating a sense of impending doom. Dark enchantments shroud the tombs, and the moans of anguished souls resound through its crumbling halls.", - "A sprawling necropolis built within a labyrinthine network of catacombs. Its halls are lined with countless alcoves, each housing the remains of the departed, while the distant sound of rattling bones fills the air.", - "A desolate necropolis where an eerie stillness reigns. Time seems frozen amidst the decaying mausoleums, and the silence is broken only by the whispers of the wind and the rustle of tattered banners.", - "A foreboding necropolis perched atop a jagged cliff, overlooking a desolate wasteland. Its towering walls harbor restless spirits, and the imposing gates bear the marks of countless battles and ancient curses." - ]); - - notes.push({id, name, legend}); -} - -function listEncounters({cells}, utils) { - const occupied = []; - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.pop[i] > 1); -} - -function addEncounter(id, cell, pack, notes, utils) { - const name = "Random encounter"; - const encounterSeed = cell; // use just cell Id to not overwhelm the Vercel KV database - const legend = `
You have encountered a character.
`; - notes.push({id, name, legend}); -} diff --git a/procedural/src/engine/modules/military-generator.js b/procedural/src/engine/modules/military-generator.js deleted file mode 100644 index cae8346b..00000000 --- a/procedural/src/engine/modules/military-generator.js +++ /dev/null @@ -1,419 +0,0 @@ -"use strict"; - -/** - * Generates military forces for states - * - * REQUIRES: - * - pack.cells.state (from BurgsAndStates module) - * - pack.states (from BurgsAndStates module) - * - pack.burgs (from BurgsAndStates module) - * - config.debug (debug configuration) - * - * PROVIDES: - * - pack.states[].military (military units for each state) - */ -export function generate(pack, config, utils, notes) { - // Check required properties exist - if (!pack.cells.state) { - throw new Error("Military module requires cells.state from BurgsAndStates module"); - } - if (!pack.states) { - throw new Error("Military module requires pack.states from BurgsAndStates module"); - } - if (!pack.burgs) { - throw new Error("Military module requires pack.burgs from BurgsAndStates module"); - } - if (!config.debug) { - throw new Error("Military module requires config.debug section"); - } - - const { minmax, rn, ra, rand, gauss, si, nth, d3, populationRate, urbanization} = utils; - const { TIME } = config.debug; - - TIME && console.time("generateMilitary"); - const {cells, states, burgs, provinces} = pack; - const {p} = cells; - const valid = states.filter(s => s.i && !s.removed); // valid states - const military = config.military || getDefaultOptions(); - - const expn = d3.sum(valid.map(s => s.expansionism)); // total expansion - const area = d3.sum(valid.map(s => s.area)); // total area - const rate = { - x: 0, - Ally: -0.2, - Friendly: -0.1, - Neutral: 0, - Suspicion: 0.1, - Enemy: 1, - Unknown: 0, - Rival: 0.5, - Vassal: 0.5, - Suzerain: -0.5 - }; - - const stateModifier = { - melee: {Nomadic: 0.5, Highland: 1.2, Lake: 1, Naval: 0.7, Hunting: 1.2, River: 1.1}, - ranged: {Nomadic: 0.9, Highland: 1.3, Lake: 1, Naval: 0.8, Hunting: 2, River: 0.8}, - mounted: {Nomadic: 2.3, Highland: 0.6, Lake: 0.7, Naval: 0.3, Hunting: 0.7, River: 0.8}, - machinery: {Nomadic: 0.8, Highland: 1.4, Lake: 1.1, Naval: 1.4, Hunting: 0.4, River: 1.1}, - naval: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.8, Hunting: 0.7, River: 1.2}, - armored: {Nomadic: 1, Highland: 0.5, Lake: 1, Naval: 1, Hunting: 0.7, River: 1.1}, - aviation: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.2, Hunting: 0.6, River: 1.2}, - magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1} - }; - - const cellTypeModifier = { - nomadic: { - melee: 0.2, - ranged: 0.5, - mounted: 3, - machinery: 0.4, - naval: 0.3, - armored: 1.6, - aviation: 1, - magical: 0.5 - }, - wetland: { - melee: 0.8, - ranged: 2, - mounted: 0.3, - machinery: 1.2, - naval: 1.0, - armored: 0.2, - aviation: 0.5, - magical: 0.5 - }, - highland: { - melee: 1.2, - ranged: 1.6, - mounted: 0.3, - machinery: 3, - naval: 1.0, - armored: 0.8, - aviation: 0.3, - magical: 2 - } - }; - - const burgTypeModifier = { - nomadic: { - melee: 0.3, - ranged: 0.8, - mounted: 3, - machinery: 0.4, - naval: 1.0, - armored: 1.6, - aviation: 1, - magical: 0.5 - }, - wetland: { - melee: 1, - ranged: 1.6, - mounted: 0.2, - machinery: 1.2, - naval: 1.0, - armored: 0.2, - aviation: 0.5, - magical: 0.5 - }, - highland: {melee: 1.2, ranged: 2, mounted: 0.3, machinery: 3, naval: 1.0, armored: 0.8, aviation: 0.3, magical: 2} - }; - - valid.forEach(s => { - s.temp = {}; - const d = s.diplomacy; - - const expansionRate = minmax(s.expansionism / expn / (s.area / area), 0.25, 4); // how much state expansionism is realized - const diplomacyRate = d.some(d => d === "Enemy") - ? 1 - : d.some(d => d === "Rival") - ? 0.8 - : d.some(d => d === "Suspicion") - ? 0.5 - : 0.1; // peacefulness - const neighborsRateRaw = s.neighbors - .map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")) - .reduce((s, r) => (s += rate[r]), 0.5); - const neighborsRate = minmax(neighborsRateRaw, 0.3, 3); // neighbors rate - s.alert = minmax(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1, 5); // alert rate (area modifier) - s.temp.platoons = []; - - // apply overall state modifiers for unit types based on state features - for (const unit of military) { - if (!stateModifier[unit.type]) continue; - - let modifier = stateModifier[unit.type][s.type] || 1; - if (unit.type === "mounted" && s.formName.includes("Horde")) modifier *= 2; - else if (unit.type === "naval" && s.form === "Republic") modifier *= 1.2; - s.temp[unit.name] = modifier * s.alert; - } - }); - - const getType = cell => { - if ([1, 2, 3, 4].includes(cells.biome[cell])) return "nomadic"; - if ([7, 8, 9, 12].includes(cells.biome[cell])) return "wetland"; - if (cells.h[cell] >= 70) return "highland"; - return "generic"; - }; - - function passUnitLimits(unit, biome, state, culture, religion) { - if (unit.biomes && !unit.biomes.includes(biome)) return false; - if (unit.states && !unit.states.includes(state)) return false; - if (unit.cultures && !unit.cultures.includes(culture)) return false; - if (unit.religions && !unit.religions.includes(religion)) return false; - return true; - } - - // rural cells - for (const i of cells.i) { - if (!cells.pop[i]) continue; - - const biome = cells.biome[i]; - const state = cells.state[i]; - const culture = cells.culture[i]; - const religion = cells.religion[i]; - - const stateObj = states[state]; - if (!state || stateObj.removed) continue; - - let modifier = cells.pop[i] / 100; // basic rural army in percentages - if (culture !== stateObj.culture) modifier = stateObj.form === "Union" ? modifier / 1.2 : modifier / 2; // non-dominant culture - if (religion !== cells.religion[stateObj.center]) - modifier = stateObj.form === "Theocracy" ? modifier / 2.2 : modifier / 1.4; // non-dominant religion - if (cells.f[i] !== cells.f[stateObj.center]) - modifier = stateObj.type === "Naval" ? modifier / 1.2 : modifier / 1.8; // different landmass - const type = getType(i); - - for (const unit of military) { - const perc = +unit.rural; - if (isNaN(perc) || perc <= 0 || !stateObj.temp[unit.name]) continue; - if (!passUnitLimits(unit, biome, state, culture, religion)) continue; - if (unit.type === "naval" && !cells.haven[i]) continue; // only near-ocean cells create naval units - - const cellTypeMod = type === "generic" ? 1 : cellTypeModifier[type][unit.type]; // cell specific modifier - const army = modifier * perc * cellTypeMod; // rural cell army - const total = rn(army * stateObj.temp[unit.name] * populationRate); // total troops - if (!total) continue; - - let [x, y] = p[i]; - let n = 0; - - // place naval units to sea - if (unit.type === "naval") { - const haven = cells.haven[i]; - [x, y] = p[haven]; - n = 1; - } - - stateObj.temp.platoons.push({ - cell: i, - a: total, - t: total, - x, - y, - u: unit.name, - n, - s: unit.separate, - type: unit.type - }); - } - } - - // burgs - for (const b of burgs) { - if (!b.i || b.removed || !b.state || !b.population) continue; - - const biome = cells.biome[b.cell]; - const state = b.state; - const culture = b.culture; - const religion = cells.religion[b.cell]; - - const stateObj = states[state]; - let m = (b.population * urbanization) / 100; // basic urban army in percentages - if (b.capital) m *= 1.2; // capital has household troops - if (culture !== stateObj.culture) m = stateObj.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture - if (religion !== cells.religion[stateObj.center]) m = stateObj.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion - if (cells.f[b.cell] !== cells.f[stateObj.center]) m = stateObj.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass - const type = getType(b.cell); - - for (const unit of military) { - const perc = +unit.urban; - if (isNaN(perc) || perc <= 0 || !stateObj.temp[unit.name]) continue; - if (!passUnitLimits(unit, biome, state, culture, religion)) continue; - if (unit.type === "naval" && (!b.port || !cells.haven[b.cell])) continue; // only ports create naval units - - const mod = type === "generic" ? 1 : burgTypeModifier[type][unit.type]; // cell specific modifier - const army = m * perc * mod; // urban cell army - const total = rn(army * stateObj.temp[unit.name] * populationRate); // total troops - if (!total) continue; - - let [x, y] = p[b.cell]; - let n = 0; - - // place naval to sea - if (unit.type === "naval") { - const haven = cells.haven[b.cell]; - [x, y] = p[haven]; - n = 1; - } - - stateObj.temp.platoons.push({ - cell: b.cell, - a: total, - t: total, - x, - y, - u: unit.name, - n, - s: unit.separate, - type: unit.type - }); - } - } - - const expected = 3 * populationRate; // expected regiment size - const mergeable = (n0, n1) => (!n0.s && !n1.s) || n0.u === n1.u; // check if regiments can be merged - - // get regiments for each state - valid.forEach(s => { - s.military = createRegiments(s.temp.platoons, s, pack, config, utils, notes); - delete s.temp; // do not store temp data - }); - - function createRegiments(nodes, s, pack, config, utils, notes) { - if (!nodes.length) return []; - - nodes.sort((a, b) => a.a - b.a); // form regiments in cells with most troops - const tree = d3.quadtree( - nodes, - d => d.x, - d => d.y - ); - - nodes.forEach(node => { - tree.remove(node); - const overlap = tree.find(node.x, node.y, 20); - if (overlap && overlap.t && mergeable(node, overlap)) { - merge(node, overlap); - return; - } - if (node.t > expected) return; - const r = (expected - node.t) / (node.s ? 40 : 20); // search radius - const candidates = tree.findAll(node.x, node.y, r); - for (const c of candidates) { - if (c.t < expected && mergeable(node, c)) { - merge(node, c); - break; - } - } - }); - - // add n0 to n1's ultimate parent - function merge(n0, n1) { - if (!n1.childen) n1.childen = [n0]; - else n1.childen.push(n0); - if (n0.childen) n0.childen.forEach(n => n1.childen.push(n)); - n1.t += n0.t; - n0.t = 0; - } - - // parse regiments data - const regiments = nodes - .filter(n => n.t) - .sort((a, b) => b.t - a.t) - .map((r, i) => { - const u = {}; - u[r.u] = r.a; - (r.childen || []).forEach(n => (u[n.u] = u[n.u] ? (u[n.u] += n.a) : n.a)); - return {i, a: r.t, cell: r.cell, x: r.x, y: r.y, bx: r.x, by: r.y, u, n: r.n, name: null, state: s.i}; - }); - - // generate name for regiments - regiments.forEach(r => { - r.name = getName(r, regiments, pack, utils); - r.icon = getEmblem(r, pack, config); - generateNote(r, s, pack, config, utils, notes); - }); - - return regiments; - } - - TIME && console.timeEnd("generateMilitary"); - - return pack; -} - -export function getDefaultOptions() { - return [ - {icon: "⚔️", name: "infantry", rural: 0.25, urban: 0.2, crew: 1, power: 1, type: "melee", separate: 0}, - {icon: "🏹", name: "archers", rural: 0.12, urban: 0.2, crew: 1, power: 1, type: "ranged", separate: 0}, - {icon: "🐴", name: "cavalry", rural: 0.12, urban: 0.03, crew: 2, power: 2, type: "mounted", separate: 0}, - {icon: "💣", name: "artillery", rural: 0, urban: 0.03, crew: 8, power: 12, type: "machinery", separate: 0}, - {icon: "🌊", name: "fleet", rural: 0, urban: 0.015, crew: 100, power: 50, type: "naval", separate: 1} - ]; -} - -// utilize si function to make regiment total text fit regiment box -export function getTotal(reg, utils) { - const {si} = utils; - return reg.a > (reg.n ? 999 : 99999) ? si(reg.a) : reg.a; -} - -export function getName(r, regiments, pack, utils) { - const {nth} = utils; - const {cells, provinces, burgs} = pack; - const proper = r.n - ? null - : cells.province[r.cell] && provinces[cells.province[r.cell]] - ? provinces[cells.province[r.cell]].name - : cells.burg[r.cell] && burgs[cells.burg[r.cell]] - ? burgs[cells.burg[r.cell]].name - : null; - const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length + 1); - const form = r.n ? "Fleet" : "Regiment"; - return `${number}${proper ? ` (${proper}) ` : ` `}${form}`; -} - -// get default regiment emblem -export function getEmblem(r, pack, config) { - if (!r.n && !Object.values(r.u).length) return "🔰"; // "Newbie" regiment without troops - if ( - !r.n && - pack.states[r.state].form === "Monarchy" && - pack.cells.burg[r.cell] && - pack.burgs[pack.cells.burg[r.cell]].capital - ) - return "👑"; // "Royal" regiment based in capital - const mainUnit = Object.entries(r.u).sort((a, b) => b[1] - a[1])[0][0]; // unit with more troops in regiment - const unit = config.military.find(u => u.name === mainUnit); - return unit.icon; -} - -export function generateNote(r, s, pack, config, utils, notes) { - const {ra, rand, gauss} = utils; - const {cells, burgs, provinces} = pack; - const base = - cells.burg[r.cell] && burgs[cells.burg[r.cell]] - ? burgs[cells.burg[r.cell]].name - : cells.province[r.cell] && provinces[cells.province[r.cell]] - ? provinces[cells.province[r.cell]].fullName - : null; - const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : ""; - - const composition = r.a - ? Object.keys(r.u) - .map(t => `— ${t}: ${r.u[t]}`) - .join("\r\n") - : null; - const troops = composition - ? `\r\n\r\nRegiment composition in ${config.year} ${config.eraShort}:\r\n${composition}.` - : ""; - - const campaign = s.campaigns ? ra(s.campaigns) : null; - const year = campaign - ? rand(campaign.start, campaign.end || config.year) - : gauss(config.year - 100, 150, 1, config.year - 6); - const conflict = campaign ? ` during the ${campaign.name}` : ""; - const legend = `Regiment was formed in ${year} ${config.era}${conflict}. ${station}${troops}`; - notes.push({id: `regiment${s.i}-${r.i}`, name: r.name, legend}); -} diff --git a/procedural/src/engine/modules/names-generator.js b/procedural/src/engine/modules/names-generator.js deleted file mode 100644 index cd04a95a..00000000 --- a/procedural/src/engine/modules/names-generator.js +++ /dev/null @@ -1,335 +0,0 @@ -"use strict"; - -let chains = []; - -// calculate Markov chain for a namesbase -const calculateChain = function (string) { - const chain = []; - const array = string.split(","); - - for (const n of array) { - let name = n.trim().toLowerCase(); - const basic = !/[^\u0000-\u007f]/.test(name); // basic chars and English rules can be applied - - // split word into pseudo-syllables - for (let i = -1, syllable = ""; i < name.length; i += syllable.length || 1, syllable = "") { - let prev = name[i] || ""; // pre-onset letter - let v = 0; // 0 if no vowels in syllable - - for (let c = i + 1; name[c] && syllable.length < 5; c++) { - const that = name[c], - next = name[c + 1]; // next char - syllable += that; - if (syllable === " " || syllable === "-") break; // syllable starts with space or hyphen - if (!next || next === " " || next === "-") break; // no need to check - - if (vowel(that)) v = 1; // check if letter is vowel - - // do not split some diphthongs - if (that === "y" && next === "e") continue; // 'ye' - if (basic) { - // English-like - if (that === "o" && next === "o") continue; // 'oo' - if (that === "e" && next === "e") continue; // 'ee' - if (that === "a" && next === "e") continue; // 'ae' - if (that === "c" && next === "h") continue; // 'ch' - } - - if (vowel(that) === next) break; // two same vowels in a row - if (v && vowel(name[c + 2])) break; // syllable has vowel and additional vowel is expected soon - } - - if (chain[prev] === undefined) chain[prev] = []; - chain[prev].push(syllable); - } - } - - return chain; -}; - -const updateChain = (i, nameBases) => { - chains[i] = nameBases[i]?.b ? calculateChain(nameBases[i].b) : null; -}; - -const clearChains = () => { - chains = []; -}; - -// generate name using Markov's chain -const getBase = function (base, min, max, dupl, nameBases, utils) { - const { ERROR, ra, last, P } = utils; - - if (base === undefined) return ERROR && console.error("Please define a base"); - - if (nameBases[base] === undefined) { - if (nameBases[0]) { - ERROR && console.warn("Namebase " + base + " is not found. First available namebase will be used"); - base = 0; - } else { - ERROR && console.error("Namebase " + base + " is not found"); - return "ERROR"; - } - } - - if (!chains[base]) updateChain(base, nameBases); - - const data = chains[base]; - if (!data || data[""] === undefined) { - ERROR && console.error("Namebase " + base + " is incorrect!"); - return "ERROR"; - } - - if (!min) min = nameBases[base].min; - if (!max) max = nameBases[base].max; - if (dupl !== "") dupl = nameBases[base].d; - - let v = data[""], - cur = ra(v), - w = ""; - for (let i = 0; i < 20; i++) { - if (cur === "") { - // end of word - if (w.length < min) { - cur = ""; - w = ""; - v = data[""]; - } else break; - } else { - if (w.length + cur.length > max) { - // word too long - if (w.length < min) w += cur; - break; - } else v = data[last(cur)] || data[""]; - } - - w += cur; - cur = ra(v); - } - - // parse word to get a final name - const l = last(w); // last letter - if (l === "'" || l === " " || l === "-") w = w.slice(0, -1); // not allow some characters at the end - - let name = [...w].reduce(function (r, c, i, d) { - if (c === d[i + 1] && !dupl.includes(c)) return r; // duplication is not allowed - if (!r.length) return c.toUpperCase(); - if (r.slice(-1) === "-" && c === " ") return r; // remove space after hyphen - if (r.slice(-1) === " ") return r + c.toUpperCase(); // capitalize letter after space - if (r.slice(-1) === "-") return r + c.toUpperCase(); // capitalize letter after hyphen - if (c === "a" && d[i + 1] === "e") return r; // "ae" => "e" - if (i + 2 < d.length && c === d[i + 1] && c === d[i + 2]) return r; // remove three same letters in a row - return r + c; - }, ""); - - // join the word if any part has only 1 letter - if (name.split(" ").some(part => part.length < 2)) - name = name - .split(" ") - .map((p, i) => (i ? p.toLowerCase() : p)) - .join(""); - - if (name.length < 2) { - ERROR && console.error("Name is too short! Random name will be selected"); - name = ra(nameBases[base].b.split(",")); - } - - return name; -}; - -// generate name for culture -const getCulture = function (culture, min, max, dupl, cultures, nameBases, utils) { - const { ERROR } = utils; - - if (culture === undefined) return ERROR && console.error("Please define a culture"); - const base = cultures[culture].base; - return getBase(base, min, max, dupl, nameBases, utils); -}; - -// generate short name for culture -const getCultureShort = function (culture, cultures, nameBases, utils) { - const { ERROR } = utils; - - if (culture === undefined) return ERROR && console.error("Please define a culture"); - return getBaseShort(cultures[culture].base, nameBases, utils); -}; - -// generate short name for base -const getBaseShort = function (base, nameBases, utils) { - const min = nameBases[base] ? nameBases[base].min - 1 : null; - const max = min ? Math.max(nameBases[base].max - 2, min) : null; - return getBase(base, min, max, "", nameBases, utils); -}; - -// generate state name based on capital or random name and culture-specific suffix -const getState = function (name, culture, base, cultures, nameBases, utils) { - const { ERROR, P, capitalize, vowel } = utils; - - if (name === undefined) return ERROR && console.error("Please define a base name"); - if (culture === undefined && base === undefined) return ERROR && console.error("Please define a culture"); - if (base === undefined) base = cultures[culture].base; - - // exclude endings inappropriate for states name - if (name.includes(" ")) name = capitalize(name.replace(/ /g, "").toLowerCase()); // don't allow multiword state names - if (name.length > 6 && name.slice(-4) === "berg") name = name.slice(0, -4); // remove -berg for any - if (name.length > 5 && name.slice(-3) === "ton") name = name.slice(0, -3); // remove -ton for any - - if (base === 5 && ["sk", "ev", "ov"].includes(name.slice(-2))) name = name.slice(0, -2); - // remove -sk/-ev/-ov for Ruthenian - else if (base === 12) return vowel(name.slice(-1)) ? name : name + "u"; - // Japanese ends on any vowel or -u - else if (base === 18 && P(0.4)) - name = vowel(name.slice(0, 1).toLowerCase()) ? "Al" + name.toLowerCase() : "Al " + name; // Arabic starts with -Al - - // no suffix for fantasy bases - if (base > 32 && base < 42) return name; - - // define if suffix should be used - if (name.length > 3 && vowel(name.slice(-1))) { - if (vowel(name.slice(-2, -1)) && P(0.85)) name = name.slice(0, -2); - // 85% for vv - else if (P(0.7)) name = name.slice(0, -1); - // ~60% for cv - else return name; - } else if (P(0.4)) return name; // 60% for cc and vc - - // define suffix - let suffix = "ia"; // standard suffix - - const rnd = Math.random(), - l = name.length; - if (base === 3 && rnd < 0.03 && l < 7) suffix = "terra"; - // Italian - else if (base === 4 && rnd < 0.03 && l < 7) suffix = "terra"; - // Spanish - else if (base === 13 && rnd < 0.03 && l < 7) suffix = "terra"; - // Portuguese - else if (base === 2 && rnd < 0.03 && l < 7) suffix = "terre"; - // French - else if (base === 0 && rnd < 0.5 && l < 7) suffix = "land"; - // German - else if (base === 1 && rnd < 0.4 && l < 7) suffix = "land"; - // English - else if (base === 6 && rnd < 0.3 && l < 7) suffix = "land"; - // Nordic - else if (base === 32 && rnd < 0.1 && l < 7) suffix = "land"; - // generic Human - else if (base === 7 && rnd < 0.1) suffix = "eia"; - // Greek - else if (base === 9 && rnd < 0.35) suffix = "maa"; - // Finnic - else if (base === 15 && rnd < 0.4 && l < 6) suffix = "orszag"; - // Hungarian - else if (base === 16) suffix = rnd < 0.6 ? "yurt" : "eli"; - // Turkish - else if (base === 10) suffix = "guk"; - // Korean - else if (base === 11) suffix = " Guo"; - // Chinese - else if (base === 14) suffix = rnd < 0.5 && l < 6 ? "tlan" : "co"; - // Nahuatl - else if (base === 17 && rnd < 0.8) suffix = "a"; - // Berber - else if (base === 18 && rnd < 0.8) suffix = "a"; // Arabic - - return validateSuffix(name, suffix, utils); -}; - -function validateSuffix(name, suffix, utils) { - const { vowel } = utils; - - if (name.slice(-1 * suffix.length) === suffix) return name; // no suffix if name already ends with it - const s1 = suffix.charAt(0); - if (name.slice(-1) === s1) name = name.slice(0, -1); // remove name last letter if it's a suffix first letter - if (vowel(s1) === vowel(name.slice(-1)) && vowel(s1) === vowel(name.slice(-2, -1))) name = name.slice(0, -1); // remove name last char if 2 last chars are the same type as suffix's 1st - if (name.slice(-1) === s1) name = name.slice(0, -1); // remove name last letter if it's a suffix first letter - return name + suffix; -} - -// generate name for the map -const getMapName = function (nameBases, config, utils) { - const { P, rand } = utils; - - const base = P(0.7) ? 2 : P(0.5) ? rand(0, 6) : rand(0, 31); - if (!nameBases[base]) { - return ""; - } - const min = nameBases[base].min - 1; - const max = Math.max(nameBases[base].max - 3, min); - const baseName = getBase(base, min, max, "", nameBases, utils); - const name = P(0.7) ? addSuffix(baseName, utils) : baseName; - return name; -}; - -function addSuffix(name, utils) { - const { P } = utils; - - const suffix = P(0.8) ? "ia" : "land"; - if (suffix === "ia" && name.length > 6) name = name.slice(0, -(name.length - 3)); - else if (suffix === "land" && name.length > 6) name = name.slice(0, -(name.length - 5)); - return validateSuffix(name, suffix, utils); -} - -const getNameBases = function () { - // name, min length, max length, letters to allow duplication, multi-word name rate [deprecated] - // prettier-ignore - return [ - {name: "German", i: 0, min: 5, max: 12, d: "lt", m: 0, b: "Achern,Aichhalden,Aitern,Albbruck,Alpirsbach,Altensteig,Althengstett,Appenweier,Auggen,Badenen,Badenweiler,Baiersbronn,Ballrechten,Bellingen,Berghaupten,Bernau,Biberach,Biederbach,Binzen,Birkendorf,Birkenfeld,Bischweier,Blumberg,Bollen,Bollschweil,Bonndorf,Bosingen,Braunlingen,Breisach,Breisgau,Breitnau,Brigachtal,Buchenbach,Buggingen,Buhl,Buhlertal,Calw,Dachsberg,Dobel,Donaueschingen,Dornhan,Dornstetten,Dottingen,Dunningen,Durbach,Durrheim,Ebhausen,Ebringen,Efringen,Egenhausen,Ehrenkirchen,Ehrsberg,Eimeldingen,Eisenbach,Elzach,Elztal,Emmendingen,Endingen,Engelsbrand,Enz,Enzklosterle,Eschbronn,Ettenheim,Ettlingen,Feldberg,Fischerbach,Fischingen,Fluorn,Forbach,Freiamt,Freiburg,Freudenstadt,Friedenweiler,Friesenheim,Frohnd,Furtwangen,Gaggenau,Geisingen,Gengenbach,Gernsbach,Glatt,Glatten,Glottertal,Gorwihl,Gottenheim,Grafenhausen,Grenzach,Griesbach,Gutach,Gutenbach,Hag,Haiterbach,Hardt,Harmersbach,Hasel,Haslach,Hausach,Hausen,Hausern,Heitersheim,Herbolzheim,Herrenalb,Herrischried,Hinterzarten,Hochenschwand,Hofen,Hofstetten,Hohberg,Horb,Horben,Hornberg,Hufingen,Ibach,Ihringen,Inzlingen,Kandern,Kappel,Kappelrodeck,Karlsbad,Karlsruhe,Kehl,Keltern,Kippenheim,Kirchzarten,Konigsfeld,Krozingen,Kuppenheim,Kussaberg,Lahr,Lauchringen,Lauf,Laufenburg,Lautenbach,Lauterbach,Lenzkirch,Liebenzell,Loffenau,Loffingen,Lorrach,Lossburg,Mahlberg,Malsburg,Malsch,March,Marxzell,Marzell,Maulburg,Monchweiler,Muhlenbach,Mullheim,Munstertal,Murg,Nagold,Neubulach,Neuenburg,Neuhausen,Neuried,Neuweiler,Niedereschach,Nordrach,Oberharmersbach,Oberkirch,Oberndorf,Oberbach,Oberried,Oberwolfach,Offenburg,Ohlsbach,Oppenau,Ortenberg,otigheim,Ottenhofen,Ottersweier,Peterstal,Pfaffenweiler,Pfalzgrafenweiler,Pforzheim,Rastatt,Renchen,Rheinau,Rheinfelden,Rheinmunster,Rickenbach,Rippoldsau,Rohrdorf,Rottweil,Rummingen,Rust,Sackingen,Sasbach,Sasbachwalden,Schallbach,Schallstadt,Schapbach,Schenkenzell,Schiltach,Schliengen,Schluchsee,Schomberg,Schonach,Schonau,Schonenberg,Schonwald,Schopfheim,Schopfloch,Schramberg,Schuttertal,Schwenningen,Schworstadt,Seebach,Seelbach,Seewald,Sexau,Simmersfeld,Simonswald,Sinzheim,Solden,Staufen,Stegen,Steinach,Steinen,Steinmauern,Straubenhardt,Stuhlingen,Sulz,Sulzburg,Teinach,Tiefenbronn,Tiengen,Titisee,Todtmoos,Todtnau,Todtnauberg,Triberg,Tunau,Tuningen,uhlingen,Unterkirnach,Reichenbach,Utzenfeld,Villingen,Villingendorf,Vogtsburg,Vohrenbach,Waldachtal,Waldbronn,Waldkirch,Waldshut,Wehr,Weil,Weilheim,Weisenbach,Wembach,Wieden,Wiesental,Wildbad,Wildberg,Winzeln,Wittlingen,Wittnau,Wolfach,Wutach,Wutoschingen,Wyhlen,Zavelstein"}, - {name: "English", i: 1, min: 6, max: 11, d: "", m: .1, b: "Abingdon,Albrighton,Alcester,Almondbury,Altrincham,Amersham,Andover,Appleby,Ashboume,Atherstone,Aveton,Axbridge,Aylesbury,Baldock,Bamburgh,Barton,Basingstoke,Berden,Bere,Berkeley,Berwick,Betley,Bideford,Bingley,Birmingham,Blandford,Blechingley,Bodmin,Bolton,Bootham,Boroughbridge,Boscastle,Bossinney,Bramber,Brampton,Brasted,Bretford,Bridgetown,Bridlington,Bromyard,Bruton,Buckingham,Bungay,Burton,Calne,Cambridge,Canterbury,Carlisle,Castleton,Caus,Charmouth,Chawleigh,Chichester,Chillington,Chinnor,Chipping,Chisbury,Cleobury,Clifford,Clifton,Clitheroe,Cockermouth,Coleshill,Combe,Congleton,Crafthole,Crediton,Cuddenbeck,Dalton,Darlington,Dodbrooke,Drax,Dudley,Dunstable,Dunster,Dunwich,Durham,Dymock,Exeter,Exning,Faringdon,Felton,Fenny,Finedon,Flookburgh,Fowey,Frampton,Gateshead,Gatton,Godmanchester,Grampound,Grantham,Guildford,Halesowen,Halton,Harbottle,Harlow,Hatfield,Hatherleigh,Haydon,Helston,Henley,Hertford,Heytesbury,Hinckley,Hitchin,Holme,Hornby,Horsham,Kendal,Kenilworth,Kilkhampton,Kineton,Kington,Kinver,Kirby,Knaresborough,Knutsford,Launceston,Leighton,Lewes,Linton,Louth,Luton,Lyme,Lympstone,Macclesfield,Madeley,Malborough,Maldon,Manchester,Manningtree,Marazion,Marlborough,Marshfield,Mere,Merryfield,Middlewich,Midhurst,Milborne,Mitford,Modbury,Montacute,Mousehole,Newbiggin,Newborough,Newbury,Newenden,Newent,Norham,Northleach,Noss,Oakham,Olney,Orford,Ormskirk,Oswestry,Padstow,Paignton,Penkneth,Penrith,Penzance,Pershore,Petersfield,Pevensey,Pickering,Pilton,Pontefract,Portsmouth,Preston,Quatford,Reading,Redcliff,Retford,Rockingham,Romney,Rothbury,Rothwell,Salisbury,Saltash,Seaford,Seasalter,Sherston,Shifnal,Shoreham,Sidmouth,Skipsea,Skipton,Solihull,Somerton,Southam,Southwark,Standon,Stansted,Stapleton,Stottesdon,Sudbury,Swavesey,Tamerton,Tarporley,Tetbury,Thatcham,Thaxted,Thetford,Thornbury,Tintagel,Tiverton,Torksey,Totnes,Towcester,Tregoney,Trematon,Tutbury,Uxbridge,Wallingford,Wareham,Warenmouth,Wargrave,Warton,Watchet,Watford,Wendover,Westbury,Westcheap,Weymouth,Whitford,Wickwar,Wigan,Wigmore,Winchelsea,Winkleigh,Wiscombe,Witham,Witheridge,Wiveliscombe,Woodbury,Yeovil"}, - {name: "French", i: 2, min: 5, max: 13, d: "nlrs", m: .1, b: "Adon,Aillant,Amilly,Andonville,Ardon,Artenay,Ascheres,Ascoux,Attray,Aubin,Audeville,Aulnay,Autruy,Auvilliers,Auxy,Aveyron,Baccon,Bardon,Barville,Batilly,Baule,Bazoches,Beauchamps,Beaugency,Beaulieu,Beaune,Bellegarde,Boesses,Boigny,Boiscommun,Boismorand,Boisseaux,Bondaroy,Bonnee,Bonny,Bordes,Bou,Bougy,Bouilly,Boulay,Bouzonville,Bouzy,Boynes,Bray,Breteau,Briare,Briarres,Bricy,Bromeilles,Bucy,Cepoy,Cercottes,Cerdon,Cernoy,Cesarville,Chailly,Chaingy,Chalette,Chambon,Champoulet,Chanteau,Chantecoq,Chapell,Charme,Charmont,Charsonville,Chateau,Chateauneuf,Chatel,Chatenoy,Chatillon,Chaussy,Checy,Chevannes,Chevillon,Chevilly,Chevry,Chilleurs,Choux,Chuelles,Clery,Coinces,Coligny,Combleux,Combreux,Conflans,Corbeilles,Corquilleroy,Cortrat,Coudroy,Coullons,Coulmiers,Courcelles,Courcy,Courtemaux,Courtempierre,Courtenay,Cravant,Crottes,Dadonville,Dammarie,Dampierre,Darvoy,Desmonts,Dimancheville,Donnery,Dordives,Dossainville,Douchy,Dry,Echilleuses,Egry,Engenville,Epieds,Erceville,Ervauville,Escrennes,Escrignelles,Estouy,Faverelles,Fay,Feins,Ferolles,Ferrieres,Fleury,Fontenay,Foret,Foucherolles,Freville,Gatinais,Gaubertin,Gemigny,Germigny,Gidy,Gien,Girolles,Givraines,Gondreville,Grangermont,Greneville,Griselles,Guigneville,Guilly,Gyleslonains,Huetre,Huisseau,Ingrannes,Ingre,Intville,Isdes,Ivre,Jargeau,Jouy,Juranville,Bussiere,Laas,Ladon,Lailly,Langesse,Leouville,Ligny,Lombreuil,Lorcy,Lorris,Loury,Louzouer,Malesherbois,Marcilly,Mardie,Mareau,Marigny,Marsainvilliers,Melleroy,Menestreau,Merinville,Messas,Meung,Mezieres,Migneres,Mignerette,Mirabeau,Montargis,Montbarrois,Montbouy,Montcresson,Montereau,Montigny,Montliard,Mormant,Morville,Moulinet,Moulon,Nancray,Nargis,Nesploy,Neuville,Neuvy,Nevoy,Nibelle,Nogent,Noyers,Ocre,Oison,Olivet,Ondreville,Onzerain,Orleans,Ormes,Orville,Oussoy,Outarville,Ouzouer,Pannecieres,Pannes,Patay,Paucourt,Pers,Pierrefitte,Pithiverais,Pithiviers,Poilly,Potier,Prefontaines,Presnoy,Pressigny,Puiseaux,Quiers,Ramoulu,Rebrechien,Rouvray,Rozieres,Rozoy,Ruan,Sandillon,Santeau,Saran,Sceaux,Seichebrieres,Semoy,Sennely,Sermaises,Sigloy,Solterre,Sougy,Sully,Sury,Tavers,Thignonville,Thimory,Thorailles,Thou,Tigy,Tivernon,Tournoisis,Trainou,Treilles,Trigueres,Trinay,Vannes,Varennes,Vennecy,Vieilles,Vienne,Viglain,Vignes,Villamblain,Villemandeur,Villemoutiers,Villemurlin,Villeneuve,Villereau,Villevoques,Villorceau,Vimory,Vitry,Vrigny"}, - {name: "Italian", i: 3, min: 5, max: 12, d: "cltr", m: .1, b: "Accumoli,Acquafondata,Acquapendente,Acuto,Affile,Agosta,Alatri,Albano,Allumiere,Alvito,Amaseno,Amatrice,Anagni,Anguillara,Anticoli,Antrodoco,Anzio,Aprilia,Aquino,Arcinazzo,Ariccia,Arpino,Arsoli,Ausonia,Bagnoregio,Bassiano,Bellegra,Belmonte,Bolsena,Bomarzo,Borgorose,Boville,Bracciano,Broccostella,Calcata,Camerata,Campagnano,Campoli,Canale,Canino,Cantalice,Cantalupo,Capranica,Caprarola,Carbognano,Casalattico,Casalvieri,Castelforte,Castelnuovo,Castiglione,Castro,Castrocielo,Ceccano,Celleno,Cellere,Cerreto,Cervara,Cerveteri,Ciampino,Ciciliano,Cittaducale,Cittareale,Civita,Civitella,Colfelice,Colleferro,Collepardo,Colonna,Concerviano,Configni,Contigliano,Cori,Cottanello,Esperia,Faleria,Farnese,Ferentino,Fiamignano,Filacciano,Fiuggi,Fiumicino,Fondi,Fontana,Fonte,Fontechiari,Formia,Frascati,Frasso,Frosinone,Fumone,Gaeta,Gallese,Gavignano,Genazzano,Giuliano,Gorga,Gradoli,Grottaferrata,Grotte,Guarcino,Guidonia,Ischia,Isola,Labico,Labro,Ladispoli,Latera,Lenola,Leonessa,Licenza,Longone,Lubriano,Maenza,Magliano,Marano,Marcellina,Marcetelli,Marino,Mazzano,Mentana,Micigliano,Minturno,Montalto,Montasola,Montebuono,Monteflavio,Montelanico,Monteleone,Montenero,Monterosi,Moricone,Morlupo,Nazzano,Nemi,Nerola,Nespolo,Nettuno,Norma,Olevano,Onano,Oriolo,Orte,Orvinio,Paganico,Paliano,Palombara,Patrica,Pescorocchiano,Petrella,Piansano,Picinisco,Pico,Piedimonte,Piglio,Pignataro,Poggio,Poli,Pomezia,Pontecorvo,Pontinia,Ponzano,Posta,Pozzaglia,Priverno,Proceno,Rignano,Riofreddo,Ripi,Rivodutri,Rocca,Roccagorga,Roccantica,Roccasecca,Roiate,Ronciglione,Roviano,Salisano,Sambuci,Santa,Santini,Scandriglia,Segni,Selci,Sermoneta,Serrone,Settefrati,Sezze,Sgurgola,Sonnino,Sora,Soriano,Sperlonga,Spigno,Subiaco,Supino,Sutri,Tarano,Tarquinia,Terelle,Terracina,Tivoli,Toffia,Tolfa,Torrice,Torricella,Trevi,Trevignano,Trivigliano,Turania,Tuscania,Valentano,Vallecorsa,Vallemaio,Vallepietra,Vallerano,Vasanello,Vejano,Velletri,Ventotene,Veroli,Vetralla,Vicalvi,Vico,Vicovaro,Vignanello,Viterbo,Viticuso,Vitorchiano,Vivaro,Zagarolo"}, - {name: "Castillian", i: 4, min: 5, max: 11, d: "lr", m: 0, b: "Ajofrin,Alameda,Alaminos,Albares,Albarreal,Albendiego,Alcanizo,Alcaudete,Alcolea,Aldea,Aldeanueva,Algar,Algora,Alhondiga,Almadrones,Almendral,Alovera,Anguita,Arbancon,Argecilla,Arges,Arroyo,Atanzon,Atienza,Azuqueca,Baides,Banos,Bargas,Barriopedro,Belvis,Berninches,Brihuega,Buenaventura,Burgos,Burguillos,Bustares,Cabanillas,Calzada,Camarena,Campillo,Cantalojas,Cardiel,Carmena,Casas,Castejon,Castellar,Castilforte,Castillo,Castilnuevo,Cazalegas,Centenera,Cervera,Checa,Chozas,Chueca,Cifuentes,Cincovillas,Ciruelas,Cogollor,Cogolludo,Consuegra,Copernal,Corral,Cuerva,Domingo,Dosbarrios,Driebes,Duron,Escalona,Escalonilla,Escamilla,Escopete,Espinosa,Esplegares,Esquivias,Estables,Estriegana,Fontanar,Fuembellida,Fuensalida,Fuentelsaz,Gajanejos,Galvez,Gascuena,Gerindote,Guadamur,Heras,Herreria,Herreruela,Hinojosa,Hita,Hombrados,Hontanar,Hormigos,Huecas,Huerta,Humanes,Illana,Illescas,Iniestola,Irueste,Jadraque,Jirueque,Lagartera,Ledanca,Lillo,Lominchar,Loranca,Lucillos,Luzaga,Luzon,Madrid,Magan,Malaga,Malpica,Manzanar,Maqueda,Masegoso,Matillas,Medranda,Megina,Mejorada,Millana,Milmarcos,Mirabueno,Miralrio,Mocejon,Mochales,Molina,Mondejar,Montarron,Mora,Moratilla,Morenilla,Navas,Negredo,Noblejas,Numancia,Nuno,Ocana,Ocentejo,Olias,Olmeda,Ontigola,Orea,Orgaz,Oropesa,Otero,Palma,Pardos,Paredes,Penalver,Pepino,Peralejos,Pinilla,Pioz,Piqueras,Portillo,Poveda,Pozo,Pradena,Prados,Puebla,Puerto,Quero,Quintanar,Rebollosa,Retamoso,Riba,Riofrio,Robledo,Romanillos,Romanones,Rueda,Salmeron,Santiuste,Santo,Sauca,Segura,Selas,Semillas,Sesena,Setiles,Sevilla,Siguenza,Solanillos,Somolinos,Sonseca,Sotillo,Talavera,Taravilla,Tembleque,Tendilla,Tierzo,Torralba,Torre,Torrejon,Torrijos,Tortola,Tortuera,Totanes,Trillo,Uceda,Ugena,Urda,Utande,Valdesotos,Valhermoso,Valtablado,Valverde,Velada,Viana,Yebra,Yuncos,Yunquera,Zaorejas,Zarzuela,Zorita"}, - {name: "Ruthenian", i: 5, min: 5, max: 10, d: "", m: 0, b: "Belgorod,Beloberezhye,Belyi,Belz,Berestiy,Berezhets,Berezovets,Berezutsk,Bobruisk,Bolonets,Borisov,Borovsk,Bozhesk,Bratslav,Bryansk,Brynsk,Buryn,Byhov,Chechersk,Chemesov,Cheremosh,Cherlen,Chern,Chernigov,Chernitsa,Chernobyl,Chernogorod,Chertoryesk,Chetvertnia,Demyansk,Derevesk,Devyagoresk,Dichin,Dmitrov,Dorogobuch,Dorogobuzh,Drestvin,Drokov,Drutsk,Dubechin,Dubichi,Dubki,Dubkov,Dveren,Galich,Glebovo,Glinsk,Goloty,Gomiy,Gorodets,Gorodische,Gorodno,Gorohovets,Goroshin,Gorval,Goryshon,Holm,Horobor,Hoten,Hotin,Hotmyzhsk,Ilovech,Ivan,Izborsk,Izheslavl,Kamenets,Kanev,Karachev,Karna,Kavarna,Klechesk,Klyapech,Kolomyya,Kolyvan,Kopyl,Korec,Kornik,Korochunov,Korshev,Korsun,Koshkin,Kotelno,Kovyla,Kozelsk,Kozelsk,Kremenets,Krichev,Krylatsk,Ksniatin,Kulatsk,Kursk,Kursk,Lebedev,Lida,Logosko,Lomihvost,Loshesk,Loshichi,Lubech,Lubno,Lubutsk,Lutsk,Luchin,Luki,Lukoml,Luzha,Lvov,Mtsensk,Mdin,Medniki,Melecha,Merech,Meretsk,Mescherskoe,Meshkovsk,Metlitsk,Mezetsk,Mglin,Mihailov,Mikitin,Mikulino,Miloslavichi,Mogilev,Mologa,Moreva,Mosalsk,Moschiny,Mozyr,Mstislav,Mstislavets,Muravin,Nemech,Nemiza,Nerinsk,Nichan,Novgorod,Novogorodok,Obolichi,Obolensk,Obolensk,Oleshsk,Olgov,Omelnik,Opoka,Opoki,Oreshek,Orlets,Osechen,Oster,Ostrog,Ostrov,Perelai,Peremil,Peremyshl,Pererov,Peresechen,Perevitsk,Pereyaslav,Pinsk,Ples,Polotsk,Pronsk,Proposhesk,Punia,Putivl,Rechitsa,Rodno,Rogachev,Romanov,Romny,Roslavl,Rostislavl,Rostovets,Rsha,Ruza,Rybchesk,Rylsk,Rzhavesk,Rzhev,Rzhischev,Sambor,Serensk,Serensk,Serpeysk,Shilov,Shuya,Sinech,Sizhka,Skala,Slovensk,Slutsk,Smedin,Sneporod,Snitin,Snovsk,Sochevo,Sokolec,Starica,Starodub,Stepan,Sterzh,Streshin,Sutesk,Svinetsk,Svisloch,Terebovl,Ternov,Teshilov,Teterin,Tiversk,Torchevsk,Toropets,Torzhok,Tripolye,Trubchevsk,Tur,Turov,Usvyaty,Uteshkov,Vasilkov,Velil,Velye,Venev,Venicha,Verderev,Vereya,Veveresk,Viazma,Vidbesk,Vidychev,Voino,Volodimer,Volok,Volyn,Vorobesk,Voronich,Voronok,Vorotynsk,Vrev,Vruchiy,Vselug,Vyatichsk,Vyatka,Vyshegorod,Vyshgorod,Vysokoe,Yagniatin,Yaropolch,Yasenets,Yuryev,Yuryevets,Zaraysk,Zhitomel,Zholvazh,Zizhech,Zubkov,Zudechev,Zvenigorod"}, - {name: "Nordic", i: 6, min: 6, max: 10, d: "kln", m: .1, b: "Akureyri,Aldra,Alftanes,Andenes,Austbo,Auvog,Bakkafjordur,Ballangen,Bardal,Beisfjord,Bifrost,Bildudalur,Bjerka,Bjerkvik,Bjorkosen,Bliksvaer,Blokken,Blonduos,Bolga,Bolungarvik,Borg,Borgarnes,Bosmoen,Bostad,Bostrand,Botsvika,Brautarholt,Breiddalsvik,Bringsli,Brunahlid,Budardalur,Byggdakjarni,Dalvik,Djupivogur,Donnes,Drageid,Drangsnes,Egilsstadir,Eiteroga,Elvenes,Engavogen,Ertenvog,Eskifjordur,Evenes,Eyrarbakki,Fagernes,Fallmoen,Fellabaer,Fenes,Finnoya,Fjaer,Fjelldal,Flakstad,Flateyri,Flostrand,Fludir,Gardaber,Gardur,Gimstad,Givaer,Gjeroy,Gladstad,Godoya,Godoynes,Granmoen,Gravdal,Grenivik,Grimsey,Grindavik,Grytting,Hafnir,Halsa,Hauganes,Haugland,Hauknes,Hella,Helland,Hellissandur,Hestad,Higrav,Hnifsdalur,Hofn,Hofsos,Holand,Holar,Holen,Holkestad,Holmavik,Hopen,Hovden,Hrafnagil,Hrisey,Husavik,Husvik,Hvammstangi,Hvanneyri,Hveragerdi,Hvolsvollur,Igeroy,Indre,Inndyr,Innhavet,Innes,Isafjordur,Jarklaustur,Jarnsreykir,Junkerdal,Kaldvog,Kanstad,Karlsoy,Kavosen,Keflavik,Kjelde,Kjerstad,Klakk,Kopasker,Kopavogur,Korgen,Kristnes,Krutoga,Krystad,Kvina,Lande,Laugar,Laugaras,Laugarbakki,Laugarvatn,Laupstad,Leines,Leira,Leiren,Leland,Lenvika,Loding,Lodingen,Lonsbakki,Lopsmarka,Lovund,Luroy,Maela,Melahverfi,Meloy,Mevik,Misvaer,Mornes,Mosfellsber,Moskenes,Myken,Naurstad,Nesberg,Nesjahverfi,Nesset,Nevernes,Obygda,Ofoten,Ogskardet,Okervika,Oknes,Olafsfjordur,Oldervika,Olstad,Onstad,Oppeid,Oresvika,Orsnes,Orsvog,Osmyra,Overdal,Prestoya,Raudalaekur,Raufarhofn,Reipo,Reykholar,Reykholt,Reykjahlid,Rif,Rinoya,Rodoy,Rognan,Rosvika,Rovika,Salhus,Sanden,Sandgerdi,Sandoker,Sandset,Sandvika,Saudarkrokur,Selfoss,Selsoya,Sennesvik,Setso,Siglufjordur,Silvalen,Skagastrond,Skjerstad,Skonland,Skorvogen,Skrova,Sleneset,Snubba,Softing,Solheim,Solheimar,Sorarnoy,Sorfugloy,Sorland,Sormela,Sorvaer,Sovika,Stamsund,Stamsvika,Stave,Stokka,Stokkseyri,Storjord,Storo,Storvika,Strand,Straumen,Strendene,Sudavik,Sudureyri,Sundoya,Sydalen,Thingeyri,Thorlakshofn,Thorshofn,Tjarnabyggd,Tjotta,Tosbotn,Traelnes,Trofors,Trones,Tverro,Ulvsvog,Unnstad,Utskor,Valla,Vandved,Varmahlid,Vassos,Vevelstad,Vidrek,Vik,Vikholmen,Vogar,Vogehamn,Vopnafjordur"}, - {name: "Greek", i: 7, min: 5, max: 11, d: "s", m: .1, b: "Abdera,Acharnae,Aegae,Aegina,Agrinion,Aigosthena,Akragas,Akroinon,Akrotiri,Alalia,Alexandria,Amarynthos,Amaseia,Amphicaea,Amphigeneia,Amphipolis,Antipatrea,Antiochia,Apamea,Aphidna,Apollonia,Argos,Artemita,Argyropolis,Asklepios,Athenai,Athmonia,Bhrytos,Borysthenes,Brauron,Byblos,Byzantion,Bythinion,Calydon,Chamaizi,Chalcis,Chios,Cleona,Corcyra,Croton,Cyrene,Cythera,Decelea,Delos,Delphi,Dicaearchia,Didyma,Dion,Dioscurias,Dodona,Dorylaion,Elateia,Eleusis,Eleutherna,Emporion,Ephesos,Epidamnos,Epidauros,Epizephyrian,Erythrae,Eubea,Golgi,Gonnos,Gorgippia,Gournia,Gortyn,Gytion,Hagios,Halicarnassos,Heliopolis,Hellespontos,Heloros,Heraclea,Hierapolis,Himera,Histria,Hubla,Hyele,Ialysos,Iasos,Idalion,Imbros,Iolcos,Itanos,Ithaca,Juktas,Kallipolis,Kameiros,Karistos,Kasmenai,Kepoi,Kimmerikon,Knossos,Korinthos,Kos,Kourion,Kydonia,Kyrenia,Lamia,Lampsacos,Laodicea,Lapithos,Larissa,Lebena,Lefkada,Lekhaion,Leibethra,Leontinoi,Lilaea,Lindos,Lissos,Magnesia,Mantineia,Marathon,Marmara,Massalia,Megalopolis,Megara,Metapontion,Methumna,Miletos,Morgantina,Mulai,Mukenai,Myonia,Myra,Myrmekion,Myos,Nauplios,Naucratis,Naupaktos,Naxos,Neapolis,Nemea,Nicaea,Nicopolis,Nymphaion,Nysa,Odessos,Olbia,Olympia,Olynthos,Opos,Orchomenos,Oricos,Orestias,Oreos,Onchesmos,Pagasae,Palaikastro,Pandosia,Panticapaion,Paphos,Pargamon,Paros,Pegai,Pelion,Peiraies,Phaistos,Phaleron,Pharos,Pithekussa,Philippopolis,Phocaea,Pinara,Pisa,Pitane,Plataea,Poseidonia,Potidaea,Pseira,Psychro,Pteleos,Pydna,Pylos,Pyrgos,Rhamnos,Rhithymna,Rhypae,Rizinia,Rodos,Salamis,Samos,Skyllaion,Seleucia,Semasos,Sestos,Scidros,Sicyon,,Sinope,Siris,Smyrna,Sozopolis,Sparta,Stagiros,Stratos,Stymphalos,Sybaris,Surakousai,Taras,Tanagra,Tanais,Tauromenion,Tegea,Temnos,Teos,Thapsos,Thassos,Thebai,Theodosia,Therma,Thespian,Thronion,Thoricos,Thurii,Thyreum,Thyria,Tithoraea,Tomis,Tragurion,Tripolis,Troliton,Troy,Tylissos,Tyros,Vathypetros,Zakynthos,Zakros"}, - {name: "Roman", i: 8, min: 6, max: 11, d: "ln", m: .1, b: "Abila,Adflexum,Adnicrem,Aelia,Aelius,Aeminium,Aequum,Agrippina,Agrippinae,Ala,Albanianis,Aleria,Ambianum,Andautonia,Apulum,Aquae,Aquaegranni,Aquensis,Aquileia,Aquincum,Arae,Argentoratum,Ariminum,Ascrivium,Asturica,Atrebatum,Atuatuca,Augusta,Aurelia,Aurelianorum,Batavar,Batavorum,Belum,Biriciana,Blestium,Bonames,Bonna,Bononia,Borbetomagus,Bovium,Bracara,Brigantium,Burgodunum,Caesaraugusta,Caesarea,Caesaromagus,Calleva,Camulodunum,Cannstatt,Cantiacorum,Capitolina,Caralis,Castellum,Castra,Castrum,Cibalae,Clausentum,Colonia,Concangis,Condate,Confluentes,Conimbriga,Corduba,Coria,Corieltauvorum,Corinium,Coriovallum,Cornoviorum,Danum,Deva,Dianium,Divodurum,Dobunnorum,Drusi,Dubris,Dumnoniorum,Durnovaria,Durocobrivis,Durocornovium,Duroliponte,Durovernum,Durovigutum,Eboracum,Ebusus,Edetanorum,Emerita,Emona,Emporiae,Euracini,Faventia,Flaviae,Florentia,Forum,Gerulata,Gerunda,Gesoscribate,Glevensium,Hadriani,Herculanea,Isca,Italica,Iulia,Iuliobrigensium,Iuvavum,Lactodurum,Lagentium,Lapurdum,Lauri,Legionis,Lemanis,Lentia,Lepidi,Letocetum,Lindinis,Lindum,Lixus,Londinium,Lopodunum,Lousonna,Lucus,Lugdunum,Luguvalium,Lutetia,Mancunium,Marsonia,Martius,Massa,Massilia,Matilo,Mattiacorum,Mediolanum,Mod,Mogontiacum,Moridunum,Mursa,Naissus,Nervia,Nida,Nigrum,Novaesium,Noviomagus,Olicana,Olisippo,Ovilava,Parisiorum,Partiscum,Paterna,Pistoria,Placentia,Pollentia,Pomaria,Pompeii,Pons,Portus,Praetoria,Praetorium,Pullum,Ragusium,Ratae,Raurica,Ravenna,Regina,Regium,Regulbium,Rigomagus,Roma,Romula,Rutupiae,Salassorum,Salernum,Salona,Scalabis,Segovia,Silurum,Sirmium,Siscia,Sorviodurum,Sumelocenna,Tarraco,Taurinorum,Theranda,Traiectum,Treverorum,Tungrorum,Turicum,Ulpia,Valentia,Venetiae,Venta,Verulamium,Vesontio,Vetera,Victoriae,Victrix,Villa,Viminacium,Vindelicorum,Vindobona,Vinovia,Viroconium"}, - {name: "Finnic", i: 9, min: 5, max: 11, d: "akiut", m: 0, b: "Aanekoski,Ahlainen,Aholanvaara,Ahtari,Aijala,Akaa,Alajarvi,Antsla,Aspo,Bennas,Bjorkoby,Elva,Emasalo,Espoo,Esse,Evitskog,Forssa,Haapamaki,Haapavesi,Haapsalu,Hameenlinna,Hanko,Harjavalta,Hattuvaara,Hautajarvi,Havumaki,Heinola,Hetta,Hinkabole,Hirmula,Hossa,Huittinen,Husula,Hyryla,Hyvinkaa,Ikaalinen,Iskmo,Itakoski,Jamsa,Jarvenpaa,Jeppo,Jioesuu,Jiogeva,Joensuu,Jokikyla,Jungsund,Jyvaskyla,Kaamasmukka,Kajaani,Kalajoki,Kallaste,Kankaanpaa,Karkku,Karpankyla,Kaskinen,Kasnas,Kauhajoki,Kauhava,Kauniainen,Kauvatsa,Kehra,Kellokoski,Kelottijarvi,Kemi,Kemijarvi,Kerava,Keuruu,Kiljava,Kiuruvesi,Kivesjarvi,Kiviioli,Kivisuo,Klaukkala,Klovskog,Kohtlajarve,Kokemaki,Kokkola,Kolho,Koskue,Kotka,Kouva,Kaupunki,Kuhmo,Kunda,Kuopio,Kuressaare,Kurikka,Kuusamo,Kylmalankyla,Lahti,Laitila,Lankipohja,Lansikyla,Lapua,Laurila,Lautiosaari,Lempaala,Lepsama,Liedakkala,Lieksa,Littoinen,Lohja,Loimaa,Loksa,Loviisa,Malmi,Mantta,Matasvaara,Maula,Miiluranta,Mioisakula,Munapirtti,Mustvee,Muurahainen,Naantali,Nappa,Narpio,Niinimaa,Niinisalo,Nikkila,Nilsia,Nivala,Nokia,Nummela,Nuorgam,Nuvvus,Obbnas,Oitti,Ojakkala,Onninen,Orimattila,Orivesi,Otanmaki,Otava,Otepaa,Oulainen,Oulu,Paavola,Paide,Paimio,Pakankyla,Paldiski,Parainen,Parkumaki,Parola,Perttula,Pieksamaki,Pioltsamaa,Piolva,Pohjavaara,Porhola,Porrasa,Porvoo,Pudasjarvi,Purmo,Pyhajarvi,Raahe,Raasepori,Raisio,Rajamaki,Rakvere,Rapina,Rapla,Rauma,Rautio,Reposaari,Riihimaki,Rovaniemi,Roykka,Ruonala,Ruottala,Rutalahti,Saarijarvi,Salo,Sastamala,Saue,Savonlinna,Seinajoki,Sillamae,Siuntio,Sompujarvi,Suonenjoki,Suurejaani,Syrjantaka,Tamsalu,Tapa,Temmes,Tiorva,Tormasenvaara,Tornio,Tottijarvi,Tulppio,Turenki,Turi,Tuukkala,Tuurala,Tuuri,Tuuski,Tuusniemi,Ulvila,Unari,Upinniemi,Utti,Uusikaupunki,Vaaksy,Vaalimaa,Vaarinmaja,Vaasa,Vainikkala,Valga,Valkeakoski,Vantaa,Varkaus,Vehkapera,Vehmasmaki,Vieki,Vierumaki,Viitasaari,Viljandi,Vilppula,Viohma,Vioru,Virrat,Ylike,Ylivieska,Ylojarvi"}, - {name: "Korean", i: 10, min: 5, max: 11, d: "", m: 0, b: "Anjung,Ansan,Anseong,Anyang,Aphae,Apo,Baekseok,Baeksu,Beolgyo,Boeun,Boseong,Busan,Buyeo,Changnyeong,Changwon,Cheonan,Cheongdo,Cheongjin,Cheongsong,Cheongyang,Cheorwon,Chirwon,Chuncheon,Chungju,Daedeok,Daegaya,Daejeon,Damyang,Dangjin,Dasa,Donghae,Dongsong,Doyang,Eonyang,Gaeseong,Ganggyeong,Ganghwa,Gangneung,Ganseong,Gaun,Geochang,Geoje,Geoncheon,Geumho,Geumil,Geumwang,Gijang,Gimcheon,Gimhwa,Gimje,Goa,Gochang,Gohan,Gongdo,Gongju,Goseong,Goyang,Gumi,Gunpo,Gunsan,Guri,Gurye,Gwangju,Gwangyang,Gwansan,Gyeongseong,Hadong,Hamchang,Hampyeong,Hamyeol,Hanam,Hapcheon,Hayang,Heungnam,Hongnong,Hongseong,Hwacheon,Hwando,Hwaseong,Hwasun,Hwawon,Hyangnam,Incheon,Inje,Iri,Janghang,Jangheung,Jangseong,Jangseungpo,Jangsu,Jecheon,Jeju,Jeomchon,Jeongeup,Jeonggwan,Jeongju,Jeongok,Jeongseon,Jeonju,Jido,Jiksan,Jinan,Jincheon,Jindo,Jingeon,Jinjeop,Jinnampo,Jinyeong,Jocheon,Jochiwon,Jori,Maepo,Mangyeong,Mokpo,Muju,Munsan,Naesu,Naju,Namhae,Namwon,Namyang,Namyangju,Nongong,Nonsan,Ocheon,Okcheon,Okgu,Onam,Onsan,Onyang,Opo,Paengseong,Pogok,Poseung,Pungsan,Pyeongchang,Pyeonghae,Pyeongyang,Sabi,Sacheon,Samcheok,Samho,Samrye,Sancheong,Sangdong,Sangju,Sapgyo,Sariwon,Sejong,Seocheon,Seogwipo,Seonghwan,Seongjin,Seongju,Seongnam,Seongsan,Seosan,Seungju,Siheung,Sindong,Sintaein,Soheul,Sokcho,Songak,Songjeong,Songnim,Songtan,Suncheon,Taean,Taebaek,Tongjin,Uijeongbu,Uiryeong,Uiwang,Uljin,Ulleung,Unbong,Ungcheon,Ungjin,Waegwan,Wando,Wayang,Wiryeseong,Wondeok,Yangju,Yangsan,Yangyang,Yecheon,Yeomchi,Yeoncheon,Yeongam,Yeongcheon,Yeongdeok,Yeongdong,Yeonggwang,Yeongju,Yeongwol,Yeongyang,Yeonil,Yongin,Yongjin,Yugu"}, - {name: "Chinese", i: 11, min: 5, max: 10, d: "", m: 0, b: "Anding,Anlu,Anqing,Anshun,Baixing,Banyang,Baoqing,Binzhou,Caozhou,Changbai,Changchun,Changde,Changling,Changsha,Changzhou,Chengdu,Chenzhou,Chizhou,Chongqing,Chuxiong,Chuzhou,Dading,Daming,Datong,Daxing,Dengzhou,Deqing,Dihua,Dingli,Dongan,Dongchang,Dongchuan,Dongping,Duyun,Fengtian,Fengxiang,Fengyang,Fenzhou,Funing,Fuzhou,Ganzhou,Gaoyao,Gaozhou,Gongchang,Guangnan,Guangning,Guangping,Guangxin,Guangzhou,Guiyang,Hailong,Hangzhou,Hanyang,Hanzhong,Heihe,Hejian,Henan,Hengzhou,Hezhong,Huaian,Huaiqing,Huanglong,Huangzhou,Huining,Hulan,Huzhou,Jiading,Jian,Jianchang,Jiangning,Jiankang,Jiaxing,Jiayang,Jilin,Jinan,Jingjiang,Jingzhao,Jinhua,Jinzhou,Jiujiang,Kaifeng,Kaihua,Kangding,Kuizhou,Laizhou,Lianzhou,Liaoyang,Lijiang,Linan,Linhuang,Lintao,Liping,Liuzhou,Longan,Longjiang,Longxing,Luan,Lubin,Luzhou,Mishan,Nanan,Nanchang,Nandian,Nankang,Nanyang,Nenjiang,Ningbo,Ningguo,Ningwu,Ningxia,Ningyuan,Pingjiang,Pingliang,Pingyang,Puer,Puzhou,Qianzhou,Qingyang,Qingyuan,Qingzhou,Qujing,Quzhou,Raozhou,Rende,Ruian,Ruizhou,Shafeng,Shajing,Shaoqing,Shaowu,Shaoxing,Shaozhou,Shinan,Shiqian,Shouchun,Shuangcheng,Shulei,Shunde,Shuntian,Shuoping,Sicheng,Sinan,Sizhou,Songjiang,Suiding,Suihua,Suining,Suzhou,Taian,Taibei,Taiping,Taiwan,Taiyuan,Taizhou,Taonan,Tengchong,Tingzhou,Tongchuan,Tongqing,Tongzhou,Weihui,Wensu,Wenzhou,Wuchang,Wuding,Wuzhou,Xian,Xianchun,Xianping,Xijin,Xiliang,Xincheng,Xingan,Xingde,Xinghua,Xingjing,Xingyi,Xingyuan,Xingzhong,Xining,Xinmen,Xiping,Xuanhua,Xunzhou,Xuzhou,Yanan,Yangzhou,Yanji,Yanping,Yanzhou,Yazhou,Yichang,Yidu,Yilan,Yili,Yingchang,Yingde,Yingtian,Yingzhou,Yongchang,Yongping,Yongshun,Yuanzhou,Yuezhou,Yulin,Yunnan,Yunyang,Zezhou,Zhang,Zhangzhou,Zhaoqing,Zhaotong,Zhenan,Zhending,Zhenhai,Zhenjiang,Zhenxi,Zhenyun,Zhongshan,Zunyi"}, - {name: "Japanese", i: 12, min: 4, max: 10, d: "", m: 0, b: "Abira,Aga,Aikawa,Aizumisato,Ajigasawa,Akkeshi,Amagi,Ami,Ando,Asakawa,Ashikita,Bandai,Biratori,Chonan,Esashi,Fuchu,Fujimi,Funagata,Genkai,Godo,Goka,Gonohe,Gyokuto,Haboro,Hamatonbetsu,Harima,Hashikami,Hayashima,Heguri,Hidaka,Higashiura,Hiranai,Hirogawa,Hiroo,Hodatsushimizu,Hoki,Hokuei,Hokuryu,Horokanai,Ibigawa,Ichikai,Ichikawa,Ichinohe,Iijima,Iizuna,Ikawa,Inagawa,Itakura,Iwaizumi,Iwate,Kaisei,Kamifurano,Kamiita,Kamijima,Kamikawa,Kamishihoro,Kamiyama,Kanda,Kanna,Kasagi,Kasuya,Katsuura,Kawabe,Kawamoto,Kawanehon,Kawanishi,Kawara,Kawasaki,Kawatana,Kawazu,Kihoku,Kikonai,Kin,Kiso,Kitagata,Kitajima,Kiyama,Kiyosato,Kofu,Koge,Kohoku,Kokonoe,Kora,Kosa,Kotohira,Kudoyama,Kumejima,Kumenan,Kumiyama,Kunitomi,Kurate,Kushimoto,Kutchan,Kyonan,Kyotamba,Mashike,Matsumae,Mifune,Mihama,Minabe,Minami,Minamiechizen,Minamitane,Misaki,Misasa,Misato,Miyashiro,Miyoshi,Mori,Moseushi,Mutsuzawa,Nagaizumi,Nagatoro,Nagayo,Nagomi,Nakadomari,Nakanojo,Nakashibetsu,Namegawa,Nanbu,Nanporo,Naoshima,Nasu,Niseko,Nishihara,Nishiizu,Nishikatsura,Nishikawa,Nishinoshima,Nishiwaga,Nogi,Noto,Nyuzen,Oarai,Obuse,Odai,Ogawara,Oharu,Oirase,Oishida,Oiso,Oizumi,Oji,Okagaki,Okutama,Omu,Ono,Osaka,Otobe,Otsuki,Owani,Reihoku,Rifu,Rikubetsu,Rishiri,Rokunohe,Ryuo,Saka,Sakuho,Samani,Satsuma,Sayo,Saza,Setana,Shakotan,Shibayama,Shikama,Shimamoto,Shimizu,Shintomi,Shirakawa,Shisui,Shitara,Sobetsu,Sue,Sumita,Suooshima,Suttsu,Tabuse,Tachiarai,Tadami,Tadaoka,Taiji,Taiki,Takachiho,Takahama,Taketoyo,Taragi,Tateshina,Tatsugo,Tawaramoto,Teshikaga,Tobe,Tokigawa,Toma,Tomioka,Tonosho,Tosa,Toyokoro,Toyotomi,Toyoyama,Tsubata,Tsubetsu,Tsukigata,Tsuno,Tsuwano,Umi,Wakasa,Yamamoto,Yamanobe,Yamatsuri,Yanaizu,Yasuda,Yoichi,Yonaguni,Yoro,Yoshino,Yubetsu,Yugawara,Yuni,Yusuhara,Yuza"}, - {name: "Portuguese", i: 13, min: 5, max: 11, d: "", m: .1, b: "Abrigada,Afonsoeiro,Agueda,Aguilada,Alagoas,Alagoinhas,Albufeira,Alcanhoes,Alcobaca,Alcoutim,Aldoar,Alenquer,Alfeizerao,Algarve,Almada,Almagreira,Almeirim,Alpalhao,Alpedrinha,Alvorada,Amieira,Anapolis,Apelacao,Aranhas,Arganil,Armacao,Assenceira,Aveiro,Avelar,Balsas,Barcarena,Barreiras,Barretos,Batalha,Beira,Benavente,Betim,Braga,Braganca,Brasilia,Brejo,Cabeceiras,Cabedelo,Cachoeiras,Cadafais,Calhandriz,Calheta,Caminha,Campinas,Canidelo,Canoas,Capinha,Carmoes,Cartaxo,Carvalhal,Carvoeiro,Cascavel,Castanhal,Caxias,Chapadinha,Chaves,Cocais,Coentral,Coimbra,Comporta,Conde,Coqueirinho,Coruche,Damaia,Dourados,Enxames,Ericeira,Ervidel,Escalhao,Esmoriz,Espinhal,Estela,Estoril,Eunapolis,Evora,Famalicao,Fanhoes,Faro,Fatima,Felgueiras,Ferreira,Figueira,Flecheiras,Florianopolis,Fornalhas,Fortaleza,Freiria,Freixeira,Fronteira,Fundao,Gracas,Gradil,Grainho,Gralheira,Guimaraes,Horta,Ilhavo,Ilheus,Lages,Lagos,Laranjeiras,Lavacolhos,Leiria,Limoeiro,Linhares,Lisboa,Lomba,Lorvao,Lourical,Lourinha,Luziania,Macedo,Machava,Malveira,Marinhais,Maxial,Mealhada,Milharado,Mira,Mirandela,Mogadouro,Montalegre,Mourao,Nespereira,Nilopolis,Obidos,Odemira,Odivelas,Oeiras,Oleiros,Olhalvo,Olinda,Olival,Oliveira,Oliveirinha,Palheiros,Palmeira,Palmital,Pampilhosa,Pantanal,Paradinha,Parelheiros,Pedrosinho,Pegoes,Penafiel,Peniche,Pinhao,Pinheiro,Pombal,Pontal,Pontinha,Portel,Portimao,Quarteira,Queluz,Ramalhal,Reboleira,Recife,Redinha,Ribadouro,Ribeira,Ribeirao,Rosais,Sabugal,Sacavem,Sagres,Sandim,Sangalhos,Santarem,Santos,Sarilhos,Seixas,Seixezelo,Seixo,Silvares,Silveira,Sinhaem,Sintra,Sobral,Sobralinho,Tabuaco,Tabuleiro,Taveiro,Teixoso,Telhado,Telheiro,Tomar,Torreira,Trancoso,Troviscal,Vagos,Varzea,Velas,Viamao,Viana,Vidigal,Vidigueira,Vidual,Vilamar,Vimeiro,Vinhais,Vitoria"}, - {name: "Nahuatl", i: 14, min: 6, max: 13, d: "l", m: 0, b: "Acapulco,Acatepec,Acatlan,Acaxochitlan,Acolman,Actopan,Acuamanala,Ahuacatlan,Almoloya,Amacuzac,Amanalco,Amaxac,Apaxco,Apetatitlan,Apizaco,Atenco,Atizapan,Atlacomulco,Atlapexco,Atotonilco,Axapusco,Axochiapan,Axocomanitla,Axutla,Azcapotzalco,Aztahuacan,Calimaya,Calnali,Calpulalpan,Camotlan,Capulhuac,Chalco,Chapulhuacan,Chapultepec,Chiapan,Chiautempan,Chiconautla,Chihuahua,Chilcuautla,Chimalhuacan,Cholollan,Cihuatlan,Coahuila,Coatepec,Coatetelco,Coatlan,Coatlinchan,Coatzacoalcos,Cocotitlan,Cohetzala,Colima,Colotlan,Coyoacan,Coyohuacan,Cuapiaxtla,Cuauhnahuac,Cuauhtemoc,Cuauhtitlan,Cuautepec,Cuautla,Cuaxomulco,Culhuacan,Ecatepec,Eloxochitlan,Epatlan,Epazoyucan,Huamantla,Huascazaloya,Huatlatlauca,Huautla,Huehuetlan,Huehuetoca,Huexotla,Hueyapan,Hueyotlipan,Hueypoxtla,Huichapan,Huimilpan,Huitzilac,Ixtapallocan,Iztacalco,Iztaccihuatl,Iztapalapa,Lolotla,Malinalco,Mapachtlan,Mazatepec,Mazatlan,Metepec,Metztitlan,Mexico,Miacatlan,Michoacan,Minatitlan,Mixcoac,Mixtla,Molcaxac,Nanacamilpa,Naucalpan,Naupan,Nextlalpan,Nezahualcoyotl,Nopalucan,Oaxaca,Ocotepec,Ocotitlan,Ocotlan,Ocoyoacac,Ocuilan,Ocuituco,Omitlan,Otompan,Otzoloapan,Pacula,Pahuatlan,Panotla,Papalotla,Patlachican,Piaztla,Popocatepetl,Sultepec,Tecamac,Tecolotlan,Tecozautla,Temamatla,Temascalapa,Temixco,Temoac,Temoaya,Tenayuca,Tenochtitlan,Teocuitlatlan,Teotihuacan,Teotlalco,Tepeacac,Tepeapulco,Tepehuacan,Tepetitlan,Tepeyanco,Tepotzotlan,Tepoztlan,Tetecala,Tetlatlahuca,Texcalyacac,Texcoco,Tezontepec,Tezoyuca,Timilpan,Tizapan,Tizayuca,Tlacopan,Tlacotenco,Tlahuac,Tlahuelilpan,Tlahuiltepa,Tlalmanalco,Tlalnepantla,Tlalpan,Tlanchinol,Tlatelolco,Tlaxcala,Tlaxcoapan,Tlayacapan,Tocatlan,Tolcayuca,Toluca,Tonanitla,Tonantzintla,Tonatico,Totolac,Totolapan,Tototlan,Tuchtlan,Tulantepec,Tultepec,Tzompantepec,Xalatlaco,Xaloztoc,Xaltocan,Xiloxoxtla,Xochiatipan,Xochicoatlan,Xochimilco,Xochitepec,Xolotlan,Xonacatlan,Yahualica,Yautepec,Yecapixtla,Yehaultepec,Zacatecas,Zacazonapan,Zacoalco,Zacualpan,Zacualtipan,Zapotlan,Zimapan,Zinacantepec,Zoyaltepec,Zumpahuacan"}, - {name: "Hungarian", i: 15, min: 6, max: 13, d: "", m: 0.1, b: "Aba,Abadszalok,Adony,Ajak,Albertirsa,Alsozsolca,Aszod,Babolna,Bacsalmas,Baktaloranthaza,Balassagyarmat,Balatonalmadi,Balatonboglar,Balkany,Balmazujvaros,Barcs,Bataszek,Batonyterenye,Battonya,Bekes,Berettyoujfalu,Berhida,Biatorbagy,Bicske,Biharkeresztes,Bodajk,Boly,Bonyhad,Budakalasz,Budakeszi,Celldomolk,Csakvar,Csenger,Csongrad,Csorna,Csorvas,Csurgo,Dabas,Demecser,Derecske,Devavanya,Devecser,Dombovar,Dombrad,Dunafoldvar,Dunaharaszti,Dunavarsany,Dunavecse,Edeleny,Elek,Emod,Encs,Enying,Ercsi,Fegyvernek,Fehergyarmat,Felsozsolca,Fertoszentmiklos,Fonyod,Fot,Fuzesabony,Fuzesgyarmat,Gardony,God,Gyal,Gyomaendrod,Gyomro,Hajdudorog,Hajduhadhaz,Hajdusamson,Hajduszoboszlo,Halasztelek,Harkany,Hatvan,Heves,Heviz,Ibrany,Isaszeg,Izsak,Janoshalma,Janossomorja,Jaszapati,Jaszarokszallas,Jaszfenyszaru,Jaszkiser,Kaba,Kalocsa,Kapuvar,Karcag,Kecel,Kemecse,Kenderes,Kerekegyhaza,Keszthely,Kisber,Kiskunmajsa,Kistarcsa,Kistelek,Kisujszallas,Kisvarda,Komadi,Komarom,Komlo,Kormend,Korosladany,Koszeg,Kozarmisleny,Kunhegyes,Kunszentmarton,Kunszentmiklos,Labatlan,Lajosmizse,Lenti,Letavertes,Letenye,Lorinci,Maglod,Mako,Mandok,Marcali,Martonvasar,Mateszalka,Melykut,Mezobereny,Mezocsat,Mezohegyes,Mezokeresztes,Mezokovesd,Mezotur,Mindszent,Mohacs,Monor,Mor,Morahalom,Nadudvar,Nagyatad,Nagyecsed,Nagyhalasz,Nagykallo,Nagykoros,Nagymaros,Nyekladhaza,Nyergesujfalu,Nyirbator,Nyirmada,Nyirtelek,Ocsa,Orkeny,Oroszlany,Paks,Pannonhalma,Paszto,Pecel,Pecsvarad,Pilisvorosvar,Polgar,Polgardi,Pomaz,Puspokladany,Pusztaszabolcs,Putnok,Racalmas,Rackeve,Rakamaz,Rakoczifalva,Sajoszent,Sandorfalva,Sarbogard,Sarkad,Sarospatak,Sarvar,Satoraljaujhely,Siklos,Simontornya,Soltvadkert,Sumeg,Szabadszallas,Szarvas,Szazhalombatta,Szecseny,Szeghalom,Szentgotthard,Szentlorinc,Szerencs,Szigethalom,Szigetvar,Szikszo,Tab,Tamasi,Tapioszele,Tapolca,Teglas,Tet,Tiszafoldvar,Tiszafured,Tiszakecske,Tiszalok,Tiszaujvaros,Tiszavasvari,Tokaj,Tokol,Tompa,Torokbalint,Torokszentmiklos,Totkomlos,Tura,Turkeve,Ujkigyos,ujszasz,Vamospercs,Varpalota,Vasarosnameny,Vasvar,Vecses,Veresegyhaz,Verpelet,Veszto,Zahony,Zalaszentgrot,Zirc,Zsambek"}, - {name: "Turkish", i: 16, min: 4, max: 10, d: "", m: 0, b: "Yelkaya,Buyrukkaya,Erdemtepe,Alakesen,Baharbeyli,Bozbay,Karaoklu,Altunbey,Yalkale,Yalkut,Akardere,Altayburnu,Esentepe,Okbelen,Derinsu,Alaoba,Yamanbeyli,Aykor,Ekinova,Saztepe,Baharkale,Devrekdibi,Alpseki,Ormanseki,Erkale,Yalbelen,Aytay,Yamanyaka,Altaydelen,Esen,Yedieli,Alpkor,Demirkor,Yediyol,Erdemkaya,Yayburnu,Ganiler,Bayatyurt,Kopuzteke,Aytepe,Deniz,Ayan,Ayazdere,Tepe,Kayra,Ayyaka,Deren,Adatepe,Kalkaneli,Bozkale,Yedidelen,Kocayolu,Sazdere,Bozkesen,Oguzeli,Yayladibi,Uluyol,Altay,Ayvar,Alazyaka,Yaloba,Suyaka,Baltaberi,Poyrazdelen,Eymir,Yediyuva,Kurt,Yeltepe,Oktar,Kara Ok,Ekinberi,Er Yurdu,Eren,Erenler,Ser,Oguz,Asay,Bozokeli,Aykut,Ormanyol,Yazkaya,Kalkanova,Yazbeyli,Dokuz Teke,Bilge,Ertensuyu,Kopuzyuva,Buyrukkut,Akardiken,Aybaray,Aslanbeyli,Altun Kaynak,Atikobasi,Yayla Eli,Kor Tepe,Salureli,Kor Kaya,Aybarberi,Kemerev,Yanaray,Beydileli,Buyrukoba,Yolduman,Tengri Tepe,Dokuzsu,Uzunkor,Erdem Yurdu,Kemer,Korteke,Bozokev,Bozoba,Ormankale,Askale,Oguztoprak,Yolberi,Kumseki,Esenobasi,Turkbelen,Ayazseki,Cereneli,Taykut,Bayramdelen,Beydilyaka,Boztepe,Uluoba,Yelyaka,Ulgardiken,Esensu,Baykale,Cerenkor,Bozyol,Duranoba,Aladuman,Denizli,Bahar,Yarkesen,Dokuzer,Yamankaya,Kocatarla,Alayaka,Toprakeli,Sarptarla,Sarpkoy,Serkaynak,Adayaka,Ayazkaynak,Kopuz,Turk,Kart,Kum,Erten,Buyruk,Yel,Ada,Alazova,Ayvarduman,Buyrukok,Ayvartoprak,Uzuntepe,Binseki,Yedibey,Durankale,Alaztoprak,Sarp Ok,Yaparobasi,Yaytepe,Asberi,Kalkankor,Beydiltepe,Adaberi,Bilgeyolu,Ganiyurt,Alkanteke,Esenerler,Asbey,Erdemkale,Erenkaynak,Oguzkoyu,Ayazoba,Boynuztoprak,Okova,Yaloklu,Sivriberi,Yuladiken,Sazbey,Karakaynak,Kopuzkoyu,Buyrukay,Kocakaya,Tepeduman,Yanarseki,Atikyurt,Esenev,Akarbeyli,Yayteke,Devreksungur,Akseki,Baykut,Kalkandere,Ulgarova,Devrekev,Yulabey,Bayatev,Yazsu,Vuraleli,Sivribeyli,Alaova,Alpobasi,Yalyurt,Elmatoprak,Alazkaynak,Esenay,Ertenev,Salurkor,Ekinok,Yalbey,Yeldere,Ganibay,Altaykut,Baltaboy,Ereli,Ayvarsu,Uzunsaz,Bayeli,Erenyol,Kocabay,Derintay,Ayazyol,Aslanoba,Esenkaynak,Ekinlik,Alpyolu,Alayunt,Bozeski,Erkil,Duransuyu,Yulak,Kut,Dodurga,Kutlubey,Kutluyurt,Boynuz,Alayol,Aybar,Aslaneli,Kemerseki,Baltasuyu,Akarer,Ayvarburnu,Boynuzbeyli,Adasungur,Esenkor,Yamanoba,Toprakkor,Uzunyurt,Sungur,Bozok,Kemerli,Alaz,Demirci,Kartepe"}, - {name: "Berber", i: 17, min: 4, max: 10, d: "s", m: .2, b: "Abkhouch,Adrar,Aeraysh,Afrag,Agadir,Agelmam,Aghmat,Agrakal,Agulmam,Ahaggar,Ait Baha,Ajdir,Akka,Almou,Amegdul,Amizmiz,Amknas,Amlil,Amurakush,Anfa,Annaba,Aousja,Arbat,Arfud,Argoub,Arif,Asfi,Asfru,Ashawen,Assamer,Assif,Awlluz,Ayt Melel,Azaghar,Azila,Azilal,Azmour,Azro,Azrou,Beccar,Beja,Bennour,Benslimane,Berkane,Berrechid,Bizerte,Bjaed,Bouayach,Boudenib,Boufrah,Bouskoura,Boutferda,Darallouch,Dar Bouazza,Darchaabane,Dcheira,Demnat,Denden,Djebel,Djedeida,Drargua,Elhusima,Essaouira,Ezzahra,Fas,Fnideq,Ghezeze,Goubellat,Grisaffen,Guelmim,Guercif,Hammamet,Harrouda,Hdifa,Hoceima,Houara,Idhan,Idurar,Ifendassen,Ifoghas,Ifrane,Ighoud,Ikbir,Imilchil,Imzuren,Inezgane,Irherm,Izoughar,Jendouba,Kacem,Kelibia,Kenitra,Kerrando,Khalidia,Khemisset,Khenifra,Khouribga,Khourigba,Kidal,Korba,Korbous,Lahraouyine,Larache,Leyun,Lqliaa,Manouba,Martil,Mazagan,Mcherga,Mdiq,Megrine,Mellal,Melloul,Midelt,Misur,Mohammedia,Mornag,Mrirt,Nabeul,Nadhour,Nador,Nawaksut,Nefza,Ouarzazate,Ouazzane,Oued Zem,Oujda,Ouladteima,Qsentina,Rades,Rafraf,Safi,Sefrou,Sejnane,Settat,Sijilmassa,Skhirat,Slimane,Somaa,Sraghna,Susa,Tabarka,Tadrart,Taferka,Tafilalt,Tafrawt,Tafza,Tagbalut,Tagerdayt,Taghzut,Takelsa,Taliouine,Tanja,Tantan,Taourirt,Targuist,Taroudant,Tarudant,Tasfelalayt,Tassort,Tata,Tattiwin,Tawnat,Taza,Tazagurt,Tazerka,Tazizawt,Taznakht,Tebourba,Teboursouk,Temara,Testour,Tetouan,Tibeskert,Tifelt,Tijdit,Tinariwen,Tinduf,Tinja,Tittawan,Tiznit,Toubkal,Trables,Tubqal,Tunes,Ultasila,Urup,Wagguten,Wararni,Warzazat,Watlas,Wehran,Wejda,Xamida,Yedder,Youssoufia,Zaghouan,Zahret,Zemmour,Zriba"}, - {name: "Arabic", i: 18, min: 4, max: 9, d: "ae", m: .2, b: "Abha,Ajman,Alabar,Alarjam,Alashraf,Alawali,Albawadi,Albirk,Aldhabiyah,Alduwaid,Alfareeq,Algayed,Alhazim,Alhrateem,Alhudaydah,Alhuwaya,Aljahra,Aljubail,Alkhafah,Alkhalas,Alkhawaneej,Alkhen,Alkhobar,Alkhuznah,Allisafah,Almshaykh,Almurjan,Almuwayh,Almuzaylif,Alnaheem,Alnashifah,Alqah,Alqouz,Alqurayyat,Alradha,Alraqmiah,Alsadyah,Alsafa,Alshagab,Alshuqaiq,Alsilaa,Althafeer,Alwasqah,Amaq,Amran,Annaseem,Aqbiyah,Arafat,Arar,Ardah,Asfan,Ashayrah,Askar,Ayaar,Aziziyah,Baesh,Bahrah,Balhaf,Banizayd,Bidiyah,Bisha,Biyatah,Buqhayq,Burayda,Dafiyat,Damad,Dammam,Dariyah,Dhafar,Dhahran,Dhalkut,Dhurma,Dibab,Doha,Dukhan,Duwaibah,Enaker,Fadhla,Fahaheel,Fanateer,Farasan,Fardah,Fujairah,Ghalilah,Ghar,Ghizlan,Ghomgyah,Ghran,Hadiyah,Haffah,Hajanbah,Hajrah,Haqqaq,Haradh,Hasar,Hawiyah,Hebaa,Hefar,Hijal,Husnah,Huwailat,Huwaitah,Irqah,Isharah,Ithrah,Jamalah,Jarab,Jareef,Jazan,Jeddah,Jiblah,Jihanah,Jilah,Jizan,Joraibah,Juban,Jumeirah,Kamaran,Keyad,Khab,Khaiybar,Khasab,Khathirah,Khawarah,Khulais,Kumzar,Limah,Linah,Madrak,Mahab,Mahalah,Makhtar,Mashwar,Masirah,Masliyah,Mastabah,Mazhar,Medina,Meeqat,Mirbah,Mokhtara,Muharraq,Muladdah,Musaykah,Mushayrif,Musrah,Mussafah,Nafhan,Najran,Nakhab,Nizwa,Oman,Qadah,Qalhat,Qamrah,Qasam,Qosmah,Qurain,Quriyat,Qurwa,Radaa,Rafha,Rahlah,Rakamah,Rasheedah,Rasmadrakah,Risabah,Rustaq,Ryadh,Sabtaljarah,Sadah,Safinah,Saham,Saihat,Salalah,Salmiya,Shabwah,Shalim,Shaqra,Sharjah,Sharurah,Shatifiyah,Shidah,Shihar,Shoqra,Shuwaq,Sibah,Sihmah,Sinaw,Sirwah,Sohar,Suhailah,Sulaibiya,Sunbah,Tabuk,Taif,Taqah,Tarif,Tharban,Thuqbah,Thuwal,Tubarjal,Turaif,Turbah,Tuwaiq,Ubar,Umaljerem,Urayarah,Urwah,Wabrah,Warbah,Yabreen,Yadamah,Yafur,Yarim,Yemen,Yiyallah,Zabid,Zahwah,Zallaq,Zinjibar,Zulumah"}, - {name: "Inuit", i: 19, min: 5, max: 15, d: "alutsn", m: 0, b: "Aaluik,Aappilattoq,Aasiaat,Agissat,Agssaussat,Akuliarutsip,Akunnaaq,Alluitsup,Alluttoq,Amitsorsuaq,Ammassalik,Anarusuk,Anguniartarfik,Annertussoq,Annikitsoq,Apparsuit,Apusiaajik,Arsivik,Arsuk,Atammik,Ateqanaq,Atilissuaq,Attu,Augpalugtoq,Aukarnersuaq,Aumat,Auvilkikavsaup,Avadtlek,Avallersuaq,Bjornesk,Blabaerdalen,Blomsterdalen,Brattalhid,Bredebrae,Brededal,Claushavn,Edderfulegoer,Egger,Eqalugalinnguit,Eqalugarssuit,Eqaluit,Eqqua,Etah,Graah,Hakluyt,Haredalen,Hareoen,Hundeo,Igaliku,Igdlorssuit,Igdluluarssuk,Iginniafik,Ikamiut,Ikarissat,Ikateq,Ikermiut,Ikermoissuaq,Ikorfarssuit,Ilimanaq,Illorsuit,Illunnguit,Iluileq,Ilulissat,Imaarsivik,Imartunarssuk,Immikkoortukajik,Innaarsuit,Inneruulalik,Inussullissuaq,Iperaq,Ippik,Iqek,Isortok,Isungartussoq,Itileq,Itissaalik,Itivdleq,Ittit,Ittoqqortoormiit,Ivingmiut,Ivittuut,Kanajoorartuut,Kangaamiut,Kangeq,Kangerluk,Kangerlussuaq,Kanglinnguit,Kapisillit,Kekertamiut,Kiatak,Kiataussaq,Kigatak,Kinaussak,Kingittorsuaq,Kitak,Kitsissuarsuit,Kitsissut,Klenczner,Kook,Kraulshavn,Kujalleq,Kullorsuaq,Kulusuk,Kuurmiit,Kuusuaq,Laksedalen,Maniitsoq,Marrakajik,Mattaangassut,Mernoq,Mittivakkat,Moriusaq,Myggbukta,Naajaat,Nangissat,Nanuuseq,Nappassoq,Narsarmijt,Narsarsuaq,Narssaq,Nasiffik,Natsiarsiorfik,Naujanguit,Niaqornaarsuk,Niaqornat,Nordfjordspasset,Nugatsiaq,Nunarssit,Nunarsuaq,Nunataaq,Nunatakavsaup,Nutaarmiut,Nuugaatsiaq,Nuuk,Nuukullak,Olonkinbyen,Oodaaq,Oqaatsut,Oqaitsunguit,Oqonermiut,Paagussat,Paamiut,Paatuut,Palungataq,Pamialluk,Perserajoq,Pituffik,Puugutaa,Puulkuip,Qaanaq,Qaasuitsup,Qaersut,Qajartalik,Qallunaat,Qaneq,Qaqortok,Qasigiannguit,Qassimiut,Qeertartivaq,Qeqertaq,Qeqertasussuk,Qeqqata,Qernertoq,Qernertunnguit,Qianarreq,Qingagssat,Qoornuup,Qorlortorsuaq,Qullikorsuit,Qunnerit,Qutdleq,Ravnedalen,Ritenbenk,Rypedalen,Saarloq,Saatorsuaq,Saattut,Salliaruseq,Sammeqqat,Sammisoq,Sanningassoq,Saqqaq,Saqqarlersuaq,Saqqarliit,Sarfannguit,Sattiaatteq,Savissivik,Serfanguaq,Sermersooq,Sermiligaaq,Sermilik,Sermitsiaq,Simitakaja,Simiutaq,Singamaq,Siorapaluk,Sisimiut,Sisuarsuit,Sullorsuaq,Suunikajik,Sverdrup,Taartoq,Takiseeq,Tasirliaq,Tasiusak,Tiilerilaaq,Timilersua,Timmiarmiut,Tukingassoq,Tussaaq,Tuttulissuup,Tuujuk,Uiivaq,Uilortussoq,Ujuaakajiip,Ukkusissat,Upernavik,Uttorsiutit,Uumannaq,Uunartoq,Uvkusigssat,Ymer"}, - {name: "Basque", i: 20, min: 4, max: 11, d: "r", m: .1, b: "Agurain,Aia,Aiara,Albiztur,Alkiza,Altzaga,Amorebieta,Amurrio,Andoain,Anoeta,Antzuola,Arakaldo,Arantzazu,Arbatzegi,Areatza,Arratzua,Arrieta,Artea,Artziniega,Asteasu,Astigarraga,Ataun,Atxondo,Aulesti,Azkoitia,Azpeitia,Bakio,Baliarrain,Barakaldo,Barrika,Barrundia,Basauri,Beasain,Bedia,Beizama,Belauntza,Berastegi,Bergara,Bermeo,Bernedo,Berriatua,Berriz,Bidania,Bilar,Bilbao,Busturia,Deba,Derio,Donostia,Dulantzi,Durango,Ea,Eibar,Elantxobe,Elduain,Elgeta,Elgoibar,Elorrio,Erandio,Ergoitia,Ermua,Errenteria,Errezil,Eskoriatza,Eskuernaga,Etxebarri,Etxebarria,Ezkio,Forua,Gabiria,Gaintza,Galdakao,Gamiz,Garai,Gasteiz,Gatzaga,Gaubea,Gautegiz,Gaztelu,Gernika,Gerrikaitz,Getaria,Getxo,Gizaburuaga,Goiatz,Gorliz,Gorriaga,Harana,Hernani,Hondarribia,Ibarra,Ibarrangelu,Idiazabal,Iekora,Igorre,Ikaztegieta,Irun,Irura,Iruraiz,Itsaso,Itsasondo,Iurreta,Izurtza,Jatabe,Kanpezu,Karrantza,Kortezubi,Kripan,Kuartango,Lanestosa,Lantziego,Larrabetzu,Lasarte,Laukiz,Lazkao,Leaburu,Legazpi,Legorreta,Legutio,Leintz,Leioa,Lekeitio,Lemoa,Lemoiz,Leza,Lezama,Lezo,Lizartza,Maeztu,Mallabia,Manaria,Markina,Maruri,Menaka,Mendaro,Mendata,Mendexa,Morga,Mundaka,Mungia,Munitibar,Murueta,Muskiz,Mutiloa,Mutriku,Nabarniz,Oiartzun,Oion,Okondo,Olaberria,Onati,Ondarroa,Ordizia,Orendain,Orexa,Oria,Orio,Ormaiztegi,Orozko,Ortuella,Otegi,Otxandio,Pasaia,Plentzia,Santurtzi,Sestao,Sondika,Soraluze,Sukarrieta,Tolosa,Trapagaran,Turtzioz,Ubarrundia,Ubide,Ugao,Urdua,Urduliz,Urizaharra,Urkabustaiz,Urnieta,Urretxu,Usurbil,Xemein,Zabaleta,Zaia,Zaldibar,Zambrana,Zamudio,Zaratamo,Zarautz,Zeberio,Zegama,Zerain,Zestoa,Zierbena,Zigoitia,Ziortza,Zuia,Zumaia,Zumarraga"}, - {name: "Nigerian", i: 21, min: 4, max: 10, d: "", m: .3, b: "Abadogo,Abafon,Adealesu,Adeto,Adyongo,Afaga,Afamju,Agigbigi,Agogoke,Ahute,Aiyelaboro,Ajebe,Ajola,Akarekwu,Akunuba,Alawode,Alkaijji,Amangam,Amgbaye,Amtasa,Amunigun,Animahun,Anyoko,Arapagi,Asande,Awgbagba,Awhum,Awodu,Babateduwa,Bandakwai,Bangdi,Bilikani,Birnindodo,Braidu,Bulakawa,Buriburi,Cainnan,Chakum,Chondugh,Dagwarga,Darpi,Dokatofa,Dozere,Ebelibri,Efem,Ekoku,Ekpe,Ewhoeviri,Galea,Gamen,Ganjin,Gantetudu,Gargar,Garinbode,Gbure,Gerti,Gidan,Gitabaremu,Giyagiri,Giyawa,Gmawa,Golakochi,Golumba,Gunji,Gwambula,Gwodoti,Hayinlere,Hayinmaialewa,Hirishi,Hombo,Ibefum,Iberekodo,Icharge,Idofin,Idofinoka,Igbogo,Ijoko,Ijuwa,Ikawga,Ikhin,Ikpakidout,Ikpeoniong,Imuogo,Ipawo,Ipinlerere,Isicha,Itakpa,Jangi,Jare,Jataudakum,Jaurogomki,Jepel,Kafinmalama,Katab,Katanga,Katinda,Katirije,Kaurakimba,Keffinshanu,Kellumiri,Kiagbodor,Kirbutu,Kita,Kogogo,Kopje,Korokorosei,Kotoku,Kuata,Kujum,Kukau,Kunboon,Kuonubogbene,Kurawe,Kushinahu,Kwaramakeri,Ladimeji,Lafiaro,Lahaga,Laindebajanle,Laindegoro,Lakati,Litenswa,Maba,Madarzai,Maianita,Malikansaa,Mata,Megoyo,Meku,Miama,Modi,Mshi,Msugh,Muduvu,Murnachehu,Namnai,Ndamanma,Ndiwulunbe,Ndonutim,Ngbande,Nguengu,Ntoekpe,Nyajo,Nyior,Odajie,Ogbaga,Ogultu,Ogunbunmi,Ojopode,Okehin,Olugunna,Omotunde,Onipede,Onma,Orhere,Orya,Otukwang,Otunade,Rampa,Rimi,Rugan,Rumbukawa,Sabiu,Sangabama,Sarabe,Seboregetore,Shafar,Shagwa,Shata,Shengu,Sokoron,Sunnayu,Tafoki,Takula,Talontan,Tarhemba,Tayu,Ter,Timtim,Timyam,Tindirke,Tokunbo,Torlwam,Tseakaadza,Tseanongo,Tsebeeve,Tsepaegh,Tuba,Tumbo,Tungalombo,Tunganyakwe,Uhkirhi,Umoru,Umuabai,Umuajuju,Unchida,Ungua,Unguwar,Unongo,Usha,Utongbo,Vembera,Wuro,Yanbashi,Yanmedi,Yoku,Zarunkwari,Zilumo,Zulika"}, - {name: "Celtic", i: 22, min: 4, max: 12, d: "nld", m: 0, b: "Aberaman,Aberangell,Aberarth,Aberavon,Aberbanc,Aberbargoed,Aberbeeg,Abercanaid,Abercarn,Abercastle,Abercegir,Abercraf,Abercregan,Abercych,Abercynon,Aberdare,Aberdaron,Aberdaugleddau,Aberdeen,Aberdulais,Aberdyfi,Aberedw,Abereiddy,Abererch,Abereron,Aberfan,Aberffraw,Aberffrwd,Abergavenny,Abergele,Aberglasslyn,Abergorlech,Abergwaun,Abergwesyn,Abergwili,Abergwynfi,Abergwyngregyn,Abergynolwyn,Aberhafesp,Aberhonddu,Aberkenfig,Aberllefenni,Abermain,Abermaw,Abermorddu,Abermule,Abernant,Aberpennar,Aberporth,Aberriw,Abersoch,Abersychan,Abertawe,Aberteifi,Aberthin,Abertillery,Abertridwr,Aberystwyth,Achininver,Afonhafren,Alisaha,Anfosadh,Antinbhearmor,Ardenna,Attacon,Banwen,Beira,Bhrura,Bleddfa,Boioduro,Bona,Boskyny,Boslowenpolbrogh,Boudobriga,Bravon,Brigant,Briganta,Briva,Brosnach,Caersws,Cambodunum,Cambra,Caracta,Catumagos,Centobriga,Ceredigion,Chalain,Chearbhallain,Chlasaigh,Chormaic,Cuileannach,Dinn,Diwa,Dubingen,Duibhidighe,Duro,Ebora,Ebruac,Eburodunum,Eccles,Egloskuri,Eighe,Eireann,Elerghi,Ferkunos,Fhlaithnin,Gallbhuaile,Genua,Ghrainnse,Gwyles,Heartsease,Hebron,Hordh,Inbhear,Inbhir,Inbhirair,Innerleithen,Innerleven,Innerwick,Inver,Inveraldie,Inverallan,Inveralmond,Inveramsay,Inveran,Inveraray,Inverarnan,Inverbervie,Inverclyde,Inverell,Inveresk,Inverfarigaig,Invergarry,Invergordon,Invergowrie,Inverhaddon,Inverkeilor,Inverkeithing,Inverkeithney,Inverkip,Inverleigh,Inverleith,Inverloch,Inverlochlarig,Inverlochy,Invermay,Invermoriston,Inverness,Inveroran,Invershin,Inversnaid,Invertrossachs,Inverugie,Inveruglas,Inverurie,Iubhrach,Karardhek,Kilninver,Kirkcaldy,Kirkintilloch,Krake,Lanngorrow,Latense,Leming,Lindomagos,Llanaber,Llandidiwg,Llandyrnog,Llanfarthyn,Llangadwaldr,Llansanwyr,Lochinver,Lugduno,Magoduro,Mheara,Monmouthshire,Nanshiryarth,Narann,Novioduno,Nowijonago,Octoduron,Penning,Pheofharain,Ponsmeur,Raithin,Ricomago,Rossinver,Salodurum,Seguia,Sentica,Theorsa,Tobargeal,Trealaw,Trefesgob,Trewedhenek,Trewythelan,Tuaisceart,Uige,Vitodurum,Windobona"}, - {name: "Mesopotamian", i: 23, min: 4, max: 9, d: "srpl", m: .1, b: "Adab,Adamndun,Adma,Admatum,Agrab,Akkad,Akshak,Amnanum,Andarig,Anshan,Apiru,Apum,Arantu,Arbid,Arpachiyah,Arpad,Arrapha,Ashlakka,Assur,Awan,Babilim,Bad-Tibira,Balawat,Barsip,Birtu,Bit-Bunakki,Borsippa,Chuera,Dashrah,Der,Dilbat,Diniktum,Doura,Dur-Kurigalzu,Dur-Sharrukin,Dur-Untash,Dûr-gurgurri,Ebla,Ekallatum,Ekalte,Emar,Erbil,Eresh,Eridu,Eshnunn,Eshnunna,Gargamish,Gasur,Gawra,Gibil,Girsu,Gizza,Habirun,Habur,Hadatu,Hakkulan,Halab,Halabit,Hamazi,Hamoukar,Haradum,Harbidum,Harran,Harranu,Hassuna,Hatarikka,Hatra,Hissar,Hiyawa,Hormirzad,Ida-Maras,Idamaraz,Idu,Imerishu,Imgur-Enlil,Irisagrig,Irnina,Irridu,Isin,Issinnitum,Iturungal,Izubitum,Jarmo,Jemdet,Kabnak,Kadesh,Kahat,Kalhu,Kar-Shulmanu-Asharedu,Kar-Tukulti-Ninurta,Kar-shulmanu-asharedu,Karana,Karatepe,Kartukulti,Kazallu,Kesh,Kidsha,Kinza,Kish,Kisiga,Kisurra,Kuara,Kurda,Kurruhanni,Kutha,Lagaba,Lagash,Larak,Larsa,Leilan,Malgium,Marad,Mardaman,Mari,Marlik,Mashkan,Mashkan-shapir,Matutem,Me-Turan,Meliddu,Mumbaqat,Nabada,Nagar,Nanagugal,Nerebtum,Nigin,Nimrud,Nina,Nineveh,Ninua,Nippur,Niru,Niya,Nuhashe,Nuhasse,Nuzi,Puzrish-Dagan,Qalatjarmo,Qatara,Qatna,Qattunan,Qidshu,Rapiqum,Rawda,Sagaz,Shaduppum,Shaggaratum,Shalbatu,Shanidar,Sharrukin,Shawwan,Shehna,Shekhna,Shemshara,Shibaniba,Shubat-Enlil,Shurkutir,Shuruppak,Shusharra,Shushin,Sikan,Sippar,Sippar-Amnanum,Sippar-sha-Annunitum,Subatum,Susuka,Tadmor,Tarbisu,Telul,Terqa,Tirazish,Tisbon,Tuba,Tushhan,Tuttul,Tutub,Ubaid,Umma,Ur,Urah,Urbilum,Urkesh,Ursa'um,Uruk,Urum,Uzarlulu,Warka,Washukanni,Zabalam,Zarri-Amnan"}, - {name: "Iranian", i: 24, min: 5, max: 11, d: "", m: .1, b: "Abali,Abrisham,Absard,Abuzeydabad,Afus,Alavicheh,Alikosh,Amol,Anarak,Anbar,Andisheh,Anshan,Aran,Ardabil,Arderica,Ardestan,Arjomand,Asgaran,Asgharabad,Ashian,Awan,Babajan,Badrud,Bafran,Baghestan,Baghshad,Bahadoran,Baharan Shahr,Baharestan,Bakun,Bam,Baqershahr,Barzok,Bastam,Behistun,Bitistar,Bumahen,Bushehr,Chadegan,Chahardangeh,Chamgardan,Chermahin,Choghabonut,Chugan,Damaneh,Damavand,Darabgard,Daran,Dastgerd,Dehaq,Dehaqan,Dezful,Dizicheh,Dorcheh,Dowlatabad,Duruntash,Ecbatana,Eslamshahr,Estakhr,Ezhiyeh,Falavarjan,Farrokhi,Fasham,Ferdowsieh,Fereydunshahr,Ferunabad,Firuzkuh,Fuladshahr,Ganjdareh,Ganzak,Gaz,Geoy,Godin,Goldasht,Golestan,Golpayegan,Golshahr,Golshan,Gorgab,Guged,Habibabad,Hafshejan,Hajjifiruz,Hana,Harand,Hasanabad,Hasanlu,Hashtgerd,Hecatompylos,Hormirzad,Imanshahr,Isfahan,Jandaq,Javadabad,Jiroft,Jowsheqan ,Jowzdan,Kabnak,Kahrizak,Kahriz Sang,Kangavar,Karaj,Karkevand,Kashan,Kelishad,Kermanshah,Khaledabad,Khansar,Khorramabad,Khur,Khvorzuq,Kilan,Komeh,Komeshcheh,Konar,Kuhpayeh,Kul,Kushk,Lavasan,Laybid,Liyan,Lyan,Mahabad,Mahallat,Majlesi,Malard,Manzariyeh,Marlik,Meshkat,Meymeh,Miandasht,Mish,Mobarakeh,Nahavand,Nain,Najafabad,Naqshe,Narezzash,Nasimshahr,Nasirshahr,Nasrabad,Natanz,Neyasar,Nikabad,Nimvar,Nushabad,Pakdasht,Parand,Pardis,Parsa,Pasargadai,Patigrabana,Pir Bakran,Pishva,Qahderijan,Qahjaverestan,Qamsar,Qarchak,Qods,Rabat,Ray-shahr,Rezvanshahr,Rhages,Robat Karim,Rozveh,Rudehen,Sabashahr,Safadasht,Sagzi,Salehieh,Sandal,Sarvestan,Sedeh,Sefidshahr,Semirom,Semnan,Shadpurabad,Shah,Shahdad,Shahedshahr,Shahin,Shahpour,Shahr,Shahreza,Shahriar,Sharifabad,Shemshak,Shiraz,Shushan,Shushtar,Sialk,Sin,Sukhteh,Tabas,Tabriz,Takhte,Talkhuncheh,Talli,Tarq,Temukan,Tepe,Tiran,Tudeshk,Tureng,Urmia,Vahidieh,Vahrkana,Vanak,Varamin,Varnamkhast,Varzaneh,Vazvan,Yahya,Yarim,Yasuj,Zarrin Shahr,Zavareh,Zayandeh,Zazeran,Ziar,Zibashahr,Zranka"}, - {name: "Hawaiian", i: 25, min: 5, max: 10, d: "auo", m: 1, b: "Aapueo,Ahoa,Ahuakaio,Ahupau,Alaakua,Alae,Alaeloa,Alamihi,Aleamai,Alena,Alio,Aupokopoko,Halakaa,Haleu,Haliimaile,Hamoa,Hanakaoo,Hanaulu,Hanawana,Hanehoi,Haou,Hikiaupea,Hokuula,Honohina,Honokahua,Honokeana,Honokohau,Honolulu,Honomaele,Hononana,Honopou,Hoolawa,Huelo,Kaalaea,Kaapahu,Kaeo,Kahalehili,Kahana,Kahuai,Kailua,Kainehe,Kakalahale,Kakanoni,Kalenanui,Kaleoaihe,Kalialinui,Kalihi,Kalimaohe,Kaloi,Kamani,Kamehame,Kanahena,Kaniaula,Kaonoulu,Kapaloa,Kapohue,Kapuaikini,Kapunakea,Kauau,Kaulalo,Kaulanamoa,Kauluohana,Kaumakani,Kaumanu,Kaunauhane,Kaupakulua,Kawaloa,Keaa,Keaaula,Keahua,Keahuapono,Kealahou,Keanae,Keauhou,Kelawea,Keokea,Keopuka,Kikoo,Kipapa,Koakupuna,Koali,Kolokolo,Kopili,Kou,Kualapa,Kuhiwa,Kuholilea,Kuhua,Kuia,Kuikui,Kukoae,Kukohia,Kukuiaeo,Kukuipuka,Kukuiula,Kulahuhu,Lapakea,Lapueo,Launiupoko,Lole,Maalo,Mahinahina,Mailepai,Makaakini,Makaalae,Makaehu,Makaiwa,Makaliua,Makapipi,Makapuu,Maluaka,Manawainui,Mehamenui,Moalii,Moanui,Mohopili,Mokae,Mokuia,Mokupapa,Mooiki,Mooloa,Moomuku,Muolea,Nakaaha,Nakalepo,Nakaohu,Nakapehu,Nakula,Napili,Niniau,Nuu,Oloewa,Olowalu,Omaopio,Onau,Onouli,Opaeula,Opana,Opikoula,Paakea,Paeahu,Paehala,Paeohi,Pahoa,Paia,Pakakia,Palauea,Palemo,Paniau,Papaaea,Papaanui,Papaauhau,Papaka,Papauluana,Pauku,Paunau,Pauwalu,Pauwela,Pohakanele,Polaiki,Polanui,Polapola,Poopoo,Poponui,Poupouwela,Puahoowali,Puakea,Puako,Pualaea,Puehuehu,Pueokauiki,Pukaauhuhu,Pukuilua,Pulehu,Puolua,Puou,Puuhaehae,Puuiki,Puuki,Puulani,Puunau,Puuomaile,Uaoa,Uhao,Ukumehame,Ulaino,Ulumalu,Wahikuli,Waianae,Waianu,Waiawa,Waiehu,Waieli,Waikapu,Wailamoa,Wailaulau,Wainee,Waiohole,Waiohonu,Waiohuli,Waiokama,Waiokila,Waiopai,Waiopua,Waipao,Waipionui,Waipouli"}, - {name: "Karnataka", i: 26, min: 5, max: 11, d: "tnl", m: 0, b: "Adityapatna,Adyar,Afzalpur,Aland,Alnavar,Alur,Ambikanagara,Anekal,Ankola,Annigeri,Arkalgud,Arsikere,Athni,Aurad,Badami,Bagalkot,Bagepalli,Bail,Bajpe,Bangalore,Bangarapet,Bankapura,Bannur,Bantval,Basavakalyan,Basavana,Belgaum,Beltangadi,Belur,Bhadravati,Bhalki,Bhatkal,Bhimarayanagudi,Bidar,Bijapur,Bilgi,Birur,Bommasandra,Byadgi,Challakere,Chamarajanagar,Channagiri,Channapatna,Channarayapatna,Chik,Chikmagalur,Chiknayakanhalli,Chikodi,Chincholi,Chintamani,Chitapur,Chitgoppa,Chitradurga,Dandeli,Dargajogihalli,Devadurga,Devanahalli,Dod,Donimalai,Gadag,Gajendragarh,Gangawati,Gauribidanur,Gokak,Gonikoppal,Gubbi,Gudibanda,Gulbarga,Guledgudda,Gundlupet,Gurmatkal,Haliyal,Hangal,Harapanahalli,Harihar,Hassan,Hatti,Haveri,Hebbagodi,Heggadadevankote,Hirekerur,Holalkere,Hole,Homnabad,Honavar,Honnali,Hoovina,Hosakote,Hosanagara,Hosdurga,Hospet,Hubli,Hukeri,Hungund,Hunsur,Ilkal,Indi,Jagalur,Jamkhandi,Jevargi,Jog,Kadigenahalli,Kadur,Kalghatgi,Kamalapuram,Kampli,Kanakapura,Karkal,Karwar,Khanapur,Kodiyal,Kolar,Kollegal,Konnur,Koppa,Koppal,Koratagere,Kotturu,Krishnarajanagara,Krishnarajasagara,Krishnarajpet,Kudchi,Kudligi,Kudremukh,Kumta,Kundapura,Kundgol,Kunigal,Kurgunta,Kushalnagar,Kushtagi,Lakshmeshwar,Lingsugur,Londa,Maddur,Madhugiri,Madikeri,Mahalingpur,Malavalli,Mallar,Malur,Mandya,Mangalore,Manvi,Molakalmuru,Mudalgi,Mudbidri,Muddebihal,Mudgal,Mudhol,Mudigere,Mulbagal,Mulgund,Mulki,Mulur,Mundargi,Mundgod,Munirabad,Mysore,Nagamangala,Nanjangud,Narasimharajapura,Naregal,Nargund,Navalgund,Nipani,Pandavapura,Pavagada,Piriyapatna,Pudu,Puttur,Rabkavi,Raichur,Ramanagaram,Ramdurg,Ranibennur,Raybag,Robertson,Ron,Sadalgi,Sagar,Sakleshpur,Saligram,Sandur,Sankeshwar,Saundatti,Savanur,Sedam,Shahabad,Shahpur,Shaktinagar,Shiggaon,Shikarpur,Shirhatti,Shorapur,Shrirangapattana,Siddapur,Sidlaghatta,Sindgi,Sindhnur,Sira,Siralkoppa,Sirsi,Siruguppa,Somvarpet,Sorab,Sringeri,Srinivaspur,Sulya,Talikota,Tarikere,Tekkalakote,Terdal,Thumbe,Tiptur,Tirthahalli,Tirumakudal,Tumkur,Turuvekere,Udupi,Vijayapura,Wadi,Yadgir,Yelandur,Yelbarga,Yellapur,Yenagudde"}, - {name: "Quechua", i: 27, min: 6, max: 12, d: "l", m: 0, b: "Alpahuaycco,Anchihuay,Anqea,Apurimac,Arequipa,Atahuallpa,Atawalpa,Atico,Ayacucho,Ayahuanco,Ayllu,Cajamarca,Canayre,Canchacancha,Carapo,Carhuac,Carhuacatac,Cashan,Caullaraju,Caxamalca,Cayesh,Ccahuasno,Ccarhuacc,Ccopayoc,Chacchapunta,Chacraraju,Challhuamayo,Champara,Chanchan,Chekiacraju,Chillihua,Chinchey,Chontah,Chopicalqui,Chucuito,Chuito,Chullo,Chumpi,Chuncho,Chupahuacho,Chuquiapo,Chuquisaca,Churup,Cocapata,Cochabamba,Cojup,Collota,Conococha,Corihuayrachina,Cuchoquesera,Cusichaca,Haika,Hanpiq,Hatun,Haywarisqa,Huaca,Huachinga,Hualcan,Hualchancca,Huamanga,Huamashraju,Huancarhuas,Huandoy,Huantsan,Huanupampa,Huarmihuanusca,Huascaran,Huaylas,Huayllabamba,Huayrana,Huaytara,Huichajanca,Huinayhuayna,Huinche,Huinioch,Illiasca,Intipunku,Iquicha,Ishinca,Jahuacocha,Jirishanca,Juli,Jurau,Kakananpunta,Kamasqa,Karpay,Kausay,Khuya,Kuelap,Lanccochayocc,Llaca,Llactapata,Llanganuco,Llaqta,Lloqllasca,Llupachayoc,Luricocha,Machu,Mallku,Matarraju,Mechecc,Mikhuy,Milluacocha,Morochuco,Munay,Ocshapalca,Ollantaytambo,Oroccahua,Oronccoy,Oyolo,Pacamayo,Pacaycasa,Paccharaju,Pachacamac,Pachakamaq,Pachakuteq,Pachakuti,Pachamama,Paititi,Pajaten,Palcaraju,Pallccas,Pampa,Panaka,Paqarina,Paqo,Parap,Paria,Patahuasi,Patallacta,Patibamba,Pisac,Pisco,Pongos,Pucacolpa,Pucahirca,Pucaranra,Pumatambo,Puscanturpa,Putaca,Puyupatamarca,Qawaq,Qayqa,Qochamoqo,Qollana,Qorihuayrachina,Qorimoqo,Qotupuquio,Quenuaracra,Queshque,Quillcayhuanca,Quillya,Quitaracsa,Quitaraju,Qusqu,Rajucolta,Rajutakanan,Rajutuna,Ranrahirca,Ranrapalca,Raria,Rasac,Rimarima,Riobamba,Runkuracay,Rurec,Sacsa,Sacsamarca,Saiwa,Sarapo,Sayacmarca,Sayripata,Sinakara,Sonccopa,Taripaypacha,Taulliraju,Tawantinsuyu,Taytanchis,Tiwanaku,Tocllaraju,Tsacra,Tuco,Tucubamba,Tullparaju,Tumbes,Uchuraccay,Uchuraqay,Ulta,Urihuana,Uruashraju,Vallunaraju,Vilcabamba,Wacho,Wankawillka,Wayra,Yachay,Yahuarraju,Yanamarey,Yanaqucha,Yanesha,Yerupaja"}, - {name: "Swahili", i: 28, min: 4, max: 9, d: "", m: 0, b: "Abim,Adjumani,Alebtong,Amolatar,Amuru,Apac,Arua,Arusha,Babati,Baragoi,Bombo,Budaka,Bugembe,Bugiri,Buikwe,Bukedea,Bukoba,Bukomansimbi,Bukungu,Buliisa,Bundibugyo,Bungoma,Busembatya,Bushenyi,Busia,Busolwe,Butaleja,Butambala,Butere,Buwenge,Buyende,Dadaab,Dodoma,Dokolo,Eldoret,Elegu,Emali,Embu,Entebbe,Garissa,Gede,Gulu,Handeni,Hima,Hoima,Hola,Ibanda,Iganga,Iringa,Isingiro,Isiolo,Jinja,Kaabong,Kabuyanda,Kabwohe,Kagadi,Kajiado,Kakinga,Kakiri,Kakuma,Kalangala,Kaliro,Kalongo,Kalungu,Kampala,Kamwenge,Kanungu,Kapchorwa,Kasese,Kasulu,Katakwi,Kayunga,Keroka,Kiambu,Kibaale,Kibaha,Kibingo,Kibwezi,Kigoma,Kihiihi,Kilifi,Kiruhura,Kiryandongo,Kisii,Kisoro,Kisumu,Kitale,Kitgum,Kitui,Koboko,Korogwe,Kotido,Kumi,Kyazanga,Kyegegwa,Kyenjojo,Kyotera,Lamu,Langata,Lindi,Lodwar,Lokichoggio,Londiani,Loyangalani,Lugazi,Lukaya,Luweero,Lwakhakha,Lwengo,Lyantonde,Machakos,Mafinga,Makambako,Makindu,Malaba,Malindi,Manafwa,Mandera,Marsabit,Masaka,Masindi,Masulita,Matugga,Mayuge,Mbale,Mbarara,Mbeya,Meru,Mitooma,Mityana,Mombasa,Morogoro,Moroto,Moyale,Moyo,Mpanda,Mpigi,Mpondwe,Mtwara,Mubende,Mukono,Muranga,Musoma,Mutomo,Mutukula,Mwanza,Nagongera,Nairobi,Naivasha,Nakapiripirit,Nakaseke,Nakasongola,Nakuru,Namanga,Namayingo,Namutumba,Nansana,Nanyuki,Narok,Naromoru,Nebbi,Ngora,Njeru,Njombe,Nkokonjeru,Ntungamo,Nyahururu,Nyeri,Oyam,Pader,Paidha,Pakwach,Pallisa,Rakai,Ruiru,Rukungiri,Rwimi,Sanga,Sembabule,Shimoni,Shinyanga,Singida,Sironko,Songea,Soroti,Ssabagabo,Sumbawanga,Tabora,Takaungu,Tanga,Thika,Tororo,Tunduma,Vihiga,Voi,Wajir,Wakiso,Watamu,Webuye,Wobulenzi,Wote,Wundanyi,Yumbe,Zanzibar"}, - {name: "Vietnamese", i: 29, min: 3, max: 12, d: "", m: 1, b: "An Giang,Anh Son,An Khe,An Nhon,Ayun Pa,Bac Giang,Bac Kan,Bac Lieu,Bac Ninh,Ba Don,Bao Loc,Ba Ria,Ba Ria-Vung Tau,Ba Thuoc,Ben Cat,Ben Tre,Bien Hoa,Bim Son,Binh Dinh,Binh Duong,Binh Long,Binh Minh,Binh Phuoc,Binh Thuan,Buon Ho,Buon Ma Thuot,Cai Lay,Ca Mau,Cam Khe,Cam Pha,Cam Ranh,Cam Thuy,Can Tho,Cao Bang,Cao Lanh,Cao Phong,Chau Doc,Chi Linh,Con Cuong,Cua Lo,Da Bac,Dak Lak,Da Lat,Da Nang,Di An,Dien Ban,Dien Bien,Dien Bien Phu,Dien Chau,Do Luong,Dong Ha,Dong Hoi,Dong Trieu,Duc Pho,Duyen Hai,Duy Tien,Gia Lai,Gia Nghia,Gia Rai,Go Cong,Ha Giang,Ha Hoa,Hai Duong,Hai Phong,Ha Long,Ha Nam,Ha Noi,Ha Tinh,Ha Trung,Hau Giang,Hoa Binh,Hoang Mai,Hoa Thanh,Ho Chi Minh,Hoi An,Hong Linh,Hong Ngu,Hue,Hung Nguyen,Hung Yen,Huong Thuy,Huong Tra,Khanh Hoa,Kien Tuong,Kim Boi,Kinh Mon,Kon Tum,Ky Anh,Ky Son,Lac Son,Lac Thuy,La Gi,Lai Chau,Lam Thao,Lang Chanh,Lang Son,Lao Cai,Long An,Long Khanh,Long My,Long Xuyen,Luong Son,Mai Chau,Mong Cai,Muong Lat,Muong Lay,My Hao,My Tho,Nam Dan,Nam Dinh,Nga Bay,Nga Nam,Nga Son,Nghe An,Nghia Dan,Nghia Lo,Nghi Loc,Nghi Son,Ngoc Lac,Nha Trang,Nhu Thanh,Nhu Xuan,Ninh Binh,Ninh Hoa,Nong Cong,Phan Rang Thap Cham,Phan Thiet,Pho Yen,Phu Ly,Phu My,Phu Ninh,Phuoc Long,Phu Tho,Phu Yen,Pleiku,Quang Binh,Quang Nam,Quang Ngai,Quang Ninh,Quang Tri,Quang Xuong,Quang Yen,Quan Hoa,Quan Son,Que Phong,Quy Chau,Quy Hop,Quynh Luu,Quy Nhon,Rach Gia,Sa Dec,Sai Gon,Sam Son,Sa Pa,Soc Trang,Song Cau,Song Cong,Son La,Son Tay,Tam Diep,Tam Ky,Tan An,Tan Chau,Tan Ky,Tan Lac,Tan Son,Tan Uyen,Tay Ninh,Thach Thanh,Thai Binh,Thai Hoa,Thai Nguyen,Thanh Chuong,Thanh Hoa,Thieu Hoa,Thuan An,Thua Thien-Hue,Thu Dau Mot,Thu Duc,Thuong Xuan,Tien Giang,Trang Bang,Tra Vinh,Trieu Son,Tu Son,Tuyen Quang,Tuy Hoa,Uong Bi,Viet Tri,Vinh,Vinh Chau,Vinh Loc,Vinh Long,Vinh Yen,Vi Thanh,Vung Tau,Yen Bai,Yen Dinh,Yen Thanh,Yen Thuy"}, - {name: "Cantonese", i: 30, min: 5, max: 11, d: "", m: 0, b: "Chaiwan,Chingchung,Chinghoi,Chingsen,Chingshing,Chiunam,Chiuon,Chiuyeung,Chiyuen,Choihung,Chuehoi,Chuiman,Chungfu,Chungsan,Chunguktsuen,Dakhing,Daopo,Daumun,Dingwu,Dinpak,Donggun,Dongyuen,Duenchau,Fachau,Fanling,Fatgong,Fatshan,Fotan,Fuktien,Fumun,Funggong,Funghoi,Fungshun,Fungtei,Gamtin,Gochau,Goming,Gonghoi,Gongshing,Goyiu,Hanghau,Hangmei,Hengon,Heungchau,Heunggong,Heungkiu,Hingning,Hohfuktong,Hoichue,Hoifung,Hoiping,Hokong,Hokshan,Hoyuen,Hunghom,Hungshuikiu,Jiuling,Kamsheung,Kamwan,Kaulongtong,Keilun,Kinon,Kinsang,Kityeung,Kongmun,Kukgong,Kwaifong,Kwaihing,Kwongchau,Kwongling,Kwongming,Kwuntong,Laichikok,Laiking,Laiwan,Lamtei,Lamtin,Leitung,Leungking,Limkong,Linping,Linshan,Loding,Lokcheong,Lokfu,Longchuen,Longgong,Longmun,Longping,Longwa,Longwu,Lowu,Luichau,Lukfung,Lukho,Lungmun,Macheung,Maliushui,Maonshan,Mauming,Maunam,Meifoo,Mingkum,Mogong,Mongkok,Muichau,Muigong,Muiyuen,Naiwai,Namcheong,Namhoi,Namhong,Namsha,Nganwai,Ngautaukok,Ngchuen,Ngwa,Onting,Pakwun,Paotoishan,Pingshan,Pingyuen,Poklo,Pongon,Poning,Potau,Puito,Punyue,Saiwanho,Saiyingpun,Samshing,Samshui,Samtsen,Samyuenlei,Sanfung,Sanhing,Sanhui,Sanwai,Seiwui,Shamshuipo,Shanmei,Shantau,Shauking,Shekmun,Shekpai,Sheungshui,Shingkui,Shiuhing,Shundak,Shunyi,Shupinwai,Simshing,Siuhei,Siuhong,Siukwan,Siulun,Suikai,Taihing,Taikoo,Taipo,Taishuihang,Taiwai,Taiwohau,Tinhau,Tinshuiwai,Tiukengleng,Toishan,Tongfong,Tonglowan,Tsakyoochung,Tsamgong,Tsangshing,Tseungkwano,Tsimshatsui,Tsinggong,Tsingshantsuen,Tsingwun,Tsingyi,Tsingyuen,Tsiuchau,Tsuenshekshan,Tsuenwan,Tuenmun,Tungchung,Waichap,Waichau,Waidong,Wailoi,Waishing,Waiyeung,Wanchai,Wanfau,Wanshing,Wingon,Wongpo,Wongtaisin,Woping,Wukaisha,Yano,Yaumatei,Yautong,Yenfa,Yeungchun,Yeungdong,Yeungsai,Yeungshan,Yimtin,Yingdak,Yiuping,Yongshing,Yongyuen,Yuenlong,Yuenshing,Yuetsau,Yuknam,Yunping"}, - {name: "Mongolian", i: 31, min: 5, max: 12, d: "aou", m: .3, b: "Adaatsag,Airag,Alag Erdene,Altai,Altanshiree,Altantsogts,Arbulag,Baatsagaan,Batnorov,Batshireet,Battsengel,Bayan Adarga,Bayan Agt,Bayanbulag,Bayandalai,Bayandun,Bayangovi,Bayanjargalan,Bayankhongor,Bayankhutag,Bayanlig,Bayanmonkh,Bayannur,Bayannuur,Bayan Ondor,Bayan Ovoo,Bayantal,Bayantsagaan,Bayantumen,Bayan Uul,Bayanzurkh,Berkh,Biger,Binder,Bogd,Bombogor,Bor Ondor,Bugat,Bugt,Bulgan,Buregkhangai,Burentogtokh,Buutsagaan,Buyant,Chandmani,Chandmani Ondor,Choibalsan,Chuluunkhoroot,Chuluut,Dadal,Dalanjargalan,Dalanzadgad,Darhan Muminggan,Darkhan,Darvi,Dashbalbar,Dashinchilen,Delger,Delgerekh,Delgerkhaan,Delgerkhangai,Delgertsogt,Deluun,Deren,Dorgon,Duut,Erdene,Erdenebulgan,Erdeneburen,Erdenedalai,Erdenemandal,Erdenetsogt,Galshar,Galt,Galuut,Govi Ugtaal,Gurvan,Gurvanbulag,Gurvansaikhan,Gurvanzagal,Hinggan,Hodong,Holingol,Hondlon,Horin Ger,Horqin,Hulunbuir,Hure,Ikhkhet,Ikh Tamir,Ikh Uul,Jargalan,Jargalant,Jargaltkhaan,Jarud,Jinst,Khairkhan,Khalhgol,Khaliun,Khanbogd,Khangai,Khangal,Khankh,Khankhongor,Khashaat,Khatanbulag,Khatgal,Kherlen,Khishig Ondor,Khokh,Kholonbuir,Khongor,Khotont,Khovd,Khovsgol,Khuld,Khureemaral,Khurmen,Khutag Ondor,Luus,Mandakh,Mandal Ovoo,Mankhan,Manlai,Matad,Mogod,Monkhkhairkhan,Moron,Most,Myangad,Nogoonnuur,Nomgon,Norovlin,Noyon,Ogii,Olgii,Olziit,Omnodelger,Ondorkhaan,Ondorshil,Ondor Ulaan,Ongniud,Ordos,Orgon,Orkhon,Rashaant,Renchinlkhumbe,Sagsai,Saikhan,Saikhandulaan,Saikhan Ovoo,Sainshand,Saintsagaan,Selenge,Sergelen,Sevrei,Sharga,Sharyngol,Shine Ider,Shinejinst,Shiveegovi,Sumber,Taishir,Tarialan,Tariat,Teshig,Togrog,Togtoh,Tolbo,Tomorbulag,Tonkhil,Tosontsengel,Tsagaandelger,Tsagaannuur,Tsagaan Ovoo,Tsagaan Uur,Tsakhir,Tseel,Tsengel,Tsenkher,Tsenkhermandal,Tsetseg,Tsetserleg,Tsogt,Tsogt Ovoo,Tsogttsetsii,Tumed,Tunel,Tuvshruulekh,Ulaanbadrakh,Ulaankhus,Ulaan Uul,Ulanhad,Ulanqab,Uyench,Yesonbulag,Zag,Zalainur,Zamyn Uud,Zereg"}, - // fantasy bases by Dopu: - {name: "Human Generic", i: 32, min: 6, max: 11, d: "peolst", m: 0, b: "Amberglen,Angelhand,Arrowden,Autumnband,Autumnkeep,Basinfrost,Basinmore,Bayfrost,Beargarde,Bearmire,Bellcairn,Bellport,Bellreach,Blackwatch,Bleakward,Bonemouth,Boulder,Bridgefalls,Bridgeforest,Brinepeak,Brittlehelm,Bronzegrasp,Castlecross,Castlefair,Cavemire,Claymond,Claymouth,Clearguard,Cliffgate,Cliffshear,Cliffshield,Cloudbay,Cloudcrest,Cloudwood,Coldholde,Cragbury,Crowgrove,Crowvault,Crystalrock,Crystalspire,Cursefield,Curseguard,Cursespell,Dawnforest,Dawnwater,Deadford,Deadkeep,Deepcairn,Deerchill,Demonfall,Dewglen,Dewmere,Diredale,Direden,Dirtshield,Dogcoast,Dogmeadow,Dragonbreak,Dragonhold,Dragonward,Dryhost,Dustcross,Dustwatch,Eaglevein,Earthfield,Earthgate,Earthpass,Ebonfront,Edgehaven,Eldergate,Eldermere,Embervault,Everchill,Evercoast,Falsevale,Faypond,Fayvale,Fayyard,Fearpeak,Flameguard,Flamewell,Freyshell,Ghostdale,Ghostpeak,Gloomburn,Goldbreach,Goldyard,Grassplains,Graypost,Greeneld,Grimegrove,Grimeshire,Heartfall,Heartford,Heartvault,Highbourne,Hillpass,Hollowstorm,Honeywater,Houndcall,Houndholde,Iceholde,Icelight,Irongrave,Ironhollow,Knightlight,Knighttide,Lagoonpass,Lakecross,Lastmere,Laststar,Lightvale,Limeband,Littlehall,Littlehold,Littlemire,Lostcairn,Lostshield,Loststar,Madfair,Madham,Midholde,Mightglen,Millstrand,Mistvault,Mondpass,Moonacre,Moongulf,Moonwell,Mosshand,Mosstide,Mosswind,Mudford,Mudwich,Mythgulch,Mythshear,Nevercrest,Neverfront,Newfalls,Nighthall,Oakenbell,Oakenrun,Oceanstar,Oldreach,Oldwall,Oldwatch,Oxbrook,Oxlight,Pearlhaven,Pinepond,Pondfalls,Pondtown,Pureshell,Quickbell,Quickpass,Ravenside,Roguehaven,Roseborn,Rosedale,Rosereach,Rustmore,Saltmouth,Sandhill,Scorchpost,Scorchstall,Shadeforest,Shademeadow,Shadeville,Shimmerrun,Shimmerwood,Shroudrock,Silentkeep,Silvercairn,Silvergulch,Smallmire,Smoothcliff,Smoothgrove,Smoothtown,Snakemere,Snowbay,Snowshield,Snowtown,Southbreak,Springmire,Springview,Stagport,Steammouth,Steamwall,Steepmoor,Stillhall,Stoneguard,Stonespell,Stormhand,Stormhorn,Sungulf,Sunhall,Swampmaw,Swangarde,Swanwall,Swiftwell,Thorncairn,Thornhelm,Thornyard,Timberside,Tradewick,Westmeadow,Westpoint,Whiteshore,Whitvalley,Wildeden,Wildwell,Wildyard,Winterhaven,Wolfpass"}, - {name: "Elven", i: 33, min: 6, max: 12, d: "lenmsrg", m: 0, b: "Adrindest,Aethel,Afranthemar,Aiqua,Alari,Allanar,Almalian,Alora,Alyanasari,Alyelona,Alyran,Ammar,Anyndell,Arasari,Aren,Ashmebel,Aymlume,Bel-Didhel,Brinorion,Caelora,Chaulssad,Chaundra,Cyhmel,Cyrang,Dolarith,Dolonde,Draethe,Dranzan,Draugaust,E'ana,Eahil,Edhil,Eebel,Efranluma,Eld-Sinnocrin,Elelthyr,Ellanalin,Ellena,Ellorthond,Eltaesi,Elunore,Emyranserine,Entheas,Eriargond,Esari,Esath,Eserius,Eshsalin,Eshthalas,Evraland,Faellenor,Famelenora,Filranlean,Filsaqua,Gafetheas,Gaf Serine,Geliene,Gondorwin,Guallu,Haeth,Hanluna,Haulssad,Heloriath,Himlarien,Himliene,Hinnead,Hlinas,Hloireenil,Hluihei,Hlurthei,Hlynead,Iaenarion,Iaron,Illanathaes,Illfanora,Imlarlon,Imyse,Imyvelian,Inferius,Inlurth,innsshe,Iralserin,Irethtalos,Irholona,Ishal,Ishlashara,Ithelion,Ithlin,Iulil,Jaal,Jamkadi,Kaalume,Kaansera,Karanthanil,Karnosea,Kasethyr,Keatheas,Kelsya,Keth Aiqua,Kmlon,Kyathlenor,Kyhasera,Lahetheas,Lefdorei,Lelhamelle,Lilean,Lindeenil,Lindoress,Litys,Llaughei,Lya,Lyfa,Lylharion,Lynathalas,Machei,Masenoris,Mathethil,Mathentheas,Meethalas,Menyamar,Mithlonde,Mytha,Mythsemelle,Mythsthas,Naahona,Nalore,Nandeedil,Nasad Ilaurth,Nasin,Nathemar,Neadar,Neilon,Nelalon,Nellean,Nelnetaesi,Nilenathyr,Nionande,Nylm,Nytenanas,Nythanlenor,O'anlenora,Obeth,Ofaenathyr,Ollmnaes,Ollsmel,Olwen,Olyaneas,Omanalon,Onelion,Onelond,Orlormel,Ormrion,Oshana,Oshvamel,Raethei,Rauguall,Reisera,Reslenora,Ryanasera,Rymaserin,Sahnor,Saselune,Sel-Zedraazin,Selananor,Sellerion,Selmaluma,Shaeras,Shemnas,Shemserin,Sheosari,Sileltalos,Siriande,Siriathil,Srannor,Sshanntyr,Sshaulu,Syholume,Sylharius,Sylranbel,Taesi,Thalor,Tharenlon,Thelethlune,Thelhohil,Themar,Thene,Thilfalean,Thilnaenor,Thvethalas,Thylathlond,Tiregul,Tlauven,Tlindhe,Ulal,Ullve,Ulmetheas,Ulssin,Umnalin,Umye,Umyheserine,Unanneas,Unarith,Undraeth,Unysarion,Vel-Shonidor,Venas,Vin Argor,Wasrion,Wlalean,Yaeluma,Yeelume,Yethrion,Ymserine,Yueghed,Yuerran,Yuethin"}, - {name: "Dark Elven", i: 34, min: 6, max: 14, d: "nrslamg", m: .2, b: "Abaethaggar,Abburth,Afranthemar,Aharasplit,Aidanat,Ald'ruhn,Ashamanu,Ashesari,Ashletheas,Baerario,Baereghel,Baethei,Bahashae,Balmora,Bel-Didhel,Borethanil,Buiyrandyn,Caellagith,Caellathala,Caergroth,Caldras,Chaggar,Chaggaust,Channtar,Charrvhel'raugaust,Chaulssin,Chaundra,ChedNasad,ChetarIthlin,ChethRrhinn,Chymaer,Clarkarond,Cloibbra,Commoragh,Cyrangroth,Cilben,D'eldarc,Daedhrog,Dalkyn,Do'Urden,Doladress,Dolarith,Dolonde,Draethe,Dranzan,Dranzithl,Draugaust,Dreghei,Drelhei,Dryndlu,Dusklyngh,DyonG'ennivalz,Edraithion,Eld-Sinnocrin,Ellorthond,Enhethyr,Entheas,ErrarIthinn,Eryndlyn,Faladhell,Faneadar,Fethalas,Filranlean,Formarion,Ferdor,Gafetheas,Ghrond,Gilranel,Glamordis,Gnaarmok,Gnisis,Golothaer,Gondorwin,Guallidurth,Guallu,Gulshin,Haeth,Haggraef,Harganeth,Harkaldra,Haulssad,Haundrauth,Heloriath,Hlammachar,Hlaughei,Hloireenil,Hluitar,Inferius,Innsshe,Ithilaughym,Iz'aiogith,Jaal,Jhachalkhyn,Kaerabrae,Karanthanil,Karondkar,Karsoluthiyl,Kellyth,Khuul,Lahetheas,Lidurth,Lindeenil,Lirillaquen,LithMy'athar,LlurthDreier,Lolth,Lothuial,Luihaulen'tar,Maeralyn,Maerimydra,Mathathlona,Mathethil,Mellodona,Menagith,Menegwen,Menerrendil,Menzithl,Menzoberranzan,Mila-Nipal,Mithryn,Molagmar,Mundor,Myvanas,Naggarond,Nandeedil,NasadIlaurth,Nauthor,Navethas,Neadar,Nurtaleewe,Nidiel,Noruiben,Olwen,O'lalona,Obeth,Ofaenathyr,Orlormel,Orlytlar,Pelagiad,Raethei,Raugaust,Rauguall,Rilauven,Rrharrvhei,Sadrith,Sel-Zedraazin,Seydaneen,Shaz'rir,Skaal,Sschindylryn,Shamath,Shamenz,Shanntur,Sshanntynlan,Sshanntyr,Shaulssin,SzithMorcane,Szithlin,Szobaeth,Sirdhemben,T'lindhet,Tebh'zhor,Telmere,Telnarquel,Tharlarast,Thylathlond,Tlaughe,Trizex,Tyrybblyn,Ugauth,Ughym,Uhaelben,Ullmatalos,Ulmetheas,Ulrenserine,Uluitur,Undraeth,Undraurth,Undrek'Thoz,Ungethal,UstNatha,Uthaessien,V'elddrinnsshar,Vaajha,Vel-Shonidor,Velddra,Velothi,Venead,Vhalth'vha,Vinargothr,Vojha,Waethe,Waethei,Xaalkis,Yakaridan,Yeelume,Yridhremben,Yuethin,Yuethindrynn,Zirnakaynin"}, - {name: "Dwarven", i: 35, min: 4, max: 11, d: "dk", m: 0, b: "Addundad,Ahagzad,Ahazil,Akil,Akzizad,Anumush,Araddush,Arar,Arbhur,Badushund,Baragzig,Baragzund,Barakinb,Barakzig,Barakzinb,Barakzir,Baramunz,Barazinb,Barazir,Bilgabar,Bilgatharb,Bilgathaz,Bilgila,Bilnaragz,Bilnulbar,Bilnulbun,Bizaddum,Bizaddush,Bizanarg,Bizaram,Bizinbiz,Biziram,Bunaram,Bundinar,Bundushol,Bundushund,Bundushur,Buzaram,Buzundab,Buzundush,Gabaragz,Gabaram,Gabilgab,Gabilgath,Gabizir,Gabunal,Gabunul,Gabuzan,Gatharam,Gatharbhur,Gathizdum,Gathuragz,Gathuraz,Gila,Giledzir,Gilukkhath,Gilukkhel,Gunala,Gunargath,Gunargil,Gundumunz,Gundusharb,Gundushizd,Kharbharbiln,Kharbhatharb,Kharbhela,Kharbilgab,Kharbuzadd,Khatharbar,Khathizdin,Khathundush,Khazanar,Khazinbund,Khaziragz,Khaziraz,Khizdabun,Khizdusharbh,Khizdushath,Khizdushel,Khizdushur,Kholedzar,Khundabiln,Khundabuz,Khundinarg,Khundushel,Khuragzig,Khuramunz,Kibarak,Kibilnal,Kibizar,Kibunarg,Kibundin,Kibuzan,Kinbadab,Kinbaragz,Kinbarakz,Kinbaram,Kinbizah,Kinbuzar,Nala,Naledzar,Naledzig,Naledzinb,Naragzah,Naragzar,Naragzig,Narakzah,Narakzar,Naramunz,Narazar,Nargabad,Nargabar,Nargatharb,Nargila,Nargundum,Nargundush,Nargunul,Narukthar,Narukthel,Nula,Nulbadush,Nulbaram,Nulbilnarg,Nulbunal,Nulbundab,Nulbundin,Nulbundum,Nulbuzah,Nuledzah,Nuledzig,Nulukkhaz,Nulukkhund,Nulukkhur,Sharakinb,Sharakzar,Sharamunz,Sharbarukth,Shatharbhizd,Shatharbiz,Shathazah,Shathizdush,Shathola,Shaziragz,Shizdinar,Shizdushund,Sholukkharb,Shundinulb,Shundushund,Shurakzund,Shuramunz,Tumunzadd,Tumunzan,Tumunzar,Tumunzinb,Tumunzir,Ukthad,Ulbirad,Ulbirar,Ulunzar,Ulur,Umunzad,Undalar,Undukkhil,Undun,Undur,Unduzur,Unzar,Unzathun,Usharar,Zaddinarg,Zaddushur,Zaharbad,Zaharbhizd,Zarakib,Zarakzar,Zaramunz,Zarukthel,Zinbarukth,Zirakinb,Zirakzir,Ziramunz,Ziruktharbh,Zirukthur,Zundumunz"}, - {name: "Goblin", i: 36, min: 4, max: 9, d: "eag", m: 0, b: "Asinx,Bhiagielt,Biokvish,Blix,Blus,Bratliaq,Breshass,Bridvelb,Brybsil,Bugbig,Buyagh,Cel,Chalk,Chiafzia,Chox,Cielb,Cosvil,Crekork,Crild,Croibieq,Diervaq,Dobruing,Driord,Eebligz,Een,Enissee,Esz,Far,Felhob,Froihiofz,Fruict,Fygsee,Gagablin,Gigganqi,Givzieqee,Glamzofs,Glernaahx,Gneabs,Gnoklig,Gobbledak,gobbok,Gobbrin,Heszai,Hiszils,Hobgar,Honk,Iahzaarm,Ialsirt,Ilm,Ish,Jasheafta,Joimtoilm,Kass,Katmelt,Kleabtong,Kleardeek,Klilm,Kluirm,Kuipuinx,Moft,Mogg,Nilbog,Oimzoishai,Onq,Ozbiard,Paas,Phax,Phigheldai,Preang,Prolkeh,Pyreazzi,Qeerags,Qosx,Rekx,Shaxi,Sios,Slehzit,Slofboif,Slukex,Srefs,Srurd,Stiaggaltia,Stiolx,Stioskurt,Stroir,Strytzakt,Stuikvact,Styrzangai,Suirx,Swaxi,Taxai,Thelt,Thresxea,Thult,Traglila,Treaq,Ulb,Ulm,Utha,Utiarm,Veekz,Vohniots,Vreagaald,Watvielx,Wrogdilk,Wruilt,Xurx,Ziggek,Zriokots"}, - {name: "Orc", i: 37, min: 4, max: 8, d: "gzrcu", m: 0, b: "Adgoz,Adgril-Gha,Adog,Adzurd,Agkadh,Agzil-Ghal,Akh,Ariz-Dru,Arkugzo,Arrordri,Ashnedh,Azrurdrekh,Bagzildre,Bashnud,Bedgez-Graz,Bhakh,Bhegh,Bhiccozdur,Bhicrur,Bhirgoshbel,Bhog,Bhurkrukh,Bod-Rugniz,Bogzel,Bozdra,Bozgrun,Bozziz,Bral-Lazogh,Brazadh,Brogved,Brogzozir,Brolzug,Brordegeg,Brorkril-Zrog,Brugroz,Brukh-Zrabrul,Brur-Korre,Bulbredh,Bulgragh,Chaz-Charard,Chegan-Khed,Chugga,Chuzar,Dhalgron-Mog,Dhazon-Ner,Dhezza,Dhoddud,Dhodh-Brerdrodh,Dhodh-Ghigin,Dhoggun-Bhogh,Dhulbazzol,Digzagkigh,Dirdrurd,Dodkakh,Dorgri,Drizdedh,Drobagh,Drodh-Ashnugh,Drogvukh-Drodh,Drukh-Qodgoz,Drurkuz,Dududh,Dur-Khaddol,Egmod,Ekh-Beccon,Ekh-Krerdrugh,Ekh-Mezred,Gagh-Druzred,Gazdrakh-Vrard,Gegnod,Gerkradh,Ghagrocroz,Ghared-Krin,Ghedgrolbrol,Gheggor,Ghizgil,Gho-Ugnud,Gholgard,Gidh-Ucceg,Goccogmurd,Golkon,Graz-Khulgag,Gribrabrokh,Gridkog,Grigh-Kaggaz,Grirkrun-Qur,Grughokh,Grurro,Gugh-Zozgrod,Gur-Ghogkagh,Ibagh-Chol,Ibruzzed,Ibul-Brad,Iggulzaz,Ikh-Ugnan,Irdrelzug,Irmekh-Bhor,Kacruz,Kalbrugh,Karkor-Zrid,Kazzuz-Zrar,Kezul-Bruz,Kharkiz,Khebun,Khorbric,Khuldrerra,Khuzdraz,Kirgol,Koggodh,Korkrir-Grar,Kraghird,Krar-Zurmurd,Krigh-Bhurdin,Kroddadh,Krudh-Khogzokh,Kudgroccukh,Kudrukh,Kudzal,Kuzgrurd-Dedh,Larud,Legvicrodh,Lorgran,Lugekh,Lulkore,Mazgar,Merkraz,Mocculdrer,Modh-Odod,Morbraz,Mubror,Muccug-Ghuz,Mughakh-Chil,Murmad,Nazad-Ludh,Negvidh,Nelzor-Zroz,Nirdrukh,Nogvolkar,Nubud,Nuccag,Nudh-Kuldra,Nuzecro,Oddigh-Krodh,Okh-Uggekh,Ordol,Orkudh-Bhur,Orrad,Qashnagh,Qiccad-Chal,Qiddolzog,Qidzodkakh,Qirzodh,Rarurd,Reradgri,Rezegh,Rezgrugh,Rodrekh,Rogh-Chirzaz,Rordrushnokh,Rozzez,Ruddirgrad,Rurguz-Vig,Ruzgrin,Ugh-Vruron,Ughudadh,Uldrukh-Bhudh,Ulgor,Ulkin,Ummugh-Ekh,Uzaggor,Uzdriboz,Uzdroz,Uzord,Uzron,Vaddog,Vagord-Khod,Velgrudh,Verrugh,Vrazin,Vrobrun,Vrugh-Nardrer,Vrurgu,Vuccidh,Vun-Gaghukh,Zacrad,Zalbrez,Zigmorbredh,Zordrordud,Zorrudh,Zradgukh,Zragmukh,Zragrizgrakh,Zraldrozzuz,Zrard-Krodog,Zrazzuz-Vaz,Zrigud,Zrulbukh-Dekh,Zubod-Ur,Zulbriz,Zun-Bergrord"}, - {name: "Giant", i: 38, min: 5, max: 10, d: "kdtng", m: 0, b: "Addund,Aerora,Agane,Anumush,Arangrim,Bahourg,Baragzund,Barakinb,Barakzig,Barakzinb,Baramunz,Barazinb,Beornelde,Beratira,Borgbert,Botharic,Bremrol,Brerstin,Brildung,Brozu,Bundushund,Burthug,Chazruc,Chergun,Churtec,Dagdhor,Dankuc,Darnaric,Debuch,Dina,Dinez,Diru,Drard,Druguk,Dugfast,Duhal,Dulkun,Eldond,Enuz,Eraddam,Eradhelm,Froththorn,Fynwyn,Gabaragz,Gabaram,Gabizir,Gabuzan,Gagkake,Galfald,Galgrim,Gatal,Gazin,Geru,Gila,Giledzir,Girkun,Glumvat,Gluthmark,Gomruch,Gorkege,Gortho,Gostuz,Grimor,Grimtira,Guddud,Gudgiz,Gulwo,Gunargath,Gundusharb,Guril,Gurkale,Guruge,Guzi,Hargarth,Hartreo,Heimfara,Hildlaug,Idgurth,Inez,Inginy,Iora,Irkin,Jaldhor,Jarwar,Jornangar,Jornmoth,Kakkek,Kaltoch,Kegkez,Kengord,Kharbharbiln,Khatharbar,Khathizdin,Khazanar,Khaziragz,Khizdabun,Khizdushel,Khundinarg,Kibarak,Kibizar,Kigine,Kilfond,Kilkan,Kinbadab,Kinbuzar,Koril,Kostand,Kuzake,Lindira,Lingarth,Maerdis,Magald,Marbold,Marbrand,Memron,Minu,Mistoch,Morluch,Mornkin,Morntaric,Nagu,Naragzah,Naramunz,Narazar,Nargabar,Nargatharb,Nargundush,Nargunul,Natan,Natil,Neliz,Nelkun,Noluch,Norginny,Nulbaram,Nulbilnarg,Nuledzah,Nuledzig,Nulukkhaz,Nulukkhur,Nurkel,Oci,Olane,Oldstin,Orga,Ranava,Ranhera,Rannerg,Rirkan,Rizen,Rurki,Rurkoc,Sadgach,Sgandrol,Sharakzar,Shatharbiz,Shathizdush,Shathola,Shizdinar,Sholukkharb,Shundushund,Shurakzund,Sidga,Sigbeorn,Sigbi,Solfod,Somrud,Srokvan,Stighere,Sulduch,Talkale,Theoddan,Theodgrim,Throtrek,Tigkiz,Tolkeg,Toren,Tozage,Tulkug,Tumunzar,Umunzad,Undukkhil,Usharar,Valdhere,Varkud,Velfirth,Velhera,Vigkan,Vorkige,Vozig,Vylwed,Widhyrde,Wylaeya,Yili,Yotane,Yudgor,Yulkake,Zigez,Zugkan,Zugke"}, - {name: "Draconic", i: 39, min: 6, max: 14, d: "aliuszrox", m: 0, b: "Aaronarra,Adalon,Adamarondor,Aeglyl,Aerosclughpalar,Aghazstamn,Aglaraerose,Agoshyrvor,Alduin,Alhazmabad,Altagos,Ammaratha,Amrennathed,Anaglathos,Andrathanach,Araemra,Araugauthos,Arauthator,Arharzel,Arngalor,Arveiaturace,Athauglas,Augaurath,Auntyrlothtor,Azarvilandral,Azhaq,Balagos,Baratathlaer,Bleucorundum,BrazzPolis,Canthraxis,Capnolithyl,Charvekkanathor,Chellewis,Chelnadatilar,Cirrothamalan,Claugiyliamatar,Cragnortherma,Dargentum,Dendeirmerdammarar,Dheubpurcwenpyl,Domborcojh,Draconobalen,Dragansalor,Dupretiskava,Durnehviir,Eacoathildarandus,Eldrisithain,Enixtryx,Eormennoth,Esmerandanna,Evenaelorathos,Faenphaele,Felgolos,Felrivenser,Firkraag,Fll'Yissetat,Furlinastis,Galadaeros,Galglentor,Garnetallisar,Garthammus,Gaulauntyr,Ghaulantatra,Glouroth,Greshrukk,Guyanothaz,Haerinvureem,Haklashara,Halagaster,Halaglathgar,Havarlan,Heltipyre,Hethcypressarvil,Hoondarrh,Icehauptannarthanyx,Iiurrendeem,Ileuthra,Iltharagh,Ingeloakastimizilian,Irdrithkryn,Ishenalyr,Iymrith,Jaerlethket,Jalanvaloss,Jharakkan,Kasidikal,Kastrandrethilian,Khavalanoth,Khuralosothantar,Kisonraathiisar,Kissethkashaan,Kistarianth,Klauth,Klithalrundrar,Krashos,Kreston,Kriionfanthicus,Krosulhah,Krustalanos,Kruziikrel,Kuldrak,Lareth,Latovenomer,Lhammaruntosz,Llimark,Ma'fel'no'sei'kedeh'naar,MaelestorRex,Magarovallanthanz,Mahatnartorian,Mahrlee,Malaeragoth,Malagarthaul,Malazan,Maldraedior,Maldrithor,MalekSalerno,Maughrysear,Mejas,Meliordianix,Merah,Mikkaalgensis,Mirmulnir,Mistinarperadnacles,Miteach,Mithbarazak,Morueme,Moruharzel,Naaslaarum,Nahagliiv,Nalavarauthatoryl,Naxorlytaalsxar,Nevalarich,Nolalothcaragascint,Nurvureem,Nymmurh,Odahviing,Olothontor,Ormalagos,Otaaryliakkarnos,Paarthurnax,Pelath,Pelendralaar,Praelorisstan,Praxasalandos,Protanther,Qiminstiir,Quelindritar,Ralionate,Rathalylaug,Rathguul,Rauglothgor,Raumorthadar,Relonikiv,Ringreemeralxoth,Roraurim,Rynnarvyx,Sablaxaahl,Sahloknir,Sahrotaar,Samdralyrion,Saryndalaghlothtor,Sawaka,Shalamalauth,Shammagar,Sharndrel,Shianax,Skarlthoon,Skurge,Smergadas,Ssalangan,Sssurist,Sussethilasis,Sylvallitham,Tamarand,Tantlevgithus,Tarlacoal,Tenaarlaktor,Thalagyrt,Tharas'kalagram,Thauglorimorgorus,Thoklastees,Thyka,Tsenshivah,Ueurwen,Uinnessivar,Urnalithorgathla,Velcuthimmorhar,Velora,Vendrathdammarar,Venomindhar,Viinturuth,Voaraghamanthar,Voslaarum,Vr'tark,Vrondahorevos,Vuljotnaak,Vulthuryol,Wastirek,Worlathaugh,Xargithorvar,Xavarathimius,Yemere,Ylithargathril,Ylveraasahlisar,Za-Jikku,Zarlandris,Zellenesterex,Zilanthar,Zormapalearath,Zundaerazylym,Zz'Pzora"}, - {name: "Arachnid", i: 40, min: 4, max: 10, d: "erlsk", m: 0, b: "Aaqok'ser,Aiced,Aizachis,Allinqel,As'taq,Ashrash,Caaqtos,Ceek'sax,Ceezuq,Cek'sier,Cen'qi,Ceqzocer,Cezeed,Chachocaq,Charis,Chashilieth,Checib,Chernul,Chezi,Chiazu,Chishros,Chixhi,Chizhi,Chollash,Choq'sha,Cinchichail,Collul,Ecush'taid,Ekiqe,Eqas,Er'uria,Erikas,Es'tase,Esrub,Exha,Haqsho,Hiavheesh,Hitha,Hok'thi,Hossa,Iacid,Iciever,Illuq,Isnir,Keezut,Kheellavas,Kheizoh,Khiachod,Khika,Khirzur,Khonrud,Khrakku,Khraqshis,Khrethish'ti,Khriashus,Khrika,Khrirni,Klashirel,Kleil'sha,Klishuth,Krarnit,Kras'tex,Krotieqas,Lais'tid,Laizuh,Lasnoth,Len'qeer,Leqanches,Lezad,Lhilir,Lhivhath,Lhok'thu,Lialliesed,Liaraq,Liceva,Lichorro,Lilla,Lokieqib,Nakur,Neerhaca,Neet'er,Neezoh,Nenchiled,Nerhalneth,Nir'ih,Nizus,Noreeqo,On'qix,Qalitho,Qas'tor,Qasol,Qavrud,Qavud,Qazar,Qazru,Qekno,Qeqravee,Qes'tor,Qhaik'sal,Qhak'sish,Qhazsakais,Qheliva,Qhenchaqes,Qherazal,Qhon'qos,Qhosh,Qish'tur,Qisih,Qorhoci,Qranchiq,Racith,Rak'zes,Ranchis,Rarhie,Rarzi,Rarzisiaq,Ras'tih,Ravosho,Recad,Rekid,Rernee,Rertachis,Rezhokketh,Reziel,Rhacish,Rhail'shel,Rhairhizse,Rhakivex,Rhaqeer,Rhartix,Rheciezsei,Rheevid,Rhel'shir,Rhevhie,Rhiavekot,Rhikkos,Rhiqese,Rhiqi,Rhiqracar,Rhisned,Rhousnateb,Riakeesnex,Rintachal,Rir'ul,Rourk'u,Rouzakri,Sailiqei,Sanchiqed,Saqshu,Sat'ier,Sazi,Seiqas,Shieth'i,Shiqsheh,Shizha,Shrachuvo,Shranqo,Shravhos,Shravuth,Shreerhod,Shrethuh,Shriantieth,Shronqash,Shrovarhir,Shrozih,Siacaqoh,Siezosh,Siq'sha,Sirro,Sornosi,Srachussi,Szaca,Szacih,Szaqova,Szasu,Szazhilos,Szeerrud,Szeezsad,Szeknur,Szesir,Szezhirros,Szilshith,Szon'qol,Szornuq,Xeekke,Yeek'su,Yeeq'zox,Yeqil,Yeqroq,Yeveed,Yevied,Yicaveeh,Yirresh,Yisie,Yithik'thaih,Yorhaqshes,Zacheek'sa,Zakkasa,Zelraq,Zeqo,Zharuncho,Zhath'arhish,Zhavirrit,Zhazilraq,Zhazsachiel,Zhek'tha,Zhequ,Zhias'ted,Zhicat,Zhicur,Zhirhacil,Zhizri,Zhochizses,Ziarih,Zirnib"}, - {name: "Serpents", i: 41, min: 5, max: 11, d: "slrk", m: 0, b: "Aj'ha,Aj'i,Aj'tiss,Ajakess,Aksas,Aksiss,Al'en,An'jeshe,Apjige,Arkkess,Athaz,Atus,Azras,Caji,Cakrasar,Cal'arrun,Capji,Cathras,Cej'han,Ces,Cez'jenta,Cij'te,Cinash,Cizran,Coth'jus,Cothrash,Culzanek,Cunaless,Ej'tesh,Elzazash,Ergek,Eshjuk,Ethris,Gan'jas,Gapja,Gar'thituph,Gopjeguss,Gor'thesh,Gragishaph,Grar'theness,Grath'ji,Gressinas,Grolzesh,Grorjar,Grozrash,Guj'ika,Harji,Hej'hez,Herkush,Horgarrez,Illuph,Ipjar,Ithashin,Kaj'ess,Kar'kash,Kepjusha,Ki'kintus,Kissere,Koph,Kopjess,Kra'kasher,Krak,Krapjez,Krashjuless,Kraz'ji,Krirrigis,Krussin,Ma'lush,Mage,Maj'tak,Mal'a,Mapja,Mar'kash,Mar'kis,Marjin,Mas,Mathan,Men'jas,Meth'jaresh,Mij'hegak,Min'jash,Mith'jas,Monassu,Moss,Naj'hass,Najugash,Nak,Napjiph,Nar'ka,Nar'thuss,Narrusha,Nash,Nashjekez,Nataph,Nij'ass,Nij'tessiph,Nishjiss,Norkkuss,Nus,Olluruss,Or'thi,Or'thuss,Paj'a,Parkka,Pas,Pathujen,Paz'jaz,Pepjerras,Pirkkanar,Pituk,Porjunek,Pu'ke,Ragen,Ran'jess,Rargush,Razjuph,Rilzan,Riss,Rithruz,Rorgiss,Rossez,Rraj'asesh,Rraj'tass,Rrar'kess,Rrar'thuph,Rras,Rrazresh,Rrej'hish,Rrigelash,Rris,Rris,Rroksurrush,Rukrussush,Rurri,Russa,Ruth'jes,Sa'kitesh,Sar'thass,Sarjas,Sazjuzush,Ser'thez,Sezrass,Shajas,Shas,Shashja,Shass,Shetesh,Shijek,Shun'jaler,Shurjarri,Skaler,Skalla,Skallentas,Skaph,Skar'kerriz,Skath'jeruk,Sker'kalas,Skor,Skoz'ji,Sku'lu,Skuph,Skur'thur,Slalli,Slalt'har,Slelziress,Slil'ar,Sloz'jisa,Sojesh,Solle,Sorge,Sral'e,Sran'ji,Srapjess,Srar'thazur,Srash,Srath'jess,Srathrarre,Srerkkash,Srus,Sruss'tugeph,Sun,Suss'tir,Uzrash,Vargush,Vek,Vess'tu,Viph,Vult'ha,Vupjer,Vushjesash,Xagez,Xassa,Xulzessu,Zaj'tiss,Zan'jer,Zarriss,Zassegus,Zirres,Zsor,Zurjass"}, - // additional by Avengium: - {name: "Levantine", i: 42, min: 4, max: 12, d: "ankprs", m: 0, b: "Adme,Adramet,Agadir,Akko,Akzib,Alimas,Alis-Ubbo,Alqosh,Amid,Ammon,Ampi,Amurru,Andarig,Anpa,Araden,Aram,Arwad,Ashkelon,Athar,Atiq,Aza,Azeka,Baalbek,Babel,Batrun,Beerot,Beersheba,Beit Shemesh,Berytus,Bet Agus,Bet Anya,Beth-Horon,Bethel,Bethlehem,Bethuel,Bet Nahrin,Bet Nohadra,Bet Zalin,Birmula,Biruta,Bit Agushi,Bitan,Bit Zamani,Cerne,Dammeseq,Darmsuq,Dor,Eddial,Eden Ekron,Elah,Emek,Emun,Ephratah,Eyn Ganim,Finike,Gades,Galatia,Gaza,Gebal,Gedera,Gerizzim,Gethsemane,Gibeon,Gilead,Gilgal,Golgotha,Goshen,Gytte,Hagalil,Haifa,Halab,Haqel Dma,Har Habayit,Har Nevo,Har Pisga,Havilah,Hazor,Hebron,Hormah,Iboshim,Iriho,Irinem,Irridu,Israel,Kadesh,Kanaan,Kapara,Karaly,Kart-Hadasht,Keret Chadeshet,Kernah,Kesed,Keysariya,Kfar,Kfar Nahum,Khalibon,Khalpe,Khamat,Kiryat,Kittim,Kurda,Lapethos,Larna,Lepqis,Lepriptza,Liksos,Lod,Luv,Malaka,Malet,Marat,Megido,Melitta,Merdin,Metsada,Mishmarot,Mitzrayim,Moab,Mopsos,Motye,Mukish,Nampigi,Nampigu,Natzrat,Nimrud,Nineveh,Nob,Nuhadra,Oea,Ofir,Oyat,Phineka,Phoenicus,Pleshet,Qart-Tubah Sarepta,Qatna,Rabat Amon,Rakkath,Ramat Aviv,Ramitha,Ramta,Rehovot,Reshef,Rushadir,Rushakad,Samrin,Sefarad,Sehyon,Sepat,Sexi,Sharon,Shechem,Shefelat,Shfanim,Shiloh,Shmaya,Shomron,Sidon,Sinay,Sis,Solki,Sur,Suria,Tabetu,Tadmur,Tarshish,Tartus,Teberya,Tefessedt,Tekoa,Teyman,Tinga,Tipasa,Tsabratan,Tur Abdin,Tzarfat,Tziyon,Tzor,Ugarit,Unubaal,Ureshlem,Urhay,Urushalim,Vaga,Yaffa,Yamhad,Yam hamelach,Yam Kineret,Yamutbal,Yathrib,Yaudi,Yavne,Yehuda,Yerushalayim,Yev,Yevus,Yizreel,Yurdnan,Zarefat,Zeboim,Zeurta,Zeytim,Zikhron,Zmurna"} - ]; -}; - -export { - getBase, - getCulture, - getCultureShort, - getBaseShort, - getState, - updateChain, - clearChains, - getNameBases, - getMapName, - calculateChain -}; \ No newline at end of file diff --git a/procedural/src/engine/modules/ocean-layers.js b/procedural/src/engine/modules/ocean-layers.js deleted file mode 100644 index 111ea44e..00000000 --- a/procedural/src/engine/modules/ocean-layers.js +++ /dev/null @@ -1,95 +0,0 @@ -"use strict"; - -export function generateOceanLayers(grid, config, utils) { - const { lineGen, clipPoly, round, rn, P } = utils; - - if (config.outline === "none") return { layers: [] }; - - const cells = grid.cells; - const pointsN = grid.cells.i.length; - const vertices = grid.vertices; - const limits = config.outline === "random" ? randomizeOutline(P) : config.outline.split(",").map(s => +s); - - const chains = []; - const opacity = rn(0.4 / limits.length, 2); - const used = new Uint8Array(pointsN); // to detect already passed cells - - for (const i of cells.i) { - const t = cells.t[i]; - if (t > 0) continue; - if (used[i] || !limits.includes(t)) continue; - const start = findStart(i, t, cells, vertices, pointsN); - if (!start) continue; - used[i] = 1; - const chain = connectVertices(start, t, cells, vertices, pointsN, used); // vertices chain to form a path - if (chain.length < 4) continue; - const relax = 1 + t * -2; // select only n-th point - const relaxed = chain.filter((v, i) => !(i % relax) || vertices.c[v].some(c => c >= pointsN)); - if (relaxed.length < 4) continue; - const points = clipPoly( - relaxed.map(v => vertices.p[v]), - config, - 1 - ); - chains.push([t, points]); - } - - const layers = []; - for (const t of limits) { - const layer = chains.filter(c => c[0] === t); - const paths = layer.map(c => round(lineGen(c[1]))).filter(path => path); - if (paths.length > 0) { - layers.push({ - type: t, - paths: paths, - opacity: opacity - }); - } - } - - return { layers }; -} - -function randomizeOutline(P) { - const limits = []; - let odd = 0.2; - for (let l = -9; l < 0; l++) { - if (P(odd)) { - odd = 0.2; - limits.push(l); - } else { - odd *= 2; - } - } - return limits; -} - -// find eligible cell vertex to start path detection -function findStart(i, t, cells, vertices, pointsN) { - if (cells.b[i]) return cells.v[i].find(v => vertices.c[v].some(c => c >= pointsN)); // map border cell - return cells.v[i][cells.c[i].findIndex(c => cells.t[c] < t || !cells.t[c])]; -} - -// connect vertices to chain -function connectVertices(start, t, cells, vertices, pointsN, used) { - const chain = []; // vertices chain to form a path - for (let i = 0, current = start; i === 0 || (current !== start && i < 10000); i++) { - const prev = chain[chain.length - 1]; // previous vertex in chain - chain.push(current); // add current vertex to sequence - const c = vertices.c[current]; // cells adjacent to vertex - c.filter(c => cells.t[c] === t).forEach(c => (used[c] = 1)); - const v = vertices.v[current]; // neighboring vertices - const c0 = !cells.t[c[0]] || cells.t[c[0]] === t - 1; - const c1 = !cells.t[c[1]] || cells.t[c[1]] === t - 1; - const c2 = !cells.t[c[2]] || cells.t[c[2]] === t - 1; - if (v[0] !== undefined && v[0] !== prev && c0 !== c1) current = v[0]; - else if (v[1] !== undefined && v[1] !== prev && c1 !== c2) current = v[1]; - else if (v[2] !== undefined && v[2] !== prev && c0 !== c2) current = v[2]; - if (current === chain[chain.length - 1]) { - console.error("Next vertex is not found"); - break; - } - } - chain.push(chain[0]); // push first vertex as the last one - return chain; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/provinces-generator.js b/procedural/src/engine/modules/provinces-generator.js deleted file mode 100644 index 0b22a8a7..00000000 --- a/procedural/src/engine/modules/provinces-generator.js +++ /dev/null @@ -1,309 +0,0 @@ -"use strict"; - -const forms = { - Monarchy: {County: 22, Earldom: 6, Shire: 2, Landgrave: 2, Margrave: 2, Barony: 2, Captaincy: 1, Seneschalty: 1}, - Republic: {Province: 6, Department: 2, Governorate: 2, District: 1, Canton: 1, Prefecture: 1}, - Theocracy: {Parish: 3, Deanery: 1}, - Union: {Province: 1, State: 1, Canton: 1, Republic: 1, County: 1, Council: 1}, - Anarchy: {Council: 1, Commune: 1, Community: 1, Tribe: 1}, - Wild: {Territory: 10, Land: 5, Region: 2, Tribe: 1, Clan: 1, Dependency: 1, Area: 1} -}; - -/** - * Generates provinces for states - * - * REQUIRES: - * - pack.cells.state (from BurgsAndStates module) - * - pack.cells.burg (from BurgsAndStates module) - * - pack.states (from BurgsAndStates module) - * - pack.burgs (from BurgsAndStates module) - * - config.debug (debug configuration) - * - * PROVIDES: - * - pack.cells.province (province assignments) - * - pack.provinces (provinces array) - */ -export const generate = (pack, config, utils, regenerate = false, regenerateLockedStates = false) => { - // Check required properties exist - if (!pack.cells.state) { - throw new Error("Provinces module requires cells.state from BurgsAndStates module"); - } - if (!pack.cells.burg) { - throw new Error("Provinces module requires cells.burg from BurgsAndStates module"); - } - if (!pack.states) { - throw new Error("Provinces module requires pack.states from BurgsAndStates module"); - } - if (!pack.burgs) { - throw new Error("Provinces module requires pack.burgs from BurgsAndStates module"); - } - if (!config.debug) { - throw new Error("Provinces module requires config.debug section"); - } - - const { - generateSeed, - aleaPRNG, - gauss, - P, - Names, - rw, - getMixedColor, - BurgsAndStates, - COA, - FlatQueue, - d3, - rand - } = utils; - const { TIME } = config.debug; - - TIME && console.time("generateProvinces"); - const localSeed = regenerate ? generateSeed() : config.seed; - Math.random = aleaPRNG(localSeed); - - const {cells, states, burgs} = pack; - const provinces = [0]; // 0 index is reserved for "no province" - const provinceIds = new Uint16Array(cells.i.length); - - const isProvinceLocked = province => province.lock || (!regenerateLockedStates && states[province.state]?.lock); - const isProvinceCellLocked = cell => provinceIds[cell] && isProvinceLocked(provinces[provinceIds[cell]]); - - if (regenerate) { - pack.provinces.forEach(province => { - if (!province.i || province.removed || !isProvinceLocked(province)) return; - - const newId = provinces.length; - for (const i of cells.i) { - if (cells.province[i] === province.i) provinceIds[i] = newId; - } - - province.i = newId; - provinces.push(province); - }); - } - - const provincesRatio = config.provincesRatio; - const max = provincesRatio == 100 ? 1000 : gauss(20, 5, 5, 100) * provincesRatio ** 0.5; // max growth - - // generate provinces for selected burgs - states.forEach(s => { - s.provinces = []; - if (!s.i || s.removed) return; - if (provinces.length) s.provinces = provinces.filter(p => p.state === s.i).map(p => p.i); // locked provinces ids - if (s.lock && !regenerateLockedStates) return; // don't regenerate provinces of a locked state - - const stateBurgs = burgs - .filter(b => b.state === s.i && !b.removed && !provinceIds[b.cell]) - .sort((a, b) => b.population * gauss(1, 0.2, 0.5, 1.5, 3) - a.population) - .sort((a, b) => b.capital - a.capital); - if (stateBurgs.length < 2) return; // at least 2 provinces are required - const provincesNumber = Math.max(Math.ceil((stateBurgs.length * provincesRatio) / 100), 2); - - const form = Object.assign({}, forms[s.form]); - - for (let i = 0; i < provincesNumber; i++) { - const provinceId = provinces.length; - const center = stateBurgs[i].cell; - const burg = stateBurgs[i].i; - const c = stateBurgs[i].culture; - const nameByBurg = P(0.5); - const name = nameByBurg ? stateBurgs[i].name : Names.getState(Names.getCultureShort(c), c); - const formName = rw(form); - form[formName] += 10; - const fullName = name + " " + formName; - const color = getMixedColor(s.color); - const kinship = nameByBurg ? 0.8 : 0.4; - const type = BurgsAndStates.getType(center, burg.port); - const coa = COA.generate(stateBurgs[i].coa, kinship, null, type); - coa.shield = COA.getShield(c, s.i); - - s.provinces.push(provinceId); - provinces.push({i: provinceId, state: s.i, center, burg, name, formName, fullName, color, coa}); - } - }); - - // expand generated provinces - const queue = new FlatQueue(); - const cost = []; - - provinces.forEach(p => { - if (!p.i || p.removed || isProvinceLocked(p)) return; - provinceIds[p.center] = p.i; - queue.push({e: p.center, province: p.i, state: p.state, p: 0}, 0); - cost[p.center] = 1; - }); - - while (queue.length) { - const {e, p, province, state} = queue.pop(); - - cells.c[e].forEach(e => { - if (isProvinceCellLocked(e)) return; // do not overwrite cell of locked provinces - - const land = cells.h[e] >= 20; - if (!land && !cells.t[e]) return; // cannot pass deep ocean - if (land && cells.state[e] !== state) return; - const evevation = cells.h[e] >= 70 ? 100 : cells.h[e] >= 50 ? 30 : cells.h[e] >= 20 ? 10 : 100; - const totalCost = p + evevation; - - if (totalCost > max) return; - if (!cost[e] || totalCost < cost[e]) { - if (land) provinceIds[e] = province; // assign province to a cell - cost[e] = totalCost; - queue.push({e, province, state, p: totalCost}, totalCost); - } - }); - } - - // justify provinces shapes a bit - for (const i of cells.i) { - if (cells.burg[i]) continue; // do not overwrite burgs - if (isProvinceCellLocked(i)) continue; // do not overwrite cell of locked provinces - - const neibs = cells.c[i] - .filter(c => cells.state[c] === cells.state[i] && !isProvinceCellLocked(c)) - .map(c => provinceIds[c]); - const adversaries = neibs.filter(c => c !== provinceIds[i]); - if (adversaries.length < 2) continue; - - const buddies = neibs.filter(c => c === provinceIds[i]).length; - if (buddies.length > 2) continue; - - const competitors = adversaries.map(p => adversaries.reduce((s, v) => (v === p ? s + 1 : s), 0)); - const max = d3.max(competitors); - if (buddies >= max) continue; - - provinceIds[i] = adversaries[competitors.indexOf(max)]; - } - - // add "wild" provinces if some cells don't have a province assigned - const noProvince = Array.from(cells.i).filter(i => cells.state[i] && !provinceIds[i]); // cells without province assigned - states.forEach(s => { - if (!s.i || s.removed) return; - if (s.lock && !regenerateLockedStates) return; - if (!s.provinces.length) return; - - const coreProvinceNames = s.provinces.map(p => provinces[p]?.name); - const colonyNamePool = [s.name, ...coreProvinceNames].filter(name => name && !/new/i.test(name)); - const getColonyName = () => { - if (colonyNamePool.length < 1) return null; - - const index = rand(colonyNamePool.length - 1); - const spliced = colonyNamePool.splice(index, 1); - return spliced[0] ? `New ${spliced[0]}` : null; - }; - - let stateNoProvince = noProvince.filter(i => cells.state[i] === s.i && !provinceIds[i]); - while (stateNoProvince.length) { - // add new province - const provinceId = provinces.length; - const burgCell = stateNoProvince.find(i => cells.burg[i]); - const center = burgCell ? burgCell : stateNoProvince[0]; - const burg = burgCell ? cells.burg[burgCell] : 0; - provinceIds[center] = provinceId; - - // expand province - const cost = []; - cost[center] = 1; - queue.push({e: center, p: 0}, 0); - while (queue.length) { - const {e, p} = queue.pop(); - - cells.c[e].forEach(nextCellId => { - if (provinceIds[nextCellId]) return; - const land = cells.h[nextCellId] >= 20; - if (cells.state[nextCellId] && cells.state[nextCellId] !== s.i) return; - const ter = land ? (cells.state[nextCellId] === s.i ? 3 : 20) : cells.t[nextCellId] ? 10 : 30; - const totalCost = p + ter; - - if (totalCost > max) return; - if (!cost[nextCellId] || totalCost < cost[nextCellId]) { - if (land && cells.state[nextCellId] === s.i) provinceIds[nextCellId] = provinceId; // assign province to a cell - cost[nextCellId] = totalCost; - queue.push({e: nextCellId, p: totalCost}, totalCost); - } - }); - } - - // generate "wild" province name - const c = cells.culture[center]; - const f = pack.features[cells.f[center]]; - const color = getMixedColor(s.color); - - const provCells = stateNoProvince.filter(i => provinceIds[i] === provinceId); - const singleIsle = provCells.length === f.cells && !provCells.find(i => cells.f[i] !== f.i); - const isleGroup = !singleIsle && !provCells.find(i => pack.features[cells.f[i]].group !== "isle"); - const colony = !singleIsle && !isleGroup && P(0.5) && !isPassable(s.center, center); - - const name = (() => { - const colonyName = colony && P(0.8) && getColonyName(); - if (colonyName) return colonyName; - if (burgCell && P(0.5)) return burgs[burg].name; - return Names.getState(Names.getCultureShort(c), c); - })(); - - const formName = (() => { - if (singleIsle) return "Island"; - if (isleGroup) return "Islands"; - if (colony) return "Colony"; - return rw(forms["Wild"]); - })(); - - const fullName = name + " " + formName; - - const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3); - const kinship = dominion ? 0 : 0.4; - const type = BurgsAndStates.getType(center, burgs[burg]?.port); - const coa = COA.generate(s.coa, kinship, dominion, type); - coa.shield = COA.getShield(c, s.i); - - provinces.push({i: provinceId, state: s.i, center, burg, name, formName, fullName, color, coa}); - s.provinces.push(provinceId); - - // check if there is a land way within the same state between two cells - function isPassable(from, to) { - if (cells.f[from] !== cells.f[to]) return false; // on different islands - const passableQueue = [from], - used = new Uint8Array(cells.i.length), - state = cells.state[from]; - while (passableQueue.length) { - const current = passableQueue.pop(); - if (current === to) return true; // way is found - cells.c[current].forEach(c => { - if (used[c] || cells.h[c] < 20 || cells.state[c] !== state) return; - passableQueue.push(c); - used[c] = 1; - }); - } - return false; // way is not found - } - - // re-check - stateNoProvince = noProvince.filter(i => cells.state[i] === s.i && !provinceIds[i]); - } - }); - - TIME && console.timeEnd("generateProvinces"); - - return { - provinces, - provinceIds - }; -}; - -// calculate pole of inaccessibility for each province -export const getPoles = (pack, utils) => { - const { getPolesOfInaccessibility } = utils; - - const getType = cellId => pack.cells.province[cellId]; - const poles = getPolesOfInaccessibility(pack, getType); - - const updatedProvinces = pack.provinces.map(province => { - if (!province.i || province.removed) return province; - return { - ...province, - pole: poles[province.i] || [0, 0] - }; - }); - - return updatedProvinces; -}; diff --git a/procedural/src/engine/modules/religions-generator.js b/procedural/src/engine/modules/religions-generator.js deleted file mode 100644 index d4e93a30..00000000 --- a/procedural/src/engine/modules/religions-generator.js +++ /dev/null @@ -1,1004 +0,0 @@ -"use strict"; - -// name generation approach and relative chance to be selected -const approach = { - Number: 1, - Being: 3, - Adjective: 5, - "Color + Animal": 5, - "Adjective + Animal": 5, - "Adjective + Being": 5, - "Adjective + Genitive": 1, - "Color + Being": 3, - "Color + Genitive": 3, - "Being + of + Genitive": 2, - "Being + of the + Genitive": 1, - "Animal + of + Genitive": 1, - "Adjective + Being + of + Genitive": 2, - "Adjective + Animal + of + Genitive": 2 -}; - -// turn weighted array into simple array -const approaches = []; -for (const a in approach) { - for (let j = 0; j < approach[a]; j++) { - approaches.push(a); - } -} - -const base = { - number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"], - being: [ - "Ancestor", - "Ancient", - "Avatar", - "Brother", - "Champion", - "Chief", - "Council", - "Creator", - "Deity", - "Divine One", - "Elder", - "Enlightened Being", - "Father", - "Forebear", - "Forefather", - "Giver", - "God", - "Goddess", - "Guardian", - "Guide", - "Hierach", - "Lady", - "Lord", - "Maker", - "Master", - "Mother", - "Numen", - "Oracle", - "Overlord", - "Protector", - "Reaper", - "Ruler", - "Sage", - "Seer", - "Sister", - "Spirit", - "Supreme Being", - "Transcendent", - "Virgin" - ], - animal: [ - "Antelope", - "Ape", - "Badger", - "Basilisk", - "Bear", - "Beaver", - "Bison", - "Boar", - "Buffalo", - "Camel", - "Cat", - "Centaur", - "Cerberus", - "Chimera", - "Cobra", - "Cockatrice", - "Crane", - "Crocodile", - "Crow", - "Cyclope", - "Deer", - "Dog", - "Direwolf", - "Drake", - "Dragon", - "Eagle", - "Elephant", - "Elk", - "Falcon", - "Fox", - "Goat", - "Goose", - "Gorgon", - "Gryphon", - "Hare", - "Hawk", - "Heron", - "Hippogriff", - "Horse", - "Hound", - "Hyena", - "Ibis", - "Jackal", - "Jaguar", - "Kitsune", - "Kraken", - "Lark", - "Leopard", - "Lion", - "Manticore", - "Mantis", - "Marten", - "Minotaur", - "Moose", - "Mule", - "Narwhal", - "Owl", - "Ox", - "Panther", - "Pegasus", - "Phoenix", - "Python", - "Rat", - "Raven", - "Roc", - "Rook", - "Scorpion", - "Serpent", - "Shark", - "Sheep", - "Snake", - "Sphinx", - "Spider", - "Swan", - "Tiger", - "Turtle", - "Unicorn", - "Viper", - "Vulture", - "Walrus", - "Wolf", - "Wolverine", - "Worm", - "Wyvern", - "Yeti" - ], - adjective: [ - "Aggressive", - "Almighty", - "Ancient", - "Beautiful", - "Benevolent", - "Big", - "Blind", - "Blond", - "Bloody", - "Brave", - "Broken", - "Brutal", - "Burning", - "Calm", - "Celestial", - "Cheerful", - "Crazy", - "Cruel", - "Dead", - "Deadly", - "Devastating", - "Distant", - "Disturbing", - "Divine", - "Dying", - "Eternal", - "Ethernal", - "Empyreal", - "Enigmatic", - "Enlightened", - "Evil", - "Explicit", - "Fair", - "Far", - "Fat", - "Fatal", - "Favorable", - "Flying", - "Friendly", - "Frozen", - "Giant", - "Good", - "Grateful", - "Great", - "Happy", - "High", - "Holy", - "Honest", - "Huge", - "Hungry", - "Illustrious", - "Immutable", - "Ineffable", - "Infallible", - "Inherent", - "Last", - "Latter", - "Lost", - "Loud", - "Lucky", - "Mad", - "Magical", - "Main", - "Major", - "Marine", - "Mythical", - "Mystical", - "Naval", - "New", - "Noble", - "Old", - "Otherworldly", - "Patient", - "Peaceful", - "Pregnant", - "Prime", - "Proud", - "Pure", - "Radiant", - "Resplendent", - "Sacred", - "Sacrosanct", - "Sad", - "Scary", - "Secret", - "Selected", - "Serene", - "Severe", - "Silent", - "Sleeping", - "Slumbering", - "Sovereign", - "Strong", - "Sunny", - "Superior", - "Supernatural", - "Sustainable", - "Transcendent", - "Transcendental", - "Troubled", - "Unearthly", - "Unfathomable", - "Unhappy", - "Unknown", - "Unseen", - "Waking", - "Wild", - "Wise", - "Worried", - "Young" - ], - genitive: [ - "Cold", - "Day", - "Death", - "Doom", - "Fate", - "Fire", - "Fog", - "Frost", - "Gates", - "Heaven", - "Home", - "Ice", - "Justice", - "Life", - "Light", - "Lightning", - "Love", - "Nature", - "Night", - "Pain", - "Snow", - "Springs", - "Summer", - "Thunder", - "Time", - "Victory", - "War", - "Winter" - ], - theGenitive: [ - "Abyss", - "Blood", - "Dawn", - "Earth", - "East", - "Eclipse", - "Fall", - "Harvest", - "Moon", - "North", - "Peak", - "Rainbow", - "Sea", - "Sky", - "South", - "Stars", - "Storm", - "Sun", - "Tree", - "Underworld", - "West", - "Wild", - "Word", - "World" - ], - color: [ - "Amber", - "Black", - "Blue", - "Bright", - "Bronze", - "Brown", - "Coral", - "Crimson", - "Dark", - "Emerald", - "Golden", - "Green", - "Grey", - "Indigo", - "Lavender", - "Light", - "Magenta", - "Maroon", - "Orange", - "Pink", - "Plum", - "Purple", - "Red", - "Ruby", - "Sapphire", - "Teal", - "Turquoise", - "White", - "Yellow" - ] -}; - -const forms = { - Folk: { - Shamanism: 4, - Animism: 4, - Polytheism: 4, - "Ancestor Worship": 2, - "Nature Worship": 1, - Totemism: 1 - }, - Organized: { - Polytheism: 7, - Monotheism: 7, - Dualism: 3, - Pantheism: 2, - "Non-theism": 2 - }, - Cult: { - Cult: 5, - "Dark Cult": 5, - Sect: 1 - }, - Heresy: { - Heresy: 1 - } -}; - -const namingMethods = { - Folk: { - "Culture + type": 1 - }, - - Organized: { - "Random + type": 3, - "Random + ism": 1, - "Supreme + ism": 5, - "Faith of + Supreme": 5, - "Place + ism": 1, - "Culture + ism": 2, - "Place + ian + type": 6, - "Culture + type": 4 - }, - - Cult: { - "Burg + ian + type": 2, - "Random + ian + type": 1, - "Type + of the + meaning": 2 - }, - - Heresy: { - "Burg + ian + type": 3, - "Random + ism": 3, - "Random + ian + type": 2, - "Type + of the + meaning": 1 - } -}; - -const types = { - Shamanism: {Beliefs: 3, Shamanism: 2, Druidism: 1, Spirits: 1}, - Animism: {Spirits: 3, Beliefs: 1}, - Polytheism: {Deities: 3, Faith: 1, Gods: 1, Pantheon: 1}, - "Ancestor Worship": {Beliefs: 1, Forefathers: 2, Ancestors: 2}, - "Nature Worship": {Beliefs: 3, Druids: 1}, - Totemism: {Beliefs: 2, Totems: 2, Idols: 1}, - - Monotheism: {Religion: 2, Church: 3, Faith: 1}, - Dualism: {Religion: 3, Faith: 1, Cult: 1}, - Pantheism: {Religion: 1, Faith: 1}, - "Non-theism": {Beliefs: 3, Spirits: 1}, - - Cult: {Cult: 4, Sect: 2, Arcanum: 1, Order: 1, Worship: 1}, - "Dark Cult": {Cult: 2, Blasphemy: 1, Circle: 1, Coven: 1, Idols: 1, Occultism: 1}, - Sect: {Sect: 3, Society: 1}, - - Heresy: { - Heresy: 3, - Sect: 2, - Apostates: 1, - Brotherhood: 1, - Circle: 1, - Dissent: 1, - Dissenters: 1, - Iconoclasm: 1, - Schism: 1, - Society: 1 - } -}; - -const expansionismMap = { - Folk: () => 0, - Organized: (utils) => utils.gauss(5, 3, 0, 10, 1), - Cult: (utils) => utils.gauss(0.5, 0.5, 0, 5, 1), - Heresy: (utils) => utils.gauss(1, 0.5, 0, 5, 1) -}; - -/** - * Generates religions for the map - * - * REQUIRES: - * - pack.cells.culture (from cultures module) - * - pack.cells.state (from BurgsAndStates module) - * - pack.cultures (from cultures module) - * - config.religionsNumber (number of religions to generate) - * - config.debug (debug configuration) - * - * PROVIDES: - * - pack.cells.religion (religion assignments) - * - pack.religions (religions array) - */ -export function generate(pack, grid, config, utils) { - // Check required properties exist - if (!pack.cells.culture) { - throw new Error("Religions module requires cells.culture from Cultures module"); - } - if (!pack.cells.state) { - throw new Error("Religions module requires cells.state from BurgsAndStates module"); - } - if (!pack.cultures) { - throw new Error("Religions module requires pack.cultures from Cultures module"); - } - if (!config.religionsNumber) { - throw new Error("Religions module requires config.religionsNumber"); - } - if (!config.debug) { - throw new Error("Religions module requires config.debug section"); - } - - const { TIME } = config.debug; - TIME && console.time("generateReligions"); - const lockedReligions = pack.religions?.filter(r => r.i && r.lock && !r.removed) || []; - - const folkReligions = generateFolkReligions(pack, utils); - const organizedReligions = generateOrganizedReligions(pack, grid, config.religionsNumber, lockedReligions, utils); - - const namedReligions = specifyReligions(pack, [...folkReligions, ...organizedReligions], utils); - const indexedReligions = combineReligions(namedReligions, lockedReligions, utils); - const religionIds = expandReligions(pack, grid, indexedReligions, config, utils); - const religions = defineOrigins(pack, religionIds, indexedReligions, utils); - - const newPack = { - ...pack, - religions: religions, - cells: { - ...pack.cells, - religion: religionIds - } - }; - - const finalPack = checkCenters(newPack); - - TIME && console.timeEnd("generateReligions"); - return finalPack; -} - -function generateFolkReligions(pack, utils) { - const {rw} = utils; - return pack.cultures - .filter(c => c.i && !c.removed) - .map(culture => ({type: "Folk", form: rw(forms.Folk), culture: culture.i, center: culture.center})); -} - -function generateOrganizedReligions(pack, grid, desiredReligionNumber, lockedReligions, utils) { - const {rand, rw, d3, WARN} = utils; - const {graphWidth, graphHeight} = grid; - const cells = pack.cells; - const lockedReligionCount = lockedReligions.filter(({type}) => type !== "Folk").length || 0; - const requiredReligionsNumber = desiredReligionNumber - lockedReligionCount; - if (requiredReligionsNumber < 1) return []; - - const candidateCells = getCandidateCells(); - const religionCores = placeReligions(); - - const cultsCount = Math.floor((rand(1, 4) / 10) * religionCores.length); // 10-40% - const heresiesCount = Math.floor((rand(0, 3) / 10) * religionCores.length); // 0-30% - const organizedCount = religionCores.length - cultsCount - heresiesCount; - - const getType = index => { - if (index < organizedCount) return "Organized"; - if (index < organizedCount + cultsCount) return "Cult"; - return "Heresy"; - }; - - return religionCores.map((cellId, index) => { - const type = getType(index); - const form = rw(forms[type]); - const cultureId = cells.culture[cellId]; - - return {type, form, culture: cultureId, center: cellId}; - }); - - function placeReligions() { - const religionCells = []; - const religionsTree = d3.quadtree(); - - // pre-populate with locked centers - lockedReligions.forEach(({center}) => religionsTree.add(cells.p[center])); - - // min distance between religion inceptions - const spacing = (graphWidth + graphHeight) / 2 / desiredReligionNumber; - - for (const cellId of candidateCells) { - const [x, y] = cells.p[cellId]; - - if (religionsTree.find(x, y, spacing) === undefined) { - religionCells.push(cellId); - religionsTree.add([x, y]); - - if (religionCells.length === requiredReligionsNumber) return religionCells; - } - } - - WARN && console.warn(`Placed only ${religionCells.length} of ${requiredReligionsNumber} religions`); - return religionCells; - } - - function getCandidateCells() { - const validBurgs = pack.burgs.filter(b => b.i && !b.removed); - - if (validBurgs.length >= requiredReligionsNumber) - return validBurgs.sort((a, b) => b.population - a.population).map(burg => burg.cell); - return cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]); - } -} - -function specifyReligions(pack, newReligions, utils) { - const {getRandomColor, getMixedColor} = utils; - const {cells, cultures} = pack; - - const rawReligions = newReligions.map(({type, form, culture: cultureId, center}) => { - const supreme = getDeityName(cultureId, utils); - const deity = form === "Non-theism" || form === "Animism" ? null : supreme; - - const stateId = cells.state[center]; - - let [name, expansion] = generateReligionName(pack, type, form, supreme, center, utils); - if (expansion === "state" && !stateId) expansion = "global"; - - const expansionism = expansionismMap[type](utils); - const color = getReligionColor(cultures[cultureId], type); - - return {name, type, form, culture: cultureId, center, deity, expansion, expansionism, color}; - }); - - return rawReligions; - - function getReligionColor(culture, type) { - if (!culture.i) return getRandomColor(); - - if (type === "Folk") return culture.color; - if (type === "Heresy") return getMixedColor(culture.color, 0.35, 0.2); - if (type === "Cult") return getMixedColor(culture.color, 0.5, 0); - return getMixedColor(culture.color, 0.25, 0.4); - } -} - -// indexes, conditionally renames, and abbreviates religions -function combineReligions(namedReligions, lockedReligions, utils) { - const {abbreviate} = utils; - const indexedReligions = [{name: "No religion", i: 0}]; - - const {lockedReligionQueue, highestLockedIndex, codes, numberLockedFolk} = parseLockedReligions(); - const maxIndex = Math.max( - highestLockedIndex, - namedReligions.length + lockedReligions.length + 1 - numberLockedFolk - ); - - for (let index = 1, progress = 0; index < maxIndex; index = indexedReligions.length) { - // place locked religion back at its old index - if (index === lockedReligionQueue[0]?.i) { - const nextReligion = lockedReligionQueue.shift(); - indexedReligions.push(nextReligion); - continue; - } - - // slot the new religions - if (progress < namedReligions.length) { - const nextReligion = namedReligions[progress]; - progress++; - - if ( - nextReligion.type === "Folk" && - lockedReligions.some(({type, culture}) => type === "Folk" && culture === nextReligion.culture) - ) - continue; // when there is a locked Folk religion for this culture discard duplicate - - const newName = renameOld(nextReligion); - const code = abbreviate(newName, codes); - codes.push(code); - indexedReligions.push({...nextReligion, i: index, name: newName, code}); - continue; - } - - indexedReligions.push({i: index, type: "Folk", culture: 0, name: "Removed religion", removed: true}); - } - return indexedReligions; - - function parseLockedReligions() { - // copy and sort the locked religions list - const lockedReligionQueue = lockedReligions - .map(religion => { - // and filter their origins to locked religions - let newOrigin = religion.origins.filter(n => lockedReligions.some(({i: index}) => index === n)); - if (newOrigin.length === 0) newOrigin = [0]; - return {...religion, origins: newOrigin}; - }) - .sort((a, b) => a.i - b.i); - - const highestLockedIndex = Math.max(...lockedReligions.map(r => r.i)); - const codes = lockedReligions.length > 0 ? lockedReligions.map(r => r.code) : []; - const numberLockedFolk = lockedReligions.filter(({type}) => type === "Folk").length; - - return {lockedReligionQueue, highestLockedIndex, codes, numberLockedFolk}; - } - - // prepend 'Old' to names of folk religions which have organized competitors - function renameOld({name, type, culture: cultureId}) { - if (type !== "Folk") return name; - - const haveOrganized = - namedReligions.some( - ({type, culture, expansion}) => culture === cultureId && type === "Organized" && expansion === "culture" - ) || - lockedReligions.some( - ({type, culture, expansion}) => culture === cultureId && type === "Organized" && expansion === "culture" - ); - if (haveOrganized && name.slice(0, 3) !== "Old") return `Old ${name}`; - return name; - } -} - -// finally generate and stores origins trees -function defineOrigins(pack, religionIds, indexedReligions, utils) { - const {each} = utils; - const religionOriginsParamsMap = { - Organized: {clusterSize: 100, maxReligions: 2}, - Cult: {clusterSize: 50, maxReligions: 3}, - Heresy: {clusterSize: 50, maxReligions: 4} - }; - - const origins = indexedReligions.map(({i, type, culture: cultureId, expansion, center}) => { - if (i === 0) return null; // no religion - if (type === "Folk") return [0]; // folk religions originate from its parent culture only - - const folkReligion = indexedReligions.find(({culture, type}) => type === "Folk" && culture === cultureId); - const isFolkBased = folkReligion && cultureId && expansion === "culture" && each(2)(center); - if (isFolkBased) return [folkReligion.i]; - - const {clusterSize, maxReligions} = religionOriginsParamsMap[type]; - const fallbackOrigin = folkReligion?.i || 0; - return getReligionsInRadius(pack.cells.c, center, religionIds, i, clusterSize, maxReligions, fallbackOrigin); - }); - - return indexedReligions.map((religion, index) => ({...religion, origins: origins[index]})); -} - -function getReligionsInRadius(neighbors, center, religionIds, religionId, clusterSize, maxReligions, fallbackOrigin) { - const foundReligions = new Set(); - const queue = [center]; - const checked = {}; - - for (let size = 0; queue.length && size < clusterSize; size++) { - const cellId = queue.shift(); - checked[cellId] = true; - - for (const neibId of neighbors[cellId]) { - if (checked[neibId]) continue; - checked[neibId] = true; - - const neibReligion = religionIds[neibId]; - if (neibReligion && neibReligion < religionId) foundReligions.add(neibReligion); - if (foundReligions.size >= maxReligions) return [...foundReligions]; - queue.push(neibId); - } - } - - return foundReligions.size ? [...foundReligions] : [fallbackOrigin]; -} - -// growth algorithm to assign cells to religions -function expandReligions(pack, grid, religions, config, utils) { - const {FlatQueue, isWater} = utils; - const {cells, routes} = pack; - const religionIds = spreadFolkReligions(religions); - - const queue = new FlatQueue(); - const cost = []; - - // limit cost for organized religions growth - const maxExpansionCost = (cells.i.length / 20) * config.growthRate; - - religions - .filter(r => r.i && !r.lock && r.type !== "Folk" && !r.removed) - .forEach(r => { - religionIds[r.center] = r.i; - queue.push({e: r.center, p: 0, r: r.i, s: cells.state[r.center]}, 0); - cost[r.center] = 1; - }); - - const religionsMap = new Map(religions.map(r => [r.i, r])); - - while (queue.length) { - const {e: cellId, p, r, s: state} = queue.pop(); - const {culture, expansion, expansionism} = religionsMap.get(r); - - cells.c[cellId].forEach(nextCell => { - if (expansion === "culture" && culture !== cells.culture[nextCell]) return; - if (expansion === "state" && state !== cells.state[nextCell]) return; - if (religionsMap.get(religionIds[nextCell])?.lock) return; - - const cultureCost = culture !== cells.culture[nextCell] ? 10 : 0; - const stateCost = state !== cells.state[nextCell] ? 10 : 0; - const passageCost = getPassageCost(cellId, nextCell); - - const cellCost = cultureCost + stateCost + passageCost; - const totalCost = p + 10 + cellCost / expansionism; - if (totalCost > maxExpansionCost) return; - - if (!cost[nextCell] || totalCost < cost[nextCell]) { - if (cells.culture[nextCell]) religionIds[nextCell] = r; // assign religion to cell - cost[nextCell] = totalCost; - - queue.push({e: nextCell, p: totalCost, r, s: state}, totalCost); - } - }); - } - - return religionIds; - - function getPassageCost(cellId, nextCellId, utils) { - const {Routes, biomesData} = utils; - const route = Routes.getRoute(cellId, nextCellId); - if (isWater(cellId)) return route ? 50 : 500; - - const biomePassageCost = biomesData.cost[cells.biome[nextCellId]]; - - if (route) { - if (route.group === "roads") return 1; - return biomePassageCost / 3; // trails and other routes - } - - return biomePassageCost; - } -} - -// folk religions initially get all cells of their culture, and locked religions are retained -function spreadFolkReligions(religions) { - const cells = pack.cells; - const hasPrior = cells.religion && true; - const religionIds = new Uint16Array(cells.i.length); - - const folkReligions = religions.filter(religion => religion.type === "Folk" && !religion.removed); - const cultureToReligionMap = new Map(folkReligions.map(({i, culture}) => [culture, i])); - - for (const cellId of cells.i) { - const oldId = (hasPrior && cells.religion[cellId]) || 0; - if (oldId && religions[oldId]?.lock && !religions[oldId]?.removed) { - religionIds[cellId] = oldId; - continue; - } - const cultureId = cells.culture[cellId]; - religionIds[cellId] = cultureToReligionMap.get(cultureId) || 0; - } - - return religionIds; -} - -function checkCenters(pack) { - const cells = pack.cells; - const updatedReligions = pack.religions.map(r => { - if (!r.i) return r; - // move religion center if it's not within religion area after expansion - if (cells.religion[r.center] === r.i) return r; // in area - const firstCell = cells.i.find(i => cells.religion[i] === r.i); - const cultureHome = pack.cultures[r.culture]?.center; - if (firstCell) return {...r, center: firstCell}; // move center, othervise it's an extinct religion - else if (r.type === "Folk" && cultureHome) return {...r, center: cultureHome}; // reset extinct culture centers - return r; - }); - - return { - ...pack, - religions: updatedReligions - }; -} - -export function recalculate(pack, grid, config, utils) { - const newReligionIds = expandReligions(pack, grid, pack.religions, config, utils); - const newPack = { - ...pack, - cells: { - ...pack.cells, - religion: newReligionIds - } - }; - - return checkCenters(newPack); -} - -export function add(pack, center, utils) { - const {rw, getMixedColor, abbreviate} = utils; - const {cells, cultures, religions} = pack; - const religionId = cells.religion[center]; - const i = religions.length; - - const cultureId = cells.culture[center]; - const missingFolk = - cultureId !== 0 && - !religions.some(({type, culture, removed}) => type === "Folk" && culture === cultureId && !removed); - const color = missingFolk ? cultures[cultureId].color : getMixedColor(religions[religionId].color, 0.3, 0); - - const type = missingFolk - ? "Folk" - : religions[religionId].type === "Organized" - ? rw({Organized: 4, Cult: 1, Heresy: 2}) - : rw({Organized: 5, Cult: 2}); - const form = rw(forms[type]); - const deity = - type === "Heresy" - ? religions[religionId].deity - : form === "Non-theism" || form === "Animism" - ? null - : getDeityName(cultureId, utils); - - const [name, expansion] = generateReligionName(pack, type, form, deity, center, utils); - - const formName = type === "Heresy" ? religions[religionId].form : form; - const code = abbreviate( - name, - religions.map(r => r.code) - ); - const influences = getReligionsInRadius(cells.c, center, cells.religion, i, 25, 3, 0); - const origins = type === "Folk" ? [0] : influences; - - const newReligion = { - i, - name, - color, - culture: cultureId, - type, - form: formName, - deity, - expansion, - expansionism: expansionismMap[type](utils), - center, - cells: 0, - area: 0, - rural: 0, - urban: 0, - origins, - code - }; - - const newReligions = [...religions, newReligion]; - const newReligionIds = [...cells.religion]; - newReligionIds[center] = i; - - return { - ...pack, - religions: newReligions, - cells: { - ...pack.cells, - religion: newReligionIds - } - }; -} - -export function updateCultures(pack) { - const updatedReligions = pack.religions.map((religion, index) => { - if (index === 0) return religion; - return {...religion, culture: pack.cells.culture[religion.center]}; - }); - - return { - ...pack, - religions: updatedReligions - }; -} - -// get supreme deity name -export function getDeityName(culture, utils) { - const {ERROR, Names} = utils; - if (culture === undefined) { - ERROR && console.error("Please define a culture"); - return; - } - const meaning = generateMeaning(utils); - const cultureName = Names.getCulture(culture, null, null, "", 0.8); - return cultureName + ", The " + meaning; -} - -function generateMeaning(utils) { - const {ra, ERROR} = utils; - const a = ra(approaches); // select generation approach - if (a === "Number") return ra(base.number); - if (a === "Being") return ra(base.being); - if (a === "Adjective") return ra(base.adjective); - if (a === "Color + Animal") return `${ra(base.color)} ${ra(base.animal)}`; - if (a === "Adjective + Animal") return `${ra(base.adjective)} ${ra(base.animal)}`; - if (a === "Adjective + Being") return `${ra(base.adjective)} ${ra(base.being)}`; - if (a === "Adjective + Genitive") return `${ra(base.adjective)} ${ra(base.genitive)}`; - if (a === "Color + Being") return `${ra(base.color)} ${ra(base.being)}`; - if (a === "Color + Genitive") return `${ra(base.color)} ${ra(base.genitive)}`; - if (a === "Being + of + Genitive") return `${ra(base.being)} of ${ra(base.genitive)}`; - if (a === "Being + of the + Genitive") return `${ra(base.being)} of the ${ra(base.theGenitive)}`; - if (a === "Animal + of + Genitive") return `${ra(base.animal)} of ${ra(base.genitive)}`; - if (a === "Adjective + Being + of + Genitive") - return `${ra(base.adjective)} ${ra(base.being)} of ${ra(base.genitive)}`; - if (a === "Adjective + Animal + of + Genitive") - return `${ra(base.adjective)} ${ra(base.animal)} of ${ra(base.genitive)}`; - - ERROR && console.error("Unkown generation approach"); -} - -function generateReligionName(pack, variety, form, deity, center, utils) { - const {rw, Names, ra, trimVowels, getAdjective} = utils; - const {cells, cultures, burgs, states} = pack; - - const random = () => Names.getCulture(cells.culture[center], null, null, "", 0); - const type = rw(types[form]); - const supreme = deity.split(/[ ,]+/)[0]; - const culture = cultures[cells.culture[center]].name; - - const place = adj => { - const burgId = cells.burg[center]; - const stateId = cells.state[center]; - - const base = burgId ? burgs[burgId].name : states[stateId].name; - let name = trimVowels(base.split(/[ ,]+/)[0]); - return adj ? getAdjective(name) : name; - }; - - const m = rw(namingMethods[variety]); - if (m === "Random + type") return [random() + " " + type, "global"]; - if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"]; - if (m === "Supreme + ism" && deity) return [trimVowels(supreme) + "ism", "global"]; - if (m === "Faith of + Supreme" && deity) - return [ra(["Faith", "Way", "Path", "Word", "Witnesses"]) + " of " + supreme, "global"]; - if (m === "Place + ism") return [place() + "ism", "state"]; - if (m === "Culture + ism") return [trimVowels(culture) + "ism", "culture"]; - if (m === "Place + ian + type") return [place("adj") + " " + type, "state"]; - if (m === "Culture + type") return [culture + " " + type, "culture"]; - if (m === "Burg + ian + type") return [`${place("adj")} ${type}`, "global"]; - if (m === "Random + ian + type") return [`${getAdjective(random())} ${type}`, "global"]; - if (m === "Type + of the + meaning") return [`${type} of the ${generateMeaning(utils)}`, "global"]; - return [trimVowels(random()) + "ism", "global"]; // else -} diff --git a/procedural/src/engine/modules/resample.js b/procedural/src/engine/modules/resample.js deleted file mode 100644 index e0db3189..00000000 --- a/procedural/src/engine/modules/resample.js +++ /dev/null @@ -1,410 +0,0 @@ -"use strict"; - -/* - generate new map based on an existing one (resampling parentMap) - parentMap: {grid, pack, notes} from original map - projection: f(Number, Number) -> [Number, Number] - inverse: f(Number, Number) -> [Number, Number] - scale: Number -*/ -export function process({projection, inverse, scale}, grid, pack, notes, config, utils) { - const {deepCopy, generateGrid, rn, findCell, findAll, isInMap, unique, lineclip, WARN} = utils; - const parentMap = {grid: deepCopy(grid), pack: deepCopy(pack), notes: deepCopy(notes)}; - const riversData = saveRiversData(parentMap.pack.rivers, utils); - - const newGrid = generateGrid(); - const newPack = {}; - const newNotes = parentMap.notes; - - resamplePrimaryGridData(parentMap, inverse, scale, newGrid, utils); - - // External module calls that modify newGrid and newPack would need to be handled by caller - // Features.markupGrid(), addLakesInDeepDepressions(), openNearSeaLakes(), - // OceanLayers(), calculateMapCoordinates(), calculateTemperatures(), - // reGraph(), Features.markupPack(), createDefaultRuler() - - const cellData = restoreCellData(parentMap, inverse, scale, newPack, config, utils); - const rivers = restoreRivers(riversData, projection, scale, newPack, config, utils); - const cultures = restoreCultures(parentMap, projection, newPack, utils); - const burgs = restoreBurgs(parentMap, projection, scale, newPack, utils); - const states = restoreStates(parentMap, projection, newPack, config, utils); - const routes = restoreRoutes(parentMap, projection, newPack, config, utils); - const religions = restoreReligions(parentMap, projection, newPack, utils); - const provinces = restoreProvinces(parentMap, newPack, utils); - const featureDetails = restoreFeatureDetails(parentMap, inverse, newPack, utils); - const markers = restoreMarkers(parentMap, projection, newPack, utils); - const zones = restoreZones(parentMap, projection, scale, newPack, utils); - - return { - grid: newGrid, - pack: { - ...newPack, - cells: cellData.cells, - rivers: rivers, - cultures: cultures, - burgs: burgs, - states: states, - routes: routes, - religions: religions, - provinces: provinces, - markers: markers, - zones: zones, - features: newPack.features || [] - }, - notes: newNotes - }; -} - -function resamplePrimaryGridData(parentMap, inverse, scale, grid, utils) { - const {smoothHeightmap} = utils; - grid.cells.h = new Uint8Array(grid.points.length); - grid.cells.temp = new Int8Array(grid.points.length); - grid.cells.prec = new Uint8Array(grid.points.length); - - grid.points.forEach(([x, y], newGridCell) => { - const [parentX, parentY] = inverse(x, y); - const parentPackCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2]; - const parentGridCell = parentMap.pack.cells.g[parentPackCell]; - - grid.cells.h[newGridCell] = parentMap.grid.cells.h[parentGridCell]; - grid.cells.temp[newGridCell] = parentMap.grid.cells.temp[parentGridCell]; - grid.cells.prec[newGridCell] = parentMap.grid.cells.prec[parentGridCell]; - }); - - if (scale >= 2) smoothHeightmap(grid); -} - -function smoothHeightmap(grid) { - const {d3, isWater} = grid.utils || {}; - grid.cells.h.forEach((height, newGridCell) => { - const heights = [height, ...grid.cells.c[newGridCell].map(c => grid.cells.h[c])]; - const meanHeight = d3.mean(heights); - grid.cells.h[newGridCell] = isWater(grid, newGridCell) ? Math.min(meanHeight, 19) : Math.max(meanHeight, 20); - }); -} - -function restoreCellData(parentMap, inverse, scale, pack, config, utils) { - const {d3, isWater} = utils; - - const cells = { - biome: new Uint8Array(pack.cells.i.length), - fl: new Uint16Array(pack.cells.i.length), - s: new Int16Array(pack.cells.i.length), - pop: new Float32Array(pack.cells.i.length), - culture: new Uint16Array(pack.cells.i.length), - state: new Uint16Array(pack.cells.i.length), - burg: new Uint16Array(pack.cells.i.length), - religion: new Uint16Array(pack.cells.i.length), - province: new Uint16Array(pack.cells.i.length) - }; - - const parentPackCellGroups = groupCellsByType(parentMap.pack); - const parentPackLandCellsQuadtree = d3.quadtree(parentPackCellGroups.land); - - for (const newPackCell of pack.cells.i) { - const [x, y] = inverse(...pack.cells.p[newPackCell]); - if (isWater(pack, newPackCell)) continue; - - const parentPackCell = parentPackLandCellsQuadtree.find(x, y, Infinity)[2]; - const parentCellArea = parentMap.pack.cells.area[parentPackCell]; - const areaRatio = pack.cells.area[newPackCell] / parentCellArea; - const scaleRatio = areaRatio / scale; - - cells.biome[newPackCell] = parentMap.pack.cells.biome[parentPackCell]; - cells.fl[newPackCell] = parentMap.pack.cells.fl[parentPackCell]; - cells.s[newPackCell] = parentMap.pack.cells.s[parentPackCell] * scaleRatio; - cells.pop[newPackCell] = parentMap.pack.cells.pop[parentPackCell] * scaleRatio; - cells.culture[newPackCell] = parentMap.pack.cells.culture[parentPackCell]; - cells.state[newPackCell] = parentMap.pack.cells.state[parentPackCell]; - cells.religion[newPackCell] = parentMap.pack.cells.religion[parentPackCell]; - cells.province[newPackCell] = parentMap.pack.cells.province[parentPackCell]; - } - - return {cells}; -} - -function saveRiversData(parentRivers, utils) { - const {Rivers} = utils; - return parentRivers.map(river => { - const meanderedPoints = Rivers.addMeandering(river.cells, river.points); - return {...river, meanderedPoints}; - }); -} - -function restoreRivers(riversData, projection, scale, pack, config, utils) { - const {rn, isInMap, findCell, Rivers} = utils; - - pack.cells.r = new Uint16Array(pack.cells.i.length); - pack.cells.conf = new Uint8Array(pack.cells.i.length); - - const rivers = riversData - .map(river => { - let wasInMap = true; - const points = []; - - river.meanderedPoints.forEach(([parentX, parentY]) => { - const [x, y] = projection(parentX, parentY); - const inMap = isInMap(x, y, config.graphWidth, config.graphHeight); - if (inMap || wasInMap) points.push([rn(x, 2), rn(y, 2)]); - wasInMap = inMap; - }); - if (points.length < 2) return null; - - const cells = points.map(point => findCell(...point)); - cells.forEach(cellId => { - if (pack.cells.r[cellId]) pack.cells.conf[cellId] = 1; - pack.cells.r[cellId] = river.i; - }); - - const widthFactor = river.widthFactor * scale; - return {...river, cells, points, source: cells.at(0), mouth: cells.at(-2), widthFactor}; - }) - .filter(Boolean); - - rivers.forEach(river => { - river.basin = Rivers.getBasin(river.i); - river.length = Rivers.getApproximateLength(river.points); - }); - - return rivers; -} - -function restoreCultures(parentMap, projection, pack, utils) { - const {rn, isInMap, findCell, getPolesOfInaccessibility} = utils; - - const validCultures = new Set(pack.cells.culture); - const culturePoles = getPolesOfInaccessibility(pack, cellId => pack.cells.culture[cellId]); - - return parentMap.pack.cultures.map(culture => { - if (!culture.i || culture.removed) return culture; - if (!validCultures.has(culture.i)) return {...culture, removed: true, lock: false}; - - const [xp, yp] = projection(...parentMap.pack.cells.p[culture.center]); - const [x, y] = [rn(xp, 2), rn(yp, 2)]; - const centerCoords = isInMap(x, y) ? [x, y] : culturePoles[culture.i]; - const center = findCell(...centerCoords); - return {...culture, center}; - }); -} - -function restoreBurgs(parentMap, projection, scale, pack, utils) { - const {d3, rn, isInMap, findCell, isWater, WARN, BurgsAndStates} = utils; - - const packLandCellsQuadtree = d3.quadtree(groupCellsByType(pack).land); - const findLandCell = (x, y) => packLandCellsQuadtree.find(x, y, Infinity)?.[2]; - - return parentMap.pack.burgs.map(burg => { - if (!burg.i || burg.removed) return burg; - burg.population *= scale; // adjust for populationRate change - - const [xp, yp] = projection(burg.x, burg.y); - if (!isInMap(xp, yp)) return {...burg, removed: true, lock: false}; - - const closestCell = findCell(xp, yp); - const cell = isWater(pack, closestCell) ? findLandCell(xp, yp) : closestCell; - - if (pack.cells.burg[cell]) { - WARN && console.warn(`Cell ${cell} already has a burg. Removing burg ${burg.name} (${burg.i})`); - return {...burg, removed: true, lock: false}; - } - - pack.cells.burg[cell] = burg.i; - const [x, y] = getBurgCoordinates(burg, closestCell, cell, xp, yp, pack, utils); - return {...burg, cell, x, y}; - }); - - function getBurgCoordinates(burg, closestCell, cell, xp, yp, pack, utils) { - const {rn, BurgsAndStates} = utils; - const haven = pack.cells.haven[cell]; - if (burg.port && haven) return BurgsAndStates.getCloseToEdgePoint(cell, haven); - - if (closestCell !== cell) return pack.cells.p[cell]; - return [rn(xp, 2), rn(yp, 2)]; - } -} - -function restoreStates(parentMap, projection, pack, config, utils) { - const {rn, isInMap, findCell, BurgsAndStates} = utils; - - const validStates = new Set(pack.cells.state); - let states = parentMap.pack.states.map(state => { - if (!state.i || state.removed) return state; - if (validStates.has(state.i)) return state; - return {...state, removed: true, lock: false}; - }); - - BurgsAndStates.getPoles(); - const regimentCellsMap = {}; - const VERTICAL_GAP = 8; - - states = states.map(state => { - if (!state.i || state.removed) return state; - - const capital = pack.burgs[state.capital]; - state.center = !capital || capital.removed ? findCell(...state.pole) : capital.cell; - - const military = state.military.map(regiment => { - const cellCoords = projection(...parentMap.pack.cells.p[regiment.cell]); - const cell = isInMap(...cellCoords, config.graphWidth, config.graphHeight) ? findCell(...cellCoords) : state.center; - - const [xPos, yPos] = projection(regiment.x, regiment.y); - const [xBase, yBase] = projection(regiment.bx, regiment.by); - const [xCell, yCell] = pack.cells.p[cell]; - - const regsOnCell = regimentCellsMap[cell] || 0; - regimentCellsMap[cell] = regsOnCell + 1; - - const name = - isInMap(xPos, yPos, config.graphWidth, config.graphHeight) || regiment.name.includes("[relocated]") ? regiment.name : `[relocated] ${regiment.name}`; - - const pos = isInMap(xPos, yPos, config.graphWidth, config.graphHeight) - ? {x: rn(xPos, 2), y: rn(yPos, 2)} - : {x: xCell, y: yCell + regsOnCell * VERTICAL_GAP}; - - const base = isInMap(xBase, yBase, config.graphWidth, config.graphHeight) ? {bx: rn(xBase, 2), by: rn(yBase, 2)} : {bx: xCell, by: yCell}; - - return {...regiment, cell, name, ...base, ...pos}; - }); - - const neighbors = state.neighbors.filter(stateId => validStates.has(stateId)); - return {...state, neighbors, military}; - }); - - return states; -} - -function restoreRoutes(parentMap, projection, pack, config, utils) { - const {rn, isInMap, findCell, lineclip, Routes} = utils; - - const routes = parentMap.pack.routes - .map(route => { - let wasInMap = true; - const points = []; - - route.points.forEach(([parentX, parentY]) => { - const [x, y] = projection(parentX, parentY); - const inMap = isInMap(x, y, config.graphWidth, config.graphHeight); - if (inMap || wasInMap) points.push([rn(x, 2), rn(y, 2)]); - wasInMap = inMap; - }); - if (points.length < 2) return null; - - const bbox = [0, 0, config.graphWidth, config.graphHeight]; - const clipped = lineclip(points, bbox)[0].map(([x, y]) => [rn(x, 2), rn(y, 2), findCell(x, y)]); - const firstCell = clipped[0][2]; - const feature = pack.cells.f[firstCell]; - return {...route, feature, points: clipped}; - }) - .filter(Boolean); - - pack.cells.routes = Routes.buildLinks(routes); - return routes; -} - -function restoreReligions(parentMap, projection, pack, utils) { - const {rn, isInMap, findCell, getPolesOfInaccessibility} = utils; - - const validReligions = new Set(pack.cells.religion); - const religionPoles = getPolesOfInaccessibility(pack, cellId => pack.cells.religion[cellId]); - - return parentMap.pack.religions.map(religion => { - if (!religion.i || religion.removed) return religion; - if (!validReligions.has(religion.i)) return {...religion, removed: true, lock: false}; - - const [xp, yp] = projection(...parentMap.pack.cells.p[religion.center]); - const [x, y] = [rn(xp, 2), rn(yp, 2)]; - const centerCoords = isInMap(x, y) ? [x, y] : religionPoles[religion.i]; - const center = findCell(...centerCoords); - return {...religion, center}; - }); -} - -function restoreProvinces(parentMap, pack, utils) { - const {findCell, Provinces} = utils; - - const validProvinces = new Set(pack.cells.province); - const provinces = parentMap.pack.provinces.map(province => { - if (!province.i || province.removed) return province; - if (!validProvinces.has(province.i)) return {...province, removed: true, lock: false}; - - return province; - }); - - Provinces.getPoles(); - - provinces.forEach(province => { - if (!province.i || province.removed) return; - const capital = pack.burgs[province.burg]; - province.center = !capital?.removed ? capital.cell : findCell(...province.pole); - }); - - return provinces; -} - -function restoreMarkers(parentMap, projection, pack, utils) { - const {rn, isInMap, findCell, Markers} = utils; - - const markers = parentMap.pack.markers; - markers.forEach(marker => { - const [x, y] = projection(marker.x, marker.y); - if (!isInMap(x, y)) Markers.deleteMarker(marker.i); - - const cell = findCell(x, y); - marker.x = rn(x, 2); - marker.y = rn(y, 2); - marker.cell = cell; - }); - - return markers; -} - -function restoreZones(parentMap, projection, scale, pack, utils) { - const {isInMap, findAll, unique} = utils; - - const getSearchRadius = cellId => Math.sqrt(parentMap.pack.cells.area[cellId] / Math.PI) * scale; - - return parentMap.pack.zones.map(zone => { - const cells = zone.cells - .map(cellId => { - const [x, y] = projection(...parentMap.pack.cells.p[cellId]); - if (!isInMap(x, y)) return null; - return findAll(x, y, getSearchRadius(cellId)); - }) - .filter(Boolean) - .flat(); - - return {...zone, cells: unique(cells)}; - }); -} - -function restoreFeatureDetails(parentMap, inverse, pack, utils) { - pack.features.forEach(feature => { - if (!feature) return; - const [x, y] = pack.cells.p[feature.firstCell]; - const [parentX, parentY] = inverse(x, y); - const parentCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2]; - if (parentCell === undefined) return; - const parentFeature = parentMap.pack.features[parentMap.pack.cells.f[parentCell]]; - - if (parentFeature.group) feature.group = parentFeature.group; - if (parentFeature.name) feature.name = parentFeature.name; - if (parentFeature.height) feature.height = parentFeature.height; - }); - - return pack.features; -} - -function groupCellsByType(graph) { - return graph.cells.p.reduce( - (acc, [x, y], cellId) => { - const group = isWater(graph, cellId) ? "water" : "land"; - acc[group].push([x, y, cellId]); - return acc; - }, - {land: [], water: []} - ); -} - -function isWater(graph, cellId) { - return graph.cells.h[cellId] < 20; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/river-generator.js b/procedural/src/engine/modules/river-generator.js deleted file mode 100644 index b389726a..00000000 --- a/procedural/src/engine/modules/river-generator.js +++ /dev/null @@ -1,591 +0,0 @@ -"use strict"; - -/** - * Generates river systems for the map - * - * REQUIRES: - * - pack.cells.h (heights from heightmap processing) - * - pack.cells.t (distance field from features module) - * - pack.features (features from features module) - * - modules.Lakes (Lakes module dependency) - * - config.debug (debug configuration) - * - * PROVIDES: - * - pack.cells.fl (water flux) - * - pack.cells.r (river assignments) - * - pack.cells.conf (confluence data) - */ -export const generate = function (pack, grid, config, utils, modules, allowErosion = true) { - // Check required properties exist - if (!pack.cells.h) { - throw new Error("Rivers module requires pack.cells.h (heights) from heightmap processing"); - } - if (!pack.cells.t) { - throw new Error("Rivers module requires pack.cells.t (distance field) from features module"); - } - if (!pack.features) { - throw new Error("Rivers module requires pack.features from features module"); - } - if (!modules.Lakes) { - throw new Error("Rivers module requires Lakes module dependency"); - } - if (!config.debug) { - throw new Error("Rivers module requires config.debug section"); - } - - const { seed, aleaPRNG, resolveDepressionsSteps, cellsCount, graphWidth, graphHeight, WARN} = config; - const {rn, rw, each, round, d3, lineGen} = utils; - const {Lakes, Names} = modules; - const { TIME } = config.debug; - - TIME && console.time("generateRivers"); - const {cells, features} = pack; - - const riversData = {}; // rivers data - const riverParents = {}; - - const addCellToRiver = function (cell, river) { - if (!riversData[river]) riversData[river] = [cell]; - else riversData[river].push(cell); - }; - - const newCells = { - ...cells, - fl: new Uint16Array(cells.i.length), // water flux array - r: new Uint16Array(cells.i.length), // rivers array - conf: new Uint8Array(cells.i.length) // confluences array - }; - - let riverNext = 1; // first river id is 1 - - const h = alterHeights(pack, utils); - Lakes.detectCloseLakes(pack, grid, h, config); - const resolvedH = resolveDepressions(pack, config, utils, h); - const {updatedCells, updatedFeatures, updatedRivers} = drainWater(pack, grid, config, utils, modules, newCells, resolvedH, riversData, riverParents, riverNext); - const {finalCells, finalRivers} = defineRivers(pack, config, utils, updatedCells, riversData, riverParents); - - calculateConfluenceFlux(finalCells, resolvedH); - Lakes.cleanupLakeData(pack); - - let finalH = resolvedH; - if (allowErosion) { - finalH = Uint8Array.from(resolvedH); // apply gradient - downcutRivers(pack, finalCells, finalH); // downcut river beds - } - - TIME && console.timeEnd("generateRivers"); - - return { - pack: { - ...pack, - cells: { - ...pack.cells, - ...finalCells, - h: finalH - }, - features: updatedFeatures, - rivers: finalRivers - } - }; - - function drainWater(pack, grid, config, utils, modules, cells, h, riversData, riverParents, riverNext) { - const {cellsCount} = config; - const {Lakes} = modules; - const MIN_FLUX_TO_FORM_RIVER = 30; - const cellsNumberModifier = (cellsCount / 10000) ** 0.25; - - const prec = grid.cells.prec; - const land = cells.i.filter(i => h[i] >= 20).sort((a, b) => h[b] - h[a]); - const lakeOutCells = Lakes.defineClimateData(pack, grid, h, config, utils); - - land.forEach(function (i) { - cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation - - // create lake outlet if lake is not in deep depression and flux > evaporation - const lakes = lakeOutCells[i] - ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) - : []; - for (const lake of lakes) { - const lakeCell = cells.c[i].find(c => h[c] < 20 && cells.f[c] === lake.i); - cells.fl[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet - - // allow chain lakes to retain identity - if (cells.r[lakeCell] !== lake.river) { - const sameRiver = cells.c[lakeCell].some(c => cells.r[c] === lake.river); - - if (sameRiver) { - cells.r[lakeCell] = lake.river; - addCellToRiver(lakeCell, lake.river); - } else { - cells.r[lakeCell] = riverNext; - addCellToRiver(lakeCell, riverNext); - riverNext++; - } - } - - lake.outlet = cells.r[lakeCell]; - flowDown(i, cells.fl[lakeCell], lake.outlet); - } - - // assign all tributary rivers to outlet basin - const outlet = lakes[0]?.outlet; - for (const lake of lakes) { - if (!Array.isArray(lake.inlets)) continue; - for (const inlet of lake.inlets) { - riverParents[inlet] = outlet; - } - } - - // near-border cell: pour water out of the screen - if (cells.b[i] && cells.r[i]) return addCellToRiver(-1, cells.r[i]); - - // downhill cell (make sure it's not in the source lake) - let min = null; - if (lakeOutCells[i]) { - const filtered = cells.c[i].filter(c => !lakes.map(lake => lake.i).includes(cells.f[c])); - min = filtered.sort((a, b) => h[a] - h[b])[0]; - } else if (cells.haven[i]) { - min = cells.haven[i]; - } else { - min = cells.c[i].sort((a, b) => h[a] - h[b])[0]; - } - - // cells is depressed - if (h[i] <= h[min]) return; - - if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) { - // flux is too small to operate as a river - if (h[min] >= 20) cells.fl[min] += cells.fl[i]; - return; - } - - // proclaim a new river - if (!cells.r[i]) { - cells.r[i] = riverNext; - addCellToRiver(i, riverNext); - riverNext++; - } - - flowDown(min, cells.fl[i], cells.r[i]); - }); - - function flowDown(toCell, fromFlux, river) { - const toFlux = cells.fl[toCell] - cells.conf[toCell]; - const toRiver = cells.r[toCell]; - - if (toRiver) { - // downhill cell already has river assigned - if (fromFlux > toFlux) { - cells.conf[toCell] += cells.fl[toCell]; // mark confluence - if (h[toCell] >= 20) riverParents[toRiver] = river; // min river is a tributary of current river - cells.r[toCell] = river; // re-assign river if downhill part has less flux - } else { - cells.conf[toCell] += fromFlux; // mark confluence - if (h[toCell] >= 20) riverParents[river] = toRiver; // current river is a tributary of min river - } - } else cells.r[toCell] = river; // assign the river to the downhill cell - - if (h[toCell] < 20) { - // pour water to the water body - const waterBody = features[cells.f[toCell]]; - if (waterBody.type === "lake") { - if (!waterBody.river || fromFlux > waterBody.enteringFlux) { - waterBody.river = river; - waterBody.enteringFlux = fromFlux; - } - waterBody.flux = waterBody.flux + fromFlux; - if (!waterBody.inlets) waterBody.inlets = [river]; - else waterBody.inlets.push(river); - } - } else { - // propagate flux and add next river segment - cells.fl[toCell] += fromFlux; - } - - addCellToRiver(toCell, river); - } - - return { - updatedCells: cells, - updatedFeatures: features, - updatedRivers: [] - }; - } - - function defineRivers(pack, config, utils, cells, riversData, riverParents) { - const {cellsCount} = config; - const {rn} = utils; - - // re-initialize rivers and confluence arrays - const newCells = { - ...cells, - r: new Uint16Array(cells.i.length), - conf: new Uint16Array(cells.i.length) - }; - const rivers = []; - - const defaultWidthFactor = rn(1 / (cellsCount / 10000) ** 0.25, 2); - const mainStemWidthFactor = defaultWidthFactor * 1.2; - - for (const key in riversData) { - const riverCells = riversData[key]; - if (riverCells.length < 3) continue; // exclude tiny rivers - - const riverId = +key; - for (const cell of riverCells) { - if (cell < 0 || cells.h[cell] < 20) continue; - - // mark real confluences and assign river to cells - if (newCells.r[cell]) newCells.conf[cell] = 1; - else newCells.r[cell] = riverId; - } - - const source = riverCells[0]; - const mouth = riverCells[riverCells.length - 2]; - const parent = riverParents[key] || 0; - - const widthFactor = !parent || parent === riverId ? mainStemWidthFactor : defaultWidthFactor; - const meanderedPoints = addMeandering(pack, utils, riverCells); - const discharge = cells.fl[mouth]; // m3 in second - const length = getApproximateLength(utils, meanderedPoints); - const sourceWidth = getSourceWidth(utils, cells.fl[source]); - const width = getWidth(utils, - getOffset(utils, { - flux: discharge, - pointIndex: meanderedPoints.length, - widthFactor, - startingWidth: sourceWidth - }) - ); - - rivers.push({ - i: riverId, - source, - mouth, - discharge, - length, - width, - widthFactor, - sourceWidth, - parent, - cells: riverCells - }); - } - - return { - finalCells: newCells, - finalRivers: rivers - }; - } - - function downcutRivers(pack, cells, h) { - const MAX_DOWNCUT = 5; - - for (const i of pack.cells.i) { - if (cells.h[i] < 35) continue; // don't donwcut lowlands - if (!cells.fl[i]) continue; - - const higherCells = cells.c[i].filter(c => cells.h[c] > cells.h[i]); - const higherFlux = higherCells.reduce((acc, c) => acc + cells.fl[c], 0) / higherCells.length; - if (!higherFlux) continue; - - const downcut = Math.floor(cells.fl[i] / higherFlux); - if (downcut) cells.h[i] -= Math.min(downcut, MAX_DOWNCUT); - } - } - - function calculateConfluenceFlux(cells, h) { - for (const i of cells.i) { - if (!cells.conf[i]) continue; - - const sortedInflux = cells.c[i] - .filter(c => cells.r[c] && h[c] > h[i]) - .map(c => cells.fl[c]) - .sort((a, b) => b - a); - cells.conf[i] = sortedInflux.reduce((acc, flux, index) => (index ? acc + flux : acc), 0); - } - } -}; - -// add distance to water value to land cells to make map less depressed -export const alterHeights = (pack, utils) => { - const {d3} = utils; - const {h, c, t} = pack.cells; - return Array.from(h).map((h, i) => { - if (h < 20 || t[i] < 1) return h; - return h + t[i] / 100 + d3.mean(c[i].map(c => t[c])) / 10000; - }); -}; - -// depression filling algorithm (for a correct water flux modeling) -export const resolveDepressions = function (pack, config, utils, h) { - const {resolveDepressionsSteps, WARN} = config; - const {d3} = utils; - const {cells, features} = pack; - const maxIterations = resolveDepressionsSteps; - const checkLakeMaxIteration = maxIterations * 0.85; - const elevateLakeMaxIteration = maxIterations * 0.75; - - const height = i => features[cells.f[i]].height || h[i]; // height of lake or specific cell - - const lakes = features.filter(f => f.type === "lake"); - const land = cells.i.filter(i => h[i] >= 20 && !cells.b[i]); // exclude near-border cells - land.sort((a, b) => h[a] - h[b]); // lowest cells go first - - const progress = []; - let depressions = Infinity; - let prevDepressions = null; - for (let iteration = 0; depressions && iteration < maxIterations; iteration++) { - if (progress.length > 5 && d3.sum(progress) > 0) { - // bad progress, abort and set heights back - h = alterHeights(pack, utils); - depressions = progress[0]; - break; - } - - depressions = 0; - - if (iteration < checkLakeMaxIteration) { - for (const l of lakes) { - if (l.closed) continue; - const minHeight = d3.min(l.shoreline.map(s => h[s])); - if (minHeight >= 100 || l.height > minHeight) continue; - - if (iteration > elevateLakeMaxIteration) { - l.shoreline.forEach(i => (h[i] = cells.h[i])); - l.height = d3.min(l.shoreline.map(s => h[s])) - 1; - l.closed = true; - continue; - } - - depressions++; - l.height = minHeight + 0.2; - } - } - - for (const i of land) { - const minHeight = d3.min(cells.c[i].map(c => height(c))); - if (minHeight >= 100 || h[i] > minHeight) continue; - - depressions++; - h[i] = minHeight + 0.1; - } - - prevDepressions !== null && progress.push(depressions - prevDepressions); - prevDepressions = depressions; - } - - depressions && WARN && console.warn(`Unresolved depressions: ${depressions}. Edit heightmap to fix`); - return h; -}; - -// add points at 1/3 and 2/3 of a line between adjacents river cells -export const addMeandering = function (pack, utils, riverCells, riverPoints = null, meandering = 0.5) { - const {fl, h} = pack.cells; - const meandered = []; - const lastStep = riverCells.length - 1; - const points = getRiverPoints(pack, riverCells, riverPoints); - let step = h[riverCells[0]] < 20 ? 1 : 10; - - for (let i = 0; i <= lastStep; i++, step++) { - const cell = riverCells[i]; - const isLastCell = i === lastStep; - - const [x1, y1] = points[i]; - - meandered.push([x1, y1, fl[cell]]); - if (isLastCell) break; - - const nextCell = riverCells[i + 1]; - const [x2, y2] = points[i + 1]; - - if (nextCell === -1) { - meandered.push([x2, y2, fl[cell]]); - break; - } - - const dist2 = (x2 - x1) ** 2 + (y2 - y1) ** 2; // square distance between cells - if (dist2 <= 25 && riverCells.length >= 6) continue; - - const meander = meandering + 1 / step + Math.max(meandering - step / 100, 0); - const angle = Math.atan2(y2 - y1, x2 - x1); - const sinMeander = Math.sin(angle) * meander; - const cosMeander = Math.cos(angle) * meander; - - if (step < 20 && (dist2 > 64 || (dist2 > 36 && riverCells.length < 5))) { - // if dist2 is big or river is small add extra points at 1/3 and 2/3 of segment - const p1x = (x1 * 2 + x2) / 3 + -sinMeander; - const p1y = (y1 * 2 + y2) / 3 + cosMeander; - const p2x = (x1 + x2 * 2) / 3 + sinMeander / 2; - const p2y = (y1 + y2 * 2) / 3 - cosMeander / 2; - meandered.push([p1x, p1y, 0], [p2x, p2y, 0]); - } else if (dist2 > 25 || riverCells.length < 6) { - // if dist is medium or river is small add 1 extra middlepoint - const p1x = (x1 + x2) / 2 + -sinMeander; - const p1y = (y1 + y2) / 2 + cosMeander; - meandered.push([p1x, p1y, 0]); - } - } - - return meandered; -}; - -export const getRiverPoints = (pack, riverCells, riverPoints) => { - if (riverPoints) return riverPoints; - - const {p} = pack.cells; - return riverCells.map((cell, i) => { - if (cell === -1) return getBorderPoint(pack, riverCells[i - 1]); - return p[cell]; - }); -}; - -export const getBorderPoint = (pack, config, i) => { - const {graphWidth, graphHeight} = config; - const [x, y] = pack.cells.p[i]; - const min = Math.min(y, graphHeight - y, x, graphWidth - x); - if (min === y) return [x, 0]; - else if (min === graphHeight - y) return [x, graphHeight]; - else if (min === x) return [0, y]; - return [graphWidth, y]; -}; - -const FLUX_FACTOR = 500; -const MAX_FLUX_WIDTH = 1; -const LENGTH_FACTOR = 200; -const LENGTH_STEP_WIDTH = 1 / LENGTH_FACTOR; -const LENGTH_PROGRESSION = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / LENGTH_FACTOR); - -export const getOffset = (utils, {flux, pointIndex, widthFactor, startingWidth}) => { - if (pointIndex === 0) return startingWidth; - - const fluxWidth = Math.min(flux ** 0.7 / FLUX_FACTOR, MAX_FLUX_WIDTH); - const lengthWidth = pointIndex * LENGTH_STEP_WIDTH + (LENGTH_PROGRESSION[pointIndex] || LENGTH_PROGRESSION.at(-1)); - return widthFactor * (lengthWidth + fluxWidth) + startingWidth; -}; - -export const getSourceWidth = (utils, flux) => { - const {rn} = utils; - return rn(Math.min(flux ** 0.9 / FLUX_FACTOR, MAX_FLUX_WIDTH), 2); -}; - -// build polygon from a list of points and calculated offset (width) -export const getRiverPath = (utils, points, widthFactor, startingWidth) => { - const {lineGen, d3, round} = utils; - lineGen.curve(d3.curveCatmullRom.alpha(0.1)); - const riverPointsLeft = []; - const riverPointsRight = []; - let flux = 0; - - for (let pointIndex = 0; pointIndex < points.length; pointIndex++) { - const [x0, y0] = points[pointIndex - 1] || points[pointIndex]; - const [x1, y1, pointFlux] = points[pointIndex]; - const [x2, y2] = points[pointIndex + 1] || points[pointIndex]; - if (pointFlux > flux) flux = pointFlux; - - const offset = getOffset(utils, {flux, pointIndex, widthFactor, startingWidth}); - const angle = Math.atan2(y0 - y2, x0 - x2); - const sinOffset = Math.sin(angle) * offset; - const cosOffset = Math.cos(angle) * offset; - - riverPointsLeft.push([x1 - sinOffset, y1 + cosOffset]); - riverPointsRight.push([x1 + sinOffset, y1 - cosOffset]); - } - - const right = lineGen(riverPointsRight.reverse()); - let left = lineGen(riverPointsLeft); - left = left.substring(left.indexOf("C")); - - return round(right + left, 1); -}; - -export const specify = function (pack, modules, utils) { - const rivers = pack.rivers; - if (!rivers.length) return pack; - - const updatedRivers = rivers.map(river => ({ - ...river, - basin: getBasin(pack, river.i), - name: getName(pack, modules, river.mouth), - type: getType(pack, utils, river) - })); - - return { - ...pack, - rivers: updatedRivers - }; -}; - -export const getName = function (pack, modules, cell) { - const {Names} = modules; - return Names.getCulture(pack.cells.culture[cell]); -}; - -// weighted arrays of river type names -const riverTypes = { - main: { - big: {River: 1}, - small: {Creek: 9, River: 3, Brook: 3, Stream: 1} - }, - fork: { - big: {Fork: 1}, - small: {Branch: 1} - } -}; - -let smallLength = null; -export const getType = function (pack, utils, {i, length, parent}) { - const {rw, each} = utils; - if (smallLength === null) { - const threshold = Math.ceil(pack.rivers.length * 0.15); - smallLength = pack.rivers.map(r => r.length || 0).sort((a, b) => a - b)[threshold]; - } - - const isSmall = length < smallLength; - const isFork = each(3)(i) && parent && parent !== i; - return rw(riverTypes[isFork ? "fork" : "main"][isSmall ? "small" : "big"]); -}; - -export const getApproximateLength = (utils, points) => { - const {rn} = utils; - const length = points.reduce((s, v, i, p) => s + (i ? Math.hypot(v[0] - p[i - 1][0], v[1] - p[i - 1][1]) : 0), 0); - return rn(length, 2); -}; - -// Real mouth width examples: Amazon 6000m, Volga 6000m, Dniepr 3000m, Mississippi 1300m, Themes 900m, -// Danube 800m, Daugava 600m, Neva 500m, Nile 450m, Don 400m, Wisla 300m, Pripyat 150m, Bug 140m, Muchavets 40m -export const getWidth = (utils, offset) => { - const {rn} = utils; - return rn((offset / 1.5) ** 1.8, 2); // mouth width in km -}; - -// remove river and all its tributaries -export const remove = function (pack, grid, id) { - const cells = pack.cells; - const riversToRemove = pack.rivers.filter(r => r.i === id || r.parent === id || r.basin === id).map(r => r.i); - - // Update cells data - cells.r.forEach((r, i) => { - if (!r || !riversToRemove.includes(r)) return; - cells.r[i] = 0; - cells.fl[i] = grid.cells.prec[cells.g[i]]; - cells.conf[i] = 0; - }); - - const updatedRivers = pack.rivers.filter(r => !riversToRemove.includes(r.i)); - - return { - ...pack, - rivers: updatedRivers - }; -}; - -export const getBasin = function (pack, r) { - const parent = pack.rivers.find(river => river.i === r)?.parent; - if (!parent || r === parent) return r; - return getBasin(pack, parent); -}; - -export const getNextId = function (rivers) { - return rivers.length ? Math.max(...rivers.map(r => r.i)) + 1 : 1; -}; diff --git a/procedural/src/engine/modules/routes-generator.js b/procedural/src/engine/modules/routes-generator.js deleted file mode 100644 index e43c4c10..00000000 --- a/procedural/src/engine/modules/routes-generator.js +++ /dev/null @@ -1,739 +0,0 @@ -const ROUTES_SHARP_ANGLE = 135; -const ROUTES_VERY_SHARP_ANGLE = 115; - -const MIN_PASSABLE_SEA_TEMP = -4; -const ROUTE_TYPE_MODIFIERS = { - "-1": 1, // coastline - "-2": 1.8, // sea - "-3": 4, // open sea - "-4": 6, // ocean - default: 8 // far ocean -}; - -/** - * Generates routes (roads, trails, sea routes) connecting settlements - * - * REQUIRES: - * - pack.cells.burg (from BurgsAndStates module) - * - pack.burgs (from BurgsAndStates module) - * - pack.cells.h (heights from heightmap processing) - * - pack.cells.t (distance field from features module) - * - * PROVIDES: - * - pack.routes (routes array) - * - pack.cells.routes (route connections) - */ -export function generate(pack, grid, utils, lockedRoutes = []) { - // Check required properties exist - if (!pack.cells.burg) { - throw new Error("Routes module requires cells.burg from BurgsAndStates module"); - } - if (!pack.burgs) { - throw new Error("Routes module requires pack.burgs from BurgsAndStates module"); - } - if (!pack.cells.h) { - throw new Error("Routes module requires cells.h (heights) from heightmap processing"); - } - if (!pack.cells.t) { - throw new Error("Routes module requires cells.t (distance field) from features module"); - } - - const { dist2, findPath, findCell, rn } = utils; - const { capitalsByFeature, burgsByFeature, portsByFeature } = sortBurgsByFeature(pack.burgs); - - const connections = new Map(); - lockedRoutes.forEach(route => addConnections(route.points.map(p => p[2]))); - - const mainRoads = generateMainRoads(); - const trails = generateTrails(); - const seaRoutes = generateSeaRoutes(); - - const routes = createRoutesData(lockedRoutes); - const cellRoutes = buildLinks(routes); - - return { - routes, - cellRoutes - }; - - function sortBurgsByFeature(burgs) { - const burgsByFeature = {}; - const capitalsByFeature = {}; - const portsByFeature = {}; - - const addBurg = (object, feature, burg) => { - if (!object[feature]) object[feature] = []; - object[feature].push(burg); - }; - - for (const burg of burgs) { - if (burg.i && !burg.removed) { - const { feature, capital, port } = burg; - addBurg(burgsByFeature, feature, burg); - if (capital) addBurg(capitalsByFeature, feature, burg); - if (port) addBurg(portsByFeature, port, burg); - } - } - - return { burgsByFeature, capitalsByFeature, portsByFeature }; - } - - function generateMainRoads() { - const mainRoads = []; - - for (const [key, featureCapitals] of Object.entries(capitalsByFeature)) { - const points = featureCapitals.map(burg => [burg.x, burg.y]); - const urquhartEdges = calculateUrquhartEdges(points); - urquhartEdges.forEach(([fromId, toId]) => { - const start = featureCapitals[fromId].cell; - const exit = featureCapitals[toId].cell; - - const segments = findPathSegments({ isWater: false, connections, start, exit }); - for (const segment of segments) { - addConnections(segment); - mainRoads.push({ feature: Number(key), cells: segment }); - } - }); - } - - return mainRoads; - } - - function generateTrails() { - const trails = []; - - for (const [key, featureBurgs] of Object.entries(burgsByFeature)) { - const points = featureBurgs.map(burg => [burg.x, burg.y]); - const urquhartEdges = calculateUrquhartEdges(points); - urquhartEdges.forEach(([fromId, toId]) => { - const start = featureBurgs[fromId].cell; - const exit = featureBurgs[toId].cell; - - const segments = findPathSegments({ isWater: false, connections, start, exit }); - for (const segment of segments) { - addConnections(segment); - trails.push({ feature: Number(key), cells: segment }); - } - }); - } - - return trails; - } - - function generateSeaRoutes() { - const seaRoutes = []; - - for (const [featureId, featurePorts] of Object.entries(portsByFeature)) { - const points = featurePorts.map(burg => [burg.x, burg.y]); - const urquhartEdges = calculateUrquhartEdges(points); - - urquhartEdges.forEach(([fromId, toId]) => { - const start = featurePorts[fromId].cell; - const exit = featurePorts[toId].cell; - const segments = findPathSegments({ isWater: true, connections, start, exit }); - for (const segment of segments) { - addConnections(segment); - seaRoutes.push({ feature: Number(featureId), cells: segment }); - } - }); - } - - return seaRoutes; - } - - function addConnections(segment) { - for (let i = 0; i < segment.length; i++) { - const cellId = segment[i]; - const nextCellId = segment[i + 1]; - if (nextCellId) { - connections.set(`${cellId}-${nextCellId}`, true); - connections.set(`${nextCellId}-${cellId}`, true); - } - } - } - - function findPathSegments({ isWater, connections, start, exit }) { - const getCost = createCostEvaluator({ isWater, connections }); - const pathCells = findPath(start, current => current === exit, getCost); - if (!pathCells) return []; - const segments = getRouteSegments(pathCells, connections); - return segments; - } - - function createRoutesData(routes) { - const pointsArray = preparePointsArray(); - - for (const { feature, cells, merged } of mergeRoutes(mainRoads)) { - if (merged) continue; - const points = getPoints("roads", cells, pointsArray); - routes.push({ i: routes.length, group: "roads", feature, points }); - } - - for (const { feature, cells, merged } of mergeRoutes(trails)) { - if (merged) continue; - const points = getPoints("trails", cells, pointsArray); - routes.push({ i: routes.length, group: "trails", feature, points }); - } - - for (const { feature, cells, merged } of mergeRoutes(seaRoutes)) { - if (merged) continue; - const points = getPoints("searoutes", cells, pointsArray); - routes.push({ i: routes.length, group: "searoutes", feature, points }); - } - - return routes; - } - - // merge routes so that the last cell of one route is the first cell of the next route - function mergeRoutes(routes) { - let routesMerged = 0; - - for (let i = 0; i < routes.length; i++) { - const thisRoute = routes[i]; - if (thisRoute.merged) continue; - - for (let j = i + 1; j < routes.length; j++) { - const nextRoute = routes[j]; - if (nextRoute.merged) continue; - - if (nextRoute.cells.at(0) === thisRoute.cells.at(-1)) { - routesMerged++; - thisRoute.cells = thisRoute.cells.concat(nextRoute.cells.slice(1)); - nextRoute.merged = true; - } - } - } - - return routesMerged > 1 ? mergeRoutes(routes) : routes; - } - - function createCostEvaluator({ isWater, connections }) { - return isWater ? getWaterPathCost : getLandPathCost; - - function getLandPathCost(current, next) { - if (pack.cells.h[next] < 20) return Infinity; // ignore water cells - - const habitability = utils.biomesData.habitability[pack.cells.biome[next]]; - if (!habitability) return Infinity; // inhabitable cells are not passable (e.g. glacier) - - const distanceCost = dist2(pack.cells.p[current], pack.cells.p[next]); - const habitabilityModifier = 1 + Math.max(100 - habitability, 0) / 1000; // [1, 1.1]; - const heightModifier = 1 + Math.max(pack.cells.h[next] - 25, 25) / 25; // [1, 3]; - const connectionModifier = connections.has(`${current}-${next}`) ? 0.5 : 1; - const burgModifier = pack.cells.burg[next] ? 1 : 3; - - const pathCost = distanceCost * habitabilityModifier * heightModifier * connectionModifier * burgModifier; - return pathCost; - } - - function getWaterPathCost(current, next) { - if (pack.cells.h[next] >= 20) return Infinity; // ignore land cells - if (grid.cells.temp[pack.cells.g[next]] < MIN_PASSABLE_SEA_TEMP) return Infinity; // ignore too cold cells - - const distanceCost = dist2(pack.cells.p[current], pack.cells.p[next]); - const typeModifier = ROUTE_TYPE_MODIFIERS[pack.cells.t[next]] || ROUTE_TYPE_MODIFIERS.default; - const connectionModifier = connections.has(`${current}-${next}`) ? 0.5 : 1; - - const pathCost = distanceCost * typeModifier * connectionModifier; - return pathCost; - } - } - - function preparePointsArray() { - const { cells, burgs } = pack; - return cells.p.map(([x, y], cellId) => { - const burgId = cells.burg[cellId]; - if (burgId) return [burgs[burgId].x, burgs[burgId].y]; - return [x, y]; - }); - } - - function getPoints(group, cells, points) { - const data = cells.map(cellId => [...points[cellId], cellId]); - - // resolve sharp angles - if (group !== "searoutes") { - for (let i = 1; i < cells.length - 1; i++) { - const cellId = cells[i]; - if (pack.cells.burg[cellId]) continue; - - const [prevX, prevY] = data[i - 1]; - const [currX, currY] = data[i]; - const [nextX, nextY] = data[i + 1]; - - const dAx = prevX - currX; - const dAy = prevY - currY; - const dBx = nextX - currX; - const dBy = nextY - currY; - const angle = Math.abs((Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI); - - if (angle < ROUTES_SHARP_ANGLE) { - const middleX = (prevX + nextX) / 2; - const middleY = (prevY + nextY) / 2; - let newX, newY; - - if (angle < ROUTES_VERY_SHARP_ANGLE) { - newX = rn((currX + middleX * 2) / 3, 2); - newY = rn((currY + middleY * 2) / 3, 2); - } else { - newX = rn((currX + middleX) / 2, 2); - newY = rn((currY + middleY) / 2, 2); - } - - if (findCell(newX, newY) === cellId) { - data[i] = [newX, newY, cellId]; - points[cellId] = [data[i][0], data[i][1]]; // change cell coordinate for all routes - } - } - } - } - - return data; // [[x, y, cell], [x, y, cell]]; - } - - function getRouteSegments(pathCells, connections) { - const segments = []; - let segment = []; - - for (let i = 0; i < pathCells.length; i++) { - const cellId = pathCells[i]; - const nextCellId = pathCells[i + 1]; - const isConnected = connections.has(`${cellId}-${nextCellId}`) || connections.has(`${nextCellId}-${cellId}`); - - if (isConnected) { - if (segment.length) { - // segment stepped into existing segment - segment.push(pathCells[i]); - segments.push(segment); - segment = []; - } - continue; - } - - segment.push(pathCells[i]); - } - - if (segment.length > 1) segments.push(segment); - - return segments; - } - - // Urquhart graph is obtained by removing the longest edge from each triangle in the Delaunay triangulation - // this gives us an aproximation of a desired road network, i.e. connections between burgs - // code from https://observablehq.com/@mbostock/urquhart-graph - function calculateUrquhartEdges(points) { - const score = (p0, p1) => dist2(points[p0], points[p1]); - - const { halfedges, triangles } = utils.Delaunator.from(points); - const n = triangles.length; - - const removed = new Uint8Array(n); - const edges = []; - - for (let e = 0; e < n; e += 3) { - const p0 = triangles[e], - p1 = triangles[e + 1], - p2 = triangles[e + 2]; - - const p01 = score(p0, p1), - p12 = score(p1, p2), - p20 = score(p2, p0); - - removed[ - p20 > p01 && p20 > p12 - ? Math.max(e + 2, halfedges[e + 2]) - : p12 > p01 && p12 > p20 - ? Math.max(e + 1, halfedges[e + 1]) - : Math.max(e, halfedges[e]) - ] = 1; - } - - for (let e = 0; e < n; ++e) { - if (e > halfedges[e] && !removed[e]) { - const t0 = triangles[e]; - const t1 = triangles[e % 3 === 2 ? e - 2 : e + 1]; - edges.push([t0, t1]); - } - } - - return edges; - } -} - -export function buildLinks(routes) { - const links = {}; - - for (const { points, i: routeId } of routes) { - const cells = points.map(p => p[2]); - - for (let i = 0; i < cells.length - 1; i++) { - const cellId = cells[i]; - const nextCellId = cells[i + 1]; - - if (cellId !== nextCellId) { - if (!links[cellId]) links[cellId] = {}; - links[cellId][nextCellId] = routeId; - - if (!links[nextCellId]) links[nextCellId] = {}; - links[nextCellId][cellId] = routeId; - } - } - } - - return links; -} - -// connect cell with routes system by land -export function connect(cellId, pack, utils) { - const { findPath } = utils; - const getCost = createCostEvaluator({ isWater: false, connections: new Map() }); - const pathCells = findPath(cellId, isConnected, getCost); - if (!pathCells) return null; - - const pointsArray = preparePointsArray(); - const points = getPoints("trails", pathCells, pointsArray); - const feature = pack.cells.f[cellId]; - const routeId = getNextId(pack.routes); - const newRoute = { i: routeId, group: "trails", feature, points }; - - const connections = []; - for (let i = 0; i < pathCells.length; i++) { - const from = pathCells[i]; - const to = pathCells[i + 1]; - if (to) connections.push({ from, to, routeId }); - } - - return { route: newRoute, connections }; - - function createCostEvaluator({ isWater, connections }) { - const { dist2 } = utils; - return isWater ? getWaterPathCost : getLandPathCost; - - function getLandPathCost(current, next) { - if (pack.cells.h[next] < 20) return Infinity; // ignore water cells - - const habitability = utils.biomesData.habitability[pack.cells.biome[next]]; - if (!habitability) return Infinity; // inhabitable cells are not passable (e.g. glacier) - - const distanceCost = dist2(pack.cells.p[current], pack.cells.p[next]); - const habitabilityModifier = 1 + Math.max(100 - habitability, 0) / 1000; // [1, 1.1]; - const heightModifier = 1 + Math.max(pack.cells.h[next] - 25, 25) / 25; // [1, 3]; - const connectionModifier = connections.has(`${current}-${next}`) ? 0.5 : 1; - const burgModifier = pack.cells.burg[next] ? 1 : 3; - - const pathCost = distanceCost * habitabilityModifier * heightModifier * connectionModifier * burgModifier; - return pathCost; - } - - function getWaterPathCost(current, next) { - if (pack.cells.h[next] >= 20) return Infinity; // ignore land cells - if (utils.grid.cells.temp[pack.cells.g[next]] < MIN_PASSABLE_SEA_TEMP) return Infinity; // ignore too cold cells - - const distanceCost = dist2(pack.cells.p[current], pack.cells.p[next]); - const typeModifier = ROUTE_TYPE_MODIFIERS[pack.cells.t[next]] || ROUTE_TYPE_MODIFIERS.default; - const connectionModifier = connections.has(`${current}-${next}`) ? 0.5 : 1; - - const pathCost = distanceCost * typeModifier * connectionModifier; - return pathCost; - } - } - - function preparePointsArray() { - const { cells, burgs } = pack; - return cells.p.map(([x, y], cellId) => { - const burgId = cells.burg[cellId]; - if (burgId) return [burgs[burgId].x, burgs[burgId].y]; - return [x, y]; - }); - } - - function getPoints(group, cells, points) { - const { rn, findCell } = utils; - const data = cells.map(cellId => [...points[cellId], cellId]); - - // resolve sharp angles - if (group !== "searoutes") { - for (let i = 1; i < cells.length - 1; i++) { - const cellId = cells[i]; - if (pack.cells.burg[cellId]) continue; - - const [prevX, prevY] = data[i - 1]; - const [currX, currY] = data[i]; - const [nextX, nextY] = data[i + 1]; - - const dAx = prevX - currX; - const dAy = prevY - currY; - const dBx = nextX - currX; - const dBy = nextY - currY; - const angle = Math.abs((Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI); - - if (angle < ROUTES_SHARP_ANGLE) { - const middleX = (prevX + nextX) / 2; - const middleY = (prevY + nextY) / 2; - let newX, newY; - - if (angle < ROUTES_VERY_SHARP_ANGLE) { - newX = rn((currX + middleX * 2) / 3, 2); - newY = rn((currY + middleY * 2) / 3, 2); - } else { - newX = rn((currX + middleX) / 2, 2); - newY = rn((currY + middleY) / 2, 2); - } - - if (findCell(newX, newY) === cellId) { - data[i] = [newX, newY, cellId]; - points[cellId] = [data[i][0], data[i][1]]; // change cell coordinate for all routes - } - } - } - } - - return data; // [[x, y, cell], [x, y, cell]]; - } - - function isConnected(cellId) { - const routes = pack.cells.routes; - return routes[cellId] && Object.keys(routes[cellId]).length > 0; - } -} - -// utility functions -export function isConnected(cellId, pack) { - const routes = pack.cells.routes; - return routes[cellId] && Object.keys(routes[cellId]).length > 0; -} - -export function areConnected(from, to, pack) { - const routeId = pack.cells.routes[from]?.[to]; - return routeId !== undefined; -} - -export function getRoute(from, to, pack) { - const routeId = pack.cells.routes[from]?.[to]; - if (routeId === undefined) return null; - - const route = pack.routes.find(route => route.i === routeId); - if (!route) return null; - - return route; -} - -export function hasRoad(cellId, pack) { - const connections = pack.cells.routes[cellId]; - if (!connections) return false; - - return Object.values(connections).some(routeId => { - const route = pack.routes.find(route => route.i === routeId); - if (!route) return false; - return route.group === "roads"; - }); -} - -export function isCrossroad(cellId, pack) { - const connections = pack.cells.routes[cellId]; - if (!connections) return false; - if (Object.keys(connections).length > 3) return true; - const roadConnections = Object.values(connections).filter(routeId => { - const route = pack.routes.find(route => route.i === routeId); - return route?.group === "roads"; - }); - return roadConnections.length > 2; -} - -// name generator data -const models = { - roads: { burg_suffix: 3, prefix_suffix: 6, the_descriptor_prefix_suffix: 2, the_descriptor_burg_suffix: 1 }, - trails: { burg_suffix: 8, prefix_suffix: 1, the_descriptor_burg_suffix: 1 }, - searoutes: { burg_suffix: 4, prefix_suffix: 2, the_descriptor_prefix_suffix: 1 } -}; - -const prefixes = [ - "King", - "Queen", - "Military", - "Old", - "New", - "Ancient", - "Royal", - "Imperial", - "Great", - "Grand", - "High", - "Silver", - "Dragon", - "Shadow", - "Star", - "Mystic", - "Whisper", - "Eagle", - "Golden", - "Crystal", - "Enchanted", - "Frost", - "Moon", - "Sun", - "Thunder", - "Phoenix", - "Sapphire", - "Celestial", - "Wandering", - "Echo", - "Twilight", - "Crimson", - "Serpent", - "Iron", - "Forest", - "Flower", - "Whispering", - "Eternal", - "Frozen", - "Rain", - "Luminous", - "Stardust", - "Arcane", - "Glimmering", - "Jade", - "Ember", - "Azure", - "Gilded", - "Divine", - "Shadowed", - "Cursed", - "Moonlit", - "Sable", - "Everlasting", - "Amber", - "Nightshade", - "Wraith", - "Scarlet", - "Platinum", - "Whirlwind", - "Obsidian", - "Ethereal", - "Ghost", - "Spike", - "Dusk", - "Raven", - "Spectral", - "Burning", - "Verdant", - "Copper", - "Velvet", - "Falcon", - "Enigma", - "Glowing", - "Silvered", - "Molten", - "Radiant", - "Astral", - "Wild", - "Flame", - "Amethyst", - "Aurora", - "Shadowy", - "Solar", - "Lunar", - "Whisperwind", - "Fading", - "Titan", - "Dawn", - "Crystalline", - "Jeweled", - "Sylvan", - "Twisted", - "Ebon", - "Thorn", - "Cerulean", - "Halcyon", - "Infernal", - "Storm", - "Eldritch", - "Sapphire", - "Crimson", - "Tranquil", - "Paved" -]; - -const descriptors = [ - "Great", - "Shrouded", - "Sacred", - "Fabled", - "Frosty", - "Winding", - "Echoing", - "Serpentine", - "Breezy", - "Misty", - "Rustic", - "Silent", - "Cobbled", - "Cracked", - "Shaky", - "Obscure" -]; - -const suffixes = { - roads: { road: 7, route: 3, way: 2, highway: 1 }, - trails: { trail: 4, path: 1, track: 1, pass: 1 }, - searoutes: { "sea route": 5, lane: 2, passage: 1, seaway: 1 } -}; - -export function generateName({ group, points }, pack, utils) { - const { ra, rw, getAdjective } = utils; - - if (points.length < 4) return "Unnamed route segment"; - - const model = rw(models[group]); - const suffix = rw(suffixes[group]); - - const burgName = getBurgName(); - if (model === "burg_suffix" && burgName) return `${burgName} ${suffix}`; - if (model === "prefix_suffix") return `${ra(prefixes)} ${suffix}`; - if (model === "the_descriptor_prefix_suffix") return `The ${ra(descriptors)} ${ra(prefixes)} ${suffix}`; - if (model === "the_descriptor_burg_suffix" && burgName) return `The ${ra(descriptors)} ${burgName} ${suffix}`; - return "Unnamed route"; - - function getBurgName() { - const priority = [points.at(-1), points.at(0), points.slice(1, -1).reverse()]; - for (const [_x, _y, cellId] of priority) { - const burgId = pack.cells.burg[cellId]; - if (burgId) return getAdjective(pack.burgs[burgId].name); - } - return null; - } -} - -export function getNextId(routes) { - return routes.length ? Math.max(...routes.map(r => r.i)) + 1 : 0; -} - -export function remove(route, pack) { - const routes = pack.cells.routes; - const removedConnections = []; - - for (const point of route.points) { - const from = point[2]; - if (!routes[from]) continue; - - for (const [to, routeId] of Object.entries(routes[from])) { - if (routeId === route.i) { - removedConnections.push({ from, to }); - } - } - } - - const updatedRoutes = pack.routes.filter(r => r.i !== route.i); - const updatedCellRoutes = { ...routes }; - - removedConnections.forEach(({ from, to }) => { - if (updatedCellRoutes[from]) delete updatedCellRoutes[from][to]; - if (updatedCellRoutes[to]) delete updatedCellRoutes[to][from]; - }); - - return { - routes: updatedRoutes, - cellRoutes: updatedCellRoutes, - removedConnections - }; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/submap.js b/procedural/src/engine/modules/submap.js deleted file mode 100644 index 6e79556b..00000000 --- a/procedural/src/engine/modules/submap.js +++ /dev/null @@ -1,375 +0,0 @@ -"use strict"; - -/* - generate new map based on an existing one (resampling parentMap) - parentMap: {grid, pack, notes} from original map - projection: f(Number, Number) -> [Number, Number] - inverse: f(Number, Number) -> [Number, Number] - scale: Number -*/ -export function process({projection, inverse, scale}, grid, pack, notes, config, utils) { - const { - deepCopy, generateGrid, Features, addLakesInDeepDepressions, openNearSeaLakes, - OceanLayers, calculateMapCoordinates, calculateTemperatures, reGraph, - createDefaultRuler, Rivers, BurgsAndStates, Routes, Provinces, Markers, - isWater, findCell, findAll, rn, unique, d3, lineclip, getPolesOfInaccessibility, - WARN - } = utils; - - const parentMap = {grid: deepCopy(grid), pack: deepCopy(pack), notes: deepCopy(notes)}; - const riversData = saveRiversData(parentMap.pack.rivers, Rivers); - - const newGrid = generateGrid(); - const newPack = {}; - const newNotes = parentMap.notes; - - resamplePrimaryGridData(parentMap, inverse, scale, newGrid, d3, isWater); - - Features.markupGrid(newGrid); - addLakesInDeepDepressions(newGrid); - openNearSeaLakes(newGrid); - - OceanLayers(newGrid); - calculateMapCoordinates(newGrid); - calculateTemperatures(newGrid); - - reGraph(newGrid, newPack); - Features.markupPack(newPack); - createDefaultRuler(newPack); - - restoreCellData(parentMap, inverse, scale, newPack, d3, isWater, groupCellsByType); - restoreRivers(riversData, projection, scale, newPack, isInMap, findCell, rn, Rivers, config); - restoreCultures(parentMap, projection, newPack, getPolesOfInaccessibility, findCell, rn, isInMap, config); - restoreBurgs(parentMap, projection, scale, newPack, d3, groupCellsByType, findCell, isWater, isInMap, rn, BurgsAndStates, WARN, config); - restoreStates(parentMap, projection, newPack, BurgsAndStates, findCell, isInMap, rn, config); - restoreRoutes(parentMap, projection, newPack, isInMap, rn, findCell, lineclip, Routes, config); - restoreReligions(parentMap, projection, newPack, getPolesOfInaccessibility, findCell, rn, isInMap, config); - restoreProvinces(parentMap, newPack, Provinces, findCell); - restoreFeatureDetails(parentMap, inverse, newPack); - restoreMarkers(parentMap, projection, newPack, isInMap, findCell, rn, Markers); - restoreZones(parentMap, projection, scale, newPack, isInMap, findCell, findAll, unique); - - return { - grid: newGrid, - pack: newPack, - notes: newNotes - }; -} - -function resamplePrimaryGridData(parentMap, inverse, scale, grid, d3, isWater) { - grid.cells.h = new Uint8Array(grid.points.length); - grid.cells.temp = new Int8Array(grid.points.length); - grid.cells.prec = new Uint8Array(grid.points.length); - - grid.points.forEach(([x, y], newGridCell) => { - const [parentX, parentY] = inverse(x, y); - const parentPackCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2]; - const parentGridCell = parentMap.pack.cells.g[parentPackCell]; - - grid.cells.h[newGridCell] = parentMap.grid.cells.h[parentGridCell]; - grid.cells.temp[newGridCell] = parentMap.grid.cells.temp[parentGridCell]; - grid.cells.prec[newGridCell] = parentMap.grid.cells.prec[parentGridCell]; - }); - - if (scale >= 2) smoothHeightmap(grid, d3, isWater); -} - -function smoothHeightmap(grid, d3, isWater) { - grid.cells.h.forEach((height, newGridCell) => { - const heights = [height, ...grid.cells.c[newGridCell].map(c => grid.cells.h[c])]; - const meanHeight = d3.mean(heights); - grid.cells.h[newGridCell] = isWater(grid, newGridCell) ? Math.min(meanHeight, 19) : Math.max(meanHeight, 20); - }); -} - -function restoreCellData(parentMap, inverse, scale, pack, d3, isWater, groupCellsByType) { - pack.cells.biome = new Uint8Array(pack.cells.i.length); - pack.cells.fl = new Uint16Array(pack.cells.i.length); - pack.cells.s = new Int16Array(pack.cells.i.length); - pack.cells.pop = new Float32Array(pack.cells.i.length); - pack.cells.culture = new Uint16Array(pack.cells.i.length); - pack.cells.state = new Uint16Array(pack.cells.i.length); - pack.cells.burg = new Uint16Array(pack.cells.i.length); - pack.cells.religion = new Uint16Array(pack.cells.i.length); - pack.cells.province = new Uint16Array(pack.cells.i.length); - - const parentPackCellGroups = groupCellsByType(parentMap.pack); - const parentPackLandCellsQuadtree = d3.quadtree(parentPackCellGroups.land); - - for (const newPackCell of pack.cells.i) { - const [x, y] = inverse(...pack.cells.p[newPackCell]); - if (isWater(pack, newPackCell)) continue; - - const parentPackCell = parentPackLandCellsQuadtree.find(x, y, Infinity)[2]; - const parentCellArea = parentMap.pack.cells.area[parentPackCell]; - const areaRatio = pack.cells.area[newPackCell] / parentCellArea; - const scaleRatio = areaRatio / scale; - - pack.cells.biome[newPackCell] = parentMap.pack.cells.biome[parentPackCell]; - pack.cells.fl[newPackCell] = parentMap.pack.cells.fl[parentPackCell]; - pack.cells.s[newPackCell] = parentMap.pack.cells.s[parentPackCell] * scaleRatio; - pack.cells.pop[newPackCell] = parentMap.pack.cells.pop[parentPackCell] * scaleRatio; - pack.cells.culture[newPackCell] = parentMap.pack.cells.culture[parentPackCell]; - pack.cells.state[newPackCell] = parentMap.pack.cells.state[parentPackCell]; - pack.cells.religion[newPackCell] = parentMap.pack.cells.religion[parentPackCell]; - pack.cells.province[newPackCell] = parentMap.pack.cells.province[parentPackCell]; - } -} - -function saveRiversData(parentRivers, Rivers) { - return parentRivers.map(river => { - const meanderedPoints = Rivers.addMeandering(river.cells, river.points); - return {...river, meanderedPoints}; - }); -} - -function restoreRivers(riversData, projection, scale, pack, isInMap, findCell, rn, Rivers, config) { - pack.cells.r = new Uint16Array(pack.cells.i.length); - pack.cells.conf = new Uint8Array(pack.cells.i.length); - - pack.rivers = riversData - .map(river => { - let wasInMap = true; - const points = []; - - river.meanderedPoints.forEach(([parentX, parentY]) => { - const [x, y] = projection(parentX, parentY); - const inMap = isInMap(x, y, config); - if (inMap || wasInMap) points.push([rn(x, 2), rn(y, 2)]); - wasInMap = inMap; - }); - if (points.length < 2) return null; - - const cells = points.map(point => findCell(...point)); - cells.forEach(cellId => { - if (pack.cells.r[cellId]) pack.cells.conf[cellId] = 1; - pack.cells.r[cellId] = river.i; - }); - - const widthFactor = river.widthFactor * scale; - return {...river, cells, points, source: cells.at(0), mouth: cells.at(-2), widthFactor}; - }) - .filter(Boolean); - - pack.rivers.forEach(river => { - river.basin = Rivers.getBasin(river.i); - river.length = Rivers.getApproximateLength(river.points); - }); -} - -function restoreCultures(parentMap, projection, pack, getPolesOfInaccessibility, findCell, rn, isInMap, config) { - const validCultures = new Set(pack.cells.culture); - const culturePoles = getPolesOfInaccessibility(pack, cellId => pack.cells.culture[cellId]); - pack.cultures = parentMap.pack.cultures.map(culture => { - if (!culture.i || culture.removed) return culture; - if (!validCultures.has(culture.i)) return {...culture, removed: true, lock: false}; - - const [xp, yp] = projection(...parentMap.pack.cells.p[culture.center]); - const [x, y] = [rn(xp, 2), rn(yp, 2)]; - const centerCoords = isInMap(x, y, config) ? [x, y] : culturePoles[culture.i]; - const center = findCell(...centerCoords); - return {...culture, center}; - }); -} - -function restoreBurgs(parentMap, projection, scale, pack, d3, groupCellsByType, findCell, isWater, isInMap, rn, BurgsAndStates, WARN, config) { - const packLandCellsQuadtree = d3.quadtree(groupCellsByType(pack).land); - const findLandCell = (x, y) => packLandCellsQuadtree.find(x, y, Infinity)?.[2]; - - pack.burgs = parentMap.pack.burgs.map(burg => { - if (!burg.i || burg.removed) return burg; - burg.population *= scale; // adjust for populationRate change - - const [xp, yp] = projection(burg.x, burg.y); - if (!isInMap(xp, yp, config)) return {...burg, removed: true, lock: false}; - - const closestCell = findCell(xp, yp); - const cell = isWater(pack, closestCell) ? findLandCell(xp, yp) : closestCell; - - if (pack.cells.burg[cell]) { - WARN && console.warn(`Cell ${cell} already has a burg. Removing burg ${burg.name} (${burg.i})`); - return {...burg, removed: true, lock: false}; - } - - pack.cells.burg[cell] = burg.i; - const [x, y] = getBurgCoordinates(burg, closestCell, cell, xp, yp, pack, BurgsAndStates, rn); - return {...burg, cell, x, y}; - }); - - function getBurgCoordinates(burg, closestCell, cell, xp, yp, pack, BurgsAndStates, rn) { - const haven = pack.cells.haven[cell]; - if (burg.port && haven) return BurgsAndStates.getCloseToEdgePoint(cell, haven); - - if (closestCell !== cell) return pack.cells.p[cell]; - return [rn(xp, 2), rn(yp, 2)]; - } -} - -function restoreStates(parentMap, projection, pack, BurgsAndStates, findCell, isInMap, rn, config) { - const validStates = new Set(pack.cells.state); - pack.states = parentMap.pack.states.map(state => { - if (!state.i || state.removed) return state; - if (validStates.has(state.i)) return state; - return {...state, removed: true, lock: false}; - }); - - BurgsAndStates.getPoles(); - const regimentCellsMap = {}; - const VERTICAL_GAP = 8; - - pack.states = pack.states.map(state => { - if (!state.i || state.removed) return state; - - const capital = pack.burgs[state.capital]; - state.center = !capital || capital.removed ? findCell(...state.pole) : capital.cell; - - const military = state.military.map(regiment => { - const cellCoords = projection(...parentMap.pack.cells.p[regiment.cell]); - const cell = isInMap(...cellCoords, config) ? findCell(...cellCoords) : state.center; - - const [xPos, yPos] = projection(regiment.x, regiment.y); - const [xBase, yBase] = projection(regiment.bx, regiment.by); - const [xCell, yCell] = pack.cells.p[cell]; - - const regsOnCell = regimentCellsMap[cell] || 0; - regimentCellsMap[cell] = regsOnCell + 1; - - const name = - isInMap(xPos, yPos, config) || regiment.name.includes("[relocated]") ? regiment.name : `[relocated] ${regiment.name}`; - - const pos = isInMap(xPos, yPos, config) - ? {x: rn(xPos, 2), y: rn(yPos, 2)} - : {x: xCell, y: yCell + regsOnCell * VERTICAL_GAP}; - - const base = isInMap(xBase, yBase, config) ? {bx: rn(xBase, 2), by: rn(yBase, 2)} : {bx: xCell, by: yCell}; - - return {...regiment, cell, name, ...base, ...pos}; - }); - - const neighbors = state.neighbors.filter(stateId => validStates.has(stateId)); - return {...state, neighbors, military}; - }); -} - -function restoreRoutes(parentMap, projection, pack, isInMap, rn, findCell, lineclip, Routes, config) { - pack.routes = parentMap.pack.routes - .map(route => { - let wasInMap = true; - const points = []; - - route.points.forEach(([parentX, parentY]) => { - const [x, y] = projection(parentX, parentY); - const inMap = isInMap(x, y, config); - if (inMap || wasInMap) points.push([rn(x, 2), rn(y, 2)]); - wasInMap = inMap; - }); - if (points.length < 2) return null; - - const bbox = [0, 0, config.graphWidth, config.graphHeight]; - const clipped = lineclip(points, bbox)[0].map(([x, y]) => [rn(x, 2), rn(y, 2), findCell(x, y)]); - const firstCell = clipped[0][2]; - const feature = pack.cells.f[firstCell]; - return {...route, feature, points: clipped}; - }) - .filter(Boolean); - - pack.cells.routes = Routes.buildLinks(pack.routes); -} - -function restoreReligions(parentMap, projection, pack, getPolesOfInaccessibility, findCell, rn, isInMap, config) { - const validReligions = new Set(pack.cells.religion); - const religionPoles = getPolesOfInaccessibility(pack, cellId => pack.cells.religion[cellId]); - - pack.religions = parentMap.pack.religions.map(religion => { - if (!religion.i || religion.removed) return religion; - if (!validReligions.has(religion.i)) return {...religion, removed: true, lock: false}; - - const [xp, yp] = projection(...parentMap.pack.cells.p[religion.center]); - const [x, y] = [rn(xp, 2), rn(yp, 2)]; - const centerCoords = isInMap(x, y, config) ? [x, y] : religionPoles[religion.i]; - const center = findCell(...centerCoords); - return {...religion, center}; - }); -} - -function restoreProvinces(parentMap, pack, Provinces, findCell) { - const validProvinces = new Set(pack.cells.province); - pack.provinces = parentMap.pack.provinces.map(province => { - if (!province.i || province.removed) return province; - if (!validProvinces.has(province.i)) return {...province, removed: true, lock: false}; - - return province; - }); - - Provinces.getPoles(); - - pack.provinces.forEach(province => { - if (!province.i || province.removed) return; - const capital = pack.burgs[province.burg]; - province.center = !capital?.removed ? capital.cell : findCell(...province.pole); - }); -} - -function restoreMarkers(parentMap, projection, pack, isInMap, findCell, rn, Markers) { - pack.markers = parentMap.pack.markers; - pack.markers.forEach(marker => { - const [x, y] = projection(marker.x, marker.y); - if (!isInMap(x, y)) Markers.deleteMarker(marker.i); - - const cell = findCell(x, y); - marker.x = rn(x, 2); - marker.y = rn(y, 2); - marker.cell = cell; - }); -} - -function restoreZones(parentMap, projection, scale, pack, isInMap, findCell, findAll, unique) { - const getSearchRadius = cellId => Math.sqrt(parentMap.pack.cells.area[cellId] / Math.PI) * scale; - - pack.zones = parentMap.pack.zones.map(zone => { - const cells = zone.cells - .map(cellId => { - const [x, y] = projection(...parentMap.pack.cells.p[cellId]); - if (!isInMap(x, y)) return null; - return findAll(x, y, getSearchRadius(cellId)); - }) - .filter(Boolean) - .flat(); - - return {...zone, cells: unique(cells)}; - }); -} - -function restoreFeatureDetails(parentMap, inverse, pack) { - pack.features.forEach(feature => { - if (!feature) return; - const [x, y] = pack.cells.p[feature.firstCell]; - const [parentX, parentY] = inverse(x, y); - const parentCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2]; - if (parentCell === undefined) return; - const parentFeature = parentMap.pack.features[parentMap.pack.cells.f[parentCell]]; - - if (parentFeature.group) feature.group = parentFeature.group; - if (parentFeature.name) feature.name = parentFeature.name; - if (parentFeature.height) feature.height = parentFeature.height; - }); -} - -function groupCellsByType(graph) { - return graph.cells.p.reduce( - (acc, [x, y], cellId) => { - const group = isWater(graph, cellId) ? "water" : "land"; - acc[group].push([x, y, cellId]); - return acc; - }, - {land: [], water: []} - ); -} - -function isWater(graph, cellId) { - return graph.cells.h[cellId] < 20; -} - -function isInMap(x, y, config) { - return x >= 0 && x <= config.graphWidth && y >= 0 && y <= config.graphHeight; -} \ No newline at end of file diff --git a/procedural/src/engine/modules/voronoi.js b/procedural/src/engine/modules/voronoi.js deleted file mode 100644 index d73bbb8e..00000000 --- a/procedural/src/engine/modules/voronoi.js +++ /dev/null @@ -1,135 +0,0 @@ -export class Voronoi { - /** - * Creates a Voronoi diagram from the given Delaunator, a list of points, and the number of points. The Voronoi diagram is constructed using (I think) the {@link https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm |Bowyer-Watson Algorithm} - * The {@link https://github.com/mapbox/delaunator/ |Delaunator} library uses {@link https://en.wikipedia.org/wiki/Doubly_connected_edge_list |half-edges} to represent the relationship between points and triangles. - * @param {{triangles: Uint32Array, halfedges: Int32Array}} delaunay A {@link https://github.com/mapbox/delaunator/blob/master/index.js |Delaunator} instance. - * @param {[number, number][]} points A list of coordinates. - * @param {number} pointsN The number of points. - */ - constructor(delaunay, points, pointsN) { - this.delaunay = delaunay; - this.points = points; - this.pointsN = pointsN; - this.cells = { v: [], c: [], b: [] }; // voronoi cells: v = cell vertices, c = adjacent cells, b = near-border cell - this.vertices = { p: [], v: [], c: [] }; // cells vertices: p = vertex coordinates, v = neighboring vertices, c = adjacent cells - - // Half-edges are the indices into the delaunator outputs: - // delaunay.triangles[e] gives the point ID where the half-edge starts - // delaunay.halfedges[e] returns either the opposite half-edge in the adjacent triangle, or -1 if there's not an adjacent triangle. - for (let e = 0; e < this.delaunay.triangles.length; e++) { - - const p = this.delaunay.triangles[this.nextHalfedge(e)]; - if (p < this.pointsN && !this.cells.c[p]) { - const edges = this.edgesAroundPoint(e); - this.cells.v[p] = edges.map(e => this.triangleOfEdge(e)); // cell: adjacent vertex - this.cells.c[p] = edges.map(e => this.delaunay.triangles[e]).filter(c => c < this.pointsN); // cell: adjacent valid cells - this.cells.b[p] = edges.length > this.cells.c[p].length ? 1 : 0; // cell: is border - } - - const t = this.triangleOfEdge(e); - if (!this.vertices.p[t]) { - this.vertices.p[t] = this.triangleCenter(t); // vertex: coordinates - this.vertices.v[t] = this.trianglesAdjacentToTriangle(t); // vertex: adjacent vertices - this.vertices.c[t] = this.pointsOfTriangle(t); // vertex: adjacent cells - } - } - } - - /** - * Gets the IDs of the points comprising the given triangle. Taken from {@link https://mapbox.github.io/delaunator/#triangle-to-points| the Delaunator docs.} - * @param {number} t The index of the triangle - * @returns {[number, number, number]} The IDs of the points comprising the given triangle. - */ - pointsOfTriangle(t) { - return this.edgesOfTriangle(t).map(edge => this.delaunay.triangles[edge]); - } - - /** - * Identifies what triangles are adjacent to the given triangle. Taken from {@link https://mapbox.github.io/delaunator/#triangle-to-triangles| the Delaunator docs.} - * @param {number} t The index of the triangle - * @returns {number[]} The indices of the triangles that share half-edges with this triangle. - */ - trianglesAdjacentToTriangle(t) { - let triangles = []; - for (let edge of this.edgesOfTriangle(t)) { - let opposite = this.delaunay.halfedges[edge]; - triangles.push(this.triangleOfEdge(opposite)); - } - return triangles; - } - - /** - * Gets the indices of all the incoming and outgoing half-edges that touch the given point. Taken from {@link https://mapbox.github.io/delaunator/#point-to-edges| the Delaunator docs.} - * @param {number} start The index of an incoming half-edge that leads to the desired point - * @returns {number[]} The indices of all half-edges (incoming or outgoing) that touch the point. - */ - edgesAroundPoint(start) { - const result = []; - let incoming = start; - do { - result.push(incoming); - const outgoing = this.nextHalfedge(incoming); - incoming = this.delaunay.halfedges[outgoing]; - } while (incoming !== -1 && incoming !== start && result.length < 20); - return result; - } - - /** - * Returns the center of the triangle located at the given index. - * @param {number} t The index of the triangle - * @returns {[number, number]} - */ - triangleCenter(t) { - let vertices = this.pointsOfTriangle(t).map(p => this.points[p]); - return this.circumcenter(vertices[0], vertices[1], vertices[2]); - } - - /** - * Retrieves all of the half-edges for a specific triangle `t`. Taken from {@link https://mapbox.github.io/delaunator/#edge-and-triangle| the Delaunator docs.} - * @param {number} t The index of the triangle - * @returns {[number, number, number]} The edges of the triangle. - */ - edgesOfTriangle(t) { return [3 * t, 3 * t + 1, 3 * t + 2]; } - - /** - * Enables lookup of a triangle, given one of the half-edges of that triangle. Taken from {@link https://mapbox.github.io/delaunator/#edge-and-triangle| the Delaunator docs.} - * @param {number} e The index of the edge - * @returns {number} The index of the triangle - */ - triangleOfEdge(e) { return Math.floor(e / 3); } - - /** - * Moves to the next half-edge of a triangle, given the current half-edge's index. Taken from {@link https://mapbox.github.io/delaunator/#edge-to-edges| the Delaunator docs.} - * @param {number} e The index of the current half edge - * @returns {number} The index of the next half edge - */ - nextHalfedge(e) { return (e % 3 === 2) ? e - 2 : e + 1; } - - /** - * Moves to the previous half-edge of a triangle, given the current half-edge's index. Taken from {@link https://mapbox.github.io/delaunator/#edge-to-edges| the Delaunator docs.} - * @param {number} e The index of the current half edge - * @returns {number} The index of the previous half edge - */ - prevHalfedge(e) { return (e % 3 === 0) ? e + 2 : e - 1; } - - /** - * Finds the circumcenter of the triangle identified by points a, b, and c. Taken from {@link https://en.wikipedia.org/wiki/Circumscribed_circle#Circumcenter_coordinates| Wikipedia} - * @param {[number, number]} a The coordinates of the first point of the triangle - * @param {[number, number]} b The coordinates of the second point of the triangle - * @param {[number, number]} c The coordinates of the third point of the triangle - * @return {[number, number]} The coordinates of the circumcenter of the triangle. - */ - circumcenter(a, b, c) { - const [ax, ay] = a; - const [bx, by] = b; - const [cx, cy] = c; - const ad = ax * ax + ay * ay; - const bd = bx * bx + by * by; - const cd = cx * cx + cy * cy; - const D = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)); - return [ - Math.floor(1 / D * (ad * (by - cy) + bd * (cy - ay) + cd * (ay - by))), - Math.floor(1 / D * (ad * (cx - bx) + bd * (ax - cx) + cd * (bx - ax))) - ]; - } -} \ No newline at end of file diff --git a/procedural/src/engine/modules/zones-generator.js b/procedural/src/engine/modules/zones-generator.js deleted file mode 100644 index bb763e6c..00000000 --- a/procedural/src/engine/modules/zones-generator.js +++ /dev/null @@ -1,485 +0,0 @@ -"use strict"; - -const zoneConfig = { - invasion: {quantity: 2, generate: addInvasion}, // invasion of enemy lands - rebels: {quantity: 1.5, generate: addRebels}, // rebels along a state border - proselytism: {quantity: 1.6, generate: addProselytism}, // proselitism of organized religion - crusade: {quantity: 1.6, generate: addCrusade}, // crusade on heresy lands - disease: {quantity: 1.4, generate: addDisease}, // disease starting in a random city - disaster: {quantity: 1, generate: addDisaster}, // disaster starting in a random city - eruption: {quantity: 1, generate: addEruption}, // eruption aroung volcano - avalanche: {quantity: 0.8, generate: addAvalanche}, // avalanche impacting highland road - fault: {quantity: 1, generate: addFault}, // fault line in elevated areas - flood: {quantity: 1, generate: addFlood}, // flood on river banks - tsunami: {quantity: 1, generate: addTsunami} // tsunami starting near coast -}; - -export function generate(pack, notes, utils, config = {}) { - const {gauss, ra, rw, P, rand, getAdjective} = utils.random; - const {Names, Routes, FlatQueue, d3} = utils; - const {TIME = false} = config; - - TIME && console.time("generateZones"); - - const globalModifier = config.globalModifier || 1; - const usedCells = new Uint8Array(pack.cells.i.length); - const zones = []; - - Object.values(zoneConfig).forEach(type => { - const expectedNumber = type.quantity * globalModifier; - let number = gauss(expectedNumber, expectedNumber / 2, 0, 100); - while (number--) { - const zone = type.generate(pack, usedCells, utils); - if (zone) zones.push({...zone, i: zones.length}); - } - }); - - TIME && console.timeEnd("generateZones"); - - return zones; -} - -function addInvasion(pack, usedCells, utils) { - const {ra, rand, rw, P} = utils.random; - const {getAdjective} = utils.random; - const {cells, states} = pack; - - const ongoingConflicts = states - .filter(s => s.i && !s.removed && s.campaigns) - .map(s => s.campaigns) - .flat() - .filter(c => !c.end); - if (!ongoingConflicts.length) return null; - const {defender, attacker} = ra(ongoingConflicts); - - const borderCells = cells.i.filter(cellId => { - if (usedCells[cellId]) return false; - if (cells.state[cellId] !== defender) return false; - return cells.c[cellId].some(c => cells.state[c] === attacker); - }); - - const startCell = ra(borderCells); - if (startCell === undefined) return null; - - const invasionCells = []; - const queue = [startCell]; - const maxCells = rand(5, 30); - - while (queue.length) { - const cellId = P(0.4) ? queue.shift() : queue.pop(); - invasionCells.push(cellId); - if (invasionCells.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId]) return; - if (cells.state[neibCellId] !== defender) return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const subtype = rw({ - Invasion: 5, - Occupation: 4, - Conquest: 3, - Incursion: 2, - Intervention: 2, - Assault: 1, - Foray: 1, - Intrusion: 1, - Irruption: 1, - Offensive: 1, - Pillaging: 1, - Plunder: 1, - Raid: 1, - Skirmishes: 1 - }); - const name = getAdjective(states[attacker].name) + " " + subtype; - - return {name, type: "Invasion", cells: invasionCells, color: "url(#hatch1)"}; -} - -function addRebels(pack, usedCells, utils) { - const {ra, rand, rw} = utils.random; - const {getAdjective} = utils.random; - const {cells, states} = pack; - - const state = ra(states.filter(s => s.i && !s.removed && s.neighbors.some(Boolean))); - if (!state) return null; - - const neibStateId = ra(state.neighbors.filter(n => n && !states[n].removed)); - if (!neibStateId) return null; - - const cellsArray = []; - const queue = []; - const borderCellId = cells.i.find( - i => cells.state[i] === state.i && cells.c[i].some(c => cells.state[c] === neibStateId) - ); - if (borderCellId) queue.push(borderCellId); - const maxCells = rand(10, 30); - - while (queue.length) { - const cellId = queue.shift(); - cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId]) return; - if (cells.state[neibCellId] !== state.i) return; - usedCells[neibCellId] = 1; - if (neibCellId % 4 !== 0 && !cells.c[neibCellId].some(c => cells.state[c] === neibStateId)) return; - queue.push(neibCellId); - }); - } - - const rebels = rw({ - Rebels: 5, - Insurrection: 2, - Mutineers: 1, - Insurgents: 1, - Rebellion: 1, - Renegades: 1, - Revolters: 1, - Revolutionaries: 1, - Rioters: 1, - Separatists: 1, - Secessionists: 1, - Conspiracy: 1 - }); - - const name = getAdjective(states[neibStateId].name) + " " + rebels; - return {name, type: "Rebels", cells: cellsArray, color: "url(#hatch3)"}; -} - -function addProselytism(pack, usedCells, utils) { - const {ra, rand} = utils.random; - const {getAdjective} = utils.random; - const {cells, religions} = pack; - - const organizedReligions = religions.filter(r => r.i && !r.removed && r.type === "Organized"); - const religion = ra(organizedReligions); - if (!religion) return null; - - const targetBorderCells = cells.i.filter( - i => - cells.h[i] < 20 && - cells.pop[i] && - cells.religion[i] !== religion.i && - cells.c[i].some(c => cells.religion[c] === religion.i) - ); - const startCell = ra(targetBorderCells); - if (!startCell) return null; - - const targetReligionId = cells.religion[startCell]; - const proselytismCells = []; - const queue = [startCell]; - const maxCells = rand(10, 30); - - while (queue.length) { - const cellId = queue.shift(); - proselytismCells.push(cellId); - if (proselytismCells.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId]) return; - if (cells.religion[neibCellId] !== targetReligionId) return; - if (cells.h[neibCellId] < 20 || !cells.pop[neibCellId]) return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const name = `${getAdjective(religion.name.split(" ")[0])} Proselytism`; - return {name, type: "Proselytism", cells: proselytismCells, color: "url(#hatch6)"}; -} - -function addCrusade(pack, usedCells, utils) { - const {ra} = utils.random; - const {getAdjective} = utils.random; - const {cells, religions} = pack; - - const heresies = religions.filter(r => !r.removed && r.type === "Heresy"); - if (!heresies.length) return null; - - const heresy = ra(heresies); - const crusadeCells = cells.i.filter(i => !usedCells[i] && cells.religion[i] === heresy.i); - if (!crusadeCells.length) return null; - crusadeCells.forEach(i => (usedCells[i] = 1)); - - const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade"; - return { - name, - type: "Crusade", - cells: Array.from(crusadeCells), - color: "url(#hatch6)" - }; -} - -function addDisease(pack, usedCells, utils) { - const {ra, rand, rw} = utils.random; - const {FlatQueue, Routes} = utils; - const {cells, burgs} = pack; - - const burg = ra(burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed)); // random burg - if (!burg) return null; - - const cellsArray = []; - const cost = []; - const maxCells = rand(20, 40); - - const queue = new FlatQueue(); - queue.push({e: burg.cell, p: 0}, 0); - - while (queue.length) { - const next = queue.pop(); - if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e); - usedCells[next.e] = 1; - - cells.c[next.e].forEach(nextCellId => { - const c = Routes.getRoute(next.e, nextCellId) ? 5 : 100; - const p = next.p + c; - if (p > maxCells) return; - - if (!cost[nextCellId] || p < cost[nextCellId]) { - cost[nextCellId] = p; - queue.push({e: nextCellId, p}, p); - } - }); - } - - // prettier-ignore - const name = `${(() => { - const model = rw({color: 2, animal: 1, adjective: 1}); - if (model === "color") return ra(["Amber", "Azure", "Black", "Blue", "Brown", "Crimson", "Emerald", "Golden", "Green", "Grey", "Orange", "Pink", "Purple", "Red", "Ruby", "Scarlet", "Silver", "Violet", "White", "Yellow"]); - if (model === "animal") return ra(["Ape", "Bear", "Bird", "Boar", "Cat", "Cow", "Deer", "Dog", "Fox", "Goat", "Horse", "Lion", "Pig", "Rat", "Raven", "Sheep", "Spider", "Tiger", "Viper", "Wolf", "Worm", "Wyrm"]); - if (model === "adjective") return ra(["Blind", "Bloody", "Brutal", "Burning", "Deadly", "Fatal", "Furious", "Great", "Grim", "Horrible", "Invisible", "Lethal", "Loud", "Mortal", "Savage", "Severe", "Silent", "Unknown", "Venomous", "Vicious"]); - })()} ${rw({Fever: 5, Plague: 3, Cough: 3, Flu: 2, Pox: 2, Cholera: 2, Typhoid: 2, Leprosy: 1, Smallpox: 1, Pestilence: 1, Consumption: 1, Malaria: 1, Dropsy: 1})}`; - - return {name, type: "Disease", cells: cellsArray, color: "url(#hatch12)"}; -} - -function addDisaster(pack, usedCells, utils) { - const {ra, rand, rw} = utils.random; - const {getAdjective} = utils.random; - const {FlatQueue} = utils; - const {cells, burgs} = pack; - - const burg = ra(burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed)); - if (!burg) return null; - usedCells[burg.cell] = 1; - - const cellsArray = []; - const cost = []; - const maxCells = rand(5, 25); - - const queue = new FlatQueue(); - queue.push({e: burg.cell, p: 0}, 0); - - while (queue.length) { - const next = queue.pop(); - if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e); - usedCells[next.e] = 1; - - cells.c[next.e].forEach(function (e) { - const c = rand(1, 10); - const p = next.p + c; - if (p > maxCells) return; - - if (!cost[e] || p < cost[e]) { - cost[e] = p; - queue.push({e, p}, p); - } - }); - } - - const type = rw({ - Famine: 5, - Drought: 3, - Earthquake: 3, - Dearth: 1, - Tornadoes: 1, - Wildfires: 1, - Storms: 1, - Blight: 1 - }); - const name = getAdjective(burg.name) + " " + type; - return {name, type: "Disaster", cells: cellsArray, color: "url(#hatch5)"}; -} - -function addEruption(pack, usedCells, utils, notes) { - const {ra, rand, P} = utils.random; - const {cells, markers} = pack; - - const volcanoe = markers.find(m => m.type === "volcanoes" && !usedCells[m.cell]); - if (!volcanoe) return null; - usedCells[volcanoe.cell] = 1; - - const note = notes.find(n => n.id === "marker" + volcanoe.i); - if (note) note.legend = note.legend.replace("Active volcano", "Erupting volcano"); - const name = note ? note.name.replace(" Volcano", "") + " Eruption" : "Volcano Eruption"; - - const cellsArray = []; - const queue = [volcanoe.cell]; - const maxCells = rand(10, 30); - - while (queue.length) { - const cellId = P(0.5) ? queue.shift() : queue.pop(); - cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId] || cells.h[neibCellId] < 20) return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - return {name, type: "Eruption", cells: cellsArray, color: "url(#hatch7)"}; -} - -function addAvalanche(pack, usedCells, utils) { - const {ra, rand, P} = utils.random; - const {getAdjective} = utils.random; - const {Routes, Names} = utils; - const {cells} = pack; - - const routeCells = cells.i.filter(i => !usedCells[i] && Routes.isConnected(i) && cells.h[i] >= 70); - if (!routeCells.length) return null; - - const startCell = ra(routeCells); - usedCells[startCell] = 1; - - const cellsArray = []; - const queue = [startCell]; - const maxCells = rand(3, 15); - - while (queue.length) { - const cellId = P(0.3) ? queue.shift() : queue.pop(); - cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId] || cells.h[neibCellId] < 65) return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Avalanche"; - return {name, type: "Avalanche", cells: cellsArray, color: "url(#hatch5)"}; -} - -function addFault(pack, usedCells, utils) { - const {ra, rand} = utils.random; - const {getAdjective} = utils.random; - const {Names} = utils; - const cells = pack.cells; - - const elevatedCells = cells.i.filter(i => !usedCells[i] && cells.h[i] > 50 && cells.h[i] < 70); - if (!elevatedCells.length) return null; - - const startCell = ra(elevatedCells); - usedCells[startCell] = 1; - - const cellsArray = []; - const queue = [startCell]; - const maxCells = rand(3, 15); - - while (queue.length) { - const cellId = queue.pop(); - if (cells.h[cellId] >= 20) cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId] || cells.r[neibCellId]) return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Fault"; - return {name, type: "Fault", cells: cellsArray, color: "url(#hatch2)"}; -} - -function addFlood(pack, usedCells, utils) { - const {ra, rand} = utils.random; - const {getAdjective} = utils.random; - const {d3} = utils; - const cells = pack.cells; - - const fl = cells.fl.filter(Boolean); - const meanFlux = d3.mean(fl); - const maxFlux = d3.max(fl); - const fluxThreshold = (maxFlux - meanFlux) / 2 + meanFlux; - - const bigRiverCells = cells.i.filter( - i => !usedCells[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > fluxThreshold && cells.burg[i] - ); - if (!bigRiverCells.length) return null; - - const startCell = ra(bigRiverCells); - usedCells[startCell] = 1; - - const riverId = cells.r[startCell]; - const cellsArray = []; - const queue = [startCell]; - const maxCells = rand(5, 30); - - while (queue.length) { - const cellId = queue.pop(); - cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if ( - usedCells[neibCellId] || - cells.h[neibCellId] < 20 || - cells.r[neibCellId] !== riverId || - cells.h[neibCellId] > 50 || - cells.fl[neibCellId] < meanFlux - ) - return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const name = getAdjective(pack.burgs[cells.burg[startCell]].name) + " Flood"; - return {name, type: "Flood", cells: cellsArray, color: "url(#hatch13)"}; -} - -function addTsunami(pack, usedCells, utils) { - const {ra, rand} = utils.random; - const {getAdjective} = utils.random; - const {Names} = utils; - const {cells, features} = pack; - - const coastalCells = cells.i.filter( - i => !usedCells[i] && cells.t[i] === -1 && features[cells.f[i]].type !== "lake" - ); - if (!coastalCells.length) return null; - - const startCell = ra(coastalCells); - usedCells[startCell] = 1; - - const cellsArray = []; - const queue = [startCell]; - const maxCells = rand(10, 30); - - while (queue.length) { - const cellId = queue.shift(); - if (cells.t[cellId] === 1) cellsArray.push(cellId); - if (cellsArray.length >= maxCells) break; - - cells.c[cellId].forEach(neibCellId => { - if (usedCells[neibCellId]) return; - if (cells.t[neibCellId] > 2) return; - if (pack.features[cells.f[neibCellId]].type === "lake") return; - usedCells[neibCellId] = 1; - queue.push(neibCellId); - }); - } - - const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Tsunami"; - return {name, type: "Tsunami", cells: cellsArray, color: "url(#hatch13)"}; -} \ No newline at end of file diff --git a/procedural/src/engine/utils/alea.js b/procedural/src/engine/utils/alea.js deleted file mode 100644 index 63d47327..00000000 --- a/procedural/src/engine/utils/alea.js +++ /dev/null @@ -1,96 +0,0 @@ -// src/engine/utils/alea.js (Refactored into a clean ES Module) -/* - Original code ©2010 Johannes Baagøe, MIT license; Derivative ©2017-2020 W. Mac" McMeans, BSD license. - Refactored for ES Module compatibility. -*/ - -"use strict"; - -// This is the single, exported function that takes arguments. -export function aleaPRNG(...args) { - // --- Start of original inner function logic --- - var r, t, e, o, a; - var i = ""; // for storing Mash version - - function c(n) { - var mash = (function () { - var n = 4022871197; - var r = function (r) { - r = r.toString(); - for (var t = 0, e = r.length; t < e; t++) { - var o = 0.02519603282416938 * (n += r.charCodeAt(t)); - (o -= n = o >>> 0), (n = (o *= n) >>> 0), (n += 4294967296 * (o -= n)); - } - return 2.3283064365386963e-10 * (n >>> 0); - }; - r.version = "Mash 0.9"; - return r; - })(); - - r = mash(" "); - t = mash(" "); - e = mash(" "); - o = 1; - - for (var u = 0; u < n.length; u++) { - (r -= mash(n[u])) < 0 && (r += 1); - (t -= mash(n[u])) < 0 && (t += 1); - (e -= mash(n[u])) < 0 && (e += 1); - } - - i = mash.version; - mash = null; - } - - function f(n) { - return parseInt(n, 10) === n; - } - - var l = function () { - var n = 2091639 * r + 2.3283064365386963e-10 * o; - r = t; - t = e; - e = n - (o = 0 | n); - return e; - }; - - l.fract53 = function () { - return l() + 1.1102230246251565e-16 * ((2097152 * l()) | 0); - }; - l.int32 = function () { - return 4294967296 * l(); - }; - l.cycle = function (n) { - (n = void 0 === n ? 1 : +n) < 1 && (n = 1); - for (var r = 0; r < n; r++) l(); - }; - l.range = function () { - var n, r; - 1 === arguments.length ? ((n = 0), (r = arguments[0])) : ((n = arguments[0]), (r = arguments[1])); - arguments[0] > arguments[1] && ((n = arguments[1]), (r = arguments[0])); - return f(n) && f(r) ? Math.floor(l() * (r - n + 1)) + n : l() * (r - n) + n; - }; - l.restart = function () { - c(a); - }; - l.seed = function () { - c(Array.prototype.slice.call(arguments)); - }; - l.version = function () { - return "aleaPRNG 1.1.0"; - }; - l.versions = function () { - return "aleaPRNG 1.1.0, " + i; - }; - - // Replace browser-specific crypto with a simple fallback. - // The orchestrator (engine/main.js) should be responsible for providing a good seed. - if (args.length === 0) { - args = [new Date().getTime()]; - } - - a = args; // Store original seed for restart - c(args); // Seed the generator - return l; // Return the generator function - // --- End of original inner function logic --- -} diff --git a/procedural/src/engine/utils/arrayUtils.js b/procedural/src/engine/utils/arrayUtils.js deleted file mode 100644 index 76966d18..00000000 --- a/procedural/src/engine/utils/arrayUtils.js +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; - -import { UINT8_MAX, UINT16_MAX, UINT32_MAX } from "./numberUtils.js"; - -function last(array) { - return array[array.length - 1]; -} - -function unique(array) { - return [...new Set(array)]; -} - -// deep copy for Arrays (and other objects) -function deepCopy(obj) { - const id = x => x; - const dcTArray = a => a.map(id); - const dcObject = x => Object.fromEntries(Object.entries(x).map(([k, d]) => [k, dcAny(d)])); - const dcAny = x => (x instanceof Object ? (cf.get(x.constructor) || id)(x) : x); - // don't map keys, probably this is what we would expect - const dcMapCore = m => [...m.entries()].map(([k, v]) => [k, dcAny(v)]); - - const cf = new Map([ - [Int8Array, dcTArray], - [Uint8Array, dcTArray], - [Uint8ClampedArray, dcTArray], - [Int16Array, dcTArray], - [Uint16Array, dcTArray], - [Int32Array, dcTArray], - [Uint32Array, dcTArray], - [Float32Array, dcTArray], - [Float64Array, dcTArray], - [BigInt64Array, dcTArray], - [BigUint64Array, dcTArray], - [Map, m => new Map(dcMapCore(m))], - [WeakMap, m => new WeakMap(dcMapCore(m))], - [Array, a => a.map(dcAny)], - [Set, s => [...s.values()].map(dcAny)], - [Date, d => new Date(d.getTime())], - [Object, dcObject] - // ... extend here to implement their custom deep copy - ]); - - return dcAny(obj); -} - -function getTypedArray(maxValue) { - console.assert( - Number.isInteger(maxValue) && maxValue >= 0 && maxValue <= UINT32_MAX, - `Array maxValue must be an integer between 0 and ${UINT32_MAX}, got ${maxValue}` - ); - - if (maxValue <= UINT8_MAX) return Uint8Array; - if (maxValue <= UINT16_MAX) return Uint16Array; - if (maxValue <= UINT32_MAX) return Uint32Array; - return Uint32Array; -} - -function createTypedArray({maxValue, length, from}) { - const typedArray = getTypedArray(maxValue); - if (!from) return new typedArray(length); - return typedArray.from(from); -} - -export { last, unique, deepCopy, getTypedArray, createTypedArray }; diff --git a/procedural/src/engine/utils/cell.js b/procedural/src/engine/utils/cell.js deleted file mode 100644 index 6ff87f9e..00000000 --- a/procedural/src/engine/utils/cell.js +++ /dev/null @@ -1,55 +0,0 @@ -"use strict"; -// FMG utils related to cell ranking and population - -// calculate cell suitability and population based on various factors -function rankCells(pack, grid, config, utils, modules) { - const { normalize } = utils; - const { TIME } = config.debug; - const { biomesData } = modules; - - TIME && console.time("rankCells"); - - const { cells, features } = pack; - const s = new Int16Array(cells.i.length); // cell suitability array - const pop = new Float32Array(cells.i.length); // cell population array - - const flMean = utils.d3.median(cells.fl.filter(f => f)) || 0; - const flMax = utils.d3.max(cells.fl) + utils.d3.max(cells.conf); // to normalize flux - const areaMean = utils.d3.mean(cells.area); // to adjust population by cell area - - for (const i of cells.i) { - if (cells.h[i] < 20) continue; // no population in water - - let suitability = +biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability - if (!suitability) continue; // uninhabitable biomes has 0 suitability - - if (flMean) suitability += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 250; // big rivers and confluences are valued - suitability -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not; - - if (cells.t[i] === 1) { - if (cells.r[i]) suitability += 15; // estuary is valued - const feature = features[cells.f[cells.haven[i]]]; - if (feature.type === "lake") { - if (feature.group === "freshwater") suitability += 30; - else if (feature.group == "salt") suitability += 10; - else if (feature.group == "frozen") suitability += 1; - else if (feature.group == "dry") suitability -= 5; - else if (feature.group == "sinkhole") suitability -= 5; - else if (feature.group == "lava") suitability -= 30; - } else { - suitability += 5; // ocean coast is valued - if (cells.harbor[i] === 1) suitability += 20; // safe sea harbor is valued - } - } - - s[i] = suitability / 5; // general population rate - // cell rural population is suitability adjusted by cell area - pop[i] = s[i] > 0 ? (s[i] * cells.area[i]) / areaMean : 0; - } - - TIME && console.timeEnd("rankCells"); - - return { s, pop }; -} - -export { rankCells }; diff --git a/procedural/src/engine/utils/colorUtils.js b/procedural/src/engine/utils/colorUtils.js deleted file mode 100644 index 9f05f9f0..00000000 --- a/procedural/src/engine/utils/colorUtils.js +++ /dev/null @@ -1,53 +0,0 @@ -"use strict"; -// FMG utils related to colors - -import * as d3 from 'd3'; - -// convert RGB color string to HEX without # -function toHEX(rgb) { - if (rgb.charAt(0) === "#") return rgb; - - rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); - return rgb && rgb.length === 4 - ? "#" + - ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) - : ""; -} - -const C_12 = [ - "#dababf", - "#fb8072", - "#80b1d3", - "#fdb462", - "#b3de69", - "#fccde5", - "#c6b9c1", - "#bc80bd", - "#ccebc5", - "#ffed6f", - "#8dd3c7", - "#eb8de7" -]; -const scaleRainbow = d3.scaleSequential(d3.interpolateRainbow); - -// return array of standard shuffled colors -function getColors(number) { - const colors = d3.shuffle( - d3.range(number).map(i => (i < 12 ? C_12[i] : d3.color(scaleRainbow((i - 12) / (number - 12))).hex())) - ); - return colors; -} - -function getRandomColor() { - return d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex(); -} - -// mix a color with a random color -function getMixedColor(color, mix = 0.2, bright = 0.3) { - const c = color && color[0] === "#" ? color : getRandomColor(); // if provided color is not hex (e.g. harching), generate random one - return d3.color(d3.interpolate(c, getRandomColor())(mix)).brighter(bright).hex(); -} - -export { toHEX, getColors, getRandomColor, getMixedColor }; diff --git a/procedural/src/engine/utils/commonUtils.js b/procedural/src/engine/utils/commonUtils.js deleted file mode 100644 index b34c6ee6..00000000 --- a/procedural/src/engine/utils/commonUtils.js +++ /dev/null @@ -1,199 +0,0 @@ -"use strict"; -import { polygonclip } from './lineclip.js'; - -// FMG helper functions - -// clip polygon by graph bbox -function clipPoly(points, config, secure = 0) { - if (points.length < 2) return points; - if (points.some(point => point === undefined)) { - console.error("Undefined point in clipPoly", points); - return points; - } - - const graphWidth = config.graph.width || 1000; - const graphHeight = config.graph.height || 1000; - return polygonclip(points, [0, 0, graphWidth, graphHeight], secure); -} - -// get segment of any point on polyline -function getSegmentId(points, point, step = 10) { - if (points.length === 2) return 1; - - let minSegment = 1; - let minDist = Infinity; - - for (let i = 0; i < points.length - 1; i++) { - const p1 = points[i]; - const p2 = points[i + 1]; - - const length = Math.sqrt(dist2(p1, p2)); - const segments = Math.ceil(length / step); - const dx = (p2[0] - p1[0]) / segments; - const dy = (p2[1] - p1[1]) / segments; - - for (let s = 0; s < segments; s++) { - const x = p1[0] + s * dx; - const y = p1[1] + s * dy; - const dist = dist2(point, [x, y]); - - if (dist >= minDist) continue; - minDist = dist; - minSegment = i + 1; - } - } - - return minSegment; -} - -function debounce(func, ms) { - let isCooldown = false; - - return function () { - if (isCooldown) return; - func.apply(this, arguments); - isCooldown = true; - setTimeout(() => (isCooldown = false), ms); - }; -} - -function throttle(func, ms) { - let isThrottled = false; - let savedArgs; - let savedThis; - - function wrapper() { - if (isThrottled) { - savedArgs = arguments; - savedThis = this; - return; - } - - func.apply(this, arguments); - isThrottled = true; - - setTimeout(function () { - isThrottled = false; - if (savedArgs) { - wrapper.apply(savedThis, savedArgs); - savedArgs = savedThis = null; - } - }, ms); - } - - return wrapper; -} - -// parse error to get the readable string in Chrome and Firefox -function parseError(error) { - const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; - const errorString = isFirefox ? error.toString() + " " + error.stack : error.stack; - const regex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi; - const errorNoURL = errorString.replace(regex, url => "" + last(url.split("/")) + ""); - const errorParsed = errorNoURL.replace(/at /gi, "
  at "); - return errorParsed; -} - -function getBase64(url, callback) { - const xhr = new XMLHttpRequest(); - xhr.onload = function () { - const reader = new FileReader(); - reader.onloadend = function () { - callback(reader.result); - }; - reader.readAsDataURL(xhr.response); - }; - xhr.open("GET", url); - xhr.responseType = "blob"; - xhr.send(); -} - -// open URL in a new tab or window -function openURL(url) { - window.open(url, "_blank"); -} - -// open project wiki-page -function wiki(page) { - window.open("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/" + page, "_blank"); -} - -// wrap URL into html a element -function link(URL, description) { - return `${description}`; -} - -function isCtrlClick(event) { - // meta key is cmd key on MacOs - return event.ctrlKey || event.metaKey; -} - -function generateDate(from = 100, to = 1000) { - return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", { - year: "numeric", - month: "long", - day: "numeric" - }); -} - -function getLongitude(x, decimals = 2) { - return rn(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, decimals); -} - -function getLatitude(y, decimals = 2) { - return rn(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, decimals); -} - -function getCoordinates(x, y, decimals = 2) { - return [getLongitude(x, decimals), getLatitude(y, decimals)]; -} - -// prompt replacer (prompt does not work in Electron) -void (function () { - const prompt = document.getElementById("prompt"); - const form = prompt.querySelector("#promptForm"); - - const defaultText = "Please provide an input"; - const defaultOptions = {default: 1, step: 0.01, min: 0, max: 100, required: true}; - - window.prompt = function (promptText = defaultText, options = defaultOptions, callback) { - if (options.default === undefined) - return ERROR && console.error("Prompt: options object does not have default value defined"); - - const input = prompt.querySelector("#promptInput"); - prompt.querySelector("#promptText").innerHTML = promptText; - - const type = typeof options.default === "number" ? "number" : "text"; - input.type = type; - - if (options.step !== undefined) input.step = options.step; - if (options.min !== undefined) input.min = options.min; - if (options.max !== undefined) input.max = options.max; - - input.required = options.required === false ? false : true; - input.placeholder = "type a " + type; - input.value = options.default; - prompt.style.display = "block"; - - form.addEventListener( - "submit", - event => { - event.preventDefault(); - prompt.style.display = "none"; - const v = type === "number" ? +input.value : input.value; - if (callback) callback(v); - }, - {once: true} - ); - }; - - const cancel = prompt.querySelector("#promptCancel"); - cancel.addEventListener("click", () => { - prompt.style.display = "none"; - }); -})(); - -export { - clipPoly, getSegmentId, debounce, throttle, parseError, getBase64, openURL, - wiki, link, isCtrlClick, generateDate, getLongitude, getLatitude, getCoordinates -}; diff --git a/procedural/src/engine/utils/debugUtils.js b/procedural/src/engine/utils/debugUtils.js deleted file mode 100644 index 5959cf71..00000000 --- a/procedural/src/engine/utils/debugUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; -// FMG utils used for debugging - -function drawCellsValue(data) { - debug.selectAll("text").remove(); - debug - .selectAll("text") - .data(data) - .enter() - .append("text") - .attr("x", (d, i) => pack.cells.p[i][0]) - .attr("y", (d, i) => pack.cells.p[i][1]) - .text(d => d); -} - -function drawPolygons(data) { - const max = d3.max(data); - const min = d3.min(data); - const scheme = getColorScheme(terrs.select("#landHeights").attr("scheme")); - - data = data.map(d => 1 - normalize(d, min, max)); - - debug.selectAll("polygon").remove(); - debug - .selectAll("polygon") - .data(data) - .enter() - .append("polygon") - .attr("points", (d, i) => getGridPolygon(i)) - .attr("fill", d => scheme(d)) - .attr("stroke", d => scheme(d)); -} - -function drawRouteConnections() { - debug.select("#connections").remove(); - const routes = debug.append("g").attr("id", "connections").attr("stroke-width", 0.8); - - const points = pack.cells.p; - const links = pack.cells.routes; - - for (const from in links) { - for (const to in links[from]) { - const [x1, y1] = points[from]; - const [x3, y3] = points[to]; - const [x2, y2] = [(x1 + x3) / 2, (y1 + y3) / 2]; - const routeId = links[from][to]; - - routes - .append("line") - .attr("x1", x1) - .attr("y1", y1) - .attr("x2", x2) - .attr("y2", y2) - .attr("data-id", routeId) - .attr("stroke", C_12[routeId % 12]); - } - } -} - -function drawPoint([x, y], {color = "red", radius = 0.5}) { - debug.append("circle").attr("cx", x).attr("cy", y).attr("r", radius).attr("fill", color); -} - -function drawPath(points, {color = "red", width = 0.5}) { - const lineGen = d3.line().curve(d3.curveBundle); - debug - .append("path") - .attr("d", round(lineGen(points))) - .attr("stroke", color) - .attr("stroke-width", width) - .attr("fill", "none"); -} - -export { drawCellsValue, drawPolygons, drawRouteConnections, drawPoint, drawPath }; diff --git a/procedural/src/engine/utils/flatqueue.js b/procedural/src/engine/utils/flatqueue.js deleted file mode 100644 index 271aaf32..00000000 --- a/procedural/src/engine/utils/flatqueue.js +++ /dev/null @@ -1,57 +0,0 @@ -!(function (t, s) { - "object" == typeof exports && "undefined" != typeof module - ? (module.exports = s()) - : "function" == typeof define && define.amd - ? define(s) - : ((t = "undefined" != typeof globalThis ? globalThis : t || self).FlatQueue = s()); -})(this, function () { - "use strict"; - return class { - constructor() { - (this.ids = []), (this.values = []), (this.length = 0); - } - clear() { - this.length = 0; - } - push(t, s) { - let i = this.length++; - for (; i > 0; ) { - const t = (i - 1) >> 1, - e = this.values[t]; - if (s >= e) break; - (this.ids[i] = this.ids[t]), (this.values[i] = e), (i = t); - } - (this.ids[i] = t), (this.values[i] = s); - } - pop() { - if (0 === this.length) return; - const t = this.ids[0]; - if ((this.length--, this.length > 0)) { - const t = (this.ids[0] = this.ids[this.length]), - s = (this.values[0] = this.values[this.length]), - i = this.length >> 1; - let e = 0; - for (; e < i; ) { - let t = 1 + (e << 1); - const i = t + 1; - let h = this.ids[t], - l = this.values[t]; - const n = this.values[i]; - if ((i < this.length && n < l && ((t = i), (h = this.ids[i]), (l = n)), l >= s)) break; - (this.ids[e] = h), (this.values[e] = l), (e = t); - } - (this.ids[e] = t), (this.values[e] = s); - } - return t; - } - peek() { - if (0 !== this.length) return this.ids[0]; - } - peekValue() { - if (0 !== this.length) return this.values[0]; - } - shrink() { - this.ids.length = this.values.length = this.length; - } - }; -}); diff --git a/procedural/src/engine/utils/functionUtils.js b/procedural/src/engine/utils/functionUtils.js deleted file mode 100644 index 0e0da03c..00000000 --- a/procedural/src/engine/utils/functionUtils.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -// FMG helper functions - -// extracted d3 code to bypass version conflicts -// https://github.com/d3/d3-array/blob/main/src/group.js -function rollups(values, reduce, ...keys) { - return nest(values, Array.from, reduce, keys); -} - -function nest(values, map, reduce, keys) { - return (function regroup(values, i) { - if (i >= keys.length) return reduce(values); - const groups = new Map(); - const keyof = keys[i++]; - let index = -1; - for (const value of values) { - const key = keyof(value, ++index, values); - const group = groups.get(key); - if (group) group.push(value); - else groups.set(key, [value]); - } - for (const [key, values] of groups) { - groups.set(key, regroup(values, i)); - } - return map(groups); - })(values, 0); -} - -function dist2([x1, y1], [x2, y2]) { - return (x1 - x2) ** 2 + (y1 - y2) ** 2; -} - -export { rollups, nest, dist2 }; diff --git a/procedural/src/engine/utils/geography.js b/procedural/src/engine/utils/geography.js deleted file mode 100644 index a0d75297..00000000 --- a/procedural/src/engine/utils/geography.js +++ /dev/null @@ -1,289 +0,0 @@ -"use strict"; -// FMG utils related to geography and climate - -// FIX: Import all necessary functions directly at the top. -import { rn, minmax } from "./numberUtils.js"; -import { gauss, P, rand } from "./probabilityUtils.js"; - -// add lakes in cells that are too deep and cannot pour to sea -export function addLakesInDeepDepressions(grid, config) { // FIX: `utils` parameter was not used, so it's removed. - const { TIME } = config; - TIME && console.time("addLakesInDeepDepressions"); - - const elevationLimit = config.lakeElevationLimit; // FIX: Get parameter from config - if (elevationLimit >= 80) return grid; // FIX: Use correct default logic - - const { cells, features } = grid; - const { c, h, b } = cells; - - for (const i of cells.i) { - if (b[i] || h[i] < 20) continue; - - const minHeight = Math.min(...c[i].map(c => h[c])); - if (h[i] > minHeight) continue; - - let deep = true; - const threshold = h[i] + elevationLimit; - const queue = [i]; - const checked = new Uint8Array(cells.i.length); // FIX: Use a more efficient typed array - checked[i] = 1; - - while (deep && queue.length) { - const q = queue.pop(); - - for (const n of c[q]) { - if (checked[n]) continue; - if (h[n] >= threshold) continue; - if (h[n] < 20) { - deep = false; - break; - } - checked[n] = 1; - queue.push(n); - } - } - - if (deep) { - const lakeCells = [i].concat(c[i].filter(n => h[n] === h[i])); - addLake(lakeCells); - } - } - - function addLake(lakeCells) { - const f = features.length; - lakeCells.forEach(i => { - cells.h[i] = 19; - cells.t[i] = -1; - cells.f[i] = f; - c[i].forEach(n => !lakeCells.includes(n) && (cells.t[n] = 1)); - }); - features.push({ i: f, land: false, border: false, type: "lake" }); - } - - TIME && console.timeEnd("addLakesInDeepDepressions"); - return grid; -} - -// open near-sea lakes by removing shallow elevation barriers -export function openNearSeaLakes(grid, config) { // FIX: `utils` parameter was not used, so it's removed. - const { TIME } = config; - const { templateId } = config; // FIX: Get template from the correct config object - if (templateId === "Atoll") return grid; - - const { cells, features } = grid; - if (!features.find(f => f.type === "lake")) return grid; - - TIME && console.time("openLakes"); - const LIMIT = config.lakeElevationLimit; // FIX: Use the same config parameter for consistency - - for (const i of cells.i) { - const lakeFeatureId = cells.f[i]; - if (lakeFeatureId === undefined || features[lakeFeatureId].type !== "lake") continue; - - check_neighbours: for (const c of cells.c[i]) { - if (cells.t[c] !== 1 || cells.h[c] > LIMIT) continue; - - for (const n of cells.c[c]) { - if (cells.f[n] === undefined) continue; - const ocean = cells.f[n]; - if (features[ocean]?.type !== "ocean") continue; - removeLake(c, lakeFeatureId, ocean); - break check_neighbours; - } - } - } - - function removeLake(thresholdCellId, lakeFeatureId, oceanFeatureId) { - cells.h[thresholdCellId] = 19; - cells.t[thresholdCellId] = -1; - cells.f[thresholdCellId] = oceanFeatureId; - cells.c[thresholdCellId].forEach(c => { - if (cells.h[c] >= 20) cells.t[c] = 1; - }); - - cells.i.forEach(i => { - if (cells.f[i] === lakeFeatureId) cells.f[i] = oceanFeatureId; - }); - features[lakeFeatureId].type = "ocean"; - } - - TIME && console.timeEnd("openLakes"); - return grid; -} - -// FIX: This helper function is now standalone and no longer nested. -function getSizeAndLatitude(template, grid) { - // FIX: All functions like gauss and P are directly imported, not from a utils object. - if (template === "africa-centric") return [45, 53, 38]; - if (template === "arabia") return [20, 35, 35]; - if (template === "atlantics") return [42, 23, 65]; - // ... (all other template strings are fine) ... - if (template === "world-from-pacific") return [75, 32, 30]; - - const part = grid.features.some(f => f.land && f.border); - const max = part ? 80 : 100; - const lat = () => gauss(P(0.5) ? 40 : 60, 20, 2, 25, 75); // FIX: Added precision to gauss call - - if (!part) { - if (template === "pangea") return [100, 50, 50]; - if (template === "shattered" && P(0.7)) return [100, 50, 50]; - if (template === "continents" && P(0.5)) return [100, 50, 50]; - if (template === "archipelago" && P(0.35)) return [100, 50, 50]; - if (template === "highIsland" && P(0.25)) return [100, 50, 50]; - if (template === "lowIsland" && P(0.1)) return [100, 50, 50]; - } - - if (template === "pangea") return [gauss(70, 20, 2, 30, max), lat(), 50]; - if (template === "volcano") return [gauss(20, 20, 2, 10, max), lat(), 50]; - if (template === "mediterranean") return [gauss(25, 30, 2, 15, 80), lat(), 50]; - if (template === "peninsula") return [gauss(15, 15, 2, 5, 80), lat(), 50]; - if (template === "isthmus") return [gauss(15, 20, 2, 3, 80), lat(), 50]; - if (template === "atoll") return [gauss(3, 2, 2, 1, 5), lat(), 50]; - - return [gauss(30, 20, 2, 15, max), lat(), 50]; -} - -export function defineMapSize(grid, config) { // FIX: `utils` parameter removed - const { templateId } = config; - const { width, height } = config; - const [size, latitude, longitude] = getSizeAndLatitude(templateId, grid); - - return { - mapCoordinates: calculateMapCoordinates(size, latitude, longitude, width, height) // FIX: pass correct graph dimensions - }; -} - -export function calculateMapCoordinates(sizeFraction, latShift, lonShift, graphWidth, graphHeight) { // FIX: `utils` removed - const latT = rn(sizeFraction * 180 / 100, 1); - const latN = rn(90 - (180 - latT) * latShift / 100, 1); - const latS = rn(latN - latT, 1); - const lonT = rn(Math.min((graphWidth / graphHeight) * latT, 360), 1); - const lonE = rn(180 - (360 - lonT) * lonShift / 100, 1); - const lonW = rn(lonE - lonT, 1); - return { latT, latN, latS, lonT, lonW, lonE }; -} - -export function calculateTemperatures(grid, mapCoordinates, config) { // FIX: `utils` removed - const { TIME } = config; - TIME && console.time("calculateTemperatures"); - - const { cells, points, cellsX } = grid; - const { graphHeight } = config; // FIX: Get graphHeight from config - const temp = new Int8Array(cells.i.length); - - const { temperatureEquator = 30, temperatureNorthPole = -10, temperatureSouthPole = -15, heightExponent = 1.8 } = config; - const tropics = [16, -20]; - const tropicalGradient = 0.15; - const tempNorthTropic = temperatureEquator - tropics[0] * tropicalGradient; - const northernGradient = (tempNorthTropic - temperatureNorthPole) / (90 - tropics[0]); - const tempSouthTropic = temperatureEquator + tropics[1] * tropicalGradient; - const southernGradient = (tempSouthTropic - temperatureSouthPole) / (90 + tropics[1]); - - for (let i = 0; i < cells.i.length; i++) { - const y = points[i][1]; - const rowLatitude = mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT; - const tempSeaLevel = calculateSeaLevelTemp(rowLatitude); - const tempAltitudeDrop = getAltitudeTemperatureDrop(cells.h[i], heightExponent); - temp[i] = minmax(tempSeaLevel - tempAltitudeDrop, -128, 127); - } - - function calculateSeaLevelTemp(latitude) { - if (latitude <= tropics[0] && latitude >= tropics[1]) { - return temperatureEquator - Math.abs(latitude) * tropicalGradient; - } - return latitude > 0 - ? tempNorthTropic - (latitude - tropics[0]) * northernGradient - : tempSouthTropic + (latitude - tropics[1]) * southernGradient; - } - - function getAltitudeTemperatureDrop(h, exponent) { - if (h < 20) return 0; - const height = Math.pow(h - 18, exponent); - return rn((height / 1000) * 6.5); - } - - TIME && console.timeEnd("calculateTemperatures"); - return { temp }; -} - -export function generatePrecipitation(grid, mapCoordinates, config) { // FIX: `utils` removed - const { TIME } = config; - TIME && console.time("generatePrecipitation"); - - const { cells, cellsX, cellsY } = grid; - const { winds, moisture = 1 } = config; - const prec = new Uint8Array(cells.i.length); - - const cellsNumberModifier = (config / 10000) ** 0.25; - const precInputModifier = moisture / 100; - const modifier = cellsNumberModifier * precInputModifier; - - const westerly = [], easterly = []; - let southerly = 0, northerly = 0; - const latitudeModifier = [4, 2, 2, 2, 1, 1, 2, 2, 2, 2, 3, 3, 2, 2, 1, 1, 1, 0.5]; - const MAX_PASSABLE_ELEVATION = 85; - - for (let i = 0; i < cells.i.length; i += cellsX) { - const lat = mapCoordinates.latN - ((i / cellsX) / cellsY) * mapCoordinates.latT; - const latBand = Math.floor((Math.abs(lat) - 1) / 5); - const latMod = latitudeModifier[latBand] || 1; - const windTier = Math.floor(Math.abs(lat - 89) / 30); - const { isWest, isEast, isNorth, isSouth } = getWindDirections(windTier, winds); - if (isWest) westerly.push([i, latMod, windTier]); - if (isEast) easterly.push([i + cellsX - 1, latMod, windTier]); - if (isNorth) northerly++; - if (isSouth) southerly++; - } - - if (westerly.length) passWind(westerly, 120 * modifier, 1, cellsX); - if (easterly.length) passWind(easterly, 120 * modifier, -1, cellsX); - - const vertT = southerly + northerly; - if (northerly) { - const maxPrecN = (northerly / vertT) * 60 * modifier * (mapCoordinates.latT > 60 ? 2 : latitudeModifier[Math.floor((Math.abs(mapCoordinates.latN) - 1) / 5) || 0]); - passWind(Array.from({length: cellsX}, (_, i) => i), maxPrecN, cellsX, cellsY); - } - - if (southerly) { - const maxPrecS = (southerly / vertT) * 60 * modifier * (mapCoordinates.latT > 60 ? 2 : latitudeModifier[Math.floor((Math.abs(mapCoordinates.latS) - 1) / 5) || 0]); - passWind(Array.from({length: cellsX}, (_, i) => cells.i.length - cellsX + i), maxPrecS, -cellsX, cellsY); - } - - function getWindDirections(tier, winds = []) { - const angle = winds[tier] || 225; - return { isWest: angle > 40 && angle < 140, isEast: angle > 220 && angle < 320, isNorth: angle > 100 && angle < 260, isSouth: angle > 280 || angle < 80 }; - } - - function passWind(source, maxPrec, next, steps) { - for (let s of source) { - const isArray = Array.isArray(s); - let humidity = maxPrec * (isArray ? s[1] : 1) - cells.h[isArray ? s[0] : s]; - if (humidity <= 0) continue; - for (let i = 0, c = isArray ? s[0] : s; i < steps; i++, c += next) { - if (cells.temp[c] < -5) continue; - if (cells.h[c] < 20) { - if (cells.h[c + next] >= 20) prec[c + next] += Math.max(humidity / rand(10, 20), 1); - else { - humidity = Math.min(humidity + 5 * modifier, maxPrec * (isArray ? s[1] : 1)); - prec[c] += 5 * modifier; - } - continue; - } - const isPassable = cells.h[c + next] <= MAX_PASSABLE_ELEVATION; - const precipitation = isPassable ? getPrecipitation(humidity, c, next) : humidity; - prec[c] = minmax(prec[c] + precipitation, 0, 255); - humidity = isPassable ? minmax(humidity - precipitation + (precipitation > 1.5 ? 1 : 0), 0, maxPrec * (isArray ? s[1] : 1)) : 0; - } - } - } - - function getPrecipitation(humidity, i, n) { - const normalLoss = Math.max(humidity / (10 * modifier), 1); - const diff = Math.max(cells.h[i + n] - cells.h[i], 0); - const mod = (cells.h[i + n] / 70) ** 2; - return minmax(normalLoss + diff * mod, 1, humidity); - } - - TIME && console.timeEnd("generatePrecipitation"); - return { prec }; -} diff --git a/procedural/src/engine/utils/graph.js b/procedural/src/engine/utils/graph.js deleted file mode 100644 index 78629e82..00000000 --- a/procedural/src/engine/utils/graph.js +++ /dev/null @@ -1,143 +0,0 @@ -import Delaunator from 'delaunator'; -import { Voronoi } from '../modules/voronoi.js'; -import { rn } from './numberUtils.js'; // Assuming you have these helpers -import { createTypedArray } from './arrayUtils.js'; -// import { UINT16_MAX } from './arrayUtils.js'; -import * as d3 from 'd3'; // Or import specific d3 modules - - -// place random points to calculate Voronoi diagram -function placePoints(config) { - const { width, height, cellsDesired } = config; - const spacing = rn(Math.sqrt((width * height) / cellsDesired), 2); - - const boundary = getBoundaryPoints(width, height, spacing); - const points = getJitteredGrid(width, height, spacing); - const cellsX = Math.floor((width + 0.5 * spacing - 1e-10) / spacing); - const cellsY = Math.floor((height + 0.5 * spacing - 1e-10) / spacing); - - return { spacing, cellsDesired, boundary, points, cellsX, cellsY }; -} - -// calculate Delaunay and then Voronoi diagram -function calculateVoronoi(points, boundary) { - const allPoints = points.concat(boundary); - const delaunay = Delaunator.from(allPoints); - const voronoi = new Voronoi(delaunay, allPoints, points.length); - - const cells = voronoi.cells; - cells.i = createTypedArray({ maxValue: points.length, length: points.length }).map((_, i) => i); - - // Ensure all cells have neighbor arrays initialized - for (let i = 0; i < points.length; i++) { - if (!cells.c[i]) cells.c[i] = []; - if (!cells.v[i]) cells.v[i] = []; - if (!cells.b[i]) cells.b[i] = 0; - } - - const vertices = voronoi.vertices; - - return { cells, vertices }; -} - - -// add points along map edge to pseudo-clip voronoi cells -function getBoundaryPoints(width, height, spacing) { - const offset = rn(-1 * spacing); - const bSpacing = spacing * 2; - const w = width - offset * 2; - const h = height - offset * 2; - const numberX = Math.ceil(w / bSpacing) - 1; - const numberY = Math.ceil(h / bSpacing) - 1; - const points = []; - - for (let i = 0.5; i < numberX; i++) { - let x = Math.ceil((w * i) / numberX + offset); - points.push([x, offset], [x, h + offset]); - } - - for (let i = 0.5; i < numberY; i++) { - let y = Math.ceil((h * i) / numberY + offset); - points.push([offset, y], [w + offset, y]); - } - - return points; -} - -// get points on a regular square grid and jitter them a bit -function getJitteredGrid(width, height, spacing) { - const radius = spacing / 2; - const jittering = radius * 0.9; - const doubleJittering = jittering * 2; - const jitter = () => Math.random() * doubleJittering - jittering; // Uses the pre-seeded Math.random() - - let points = []; - for (let y = radius; y < height; y += spacing) { - for (let x = radius; x < width; x += spacing) { - const xj = Math.min(rn(x + jitter(), 2), width); - const yj = Math.min(rn(y + jitter(), 2), height); - points.push([xj, yj]); - } - } - return points; -} - -// convert grid graph to pack cells by filtering and adding coastal points -export function reGraph(grid, utils) { - const { createTypedArray, rn, UINT16_MAX } = utils; - const { cells: gridCells, points, features, spacing } = grid; - - const newCellsData = { p: [], g: [], h: [] }; // store new data - const spacing2 = spacing ** 2; - - for (const i of gridCells.i) { - const height = gridCells.h[i]; - const type = gridCells.t[i]; - - if (height < 20 && type !== -1 && type !== -2) continue; - if (type === -2 && (i % 4 === 0 || features[gridCells.f[i]].type === "lake")) continue; - - const [x, y] = points[i]; - addNewPoint(i, x, y, height); - - if (type === 1 || type === -1) { - if (gridCells.b[i]) continue; - gridCells.c[i].forEach(function (e) { - if (i > e) return; - if (gridCells.t[e] === type) { - const dist2 = (y - points[e][1]) ** 2 + (x - points[e][0]) ** 2; - if (dist2 < spacing2) return; - const x1 = rn((x + points[e][0]) / 2, 1); - const y1 = rn((y + points[e][1]) / 2, 1); - addNewPoint(i, x1, y1, height); - } - }); - } - } - - function addNewPoint(i, x, y, height) { - newCellsData.p.push([x, y]); - newCellsData.g.push(i); - newCellsData.h.push(height); - } - - const { cells: packCells, vertices } = calculateVoronoi(newCellsData.p, grid.boundary); - - const tempPack = { vertices, cells: { ...packCells, p: newCellsData.p } }; - - return { - vertices, - cells: { - ...packCells, - p: newCellsData.p, - g: createTypedArray({ maxValue: grid.points.length, from: newCellsData.g }), - q: d3.quadtree(newCellsData.p.map(([x, y], i) => [x, y, i])), - h: createTypedArray({ maxValue: 100, from: newCellsData.h }), - area: createTypedArray({ maxValue: UINT16_MAX, length: packCells.i.length }).map((_, cellId) => { - const polygon = tempPack.cells.v[cellId].map(v => tempPack.vertices.p[v]); - const area = Math.abs(d3.polygonArea(polygon)); - return Math.min(area, UINT16_MAX); - }) - } - }; -} diff --git a/procedural/src/engine/utils/graphUtils.js b/procedural/src/engine/utils/graphUtils.js deleted file mode 100644 index 49dec40f..00000000 --- a/procedural/src/engine/utils/graphUtils.js +++ /dev/null @@ -1,332 +0,0 @@ -"use strict"; -// FMG utils related to graph -// -import Delaunator from 'delaunator'; -import { Voronoi } from '../modules/voronoi.js'; -import { rn } from './numberUtils.js'; -import { createTypedArray } from './arrayUtils.js'; -import * as d3 from 'd3'; - -/** - * Generates the initial grid object based on configuration. - * Assumes Math.random() has already been seeded by the orchestrator. - * @param {object} config - The graph configuration, e.g., { width, height, cellsDesired }. - */ - -function generateGrid(config) { - // REMOVED: Math.random = aleaPRNG(seed); This is now handled by engine/main.js. - const { spacing, cellsDesired, boundary, points, cellsX, cellsY } = placePoints(config); - const { cells, vertices } = calculateVoronoi(points, boundary, config); - return { spacing, cellsDesired, boundary, points, cellsX, cellsY, cells, vertices }; -} - - -// place random points to calculate Voronoi diagram -function placePoints(config) { - const { TIME } = config.debug || {}; - const { width, height, cellsDesired } = config.graph; - TIME && console.time("placePoints"); - const spacing = rn(Math.sqrt((width * height) / cellsDesired), 2); // spacing between points before jirrering - - const boundary = getBoundaryPoints(width, height, spacing); - const points = getJitteredGrid(width, height, spacing); // points of jittered square grid - const cellsX = Math.floor((width + 0.5 * spacing - 1e-10) / spacing); - const cellsY = Math.floor((height + 0.5 * spacing - 1e-10) / spacing); - TIME && console.timeEnd("placePoints"); - - return {spacing, cellsDesired, boundary, points, cellsX, cellsY}; -} - -// calculate Delaunay and then Voronoi diagram -function calculateVoronoi(points, boundary, config = {}) { - const { TIME } = config.debug || {}; - TIME && console.time("calculateDelaunay"); - const allPoints = points.concat(boundary); - const delaunay = Delaunator.from(allPoints); - TIME && console.timeEnd("calculateDelaunay"); - - TIME && console.time("calculateVoronoi"); - const voronoi = new Voronoi(delaunay, allPoints, points.length); - - const cells = voronoi.cells; - cells.i = createTypedArray({maxValue: points.length, length: points.length}).map((_, i) => i); // array of indexes - const vertices = voronoi.vertices; - TIME && console.timeEnd("calculateVoronoi"); - - return {cells, vertices}; -} - -// add points along map edge to pseudo-clip voronoi cells -function getBoundaryPoints(width, height, spacing) { - const offset = rn(-1 * spacing); - const bSpacing = spacing * 2; - const w = width - offset * 2; - const h = height - offset * 2; - const numberX = Math.ceil(w / bSpacing) - 1; - const numberY = Math.ceil(h / bSpacing) - 1; - const points = []; - - for (let i = 0.5; i < numberX; i++) { - let x = Math.ceil((w * i) / numberX + offset); - points.push([x, offset], [x, h + offset]); - } - - for (let i = 0.5; i < numberY; i++) { - let y = Math.ceil((h * i) / numberY + offset); - points.push([offset, y], [w + offset, y]); - } - - return points; -} - -// get points on a regular square grid and jitter them a bit -function getJitteredGrid(width, height, spacing) { - const radius = spacing / 2; // square radius - const jittering = radius * 0.9; // max deviation - const doubleJittering = jittering * 2; - const jitter = () => Math.random() * doubleJittering - jittering; - - let points = []; - for (let y = radius; y < height; y += spacing) { - for (let x = radius; x < width; x += spacing) { - const xj = Math.min(rn(x + jitter(), 2), width); - const yj = Math.min(rn(y + jitter(), 2), height); - points.push([xj, yj]); - } - } - return points; -} - -// return cell index on a regular square grid -function findGridCell(x, y, grid) { - return ( - Math.floor(Math.min(y / grid.spacing, grid.cellsY - 1)) * grid.cellsX + - Math.floor(Math.min(x / grid.spacing, grid.cellsX - 1)) - ); -} - -// return array of cell indexes in radius on a regular square grid -function findGridAll(x, y, radius, grid) { - const c = grid.cells.c; - let r = Math.floor(radius / grid.spacing); - let found = [findGridCell(x, y, grid)]; - if (!r || radius === 1) return found; - if (r > 0) found = found.concat(c[found[0]]); - if (r > 1) { - let frontier = c[found[0]]; - while (r > 1) { - let cycle = frontier.slice(); - frontier = []; - cycle.forEach(function (s) { - c[s].forEach(function (e) { - if (found.indexOf(e) !== -1) return; - found.push(e); - frontier.push(e); - }); - }); - r--; - } - } - - return found; -} - -// return closest pack points quadtree datum -function find(x, y, radius = Infinity, pack) { - return pack.cells.q.find(x, y, radius); -} - -// return closest cell index -function findCell(x, y, radius = Infinity, pack) { - if (!pack.cells?.q) return; - const found = pack.cells.q.find(x, y, radius); - return found ? found[2] : undefined; -} - -// return array of cell indexes in radius -function findAll(x, y, radius, pack) { - const found = pack.cells.q.findAll(x, y, radius); - return found.map(r => r[2]); -} - -// get polygon points for packed cells knowing cell id -function getPackPolygon(i, pack) { - return pack.cells.v[i].map(v => pack.vertices.p[v]); -} - -// get polygon points for initial cells knowing cell id -function getGridPolygon(i, grid) { - return grid.cells.v[i].map(v => grid.vertices.p[v]); -} - -// mbostock's poissonDiscSampler -function* poissonDiscSampler(x0, y0, x1, y1, r, k = 3) { - if (!(x1 >= x0) || !(y1 >= y0) || !(r > 0)) throw new Error(); - - const width = x1 - x0; - const height = y1 - y0; - const r2 = r * r; - const r2_3 = 3 * r2; - const cellSize = r * Math.SQRT1_2; - const gridWidth = Math.ceil(width / cellSize); - const gridHeight = Math.ceil(height / cellSize); - const grid = new Array(gridWidth * gridHeight); - const queue = []; - - function far(x, y) { - const i = (x / cellSize) | 0; - const j = (y / cellSize) | 0; - const i0 = Math.max(i - 2, 0); - const j0 = Math.max(j - 2, 0); - const i1 = Math.min(i + 3, gridWidth); - const j1 = Math.min(j + 3, gridHeight); - for (let j = j0; j < j1; ++j) { - const o = j * gridWidth; - for (let i = i0; i < i1; ++i) { - const s = grid[o + i]; - if (s) { - const dx = s[0] - x; - const dy = s[1] - y; - if (dx * dx + dy * dy < r2) return false; - } - } - } - return true; - } - - function sample(x, y) { - queue.push((grid[gridWidth * ((y / cellSize) | 0) + ((x / cellSize) | 0)] = [x, y])); - return [x + x0, y + y0]; - } - - yield sample(width / 2, height / 2); - - pick: while (queue.length) { - const i = (Math.random() * queue.length) | 0; - const parent = queue[i]; - - for (let j = 0; j < k; ++j) { - const a = 2 * Math.PI * Math.random(); - const r = Math.sqrt(Math.random() * r2_3 + r2); - const x = parent[0] + r * Math.cos(a); - const y = parent[1] + r * Math.sin(a); - if (0 <= x && x < width && 0 <= y && y < height && far(x, y)) { - yield sample(x, y); - continue pick; - } - } - - const r = queue.pop(); - if (i < queue.length) queue[i] = r; - } -} - -// filter land cells -function isLand(i, pack) { - return pack.cells.h[i] >= 20; -} - -// filter water cells -function isWater(i, pack) { - return pack.cells.h[i] < 20; -} - - -// findAll d3.quandtree search from https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e -void (function addFindAll() { - const Quad = function (node, x0, y0, x1, y1) { - this.node = node; - this.x0 = x0; - this.y0 = y0; - this.x1 = x1; - this.y1 = y1; - }; - - const tree_filter = function (x, y, radius) { - const t = {x, y, x0: this._x0, y0: this._y0, x3: this._x1, y3: this._y1, quads: [], node: this._root}; - if (t.node) t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3)); - radiusSearchInit(t, radius); - - var i = 0; - while ((t.q = t.quads.pop())) { - i++; - - // Stop searching if this quadrant can’t contain a closer node. - if ( - !(t.node = t.q.node) || - (t.x1 = t.q.x0) > t.x3 || - (t.y1 = t.q.y0) > t.y3 || - (t.x2 = t.q.x1) < t.x0 || - (t.y2 = t.q.y1) < t.y0 - ) - continue; - - // Bisect the current quadrant. - if (t.node.length) { - t.node.explored = true; - var xm = (t.x1 + t.x2) / 2, - ym = (t.y1 + t.y2) / 2; - - t.quads.push( - new Quad(t.node[3], xm, ym, t.x2, t.y2), - new Quad(t.node[2], t.x1, ym, xm, t.y2), - new Quad(t.node[1], xm, t.y1, t.x2, ym), - new Quad(t.node[0], t.x1, t.y1, xm, ym) - ); - - // Visit the closest quadrant first. - if ((t.i = ((y >= ym) << 1) | (x >= xm))) { - t.q = t.quads[t.quads.length - 1]; - t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i]; - t.quads[t.quads.length - 1 - t.i] = t.q; - } - } - - // Visit this point. (Visiting coincident points isn’t necessary!) - else { - var dx = x - +this._x.call(null, t.node.data), - dy = y - +this._y.call(null, t.node.data), - d2 = dx * dx + dy * dy; - radiusSearchVisit(t, d2); - } - } - return t.result; - }; - d3.quadtree.prototype.findAll = tree_filter; - - var radiusSearchInit = function (t, radius) { - t.result = []; - (t.x0 = t.x - radius), (t.y0 = t.y - radius); - (t.x3 = t.x + radius), (t.y3 = t.y + radius); - t.radius = radius * radius; - }; - - var radiusSearchVisit = function (t, d2) { - t.node.data.scanned = true; - if (d2 < t.radius) { - do { - t.result.push(t.node.data); - t.node.data.selected = true; - } while ((t.node = t.node.next)); - } - }; -})(); - -// Export all functions -export { - generateGrid, - placePoints, - calculateVoronoi, - getBoundaryPoints, - getJitteredGrid, - findGridCell, - findGridAll, - find, - findCell, - findAll, - getPackPolygon, - getGridPolygon, - poissonDiscSampler, - isLand, - isWater - }; diff --git a/procedural/src/engine/utils/heightmap-templates.js b/procedural/src/engine/utils/heightmap-templates.js deleted file mode 100644 index bfe96174..00000000 --- a/procedural/src/engine/utils/heightmap-templates.js +++ /dev/null @@ -1,168 +0,0 @@ -"use strict"; - -const heightmapTemplates = (function () { - const volcano = `Hill 1 90-100 44-56 40-60 - Multiply 0.8 50-100 0 0 - Range 1.5 30-55 45-55 40-60 - Smooth 3 0 0 0 - Hill 1.5 35-45 25-30 20-75 - Hill 1 35-55 75-80 25-75 - Hill 0.5 20-25 10-15 20-25 - Mask 3 0 0 0`; - - const highIsland = `Hill 1 90-100 65-75 47-53 - Add 7 all 0 0 - Hill 5-6 20-30 25-55 45-55 - Range 1 40-50 45-55 45-55 - Multiply 0.8 land 0 0 - Mask 3 0 0 0 - Smooth 2 0 0 0 - Trough 2-3 20-30 20-30 20-30 - Trough 2-3 20-30 60-80 70-80 - Hill 1 10-15 60-60 50-50 - Hill 1.5 13-16 15-20 20-75 - Range 1.5 30-40 15-85 30-40 - Range 1.5 30-40 15-85 60-70 - Pit 3-5 10-30 15-85 20-80`; - - const lowIsland = `Hill 1 90-99 60-80 45-55 - Hill 1-2 20-30 10-30 10-90 - Smooth 2 0 0 0 - Hill 6-7 25-35 20-70 30-70 - Range 1 40-50 45-55 45-55 - Trough 2-3 20-30 15-85 20-30 - Trough 2-3 20-30 15-85 70-80 - Hill 1.5 10-15 5-15 20-80 - Hill 1 10-15 85-95 70-80 - Pit 5-7 15-25 15-85 20-80 - Multiply 0.4 20-100 0 0 - Mask 4 0 0 0`; - - const continents = `Hill 1 80-85 60-80 40-60 - Hill 1 80-85 20-30 40-60 - Hill 6-7 15-30 25-75 15-85 - Multiply 0.6 land 0 0 - Hill 8-10 5-10 15-85 20-80 - Range 1-2 30-60 5-15 25-75 - Range 1-2 30-60 80-95 25-75 - Range 0-3 30-60 80-90 20-80 - Strait 2 vertical 0 0 - Strait 1 vertical 0 0 - Smooth 3 0 0 0 - Trough 3-4 15-20 15-85 20-80 - Trough 3-4 5-10 45-55 45-55 - Pit 3-4 10-20 15-85 20-80 - Mask 4 0 0 0`; - - const archipelago = `Add 11 all 0 0 - Range 2-3 40-60 20-80 20-80 - Hill 5 15-20 10-90 30-70 - Hill 2 10-15 10-30 20-80 - Hill 2 10-15 60-90 20-80 - Smooth 3 0 0 0 - Trough 10 20-30 5-95 5-95 - Strait 2 vertical 0 0 - Strait 2 horizontal 0 0`; - - const atoll = `Hill 1 75-80 50-60 45-55 - Hill 1.5 30-50 25-75 30-70 - Hill .5 30-50 25-35 30-70 - Smooth 1 0 0 0 - Multiply 0.2 25-100 0 0 - Hill 0.5 10-20 50-55 48-52`; - - const mediterranean = `Range 4-6 30-80 0-100 0-10 - Range 4-6 30-80 0-100 90-100 - Hill 6-8 30-50 10-90 0-5 - Hill 6-8 30-50 10-90 95-100 - Multiply 0.9 land 0 0 - Mask -2 0 0 0 - Smooth 1 0 0 0 - Hill 2-3 30-70 0-5 20-80 - Hill 2-3 30-70 95-100 20-80 - Trough 3-6 40-50 0-100 0-10 - Trough 3-6 40-50 0-100 90-100`; - - const peninsula = `Range 2-3 20-35 40-50 0-15 - Add 5 all 0 0 - Hill 1 90-100 10-90 0-5 - Add 13 all 0 0 - Hill 3-4 3-5 5-95 80-100 - Hill 1-2 3-5 5-95 40-60 - Trough 5-6 10-25 5-95 5-95 - Smooth 3 0 0 0 - Invert 0.4 both 0 0`; - - const pangea = `Hill 1-2 25-40 15-50 0-10 - Hill 1-2 5-40 50-85 0-10 - Hill 1-2 25-40 50-85 90-100 - Hill 1-2 5-40 15-50 90-100 - Hill 8-12 20-40 20-80 48-52 - Smooth 2 0 0 0 - Multiply 0.7 land 0 0 - Trough 3-4 25-35 5-95 10-20 - Trough 3-4 25-35 5-95 80-90 - Range 5-6 30-40 10-90 35-65`; - - const isthmus = `Hill 5-10 15-30 0-30 0-20 - Hill 5-10 15-30 10-50 20-40 - Hill 5-10 15-30 30-70 40-60 - Hill 5-10 15-30 50-90 60-80 - Hill 5-10 15-30 70-100 80-100 - Smooth 2 0 0 0 - Trough 4-8 15-30 0-30 0-20 - Trough 4-8 15-30 10-50 20-40 - Trough 4-8 15-30 30-70 40-60 - Trough 4-8 15-30 50-90 60-80 - Trough 4-8 15-30 70-100 80-100 - Invert 0.25 x 0 0`; - - const shattered = `Hill 8 35-40 15-85 30-70 - Trough 10-20 40-50 5-95 5-95 - Range 5-7 30-40 10-90 20-80 - Pit 12-20 30-40 15-85 20-80`; - - const taklamakan = `Hill 1-3 20-30 30-70 30-70 - Hill 2-4 60-85 0-5 0-100 - Hill 2-4 60-85 95-100 0-100 - Hill 3-4 60-85 20-80 0-5 - Hill 3-4 60-85 20-80 95-100 - Smooth 3 0 0 0`; - - const oldWorld = `Range 3 70 15-85 20-80 - Hill 2-3 50-70 15-45 20-80 - Hill 2-3 50-70 65-85 20-80 - Hill 4-6 20-25 15-85 20-80 - Multiply 0.5 land 0 0 - Smooth 2 0 0 0 - Range 3-4 20-50 15-35 20-45 - Range 2-4 20-50 65-85 45-80 - Strait 3-7 vertical 0 0 - Trough 6-8 20-50 15-85 45-65 - Pit 5-6 20-30 10-90 10-90`; - - const fractious = `Hill 12-15 50-80 5-95 5-95 - Mask -1.5 0 0 0 - Mask 3 0 0 0 - Add -20 30-100 0 0 - Range 6-8 40-50 5-95 10-90`; - - return { - volcano: {id: 0, name: "Volcano", template: volcano, probability: 3}, - highIsland: {id: 1, name: "High Island", template: highIsland, probability: 19}, - lowIsland: {id: 2, name: "Low Island", template: lowIsland, probability: 9}, - continents: {id: 3, name: "Continents", template: continents, probability: 16}, - archipelago: {id: 4, name: "Archipelago", template: archipelago, probability: 18}, - atoll: {id: 5, name: "Atoll", template: atoll, probability: 1}, - mediterranean: {id: 6, name: "Mediterranean", template: mediterranean, probability: 5}, - peninsula: {id: 7, name: "Peninsula", template: peninsula, probability: 3}, - pangea: {id: 8, name: "Pangea", template: pangea, probability: 5}, - isthmus: {id: 9, name: "Isthmus", template: isthmus, probability: 2}, - shattered: {id: 10, name: "Shattered", template: shattered, probability: 7}, - taklamakan: {id: 11, name: "Taklamakan", template: taklamakan, probability: 1}, - oldWorld: {id: 12, name: "Old World", template: oldWorld, probability: 8}, - fractious: {id: 13, name: "Fractious", template: fractious, probability: 3} - }; -})(); - -export { heightmapTemplates }; \ No newline at end of file diff --git a/procedural/src/engine/utils/index.js b/procedural/src/engine/utils/index.js deleted file mode 100644 index 837c3092..00000000 --- a/procedural/src/engine/utils/index.js +++ /dev/null @@ -1,109 +0,0 @@ -import "./polyfills.js"; -import * as d3 from 'd3'; - -export { aleaPRNG } from './alea.js' -export { d3 }; -export { - last, - unique, - deepCopy, - getTypedArray, - createTypedArray, -} from "./arrayUtils.js"; -export { - toHEX, - getColors, - getRandomColor, - getMixedColor, -} from "./colorUtils.js"; -export { - clipPoly, - getSegmentId, - debounce, - throttle, - parseError, - getBase64, - openURL, - wiki, - link, - isCtrlClick, - generateDate, - getLongitude, - getLatitude, - getCoordinates, -} from "./commonUtils.js"; -export { - drawCellsValue, - drawPolygons, - drawRouteConnections, - drawPoint, - drawPath, -} from "./debugUtils.js"; -export { rollups, nest, dist2 } from "./functionUtils.js"; -export { - reGraph -} from "./graph.js"; -export { - generateGrid, - placePoints, - calculateVoronoi, - getBoundaryPoints, - getJitteredGrid, - findGridCell, - findGridAll, - find, - findCell, - findAll, - getPackPolygon, - getGridPolygon, - poissonDiscSampler, - isLand, - isWater -} from "./graphUtils.js"; -export { - removeParent, - getComposedPath, - getNextId, - getAbsolutePath, -} from "./nodeUtils.js"; -export { - vowel, - trimVowels, - getAdjective, - nth, - abbreviate, - list, -} from "./languageUtils.js"; -export * from "./numberUtils.js"; -export { - getIsolines, - getFillPath, - getBorderPath, - getVertexPath, - getPolesOfInaccessibility, - connectVertices, - findPath, - restorePath, -} from "./pathUtils.js"; -export { - rand, - P, - each, - gauss, - Pint, - ra, - rw, - biased, - getNumberInRange, - generateSeed, -} from "./probabilityUtils.js"; -export { - round, - capitalize, - splitInTwo, - parseTransform, -} from "./stringUtils.js"; -export { convertTemperature, si, getInteger } from "./unitUtils.js"; -export { simplify } from "./simplify.js"; -export { lineclip } from "./lineclip.js"; -export { heightmapTemplates } from "./heightmap-templates.js"; diff --git a/procedural/src/engine/utils/languageUtils.js b/procedural/src/engine/utils/languageUtils.js deleted file mode 100644 index f79a31ab..00000000 --- a/procedural/src/engine/utils/languageUtils.js +++ /dev/null @@ -1,176 +0,0 @@ -"use strict"; - -// chars that serve as vowels -const VOWELS = `aeiouyɑ'əøɛœæɶɒɨɪɔɐʊɤɯаоиеёэыуюяàèìòùỳẁȁȅȉȍȕáéíóúýẃőűâêîôûŷŵäëïöüÿẅãẽĩõũỹąęįǫųāēīōūȳăĕĭŏŭǎěǐǒǔȧėȯẏẇạẹịọụỵẉḛḭṵṳ`; -function vowel(c) { - return VOWELS.includes(c); -} - -// remove vowels from the end of the string -function trimVowels(string, minLength = 3) { - while (string.length > minLength && vowel(last(string))) { - string = string.slice(0, -1); - } - return string; -} - -const adjectivizationRules = [ - {name: "guo", probability: 1, condition: new RegExp(" Guo$"), action: noun => noun.slice(0, -4)}, - { - name: "orszag", - probability: 1, - condition: new RegExp("orszag$"), - action: noun => (noun.length < 9 ? noun + "ian" : noun.slice(0, -6)) - }, - { - name: "stan", - probability: 1, - condition: new RegExp("stan$"), - action: noun => (noun.length < 9 ? noun + "i" : trimVowels(noun.slice(0, -4))) - }, - { - name: "land", - probability: 1, - condition: new RegExp("land$"), - action: noun => { - if (noun.length > 9) return noun.slice(0, -4); - const root = trimVowels(noun.slice(0, -4), 0); - if (root.length < 3) return noun + "ic"; - if (root.length < 4) return root + "lish"; - return root + "ish"; - } - }, - { - name: "que", - probability: 1, - condition: new RegExp("que$"), - action: noun => noun.replace(/que$/, "can") - }, - { - name: "a", - probability: 1, - condition: new RegExp("a$"), - action: noun => noun + "n" - }, - { - name: "o", - probability: 1, - condition: new RegExp("o$"), - action: noun => noun.replace(/o$/, "an") - }, - { - name: "u", - probability: 1, - condition: new RegExp("u$"), - action: noun => noun + "an" - }, - { - name: "i", - probability: 1, - condition: new RegExp("i$"), - action: noun => noun + "an" - }, - { - name: "e", - probability: 1, - condition: new RegExp("e$"), - action: noun => noun + "an" - }, - { - name: "ay", - probability: 1, - condition: new RegExp("ay$"), - action: noun => noun + "an" - }, - { - name: "os", - probability: 1, - condition: new RegExp("os$"), - action: noun => { - const root = trimVowels(noun.slice(0, -2), 0); - if (root.length < 4) return noun.slice(0, -1); - return root + "ian"; - } - }, - { - name: "es", - probability: 1, - condition: new RegExp("es$"), - action: noun => { - const root = trimVowels(noun.slice(0, -2), 0); - if (root.length > 7) return noun.slice(0, -1); - return root + "ian"; - } - }, - { - name: "l", - probability: 0.8, - condition: new RegExp("l$"), - action: noun => noun + "ese" - }, - { - name: "n", - probability: 0.8, - condition: new RegExp("n$"), - action: noun => noun + "ese" - }, - { - name: "ad", - probability: 0.8, - condition: new RegExp("ad$"), - action: noun => noun + "ian" - }, - { - name: "an", - probability: 0.8, - condition: new RegExp("an$"), - action: noun => noun + "ian" - }, - { - name: "ish", - probability: 0.25, - condition: new RegExp("^[a-zA-Z]{6}$"), - action: noun => trimVowels(noun.slice(0, -1)) + "ish" - }, - { - name: "an", - probability: 0.5, - condition: new RegExp("^[a-zA-Z]{0,7}$"), - action: noun => trimVowels(noun) + "an" - } -]; - -// get adjective form from noun -function getAdjective(noun) { - for (const rule of adjectivizationRules) { - if (P(rule.probability) && rule.condition.test(noun)) { - return rule.action(noun); - } - } - return noun; // no rule applied, return noun as is -} - -// get ordinal from integer: 1 => 1st -const nth = n => n + (["st", "nd", "rd"][((((n + 90) % 100) - 10) % 10) - 1] || "th"); - -// get two-letters code (abbreviation) from string -function abbreviate(name, restricted = []) { - const parsed = name.replace("Old ", "O ").replace(/[()]/g, ""); // remove Old prefix and parentheses - const words = parsed.split(" "); - const letters = words.join(""); - - let code = words.length === 2 ? words[0][0] + words[1][0] : letters.slice(0, 2); - for (let i = 1; i < letters.length - 1 && restricted.includes(code); i++) { - code = letters[0] + letters[i].toUpperCase(); - } - return code; -} - -// conjunct array: [A,B,C] => "A, B and C" -function list(array) { - if (!Intl.ListFormat) return array.join(", "); - const conjunction = new Intl.ListFormat(window.lang || "en", {style: "long", type: "conjunction"}); - return conjunction.format(array); -} - -export { vowel, trimVowels, getAdjective, nth, abbreviate, list }; diff --git a/procedural/src/engine/utils/lineclip.js b/procedural/src/engine/utils/lineclip.js deleted file mode 100644 index ee89d9ec..00000000 --- a/procedural/src/engine/utils/lineclip.js +++ /dev/null @@ -1,3 +0,0 @@ -// lineclip by mourner, https://github.com/mapbox/lineclip -"use strict";function lineclip(t,e,n){var r,i,u,o,s,h=t.length,c=bitCode(t[0],e),f=[];for(n=n||[],r=1;re[2]&&(n|=2),t[1]e[3]&&(n|=8),n} -export { lineclip, polygonclip }; \ No newline at end of file diff --git a/procedural/src/engine/utils/nodeUtils.js b/procedural/src/engine/utils/nodeUtils.js deleted file mode 100644 index fb8be1ac..00000000 --- a/procedural/src/engine/utils/nodeUtils.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -// FMG utils related to nodes - -// remove parent element (usually if child is clicked) -function removeParent() { - this.parentNode.parentNode.removeChild(this.parentNode); -} - -// polyfill for composedPath -function getComposedPath(node) { - let parent; - if (node.parentNode) parent = node.parentNode; - else if (node.host) parent = node.host; - else if (node.defaultView) parent = node.defaultView; - if (parent !== undefined) return [node].concat(getComposedPath(parent)); - return [node]; -} - -// get next unused id -function getNextId(core, i = 1) { - while (document.getElementById(core + i)) i++; - return core + i; -} - -function getAbsolutePath(href) { - if (!href) return ""; - const link = document.createElement("a"); - link.href = href; - return link.href; -} - -export { removeParent, getComposedPath, getNextId, getAbsolutePath }; diff --git a/procedural/src/engine/utils/numberUtils.js b/procedural/src/engine/utils/numberUtils.js deleted file mode 100644 index 5d08c533..00000000 --- a/procedural/src/engine/utils/numberUtils.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -// FMG utils related to numbers - -// typed arrays max values -export const INT8_MAX = 127; -export const UINT8_MAX = 255; -export const UINT16_MAX = 65535; -export const UINT32_MAX = 4294967295; - -// round value to d decimals -function rn(v, d = 0) { - const m = Math.pow(10, d); - return Math.round(v * m) / m; -} - -function minmax(value, min, max) { - return Math.min(Math.max(value, min), max); -} - -// return value in range [0, 100] -function lim(v) { - return minmax(v, 0, 100); -} - -// normalization function -function normalize(val, min, max) { - return minmax((val - min) / (max - min), 0, 1); -} - -function lerp(a, b, t) { - return a + (b - a) * t; -} - -export { rn, minmax, lim, normalize, lerp }; diff --git a/procedural/src/engine/utils/pathUtils.js b/procedural/src/engine/utils/pathUtils.js deleted file mode 100644 index 5af145a5..00000000 --- a/procedural/src/engine/utils/pathUtils.js +++ /dev/null @@ -1,237 +0,0 @@ -"use strict"; - -// get continuous paths (isolines) for all cells at once based on getType(cellId) comparison -function getIsolines(graph, getType, options = {polygons: false, fill: false, halo: false, waterGap: false}) { - const {cells, vertices} = graph; - const isolines = {}; - - const checkedCells = new Uint8Array(cells.i.length); - const addToChecked = cellId => (checkedCells[cellId] = 1); - const isChecked = cellId => checkedCells[cellId] === 1; - - for (const cellId of cells.i) { - if (isChecked(cellId) || !getType(cellId)) continue; - addToChecked(cellId); - - const type = getType(cellId); - const ofSameType = cellId => getType(cellId) === type; - const ofDifferentType = cellId => getType(cellId) !== type; - - const onborderCell = cells.c[cellId].find(ofDifferentType); - if (onborderCell === undefined) continue; - - // check if inner lake. Note there is no shoreline for grid features - const feature = graph.features[cells.f[onborderCell]]; - if (feature.type === "lake" && feature.shoreline?.every(ofSameType)) continue; - - const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType)); - if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`); - - const vertexChain = connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing: true}); - if (vertexChain.length < 3) continue; - - addIsoline(type, vertices, vertexChain); - } - - return isolines; - - function addIsoline(type, vertices, vertexChain) { - if (!isolines[type]) isolines[type] = {}; - - if (options.polygons) { - if (!isolines[type].polygons) isolines[type].polygons = []; - isolines[type].polygons.push(vertexChain.map(vertexId => vertices.p[vertexId])); - } - - if (options.fill) { - if (!isolines[type].fill) isolines[type].fill = ""; - isolines[type].fill += getFillPath(vertices, vertexChain); - } - - if (options.waterGap) { - if (!isolines[type].waterGap) isolines[type].waterGap = ""; - const isLandVertex = vertexId => vertices.c[vertexId].every(i => cells.h[i] >= 20); - isolines[type].waterGap += getBorderPath(vertices, vertexChain, isLandVertex); - } - - if (options.halo) { - if (!isolines[type].halo) isolines[type].halo = ""; - const isBorderVertex = vertexId => vertices.c[vertexId].some(i => cells.b[i]); - isolines[type].halo += getBorderPath(vertices, vertexChain, isBorderVertex); - } - } -} - -function getFillPath(vertices, vertexChain) { - const points = vertexChain.map(vertexId => vertices.p[vertexId]); - const firstPoint = points.shift(); - return `M${firstPoint} L${points.join(" ")} Z`; -} - -function getBorderPath(vertices, vertexChain, discontinue) { - let discontinued = true; - let lastOperation = ""; - const path = vertexChain.map(vertexId => { - if (discontinue(vertexId)) { - discontinued = true; - return ""; - } - - const operation = discontinued ? "M" : "L"; - discontinued = false; - lastOperation = operation; - - const command = operation === "L" && operation === lastOperation ? "" : operation; - return ` ${command}${vertices.p[vertexId]}`; - }); - - return path.join("").trim(); -} - -// get single path for an non-continuous array of cells -function getVertexPath(cellsArray) { - const {cells, vertices} = pack; - - const cellsObj = Object.fromEntries(cellsArray.map(cellId => [cellId, true])); - const ofSameType = cellId => cellsObj[cellId]; - const ofDifferentType = cellId => !cellsObj[cellId]; - - const checkedCells = new Uint8Array(cells.c.length); - const addToChecked = cellId => (checkedCells[cellId] = 1); - const isChecked = cellId => checkedCells[cellId] === 1; - - let path = ""; - - for (const cellId of cellsArray) { - if (isChecked(cellId)) continue; - - const onborderCell = cells.c[cellId].find(ofDifferentType); - if (onborderCell === undefined) continue; - - const feature = pack.features[cells.f[onborderCell]]; - if (feature.type === "lake" && feature.shoreline) { - if (feature.shoreline.every(ofSameType)) continue; // inner lake - } - - const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType)); - if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`); - - const vertexChain = connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing: true}); - if (vertexChain.length < 3) continue; - - path += getFillPath(vertices, vertexChain); - } - - return path; -} - -function getPolesOfInaccessibility(graph, getType) { - const isolines = getIsolines(graph, getType, {polygons: true}); - - const poles = Object.entries(isolines).map(([id, isoline]) => { - const multiPolygon = isoline.polygons.sort((a, b) => b.length - a.length); - const [x, y] = polylabel(multiPolygon, 20); - return [id, [rn(x), rn(y)]]; - }); - - return Object.fromEntries(poles); -} - -function connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing}) { - const MAX_ITERATIONS = vertices.c.length; - const chain = []; // vertices chain to form a path - - let next = startingVertex; - for (let i = 0; i === 0 || next !== startingVertex; i++) { - const previous = chain.at(-1); - const current = next; - chain.push(current); - - const neibCells = vertices.c[current]; - if (addToChecked) neibCells.filter(ofSameType).forEach(addToChecked); - - const [c1, c2, c3] = neibCells.map(ofSameType); - const [v1, v2, v3] = vertices.v[current]; - - if (v1 !== previous && c1 !== c2) next = v1; - else if (v2 !== previous && c2 !== c3) next = v2; - else if (v3 !== previous && c1 !== c3) next = v3; - - if (next >= vertices.c.length) { - ERROR && console.error("ConnectVertices: next vertex is out of bounds"); - break; - } - - if (next === current) { - ERROR && console.error("ConnectVertices: next vertex is not found"); - break; - } - - if (i === MAX_ITERATIONS) { - ERROR && console.error("ConnectVertices: max iterations reached", MAX_ITERATIONS); - break; - } - } - - if (closeRing) chain.push(startingVertex); - return chain; -} - -/** - * Finds the shortest path between two cells using a cost-based pathfinding algorithm. - * @param {number} start - The ID of the starting cell. - * @param {(id: number) => boolean} isExit - A function that returns true if the cell is the exit cell. - * @param {(current: number, next: number) => number} getCost - A function that returns the path cost from current cell to the next cell. Must return `Infinity` for impassable connections. - * @returns {number[] | null} An array of cell IDs of the path from start to exit, or null if no path is found or start and exit are the same. - */ -function findPath(start, isExit, getCost) { - if (isExit(start)) return null; - - const from = []; - const cost = []; - const queue = new FlatQueue(); - queue.push(start, 0); - - while (queue.length) { - const currentCost = queue.peekValue(); - const current = queue.pop(); - - for (const next of pack.cells.c[current]) { - if (isExit(next)) { - from[next] = current; - return restorePath(next, start, from); - } - - const nextCost = getCost(current, next); - if (nextCost === Infinity) continue; // impassable cell - const totalCost = currentCost + nextCost; - - if (totalCost >= cost[next]) continue; // has cheaper path - from[next] = current; - cost[next] = totalCost; - queue.push(next, totalCost); - } - } - - return null; -} - -// supplementary function for findPath -function restorePath(exit, start, from) { - const pathCells = []; - - let current = exit; - let prev = exit; - - while (current !== start) { - pathCells.push(current); - prev = from[current]; - current = prev; - } - - pathCells.push(current); - - return pathCells.reverse(); -} - -export { getIsolines, getFillPath, getBorderPath, getVertexPath, getPolesOfInaccessibility, connectVertices, findPath, restorePath }; diff --git a/procedural/src/engine/utils/polyfills.js b/procedural/src/engine/utils/polyfills.js deleted file mode 100644 index ffc10f74..00000000 --- a/procedural/src/engine/utils/polyfills.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -// replaceAll -if (String.prototype.replaceAll === undefined) { - String.prototype.replaceAll = function (str, newStr) { - if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") return this.replace(str, newStr); - return this.replace(new RegExp(str, "g"), newStr); - }; -} - -// flat -if (Array.prototype.flat === undefined) { - Array.prototype.flat = function () { - return this.reduce((acc, val) => (Array.isArray(val) ? acc.concat(val.flat()) : acc.concat(val)), []); - }; -} - -// at -if (Array.prototype.at === undefined) { - Array.prototype.at = function (index) { - if (index < 0) index += this.length; - if (index < 0 || index >= this.length) return undefined; - return this[index]; - }; -} - -// readable stream iterator: https://bugs.chromium.org/p/chromium/issues/detail?id=929585#c10 -if (ReadableStream.prototype[Symbol.asyncIterator] === undefined) { - ReadableStream.prototype[Symbol.asyncIterator] = async function* () { - const reader = this.getReader(); - try { - while (true) { - const {done, value} = await reader.read(); - if (done) return; - yield value; - } - } finally { - reader.releaseLock(); - } - }; -} diff --git a/procedural/src/engine/utils/polylabel.js b/procedural/src/engine/utils/polylabel.js deleted file mode 100644 index 73b95746..00000000 --- a/procedural/src/engine/utils/polylabel.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).polylabel=t()}}(function(){return function t(n,e,r){function o(a,u){if(!e[a]){if(!n[a]){var f="function"==typeof require&&require;if(!u&&f)return f(a,!0);if(i)return i(a,!0);var h=new Error("Cannot find module '"+a+"'");throw h.code="MODULE_NOT_FOUND",h}var s=e[a]={exports:{}};n[a][0].call(s.exports,function(t){var e=n[a][1][t];return o(e||t)},s,s.exports,t,n,e,r)}return e[a].exports}for(var i="function"==typeof require&&require,a=0;an!=l[1]>n&&t<(l[0]-d[0])*(n-d[1])/(l[1]-d[1])+d[0]&&(r=!r),o=Math.min(o,a(t,n,d,l))}return(r?1:-1)*Math.sqrt(o)}(t,n,r),this.max=this.d+this.h*Math.SQRT2}function a(t,n,e,r){var o=e[0],i=e[1],a=r[0]-o,u=r[1]-i;if(0!==a||0!==u){var f=((t-o)*a+(n-i)*u)/(a*a+u*u);f>1?(o=r[0],i=r[1]):f>0&&(o+=a*f,i+=u*f)}return(a=t-o)*a+(u=n-i)*u}n.exports=function(t,n,e){var a,u,f,h;n=n||1;for(var s=0;sf)&&(f=d[0]),(!s||d[1]>h)&&(h=d[1])}for(var l=f-a,p=h-u,c=Math.min(l,p),v=c/2,g=new r(null,o),x=a;xw.d&&(w=b,e&&console.info("found best %d after %d probes",Math.round(1e4*b.d)/1e4,m)),b.max-w.d<=n||(v=b.h/2,g.push(new i(b.x-v,b.y-v,v,t)),g.push(new i(b.x+v,b.y-v,v,t)),g.push(new i(b.x-v,b.y+v,v,t)),g.push(new i(b.x+v,b.y+v,v,t)),m+=4)}e&&(console.info("num probes: "+m),console.info("best distance: "+w.d));return[w.x,w.y]}},{tinyqueue:2}],2:[function(t,n,e){"use strict";function r(t,n){if(!(this instanceof r))return new r(t,n);if(this.data=t||[],this.length=this.data.length,this.compare=n||o,t)for(var e=Math.floor(this.length/2);e>=0;e--)this._down(e)}function o(t,n){return tn?1:0}function i(t,n,e){var r=t[n];t[n]=t[e],t[e]=r}n.exports=r,r.prototype={push:function(t){this.data.push(t),this.length++,this._up(this.length-1)},pop:function(){var t=this.data[0];return this.data[0]=this.data[this.length-1],this.length--,this.data.pop(),this._down(0),t},peek:function(){return this.data[0]},_up:function(t){for(var n=this.data,e=this.compare;t>0;){var r=Math.floor((t-1)/2);if(!(e(n[t],n[r])<0))break;i(n,r,t),t=r}},_down:function(t){for(var n=this.data,e=this.compare,r=this.length;;){var o=2*t+1,a=o+1,u=t;if(o= 1) return true; - if (probability <= 0) return false; - return Math.random() < probability; -} - -function each(n) { - return i => i % n === 0; -} - -/* Random Gaussian number generator - * @param {number} expected - expected value - * @param {number} deviation - standard deviation - * @param {number} min - minimum value - * @param {number} max - maximum value - * @param {number} round - round value to n decimals - * @return {number} random number - */ -function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) { - return rn(minmax(d3.randomNormal(expected, deviation)(), min, max), round); -} - -// probability shorthand for floats -function Pint(float) { - return ~~float + +P(float % 1); -} - -// return random value from the array -function ra(array) { - return array[Math.floor(Math.random() * array.length)]; -} - -// return random value from weighted array {"key1":weight1, "key2":weight2} -function rw(object) { - const array = []; - for (const key in object) { - for (let i = 0; i < object[key]; i++) { - array.push(key); - } - } - return array[Math.floor(Math.random() * array.length)]; -} - -// return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min) -function biased(min, max, ex) { - return Math.round(min + (max - min) * Math.pow(Math.random(), ex)); -} - -// get number from string in format "1-3" or "2" or "0.5" -function getNumberInRange(r) { - if (typeof r !== "string") { - ERROR && console.error("Range value should be a string", r); - return 0; - } - if (!isNaN(+r)) return ~~r + +P(r - ~~r); - const sign = r[0] === "-" ? -1 : 1; - if (isNaN(+r[0])) r = r.slice(1); - const range = r.includes("-") ? r.split("-") : null; - if (!range) { - ERROR && console.error("Cannot parse the number. Check the format", r); - return 0; - } - const count = rand(range[0] * sign, +range[1]); - if (isNaN(count) || count < 0) { - ERROR && console.error("Cannot parse number. Check the format", r); - return 0; - } - return count; -} - -function generateSeed() { - return String(Math.floor(Math.random() * 1e9)); -} - -export { rand, P, each, gauss, Pint, ra, rw, biased, getNumberInRange, generateSeed }; diff --git a/procedural/src/engine/utils/simplify.js b/procedural/src/engine/utils/simplify.js deleted file mode 100644 index c227efc1..00000000 --- a/procedural/src/engine/utils/simplify.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - (c) 2017, Vladimir Agafonkin - Simplify.js, a high-performance JS polyline simplification library - mourner.github.io/simplify-js -*/ - - // square distance between 2 points - function getSqDist([x1, y1], [x2, y2]) { - const dx = x1 - x2; - const dy = y1 - y2; - - return dx * dx + dy * dy; - } - - // square distance from a point to a segment - function getSqSegDist([x1, y1], [x, y], [x2, y2]) { - let dx = x2 - x; - let dy = y2 - y; - - if (dx !== 0 || dy !== 0) { - const t = ((x1 - x) * dx + (y1 - y) * dy) / (dx * dx + dy * dy); - - if (t > 1) { - x = x2; - y = y2; - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } - - dx = x1 - x; - dy = y1 - y; - - return dx * dx + dy * dy; - } - // rest of the code doesn't care about point format - - // basic distance-based simplification - function simplifyRadialDist(points, sqTolerance) { - let prevPoint = points[0]; - const newPoints = [prevPoint]; - let point; - - for (let i = 1; i < points.length; i++) { - point = points[i]; - if (!point) continue; - - if (getSqDist(point, prevPoint) > sqTolerance) { - newPoints.push(point); - prevPoint = point; - } - } - - if (prevPoint !== point) newPoints.push(point); - return newPoints; - } - - function simplifyDPStep(points, first, last, sqTolerance, simplified) { - let maxSqDist = sqTolerance; - let index = first; - - for (let i = first + 1; i < last; i++) { - const sqDist = getSqSegDist(points[i], points[first], points[last]); - - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } - } - - if (maxSqDist > sqTolerance) { - if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified); - simplified.push(points[index]); - if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified); - } - } - - // simplification using Ramer-Douglas-Peucker algorithm - function simplifyDouglasPeucker(points, sqTolerance) { - const last = points.length - 1; - - const simplified = [points[0]]; - simplifyDPStep(points, 0, last, sqTolerance, simplified); - simplified.push(points[last]); - - return simplified; - } - - // both algorithms combined for awesome performance - export function simplify(points, tolerance, highestQuality = false) { - if (points.length <= 2) return points; - - const sqTolerance = tolerance * tolerance; - - points = highestQuality ? points : simplifyRadialDist(points, sqTolerance); - points = simplifyDouglasPeucker(points, sqTolerance); - - return points; - } diff --git a/procedural/src/engine/utils/stringUtils.js b/procedural/src/engine/utils/stringUtils.js deleted file mode 100644 index 0f1f59fc..00000000 --- a/procedural/src/engine/utils/stringUtils.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -// FMG utils related to strings - -// round numbers in string to d decimals -function round(s, d = 1) { - return s.replace(/[\d\.-][\d\.e-]*/g, function (n) { - return rn(n, d); - }); -} - -// return string with 1st char capitalized -function capitalize(string) { - return string.charAt(0).toUpperCase() + string.slice(1); -} - -// split string into 2 almost equal parts not breaking words -function splitInTwo(str) { - const half = str.length / 2; - const ar = str.split(" "); - if (ar.length < 2) return ar; // only one word - let first = "", - last = "", - middle = "", - rest = ""; - - ar.forEach((w, d) => { - if (d + 1 !== ar.length) w += " "; - rest += w; - if (!first || rest.length < half) first += w; - else if (!middle) middle = w; - else last += w; - }); - - if (!last) return [first, middle]; - if (first.length < last.length) return [first + middle, last]; - return [first, middle + last]; -} - -// transform string to array [translateX,translateY,rotateDeg,rotateX,rotateY,scale] -function parseTransform(string) { - if (!string) return [0, 0, 0, 0, 0, 1]; - - const a = string - .replace(/[a-z()]/g, "") - .replace(/[ ]/g, ",") - .split(","); - return [a[0] || 0, a[1] || 0, a[2] || 0, a[3] || 0, a[4] || 0, a[5] || 1]; -} - -// check if string is a valid for JSON parse -JSON.isValid = str => { - try { - JSON.parse(str); - } catch (e) { - return false; - } - return true; -}; - -JSON.safeParse = str => { - try { - return JSON.parse(str); - } catch (e) { - return null; - } -}; - -export { round, capitalize, splitInTwo, parseTransform }; diff --git a/procedural/src/engine/utils/unitUtils.js b/procedural/src/engine/utils/unitUtils.js deleted file mode 100644 index 5698fa12..00000000 --- a/procedural/src/engine/utils/unitUtils.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -// FMG utils related to units - -// conver temperature from °C to other scales -const temperatureConversionMap = { - "°C": temp => rn(temp) + "°C", - "°F": temp => rn((temp * 9) / 5 + 32) + "°F", - K: temp => rn(temp + 273.15) + "K", - "°R": temp => rn(((temp + 273.15) * 9) / 5) + "°R", - "°De": temp => rn(((100 - temp) * 3) / 2) + "°De", - "°N": temp => rn((temp * 33) / 100) + "°N", - "°Ré": temp => rn((temp * 4) / 5) + "°Ré", - "°Rø": temp => rn((temp * 21) / 40 + 7.5) + "°Rø" -}; - -function convertTemperature(temp, scale = temperatureScale.value || "°C") { - return temperatureConversionMap[scale](temp); -} - -// corvent number to short string with SI postfix -function si(n) { - if (n >= 1e9) return rn(n / 1e9, 1) + "B"; - if (n >= 1e8) return rn(n / 1e6) + "M"; - if (n >= 1e6) return rn(n / 1e6, 1) + "M"; - if (n >= 1e4) return rn(n / 1e3) + "K"; - if (n >= 1e3) return rn(n / 1e3, 1) + "K"; - return rn(n); -} - -// getInteger number from user input data -function getInteger(value) { - const metric = value.slice(-1); - if (metric === "K") return parseInt(value.slice(0, -1) * 1e3); - if (metric === "M") return parseInt(value.slice(0, -1) * 1e6); - if (metric === "B") return parseInt(value.slice(0, -1) * 1e9); - return parseInt(value); -} - -export { convertTemperature, si, getInteger }; diff --git a/procedural/src/libs/delaunator.min.js b/procedural/src/libs/delaunator.min.js deleted file mode 100644 index 1f63da53..00000000 --- a/procedural/src/libs/delaunator.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).Delaunator=i()}(this,(function(){"use strict";const t=134217729;function i(t,i,s,e,n){let h,r,l,o,a=i[0],f=e[0],c=0,u=0;f>a==f>-a?(h=a,a=i[++c]):(h=f,f=e[++u]);let _=0;if(ca==f>-a?(r=a+h,l=h-(r-a),a=i[++c]):(r=f+h,l=h-(r-f),f=e[++u]),h=r,0!==l&&(n[_++]=l);ca==f>-a?(r=h+a,o=r-h,l=h-(r-o)+(a-o),a=i[++c]):(r=h+f,o=r-h,l=h-(r-o)+(f-o),f=e[++u]),h=r,0!==l&&(n[_++]=l);for(;c0!=d>0)return g;const y=Math.abs(_+d);return Math.abs(g)>=33306690738754716e-32*y?g:-function(s,o,a,f,c,u,_){let d,g,y,w,b,A,k,M,p,x,S,T,z,U,m,K,L,v;const F=s-c,P=a-c,E=o-u,H=f-u;U=F*H,A=t*F,k=A-(A-F),M=F-k,A=t*H,p=A-(A-H),x=H-p,m=M*x-(U-k*p-M*p-k*x),K=E*P,A=t*E,k=A-(A-E),M=E-k,A=t*P,p=A-(A-P),x=P-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,e[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,e[1]=z-(S+b)+(b-K),v=T+S,b=v-T,e[2]=T-(v-b)+(S-b),e[3]=v;let I=function(t,i){let s=i[0];for(let e=1;e=N||-I>=N)return I;if(b=s-F,d=s-(F+b)+(b-c),b=a-P,y=a-(P+b)+(b-c),b=o-E,g=o-(E+b)+(b-u),b=f-H,w=f-(H+b)+(b-u),0===d&&0===g&&0===y&&0===w)return I;if(N=11093356479670487e-47*_+33306690738754706e-32*Math.abs(I),I+=F*w+H*d-(E*y+P*g),I>=N||-I>=N)return I;U=d*H,A=t*d,k=A-(A-d),M=d-k,A=t*H,p=A-(A-H),x=H-p,m=M*x-(U-k*p-M*p-k*x),K=g*P,A=t*g,k=A-(A-g),M=g-k,A=t*P,p=A-(A-P),x=P-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const j=i(4,e,4,l,n);U=F*w,A=t*F,k=A-(A-F),M=F-k,A=t*w,p=A-(A-w),x=w-p,m=M*x-(U-k*p-M*p-k*x),K=E*y,A=t*E,k=A-(A-E),M=E-k,A=t*y,p=A-(A-y),x=y-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const q=i(j,n,4,l,h);U=d*w,A=t*d,k=A-(A-d),M=d-k,A=t*w,p=A-(A-w),x=w-p,m=M*x-(U-k*p-M*p-k*x),K=g*y,A=t*g,k=A-(A-g),M=g-k,A=t*y,p=A-(A-y),x=y-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const D=i(q,h,4,l,r);return r[D-1]}(s,o,a,f,c,u,y)}const a=Math.pow(2,-52),f=new Uint32Array(512);class c{static from(t,i=w,s=b){const e=t.length,n=new Float64Array(2*e);for(let h=0;h>1;if(i>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const s=Math.max(2*i-5,0);this._triangles=new Uint32Array(3*s),this._halfedges=new Int32Array(3*s),this._hashSize=Math.ceil(Math.sqrt(i)),this._hullPrev=new Uint32Array(i),this._hullNext=new Uint32Array(i),this._hullTri=new Uint32Array(i),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(i),this._dists=new Float64Array(i),this.update()}update(){const{coords:t,_hullPrev:i,_hullNext:s,_hullTri:e,_hullHash:n}=this,h=t.length>>1;let r=1/0,l=1/0,f=-1/0,c=-1/0;for(let i=0;if&&(f=s),e>c&&(c=e),this._ids[i]=i}const _=(r+f)/2,y=(l+c)/2;let w,b,A,k=1/0;for(let i=0;i0&&(b=i,k=s)}let x=t[2*b],S=t[2*b+1],T=1/0;for(let i=0;ie&&(i[s++]=n,e=this._dists[n])}return this.hull=i.subarray(0,s),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(o(M,p,x,S,z,U)<0){const t=b,i=x,s=S;b=A,x=z,S=U,A=t,z=i,U=s}const m=function(t,i,s,e,n,h){const r=s-t,l=e-i,o=n-t,a=h-i,f=r*r+l*l,c=o*o+a*a,u=.5/(r*a-l*o);return{x:t+(a*f-l*c)*u,y:i+(r*c-o*f)*u}}(M,p,x,S,z,U);this._cx=m.x,this._cy=m.y;for(let i=0;i0&&Math.abs(c-h)<=a&&Math.abs(u-r)<=a)continue;if(h=c,r=u,f===w||f===b||f===A)continue;let _=0;for(let t=0,i=this._hashKey(c,u);t=0;)if(g=d,g===_){g=-1;break}if(-1===g)continue;let y=this._addTriangle(g,f,s[g],-1,-1,e[g]);e[f]=this._legalize(y+2),e[g]=y,K++;let k=s[g];for(;d=s[k],o(c,u,t[2*k],t[2*k+1],t[2*d],t[2*d+1])<0;)y=this._addTriangle(k,f,d,e[f],-1,e[k]),e[f]=this._legalize(y+2),s[k]=k,K--,k=d;if(g===_)for(;d=i[g],o(c,u,t[2*d],t[2*d+1],t[2*g],t[2*g+1])<0;)y=this._addTriangle(d,f,g,-1,e[g],e[d]),this._legalize(y+2),e[d]=y,s[g]=g,K--,g=d;this._hullStart=i[f]=g,s[g]=i[k]=f,s[f]=k,n[this._hashKey(c,u)]=f,n[this._hashKey(t[2*g],t[2*g+1])]=g}this.hull=new Uint32Array(K);for(let t=0,i=this._hullStart;t0?3-s:1+s)/4}(t-this._cx,i-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:i,_halfedges:s,coords:e}=this;let n=0,h=0;for(;;){const r=s[t],l=t-t%3;if(h=l+(t+2)%3,-1===r){if(0===n)break;t=f[--n];continue}const o=r-r%3,a=l+(t+1)%3,c=o+(r+2)%3,u=i[h],d=i[t],g=i[a],y=i[c];if(_(e[2*u],e[2*u+1],e[2*d],e[2*d+1],e[2*g],e[2*g+1],e[2*y],e[2*y+1])){i[t]=y,i[r]=u;const e=s[c];if(-1===e){let i=this._hullStart;do{if(this._hullTri[i]===c){this._hullTri[i]=t;break}i=this._hullPrev[i]}while(i!==this._hullStart)}this._link(t,e),this._link(r,s[h]),this._link(h,c);const l=o+(r+1)%3;n=s&&i[t[r]]>h;)t[r+1]=t[r--];t[r+1]=e}else{let n=s+1,h=e;y(t,s+e>>1,n),i[t[s]]>i[t[e]]&&y(t,s,e),i[t[n]]>i[t[e]]&&y(t,n,e),i[t[s]]>i[t[n]]&&y(t,s,n);const r=t[n],l=i[r];for(;;){do{n++}while(i[t[n]]l);if(h=h-s?(g(t,i,n,e),g(t,i,s,h-1)):(g(t,i,s,h-1),g(t,i,n,e))}}function y(t,i,s){const e=t[i];t[i]=t[s],t[s]=e}function w(t){return t[0]}function b(t){return t[1]}return c})); diff --git a/procedural/src/viewer/_config_data/biomes_config.md b/procedural/src/viewer/_config_data/biomes_config.md deleted file mode 100644 index bde6345c..00000000 --- a/procedural/src/viewer/_config_data/biomes_config.md +++ /dev/null @@ -1,28 +0,0 @@ -# Biomes Module - Configuration Properties - -The refactored `biomes.js` module does not require any configuration properties from DOM elements. - -## Analysis - -After careful examination of the original `biomes.js` code, no instances of the following patterns were found: - -- `byId()` calls to read DOM values -- Direct DOM element access -- Configuration parameters read from UI elements - -## Config Object - -The `config` parameter is included in the function signature for consistency with the refactoring pattern, but no properties are currently needed: - -```javascript -export function define(pack, grid, config, utils) { - // config parameter available but not used in this module -} -``` - -## Notes - -- The biomes module operates purely on data structures (`pack` and `grid`) -- All configuration is embedded within the module's default data -- No external configuration parameters are required -- The module is self-contained regarding biome generation logic \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/burgs-and-states_config.md b/procedural/src/viewer/_config_data/burgs-and-states_config.md deleted file mode 100644 index ba1f4f7e..00000000 --- a/procedural/src/viewer/_config_data/burgs-and-states_config.md +++ /dev/null @@ -1,52 +0,0 @@ -# Burgs and States Module - Configuration Properties - -The refactored `burgs-and-states.js` module requires the following configuration properties to be passed via the `config` object: - -## Required Config Properties - -### State Generation Configuration -- **`statesNumber`** - Number of states to generate - - **Original DOM call:** `byId("statesNumber").value` - - **Line reference:** Line 98 in original code - - **Usage:** Determines how many capital cities and states to create - -- **`sizeVariety`** - Variety factor for state sizes - - **Original DOM call:** `byId("sizeVariety").value` - - **Line reference:** Line 159 in original code - - **Usage:** Controls randomization of state expansionism values - -### Settlement Configuration -- **`manorsInput`** - Number of towns/settlements to generate - - **Original DOM call:** `manorsInput.value` and `manorsInput.valueAsNumber` - - **Line references:** Lines 193, 195, 220 in original code - - **Usage:** Controls the number of secondary settlements (towns) to place - -### Growth Rate Configuration -- **`growthRate`** - Global growth rate multiplier - - **Original DOM call:** `byId("growthRate").valueAsNumber` - - **Line reference:** Line 363 in original code - - **Usage:** Controls how aggressively states expand during the expansion phase - -- **`statesGrowthRate`** - State-specific growth rate multiplier - - **Original DOM call:** `byId("statesGrowthRate")?.valueAsNumber` - - **Line reference:** Line 364 in original code - - **Usage:** Additional multiplier specifically for state expansion behavior - -## Config Object Structure - -```javascript -const config = { - statesNumber: 15, // Number of states to generate - sizeVariety: 1, // State size variety factor - manorsInput: 1000, // Number of towns (1000 = auto-calculate) - growthRate: 1, // Global growth rate multiplier - statesGrowthRate: 1 // State growth rate multiplier -}; -``` - -## Usage Notes - -- **`manorsInput`**: When set to `1000`, the system auto-calculates the number of towns based on available populated cells -- **Growth rates**: Both growth rate properties default to `1` if not provided -- **`statesGrowthRate`**: Uses optional chaining (`?.`) in original code, indicating it might not always be present -- All numeric values are converted using `+` operator or `.valueAsNumber` in the original DOM calls \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/coa-generator_config.md b/procedural/src/viewer/_config_data/coa-generator_config.md deleted file mode 100644 index e18901d0..00000000 --- a/procedural/src/viewer/_config_data/coa-generator_config.md +++ /dev/null @@ -1,40 +0,0 @@ -# Configuration Properties for coa-generator.js - -The refactored `coa-generator.js` module requires a `config` object with the following properties: - -## Required Config Properties - -### emblemShape -- **Type**: String -- **Description**: The selected emblem shape value from the UI dropdown -- **Original DOM Call**: `document.getElementById("emblemShape").value` -- **Usage**: Used in `getShield()` function to determine shield type - -### emblemShapeGroup -- **Type**: String -- **Description**: The parent group label of the selected emblem shape option -- **Original DOM Call**: `emblemShape.selectedOptions[0]?.parentNode.label` -- **Default**: "Diversiform" when no parent group exists -- **Usage**: Used in `getShield()` function to determine if custom shield logic should be applied - -## Config Object Structure - -The config object should be structured as follows: - -```javascript -const config = { - emblemShape: "heater", // Value from emblem shape selector - emblemShapeGroup: "Basic" // Parent group of the selected option -}; -``` - -## Function Signatures - -### getShield(pack, culture, state, config) -The `getShield` function now accepts the config object as the fourth parameter instead of reading from the DOM directly. - -## Migration Notes - -- The original code read these values directly from the DOM using `byId("emblemShape")` -- The refactored version receives these values through the config parameter -- The calling code (Viewer/Client) is responsible for reading from the DOM and passing these values to the engine \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/coa-renderer_config.md b/procedural/src/viewer/_config_data/coa-renderer_config.md deleted file mode 100644 index b541a91a..00000000 --- a/procedural/src/viewer/_config_data/coa-renderer_config.md +++ /dev/null @@ -1,6 +0,0 @@ -# Config Properties for coa-renderer.js - -The refactored coa-renderer.js module is self-contained in terms of configuration and does not require any properties to be passed in a config object. - -Notes -All necessary parameters and definitions are derived directly from the coa (Coat of Arms) object that is passed as a primary argument to the render function. This module has no dependency on UI settings or external configuration values. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/cultures-generator_config.md b/procedural/src/viewer/_config_data/cultures-generator_config.md deleted file mode 100644 index 68da5d7b..00000000 --- a/procedural/src/viewer/_config_data/cultures-generator_config.md +++ /dev/null @@ -1,68 +0,0 @@ -# Config Properties for cultures-generator.js - -The refactored `cultures-generator.js` module requires the following configuration properties to be passed via the `config` object parameter: - -## Required Configuration Properties - -### Culture Generation Settings -- **`culturesInput`** (number) - Number of cultures requested by user - - *Replaces:* `+byId("culturesInput").value` - - *Usage:* Determines how many cultures to generate - -- **`culturesInSetNumber`** (number) - Maximum cultures available in selected culture set - - *Replaces:* `+byId("culturesSet").selectedOptions[0].dataset.max` - - *Usage:* Limits the number of cultures that can be generated based on the selected culture set - -- **`culturesSet`** (string) - Selected culture set type - - *Replaces:* `byId("culturesSet").value` - - *Usage:* Determines which predefined culture set to use - - *Valid values:* `"european"`, `"oriental"`, `"english"`, `"antique"`, `"highFantasy"`, `"darkFantasy"`, `"random"`, or default (all-world) - -### Map Dimensions -- **`graphWidth`** (number) - Width of the map/graph - - *Usage:* Used for calculating culture center spacing - - *Note:* This should be derived from the map generation settings - -- **`graphHeight`** (number) - Height of the map/graph - - *Usage:* Used for calculating culture center spacing - - *Note:* This should be derived from the map generation settings - -### Culture Expansion Settings -- **`sizeVariety`** (number) - Variety factor for culture expansionism - - *Replaces:* `byId("sizeVariety").value` - - *Usage:* Controls how much culture expansionism varies from base values - - *Typical range:* 0-2, where 1 is default variety - -- **`neutralRate`** (number, optional) - Rate modifier for culture expansion - - *Replaces:* `byId("neutralRate")?.valueAsNumber || 1` - - *Usage:* Affects maximum expansion cost calculations - - *Default:* 1 if not provided - -### Visual Settings -- **`emblemShape`** (string) - Shield/emblem shape setting - - *Replaces:* `document.getElementById("emblemShape").value` - - *Usage:* Determines shield shapes for cultures - - *Valid values:* `"random"` for random shield selection, or specific shield type names - -## Usage Example - -```javascript -const config = { - culturesInput: 8, - culturesInSetNumber: 15, - culturesSet: "european", - graphWidth: 2048, - graphHeight: 1024, - sizeVariety: 1.2, - neutralRate: 1.0, - emblemShape: "random" -}; - -const result = generate(pack, grid, config, utils); -``` - -## Notes -- All numeric values should be validated before passing to ensure they are valid numbers -- The `graphWidth` and `graphHeight` should match the actual map dimensions -- Optional properties will use sensible defaults if not provided -- The config object enables the engine to be completely independent of DOM/UI elements \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/features_config.md b/procedural/src/viewer/_config_data/features_config.md deleted file mode 100644 index f17c4aac..00000000 --- a/procedural/src/viewer/_config_data/features_config.md +++ /dev/null @@ -1,37 +0,0 @@ -# Configuration Properties for features.js - -## Analysis Results - -After thorough analysis of the original `features.js` code, **no DOM-based configuration parameters were found**. - -The original module does not contain any `byId()` calls or direct DOM reads that would need to be replaced with `config` object properties. - -## Configuration Object Structure - -The `config` object passed to the features module functions should contain: - -```javascript -const config = { - // Timing and debugging - TIME: boolean, // Enable/disable console timing logs - - // Randomization - seed: string|number, // Random seed for reproducible generation - aleaPRNG: function // Pseudo-random number generator function -}; -``` - -## Notes - -- This module is purely computational and operates entirely on the provided data structures -- All configuration is derived from the input `grid` and `pack` objects or calculated dynamically -- No user interface input values are required for this module's operation -- The module focuses on geometric and topological analysis of map features rather than user-configurable parameters - -## Comparison with Other Modules - -Unlike modules such as `burgs-and-states.js` which read values like `statesNumber` from the DOM, the `features.js` module: -- Does not read any DOM elements -- Does not require user input parameters -- Operates purely on mathematical calculations and data structure analysis -- Uses only internally calculated constants and thresholds \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/fonts_config.md b/procedural/src/viewer/_config_data/fonts_config.md deleted file mode 100644 index 179f84cf..00000000 --- a/procedural/src/viewer/_config_data/fonts_config.md +++ /dev/null @@ -1,20 +0,0 @@ -# Config Properties for fonts.js - -## Config Properties Identified: None - -The original `fonts.js` module did **not contain any DOM reads** that required configuration properties. - -## Analysis - -The original code had the following characteristics: -- No `byId()` calls to read DOM input values -- No configuration parameters read from UI elements -- All font data was hardcoded in the fonts array -- Functions operated on provided data parameters rather than reading global configuration - -## Notes - -- The refactored module is purely data-driven and functional -- All necessary data is passed as function parameters (e.g., `svgData` in `getUsedFonts()`) -- No configuration object is needed for this module -- The module provides utility functions that work with any font data provided to them \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/heightmap-generator_config.md b/procedural/src/viewer/_config_data/heightmap-generator_config.md deleted file mode 100644 index 4206363a..00000000 --- a/procedural/src/viewer/_config_data/heightmap-generator_config.md +++ /dev/null @@ -1,18 +0,0 @@ -# Config Properties for heightmap-generator.js - -The refactored heightmap-generator module requires the following configuration properties: - -## Core Configuration -- `templateId` - String identifier for the heightmap template to use (replaces `byId("templateInput").value`) -- `seed` - Random seed value for deterministic generation -- `graphWidth` - Width of the graph/map area -- `graphHeight` - Height of the graph/map area - -## Original DOM Reads Converted -- **`byId("templateInput").value`** → **`config.templateId`** - - Used in the `generate()` function to determine which heightmap template or precreated heightmap to use - -## Global Variables Now in Config -- **`graphWidth`** - Previously a global variable, now passed via config -- **`graphHeight`** - Previously a global variable, now passed via config -- **`seed`** - Previously a global variable, now passed via config \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/lakes_config.md b/procedural/src/viewer/_config_data/lakes_config.md deleted file mode 100644 index 03b0de71..00000000 --- a/procedural/src/viewer/_config_data/lakes_config.md +++ /dev/null @@ -1,44 +0,0 @@ -# Config Properties for lakes.js - -The refactored `lakes.js` module identified the following DOM reads that have been converted to config properties: - -## Config Properties - -### 1. lakeElevationLimit -- **Original DOM read:** `+byId("lakeElevationLimitOutput").value` -- **Config property:** `config.lakeElevationLimit` -- **Used in:** `detectCloseLakes()` function -- **Type:** Number -- **Description:** The elevation limit used to determine if a lake can be potentially open (not in deep depression) - -### 2. heightExponent -- **Original DOM read:** `heightExponentInput.value` -- **Config property:** `config.heightExponent` -- **Used in:** `defineClimateData()` function (specifically in `getLakeEvaporation()`) -- **Type:** Number -- **Description:** The height exponent used in evaporation calculations for lakes - -## Config Object Structure - -```javascript -const config = { - lakeElevationLimit: 50, // Example value - was read from "lakeElevationLimitOutput" element - heightExponent: 2 // Example value - was read from "heightExponentInput" element -}; -``` - -## Function Signatures - -Functions that require the config object: - -```javascript -detectCloseLakes(pack, grid, heights, config) -defineClimateData(pack, grid, heights, config, utils) -``` - -## Notes - -- Both config properties are numeric values used in mathematical calculations -- `lakeElevationLimit` affects lake classification logic -- `heightExponent` affects evaporation rate calculations -- These values were previously read directly from DOM input elements \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/markers-generator_config.md b/procedural/src/viewer/_config_data/markers-generator_config.md deleted file mode 100644 index d9a3cc3f..00000000 --- a/procedural/src/viewer/_config_data/markers-generator_config.md +++ /dev/null @@ -1,17 +0,0 @@ -# Configuration Properties for markers-generator.js - -The refactored `markers-generator.js` module requires the following configuration properties to be passed via the `config` object: - -## Required Config Properties - -### `culturesSet` (string) -- **Source**: Originally `document.getElementById("culturesSet").value` on line 80 -- **Purpose**: Determines the culture set being used for map generation -- **Usage**: Used to detect if Fantasy cultures are enabled, which affects multiplier values for fantasy-themed markers like portals, rifts, and disturbed burials -- **Example**: `"Fantasy European"`, `"Real World"` - -## Notes - -Only one DOM call was identified in the original code that needed to be converted to a configuration property. The `culturesSet` value is used to determine whether fantasy elements should be included in marker generation by checking if the string contains "Fantasy". - -The calling application should read this value from the UI and pass it in the config object when calling the marker generation functions. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/military-generator.js_config.md b/procedural/src/viewer/_config_data/military-generator.js_config.md deleted file mode 100644 index cd865b8e..00000000 --- a/procedural/src/viewer/_config_data/military-generator.js_config.md +++ /dev/null @@ -1,23 +0,0 @@ -# Config Properties for military-generator.js - -The refactored military-generator module requires the following configuration properties: - -## From options object: -- `military` - Array of military unit configurations (defaults to getDefaultOptions() if not provided) -- `year` - Current calendar year for note generation -- `eraShort` - Short era designation (e.g., "AD", "BC") -- `era` - Full era designation (e.g., "Anno Domini") - -## Usage: -```javascript -const config = { - military: options.military || getDefaultOptions(), - year: options.year, - eraShort: options.eraShort, - era: options.era -}; - -generate(pack, config, utils, notes); -``` - -**Note:** This module did not contain any direct DOM reads (byId() calls), so no additional configuration properties were needed to replace DOM access. All configuration comes from the existing global `options` object. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/names-generator_config.md b/procedural/src/viewer/_config_data/names-generator_config.md deleted file mode 100644 index 46c5bfa2..00000000 --- a/procedural/src/viewer/_config_data/names-generator_config.md +++ /dev/null @@ -1,25 +0,0 @@ -# Config Properties for names-generator.js - -The refactored `names-generator.js` module identified the following configuration properties that were previously read from the DOM: - -## Configuration Properties - -Currently, no direct config properties were identified in this module, as the original code had minimal DOM interaction. The main DOM interaction was: - -- **Map Name Storage**: The original code wrote to `mapName.value` but this was rendering logic that has been removed. - -## Notes - -- The `getMapName()` function previously wrote directly to a DOM element (`mapName.value = name`) -- This has been removed and the function now returns the generated name instead -- The calling code (Viewer/Client) should handle storing or displaying the generated map name -- All name generation functions now operate purely on the data passed to them as parameters - -## Function Signature Changes - -Functions that previously read global state now require data to be passed as parameters: - -- `getCulture()` now requires `cultures` parameter -- `getCultureShort()` now requires `cultures` parameter -- `getState()` now requires `cultures` parameter -- All functions now require `nameBases` and `utils` parameters \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/ocean-layers_config.md b/procedural/src/viewer/_config_data/ocean-layers_config.md deleted file mode 100644 index edc76e5d..00000000 --- a/procedural/src/viewer/_config_data/ocean-layers_config.md +++ /dev/null @@ -1,27 +0,0 @@ -# Config Properties for ocean-layers.js - -The refactored `ocean-layers.js` module requires the following configuration properties: - -## Required Config Properties - -### `outline` -- **Type**: `string` -- **Description**: Defines the ocean layer outline configuration -- **Values**: - - `"none"` - No ocean layers will be generated - - `"random"` - Use randomized outline limits - - Comma-separated numbers (e.g., `"-1,-2,-3"`) - Specific depth limits for layer generation -- **Original DOM Source**: `oceanLayers.attr("layers")` -- **Usage**: Controls which ocean depth levels should have visible layers drawn - -## Example Config Object - -```javascript -const config = { - outline: "-1,-2,-3" // Generate layers for depths -1, -2, and -3 -}; -``` - -## Migration Notes - -The original code read this value directly from a DOM element's `layers` attribute. In the refactored version, this configuration must be provided via the `config` parameter to maintain environment independence. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/provinces-generator.js_config.md b/procedural/src/viewer/_config_data/provinces-generator.js_config.md deleted file mode 100644 index d87b79f5..00000000 --- a/procedural/src/viewer/_config_data/provinces-generator.js_config.md +++ /dev/null @@ -1,25 +0,0 @@ -# Config Properties for provinces-generator.js - -The refactored `provinces-generator.js` module requires the following configuration properties: - -## Required Config Properties: - -### `provincesRatio` (Number) -- **Original DOM source**: `byId("provincesRatio").value` -- **Description**: Ratio determining the number of provinces to generate (0-100) -- **Usage**: Controls how many provinces are created relative to the number of burgs in each state -- **Type**: Number (typically 0-100) - -### `seed` (String/Number) -- **Description**: Random seed for province generation -- **Usage**: Used when `regenerate` parameter is false to maintain consistent generation -- **Type**: String or Number -- **Note**: This replaces the global `seed` variable access - -## Config Object Structure: -```javascript -const config = { - provincesRatio: 50, // 0-100, percentage of burgs to become province centers - seed: "some-seed-value" // Random seed for reproducible generation -}; -``` \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/religions-generator_config.md b/procedural/src/viewer/_config_data/religions-generator_config.md deleted file mode 100644 index 019a6080..00000000 --- a/procedural/src/viewer/_config_data/religions-generator_config.md +++ /dev/null @@ -1,30 +0,0 @@ -# Config Properties for religions-generator.js - -The refactored module requires the following configuration properties: - -## Required Config Properties - -### `religionsNumber` -- **Type**: `number` -- **Source**: Previously read from `religionsNumber.value` (DOM element) -- **Description**: The desired number of organized religions to generate -- **Usage**: Used in `generateOrganizedReligions()` to determine how many religions to create - -### `growthRate` -- **Type**: `number` -- **Source**: Previously read from `byId("growthRate").valueAsNumber` (DOM element) -- **Description**: Growth rate multiplier that affects how far religions can expand -- **Usage**: Used in `expandReligions()` to calculate `maxExpansionCost = (cells.i.length / 20) * config.growthRate` - -## Config Object Structure - -The config object should be structured as: - -```javascript -const config = { - religionsNumber: 5, // Number of organized religions to generate - growthRate: 1.0 // Growth rate multiplier for religion expansion -}; -``` - -These properties replace the original DOM-based configuration reads and allow the engine to be run in any environment without browser dependencies. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/resample_config.md b/procedural/src/viewer/_config_data/resample_config.md deleted file mode 100644 index a63e06ad..00000000 --- a/procedural/src/viewer/_config_data/resample_config.md +++ /dev/null @@ -1,26 +0,0 @@ -# Config Properties for resample.js - -The refactored `resample.js` module requires the following configuration properties to be passed in the `config` object: - -## Required Config Properties - -### Map Dimensions -- `graphWidth` (Number) - The width of the map/graph canvas -- `graphHeight` (Number) - The height of the map/graph canvas - -## Usage Context - -These configuration properties replace the original global variables that were accessed directly: - -### Original Global Access → Config Property -- `graphWidth` → `config.graphWidth` -- `graphHeight` → `config.graphHeight` - -## Notes - -The `graphWidth` and `graphHeight` properties are used extensively throughout the module for: -- Boundary checking with `isInMap()` function calls -- Creating bounding boxes for route clipping -- Determining if projected coordinates fall within the map area - -These values were previously accessed as global variables but are now properly injected as configuration parameters following the dependency injection pattern. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/river-generator_config.md b/procedural/src/viewer/_config_data/river-generator_config.md deleted file mode 100644 index ee767064..00000000 --- a/procedural/src/viewer/_config_data/river-generator_config.md +++ /dev/null @@ -1,66 +0,0 @@ -# Configuration Properties for river-generator.js - -## DOM-Based Configuration Parameters Identified - -The refactored `river-generator.js` module identified the following DOM reads that were converted to config properties: - -### `resolveDepressionsSteps` -- **Original DOM read:** `document.getElementById("resolveDepressionsStepsOutput").value` (line 330) -- **Purpose:** Maximum number of iterations for the depression resolution algorithm -- **Type:** Number (integer) -- **Default suggestion:** 1000 - -### Derived Configuration Parameters - -Additionally, the following parameters were extracted from global variables that should be configurable: - -### `cellsCount` -- **Original source:** `pointsInput.dataset.cells` (lines 111, 240) -- **Purpose:** Total number of cells in the map for calculations -- **Type:** Number (integer) -- **Usage:** Used in cellsNumberModifier calculations and width factor calculations - -### Graph Dimensions -- **Original source:** Global `graphWidth` and `graphHeight` (lines 449, 453) -- **Purpose:** Map boundaries for border point calculations -- **Type:** Number -- **Properties:** `graphWidth`, `graphHeight` - -### Flags and Constants -- **Original source:** Global variables -- **Properties:** - - `TIME` - Boolean flag for timing logs - - `WARN` - Boolean flag for warning messages - - `seed` - Random seed for reproducible generation - - `aleaPRNG` - Pseudo-random number generator function - -## Configuration Object Structure - -```javascript -const config = { - // DOM-derived parameters - resolveDepressionsSteps: 1000, // Max iterations for depression resolution - - // System parameters - cellsCount: 10000, // Total number of map cells - graphWidth: 1920, // Map width - graphHeight: 1080, // Map height - - // Flags and utilities - TIME: true, // Enable timing logs - WARN: true, // Enable warning messages - seed: 'map_seed_123', // Random seed - aleaPRNG: seedrandom // PRNG function -}; -``` - -## Usage Notes - -1. **`resolveDepressionsSteps`** is critical for terrain depression resolution - higher values provide more accurate results but take longer to compute -2. **`cellsCount`** affects river width calculations and flux modifiers -3. **Graph dimensions** are essential for proper border calculations when rivers flow off the map -4. **Timing flags** should be configurable for debugging and performance monitoring - -## Migration Impact - -This conversion removes the last DOM dependency from the river generation system, making it fully headless and environment-agnostic while maintaining all original functionality through proper configuration injection. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/routes-generator_config.md b/procedural/src/viewer/_config_data/routes-generator_config.md deleted file mode 100644 index 10585e87..00000000 --- a/procedural/src/viewer/_config_data/routes-generator_config.md +++ /dev/null @@ -1,20 +0,0 @@ -# Configuration Properties for routes-generator.js - -After analyzing the original `routes-generator.js` code, **no DOM-based configuration parameters were found**. - -The module does not contain any `byId()` calls or direct DOM reads that would require configuration properties. - -All configuration is done through: -- Constants defined at the module level (ROUTES_SHARP_ANGLE, MIN_PASSABLE_SEA_TEMP, etc.) -- Data passed in through function parameters (pack, grid, lockedRoutes) -- Utility functions passed through the utils object - -Therefore, no `config` object properties need to be defined for this module. - -## Constants Used (Internal to Module) -- `ROUTES_SHARP_ANGLE = 135` -- `ROUTES_VERY_SHARP_ANGLE = 115` -- `MIN_PASSABLE_SEA_TEMP = -4` -- `ROUTE_TYPE_MODIFIERS` object with water type modifiers - -These are hardcoded constants and do not require external configuration. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/submap_config.md b/procedural/src/viewer/_config_data/submap_config.md deleted file mode 100644 index 3beffe03..00000000 --- a/procedural/src/viewer/_config_data/submap_config.md +++ /dev/null @@ -1,32 +0,0 @@ -# Config Properties for submap.js - -The refactored `submap.js` module requires the following configuration properties: - -## Required Config Properties - -### `graphWidth` (Number) -- **Purpose**: Width of the map canvas/viewport -- **Usage**: Used in `isInMap()` function to determine if coordinates are within map boundaries -- **Original source**: Global variable `graphWidth` - -### `graphHeight` (Number) -- **Purpose**: Height of the map canvas/viewport -- **Usage**: Used in `isInMap()` function to determine if coordinates are within map boundaries -- **Original source**: Global variable `graphHeight` - -## Configuration Object Structure - -The config object should be structured as: - -```javascript -const config = { - graphWidth: 1920, // Map canvas width - graphHeight: 1080 // Map canvas height -}; -``` - -## Notes - -- These properties were originally accessed as global variables in the legacy code -- The `isInMap(x, y, config)` function now uses `config.graphWidth` and `config.graphHeight` instead of global variables -- These values are critical for proper coordinate validation during map resampling operations \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/voronoi_config.md b/procedural/src/viewer/_config_data/voronoi_config.md deleted file mode 100644 index 6053d1bc..00000000 --- a/procedural/src/viewer/_config_data/voronoi_config.md +++ /dev/null @@ -1,15 +0,0 @@ -# Config Properties for voronoi.js - -The refactored `voronoi.js` module requires **no config properties**. - -## Analysis: -- The Voronoi class is a pure geometric computation module -- It does not read any values from the DOM (no `byId()` calls found) -- It does not access any global configuration variables -- All necessary data is passed through constructor parameters: - - `delaunay`: Delaunator instance - - `points`: Array of coordinate pairs - - `pointsN`: Number of points - -## No Configuration Needed: -Since this module performs purely geometric calculations based on input data and does not interact with UI elements or global state, no `config` object is required for this refactoring. \ No newline at end of file diff --git a/procedural/src/viewer/_config_data/zones_config.md b/procedural/src/viewer/_config_data/zones_config.md deleted file mode 100644 index 8b462ade..00000000 --- a/procedural/src/viewer/_config_data/zones_config.md +++ /dev/null @@ -1,24 +0,0 @@ -# Configuration Properties for zones.js - -The refactored zones.js module accepts the following configuration properties: - -## Configuration Object Properties - -### `globalModifier` (optional) -- **Type**: Number -- **Default**: 1 -- **Description**: Global modifier for zone generation quantity. Multiplies the base quantity of each zone type. -- **Usage**: Controls overall density of zones generated across the map - -### `TIME` (optional) -- **Type**: Boolean -- **Default**: false -- **Description**: Debug flag to enable/disable console timing for performance measurement -- **Usage**: When true, logs execution time of zone generation to console - -## Notes - -- No DOM-dependent configuration properties were found in the original code -- The original module did not read any values from DOM elements via `byId()` -- All zone generation parameters are internally configured in the `zoneConfig` object -- The module is purely data-driven and does not require UI input for configuration \ No newline at end of file diff --git a/procedural/src/viewer/config-builder.js b/procedural/src/viewer/config-builder.js deleted file mode 100644 index 242565e0..00000000 --- a/procedural/src/viewer/config-builder.js +++ /dev/null @@ -1,387 +0,0 @@ -// config-builder.js -// Builds configuration objects PURELY from the UI state for the FMG engine. - -/** - * Main function to build a complete configuration object from the current UI state. - * @returns {Object} Complete configuration object for the FMG engine. - */ -export function buildConfigFromUI() { - const config = { - seed: getSeed(), - graph: buildGraphConfig(), - map: buildMapConfig(), - heightmap: buildHeightmapConfig(), - temperature: buildTemperatureConfig(), - precipitation: buildPrecipitationConfig(), - features: {}, // No UI config - biomes: {}, // No UI config - lakes: buildLakesConfig(), - rivers: buildRiversConfig(), - oceanLayers: buildOceanLayersConfig(), - cultures: buildCulturesConfig(), - burgs: buildBurgsConfig(), - religions: buildReligionsConfig(), - provinces: buildProvincesConfig(), - military: buildMilitaryConfig(), - markers: buildMarkersConfig(), - zones: buildZonesConfig(), - debug: buildDebugConfig() - }; - - return config; -} - -// --- Helper Functions --- - -function byId(id) { - return document.getElementById(id); -} - -function getNumericValue(id, defaultValue = 0) { - const element = byId(id); - // Ensure the element exists before trying to read its value - if (!element || element.value === "") return defaultValue; - - const value = element.valueAsNumber || +element.value; - return isNaN(value) ? defaultValue : value; -} - -function getStringValue(id, defaultValue = "") { - const element = byId(id); - return element ? element.value : defaultValue; -} - -function getCheckboxValue(id, defaultValue = false) { - const element = byId(id); - return element ? element.checked : defaultValue; -} - -// --- Section Builders --- - -function getSeed() { - const seedValue = getStringValue("seed"); - // Generate a new seed only if the input is empty - return seedValue || Date.now().toString(36) + Math.random().toString(36).substr(2); -} - -function buildGraphConfig() { - // REMOVED: Fallback to global `graphWidth` and `graphHeight`. - // The builder must only read from the DOM as the single source of truth. - return { - width: getNumericValue("mapWidth", 1920), - height: getNumericValue("mapHeight", 1080), - cellsDesired: getNumericValue("pointsInput", 10000) - // NOTE: 'cellsNumber' and 'points' are often outputs of the generation, not inputs. - // They are omitted here to avoid confusion. The engine will calculate them. - }; -} - -function buildMapConfig() { - return { - coordinatesSize: getNumericValue("coordinatesSize", 1), - latitude: getNumericValue("latitude", 0) - }; -} - -function buildHeightmapConfig() { - return { - templateId: getStringValue("templateInput", "continents") - // NOTE: `template` property was redundant with `templateId`. Removed for clarity. - }; -} - -function buildTemperatureConfig() { - return { - heightExponent: getNumericValue("heightExponentInput", 1.8), - temperatureScale: getStringValue("temperatureScale", "C"), - temperatureBase: getNumericValue("temperatureBase", 25) - }; -} - -function buildPrecipitationConfig() { - // REMOVED: Fallback to global `windPatterns`. This should be handled by presets - // or a more advanced UI control in the future. For now, it defaults to empty. - return { - winds: [], - moisture: getNumericValue("moisture", 1) - }; -} - -function buildLakesConfig() { - // Assuming a different input for lake's height exponent if needed, - // otherwise, it correctly re-uses the temperature one. - return { - lakeElevationLimit: getNumericValue("lakeElevationLimitOutput", 50), - heightExponent: getNumericValue("heightExponentInput", 2) - }; -} - -function buildRiversConfig() { - const pointsInput = byId("pointsInput"); - const cellsCount = pointsInput ? getNumericValue("pointsInput", 10000) : 10000; - - return { - resolveDepressionsSteps: getNumericValue("resolveDepressionsStepsOutput", 1000), - cellsCount: cellsCount - }; -} - -function buildOceanLayersConfig() { - const oceanLayersEl = byId("oceanLayers"); - // Reading from a data attribute is fine if that's how your UI is structured. - const outline = oceanLayersEl ? oceanLayersEl.getAttribute("layers") : "-1,-2,-3"; - - return { outline: outline || "-1,-2,-3" }; -} - -function buildCulturesConfig() { - const culturesSetEl = byId("culturesSet"); - const emblemShapeEl = byId("emblemShape"); - - let culturesInSetNumber = 15; - if (culturesSetEl?.selectedOptions[0]) { - culturesInSetNumber = +culturesSetEl.selectedOptions[0].dataset.max || 15; - } - - let emblemShapeGroup = "Diversiform"; - if (emblemShapeEl?.selectedOptions[0]?.parentNode?.label) { - emblemShapeGroup = emblemShapeEl.selectedOptions[0].parentNode.label; - } - - return { - culturesInput: getNumericValue("culturesInput", 12), - culturesInSetNumber: culturesInSetNumber, - culturesSet: getStringValue("culturesSet", "european"), - sizeVariety: getNumericValue("sizeVariety", 1), - neutralRate: getNumericValue("neutralRate", 1), - emblemShape: getStringValue("emblemShape", "random"), - emblemShapeGroup: emblemShapeGroup - }; -} - -function buildBurgsConfig() { - return { - statesNumber: getNumericValue("statesNumber", 15), - sizeVariety: getNumericValue("sizeVariety", 1), - manorsInput: getNumericValue("manorsInput", 1000), - growthRate: getNumericValue("growthRate", 1), - statesGrowthRate: getNumericValue("statesGrowthRate", 1) - }; -} - -function buildReligionsConfig() { - return { - religionsNumber: getNumericValue("religionsNumber", 5), - growthRate: getNumericValue("growthRate", 1) - }; -} - -function buildProvincesConfig() { - return { - provincesRatio: getNumericValue("provincesRatio", 50) - }; -} - -function buildMilitaryConfig() { - // REMOVED: Fallback to global `options` object. - // All military settings should be read from their own UI elements. - return { - // military: [], // This would need a dedicated UI to build - year: getNumericValue("yearInput", 1400), - eraShort: getStringValue("eraShortInput", "AD"), - era: getStringValue("eraInput", "Anno Domini") - }; -} - -function buildMarkersConfig() { - return { - culturesSet: getStringValue("culturesSet", "european") - }; -} - -function buildZonesConfig() { - return { - globalModifier: getNumericValue("zonesGlobalModifier", 1) - }; -} - -function buildDebugConfig() { - // REMOVED: Fallback to global TIME, WARN, INFO flags. - // These should be controlled by UI elements, like checkboxes. - return { - TIME: getCheckboxValue("debugTime", false), - WARN: getCheckboxValue("debugWarn", true), - INFO: getCheckboxValue("debugInfo", false), - ERROR: getCheckboxValue("debugError", true) - }; -} - -// NOTE: The preset, JSON, and UI application functions below are well-structured -// and do not need changes. They correctly support the new architecture. - -/* ... (buildConfigFromPreset, buildConfigFromJSON, saveConfigToJSON, deepMerge, applyConfigToUI) ... */ - -/** - * Build configuration from a preset object, merging with UI values - * @param {Object} preset - Preset configuration to use as base - * @returns {Object} Complete configuration object - */ -export function buildConfigFromPreset(preset) { - const uiConfig = buildConfigFromUI(); - return deepMerge(preset, uiConfig); -} - -/** - * Build configuration from JSON string - * @param {string} jsonString - JSON string containing configuration - * @returns {Object} Parsed configuration object - */ -export function buildConfigFromJSON(jsonString) { - try { - return JSON.parse(jsonString); - } catch (error) { - console.error("Failed to parse configuration JSON:", error); - return null; - } -} - -/** - * Save configuration to JSON string - * @param {Object} config - Configuration object to serialize - * @returns {string} JSON string representation - */ -export function saveConfigToJSON(config) { - return JSON.stringify(config, null, 2); -} - -/** - * Deep merge two objects - * @param {Object} target - Target object - * @param {Object} source - Source object to merge - * @returns {Object} Merged object - */ -function deepMerge(target, source) { - const result = { ...target }; - - for (const key in source) { - if (source.hasOwnProperty(key)) { - if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { - result[key] = deepMerge(result[key] || {}, source[key]); - } else { - result[key] = source[key]; - } - } - } - - return result; -} - -/** - * Apply configuration to UI elements - * @param {Object} config - Configuration object to apply to UI - */ -export function applyConfigToUI(config) { - // Core parameters - if (config.seed && byId("seed")) { - byId("seed").value = config.seed; - } - - // Graph configuration - if (config.graph) { - if (config.graph.width && byId("mapWidth")) byId("mapWidth").value = config.graph.width; - if (config.graph.height && byId("mapHeight")) byId("mapHeight").value = config.graph.height; - if (config.graph.cellsDesired && byId("pointsInput")) byId("pointsInput").value = config.graph.cellsDesired; - } - - // Heightmap configuration - if (config.heightmap && config.heightmap.templateId && byId("templateInput")) { - byId("templateInput").value = config.heightmap.templateId; - } - - // Temperature configuration - if (config.temperature) { - if (config.temperature.heightExponent && byId("heightExponentInput")) { - byId("heightExponentInput").value = config.temperature.heightExponent; - } - if (config.temperature.temperatureScale && byId("temperatureScale")) { - byId("temperatureScale").value = config.temperature.temperatureScale; - } - } - - // Lakes configuration - if (config.lakes) { - if (config.lakes.lakeElevationLimit && byId("lakeElevationLimitOutput")) { - byId("lakeElevationLimitOutput").value = config.lakes.lakeElevationLimit; - } - } - - // Rivers configuration - if (config.rivers) { - if (config.rivers.resolveDepressionsSteps && byId("resolveDepressionsStepsOutput")) { - byId("resolveDepressionsStepsOutput").value = config.rivers.resolveDepressionsSteps; - } - } - - // Cultures configuration - if (config.cultures) { - if (config.cultures.culturesInput && byId("culturesInput")) { - byId("culturesInput").value = config.cultures.culturesInput; - } - if (config.cultures.culturesSet && byId("culturesSet")) { - byId("culturesSet").value = config.cultures.culturesSet; - } - if (config.cultures.sizeVariety && byId("sizeVariety")) { - byId("sizeVariety").value = config.cultures.sizeVariety; - } - if (config.cultures.emblemShape && byId("emblemShape")) { - byId("emblemShape").value = config.cultures.emblemShape; - } - } - - // Burgs and States configuration - if (config.burgs) { - if (config.burgs.statesNumber && byId("statesNumber")) { - byId("statesNumber").value = config.burgs.statesNumber; - } - if (config.burgs.manorsInput && byId("manorsInput")) { - byId("manorsInput").value = config.burgs.manorsInput; - } - if (config.burgs.growthRate && byId("growthRate")) { - byId("growthRate").value = config.burgs.growthRate; - } - } - - // Religions configuration - if (config.religions) { - if (config.religions.religionsNumber && byId("religionsNumber")) { - byId("religionsNumber").value = config.religions.religionsNumber; - } - } - - // Provinces configuration - if (config.provinces) { - if (config.provinces.provincesRatio && byId("provincesRatio")) { - byId("provincesRatio").value = config.provinces.provincesRatio; - } - } - - // Update any UI labels or displays - updateUILabels(); -} - -/** - * Update UI labels to reflect current values - */ -function updateUILabels() { - // Update any output labels that show current values - const outputs = document.querySelectorAll("output"); - outputs.forEach(output => { - const forId = output.getAttribute("for"); - if (forId) { - const input = byId(forId); - if (input) { - output.value = input.value; - } - } - }); -} diff --git a/procedural/src/viewer/config-integration.md b/procedural/src/viewer/config-integration.md deleted file mode 100644 index 7b27a30f..00000000 --- a/procedural/src/viewer/config-integration.md +++ /dev/null @@ -1,394 +0,0 @@ -# Configuration System Integration Guide - -This document explains how to integrate the new configuration system into the Fantasy Map Generator codebase. - -## Overview - -The configuration system provides a unified interface between the UI layer and the headless generation engine. It consists of four main components: - -1. **config-schema.md** - TypeScript interface definitions -2. **config-builder.js** - Functions to build config from UI state -3. **config-presets.js** - Predefined configuration templates -4. **config-validator.js** - Validation and sanitization functions - -## Integration Steps - -### 1. Engine Integration - -The engine already expects a config object in `src/engine/main.js`: - -```javascript -import { generate } from './engine/main.js'; -import { buildConfigFromUI } from './viewer/config-builder.js'; -import { validateConfig } from './viewer/config-validator.js'; - -// Generate map from current UI state -async function generateMap() { - // Build config from UI - const config = buildConfigFromUI(); - - // Validate configuration - const validation = validateConfig(config); - if (!validation.valid) { - console.error('Configuration errors:', validation.errors); - // Handle errors or auto-fix - } - - // Generate map - const mapData = generate(config); - return mapData; -} -``` - -### 2. UI Integration - -Update the viewer's main.js to use the configuration system: - -```javascript -// src/viewer/main.js -import { - buildConfigFromUI, - applyConfigToUI, - buildConfigFromJSON, - saveConfigToJSON -} from './config-builder.js'; -import { getPreset } from './config-presets.js'; -import { validateAndFix } from './config-validator.js'; - -// Generate button handler -document.getElementById('generateButton').addEventListener('click', () => { - const config = buildConfigFromUI(); - const { fixed, validationResult } = validateAndFix(config); - - if (!validationResult.valid) { - showValidationErrors(validationResult); - return; - } - - // Call engine with validated config - const mapData = window.FMG.generate(fixed); - renderMap(mapData); -}); - -// Preset selector handler -document.getElementById('presetSelector').addEventListener('change', (e) => { - const presetName = e.target.value; - const preset = getPreset(presetName); - - if (preset) { - applyConfigToUI(preset); - } -}); -``` - -### 3. Headless/CLI Integration - -For command-line usage: - -```javascript -// cli.js -import { generate } from './src/engine/main.js'; -import { getPreset } from './src/viewer/config-presets.js'; -import { validateConfig, sanitizeConfig } from './src/viewer/config-validator.js'; -import fs from 'fs'; - -// Load config from file -function loadConfig(filepath) { - const jsonString = fs.readFileSync(filepath, 'utf8'); - return JSON.parse(jsonString); -} - -// Generate from CLI -async function generateFromCLI(options) { - let config; - - if (options.preset) { - config = getPreset(options.preset); - } else if (options.config) { - config = loadConfig(options.config); - } else { - config = getPreset('default'); - } - - // Override with CLI arguments - if (options.seed) config.seed = options.seed; - if (options.width) config.graph.width = parseInt(options.width); - if (options.height) config.graph.height = parseInt(options.height); - - // Validate and fix - const validation = validateConfig(config); - if (!validation.valid) { - console.warn('Config validation warnings:', validation.warnings); - config = sanitizeConfig(config); - } - - // Generate - const mapData = generate(config); - - // Save output - fs.writeFileSync(options.output || 'map.json', JSON.stringify(mapData)); -} -``` - -### 4. Save/Load Integration - -Implement configuration persistence: - -```javascript -// Save current configuration -function saveConfiguration() { - const config = buildConfigFromUI(); - const jsonString = saveConfigToJSON(config); - - // Save to localStorage - localStorage.setItem('fmg-last-config', jsonString); - - // Or download as file - downloadJSON(jsonString, 'map-config.json'); -} - -// Load saved configuration -function loadConfiguration(jsonString) { - const config = buildConfigFromJSON(jsonString); - - if (config) { - const { fixed, validationResult } = validateAndFix(config); - - if (validationResult.warnings.length > 0) { - console.warn('Configuration warnings:', validationResult.warnings); - } - - applyConfigToUI(fixed); - } -} - -// Auto-save on generation -function generateWithAutoSave() { - const config = buildConfigFromUI(); - localStorage.setItem('fmg-last-config', saveConfigToJSON(config)); - - const mapData = generate(config); - return mapData; -} -``` - -### 5. Testing Integration - -Create tests for the configuration system: - -```javascript -// config.test.js -import { validateConfig, sanitizeConfig } from './config-validator.js'; -import { getPreset } from './config-presets.js'; -import { buildConfigFromUI } from './config-builder.js'; - -describe('Configuration System', () => { - test('All presets should be valid', () => { - const presetNames = ['default', 'continents', 'archipelago', 'fantasy']; - - presetNames.forEach(name => { - const preset = getPreset(name); - const validation = validateConfig(preset); - expect(validation.valid).toBe(true); - }); - }); - - test('Invalid config should be fixable', () => { - const badConfig = { - graph: { - width: -100, // Invalid - height: 'abc' // Invalid - } - }; - - const fixed = sanitizeConfig(badConfig); - const validation = validateConfig(fixed); - expect(validation.valid).toBe(true); - }); - - test('Config builder should produce valid config', () => { - // Mock DOM - document.body.innerHTML = ` - - - - `; - - const config = buildConfigFromUI(); - const validation = validateConfig(config); - expect(validation.valid).toBe(true); - }); -}); -``` - -## Migration Checklist - -### Phase 1: Setup -- [ ] Copy configuration files to project -- [ ] Import configuration modules in viewer/main.js -- [ ] Add preset selector to UI - -### Phase 2: Integration -- [ ] Update generate button handler to use buildConfigFromUI() -- [ ] Add validation error display -- [ ] Implement preset loading in UI -- [ ] Add configuration save/load buttons - -### Phase 3: Testing -- [ ] Test all presets generate valid maps -- [ ] Test configuration persistence -- [ ] Test validation and auto-fix -- [ ] Test CLI integration - -### Phase 4: Cleanup -- [ ] Remove old configuration code -- [ ] Update documentation -- [ ] Add configuration examples - -## API Reference - -### config-builder.js - -```javascript -// Build config from current UI state -buildConfigFromUI(): Object - -// Build config from preset with UI overrides -buildConfigFromPreset(preset: Object): Object - -// Parse config from JSON string -buildConfigFromJSON(jsonString: string): Object - -// Serialize config to JSON -saveConfigToJSON(config: Object): string - -// Apply config values to UI elements -applyConfigToUI(config: Object): void -``` - -### config-presets.js - -```javascript -// Get preset by name -getPreset(name: string): Object | null - -// Get all preset names -getPresetNames(): string[] - -// Get preset descriptions -getPresetDescriptions(): Object -``` - -### config-validator.js - -```javascript -// Validate configuration -validateConfig(config: Object): ValidationResult - -// Fix invalid configuration -sanitizeConfig(config: Object): Object - -// Validate and auto-fix -validateAndFix(config: Object): Object - -// Get human-readable report -getValidationReport(result: ValidationResult): string -``` - -## Configuration Flow - -```mermaid -graph LR - A[UI State] --> B[buildConfigFromUI] - B --> C[Config Object] - C --> D[validateConfig] - D --> E{Valid?} - E -->|Yes| F[generate] - E -->|No| G[sanitizeConfig] - G --> F - F --> H[Map Data] - - I[Preset] --> J[getPreset] - J --> K[applyConfigToUI] - K --> A - - L[JSON File] --> M[buildConfigFromJSON] - M --> C -``` - -## Common Use Cases - -### 1. Generate with Current UI Settings -```javascript -const config = buildConfigFromUI(); -const mapData = generate(config); -``` - -### 2. Generate with Preset -```javascript -const config = getPreset('fantasy'); -const mapData = generate(config); -``` - -### 3. Generate with Custom Config -```javascript -const config = { - ...getPreset('default'), - cultures: { - ...getPreset('default').cultures, - culturesInput: 25 - } -}; -const mapData = generate(config); -``` - -### 4. Save and Restore Session -```javascript -// Save -const config = buildConfigFromUI(); -localStorage.setItem('session', saveConfigToJSON(config)); - -// Restore -const saved = localStorage.getItem('session'); -if (saved) { - const config = buildConfigFromJSON(saved); - applyConfigToUI(config); -} -``` - -## Troubleshooting - -### Configuration Not Valid -- Check the validation report for specific errors -- Use sanitizeConfig() to auto-fix common issues -- Verify all required properties are present - -### UI Not Updating -- Ensure DOM elements have correct IDs -- Check that applyConfigToUI() is called after DOM is ready -- Verify config object structure matches schema - -### Preset Not Loading -- Confirm preset name exists in config-presets.js -- Check for JavaScript errors in console -- Validate preset configuration structure - -## Best Practices - -1. **Always validate** configuration before using it -2. **Use presets** as starting points for custom configurations -3. **Save configurations** with generated maps for reproducibility -4. **Document custom properties** if extending the schema -5. **Test configurations** across different map sizes and settings -6. **Handle validation errors** gracefully in the UI -7. **Version configurations** when making breaking changes - -## Future Enhancements - -- Configuration versioning and migration -- Advanced validation rules -- Configuration diff and merge tools -- Visual configuration builder -- Configuration sharing and community presets -- Performance profiling per configuration -- A/B testing framework for configurations \ No newline at end of file diff --git a/procedural/src/viewer/config-presets.js b/procedural/src/viewer/config-presets.js deleted file mode 100644 index 8bbc124a..00000000 --- a/procedural/src/viewer/config-presets.js +++ /dev/null @@ -1,677 +0,0 @@ -// config-presets.js -// Predefined configuration presets for common map generation scenarios - -/** - * Default balanced configuration - * Suitable for most use cases with moderate complexity - */ -export const defaultConfig = { - seed: undefined, // Auto-generate - - graph: { - width: 1920, - height: 1080, - cellsDesired: 10000, - boundary: "box" - }, - - map: { - coordinatesSize: 1, - latitude: 0 - }, - - heightmap: { - templateId: "continents", - template: "continents" - }, - - temperature: { - heightExponent: 1.8, - temperatureScale: "C", - temperatureBase: 25 - }, - - precipitation: { - moisture: 1 - }, - - features: {}, - biomes: {}, - - lakes: { - lakeElevationLimit: 50, - heightExponent: 2 - }, - - rivers: { - resolveDepressionsSteps: 1000 - }, - - oceanLayers: { - outline: "-1,-2,-3" - }, - - cultures: { - culturesInput: 12, - culturesInSetNumber: 15, - culturesSet: "european", - sizeVariety: 1, - neutralRate: 1, - emblemShape: "random", - emblemShapeGroup: "Diversiform" - }, - - burgs: { - statesNumber: 15, - sizeVariety: 1, - manorsInput: 1000, - growthRate: 1, - statesGrowthRate: 1 - }, - - religions: { - religionsNumber: 5, - growthRate: 1 - }, - - provinces: { - provincesRatio: 50 - }, - - military: { - year: 1400, - eraShort: "AD", - era: "Anno Domini" - }, - - markers: { - culturesSet: "european" - }, - - zones: { - globalModifier: 1 - }, - - debug: { - TIME: false, - WARN: true, - INFO: false, - ERROR: true - } -}; - -/** - * Continents configuration - * Large landmasses with expansive territories - */ -export const continentsConfig = { - ...defaultConfig, - - graph: { - width: 2560, - height: 1440, - cellsDesired: 20000, - boundary: "box" - }, - - heightmap: { - templateId: "continents", - template: "continents" - }, - - temperature: { - heightExponent: 2, - temperatureScale: "C", - temperatureBase: 22 - }, - - lakes: { - lakeElevationLimit: 45, - heightExponent: 2.2 - }, - - rivers: { - resolveDepressionsSteps: 1500 - }, - - cultures: { - culturesInput: 18, - culturesInSetNumber: 20, - culturesSet: "all-world", - sizeVariety: 1.5, - neutralRate: 0.8, - emblemShape: "random", - emblemShapeGroup: "Diversiform" - }, - - burgs: { - statesNumber: 20, - sizeVariety: 1.2, - manorsInput: 1000, - growthRate: 1.1, - statesGrowthRate: 1.1 - }, - - religions: { - religionsNumber: 8, - growthRate: 1.2 - }, - - provinces: { - provincesRatio: 60 - } -}; - -/** - * Archipelago configuration - * Island chains and maritime cultures - */ -export const archipelagoConfig = { - ...defaultConfig, - - graph: { - width: 1920, - height: 1080, - cellsDesired: 15000, - boundary: "box" - }, - - heightmap: { - templateId: "archipelago", - template: "archipelago" - }, - - temperature: { - heightExponent: 1.5, - temperatureScale: "C", - temperatureBase: 28 - }, - - lakes: { - lakeElevationLimit: 60, - heightExponent: 1.8 - }, - - rivers: { - resolveDepressionsSteps: 800 - }, - - oceanLayers: { - outline: "-1,-2,-3,-4" - }, - - cultures: { - culturesInput: 16, - culturesInSetNumber: 20, - culturesSet: "oriental", - sizeVariety: 2, - neutralRate: 0.6, - emblemShape: "round", - emblemShapeGroup: "Basic" - }, - - burgs: { - statesNumber: 25, - sizeVariety: 2, - manorsInput: 1000, - growthRate: 0.8, - statesGrowthRate: 0.7 - }, - - religions: { - religionsNumber: 10, - growthRate: 0.6 - }, - - provinces: { - provincesRatio: 40 - }, - - markers: { - culturesSet: "oriental" - } -}; - -/** - * High Fantasy configuration - * Many diverse cultures, magical elements, varied terrain - */ -export const fantasyConfig = { - ...defaultConfig, - - graph: { - width: 2048, - height: 2048, - cellsDesired: 25000, - boundary: "box" - }, - - heightmap: { - templateId: "highland", - template: "highland" - }, - - temperature: { - heightExponent: 1.6, - temperatureScale: "C", - temperatureBase: 20 - }, - - lakes: { - lakeElevationLimit: 55, - heightExponent: 2.5 - }, - - rivers: { - resolveDepressionsSteps: 2000 - }, - - oceanLayers: { - outline: "random" - }, - - cultures: { - culturesInput: 25, - culturesInSetNumber: 30, - culturesSet: "highFantasy", - sizeVariety: 3, - neutralRate: 1.2, - emblemShape: "fantasy1", - emblemShapeGroup: "Fantasy" - }, - - burgs: { - statesNumber: 30, - sizeVariety: 2.5, - manorsInput: 1000, - growthRate: 1.5, - statesGrowthRate: 1.3 - }, - - religions: { - religionsNumber: 15, - growthRate: 1.8 - }, - - provinces: { - provincesRatio: 70 - }, - - military: { - year: 1000, - eraShort: "Age", - era: "Third Age" - }, - - markers: { - culturesSet: "highFantasy" - }, - - zones: { - globalModifier: 1.5 - } -}; - -/** - * Realistic Earth-like configuration - * Natural terrain distribution, realistic cultures - */ -export const realisticConfig = { - ...defaultConfig, - - graph: { - width: 1920, - height: 1080, - cellsDesired: 12000, - boundary: "box" - }, - - heightmap: { - templateId: "continents", - template: "continents" - }, - - temperature: { - heightExponent: 2.1, - temperatureScale: "C", - temperatureBase: 15 - }, - - precipitation: { - moisture: 0.9 - }, - - lakes: { - lakeElevationLimit: 48, - heightExponent: 2.3 - }, - - rivers: { - resolveDepressionsSteps: 1200 - }, - - oceanLayers: { - outline: "-1,-2,-3" - }, - - cultures: { - culturesInput: 8, - culturesInSetNumber: 12, - culturesSet: "european", - sizeVariety: 0.8, - neutralRate: 0.9, - emblemShape: "heater", - emblemShapeGroup: "Basic" - }, - - burgs: { - statesNumber: 10, - sizeVariety: 0.7, - manorsInput: 1000, - growthRate: 0.8, - statesGrowthRate: 0.8 - }, - - religions: { - religionsNumber: 3, - growthRate: 0.7 - }, - - provinces: { - provincesRatio: 45 - }, - - military: { - year: 1450, - eraShort: "AD", - era: "Anno Domini" - }, - - markers: { - culturesSet: "european" - }, - - zones: { - globalModifier: 0.8 - } -}; - -/** - * Island Nations configuration - * Small isolated islands with unique cultures - */ -export const islandNationsConfig = { - ...defaultConfig, - - graph: { - width: 1920, - height: 1080, - cellsDesired: 8000, - boundary: "box" - }, - - heightmap: { - templateId: "islands", - template: "islands" - }, - - temperature: { - heightExponent: 1.4, - temperatureScale: "C", - temperatureBase: 26 - }, - - lakes: { - lakeElevationLimit: 65, - heightExponent: 1.5 - }, - - rivers: { - resolveDepressionsSteps: 600 - }, - - oceanLayers: { - outline: "-1,-2" - }, - - cultures: { - culturesInput: 20, - culturesInSetNumber: 25, - culturesSet: "oriental", - sizeVariety: 2.5, - neutralRate: 0.5, - emblemShape: "oval", - emblemShapeGroup: "Basic" - }, - - burgs: { - statesNumber: 30, - sizeVariety: 3, - manorsInput: 1000, - growthRate: 0.6, - statesGrowthRate: 0.5 - }, - - religions: { - religionsNumber: 12, - growthRate: 0.5 - }, - - provinces: { - provincesRatio: 35 - }, - - zones: { - globalModifier: 0.7 - } -}; - -/** - * Ancient World configuration - * Classical antiquity setting - */ -export const ancientWorldConfig = { - ...defaultConfig, - - graph: { - width: 1920, - height: 1080, - cellsDesired: 10000, - boundary: "box" - }, - - heightmap: { - templateId: "inland", - template: "inland" - }, - - temperature: { - heightExponent: 1.9, - temperatureScale: "C", - temperatureBase: 24 - }, - - lakes: { - lakeElevationLimit: 52, - heightExponent: 2.1 - }, - - rivers: { - resolveDepressionsSteps: 1100 - }, - - cultures: { - culturesInput: 10, - culturesInSetNumber: 12, - culturesSet: "antique", - sizeVariety: 1.1, - neutralRate: 1, - emblemShape: "roman", - emblemShapeGroup: "Historical" - }, - - burgs: { - statesNumber: 12, - sizeVariety: 1.3, - manorsInput: 1000, - growthRate: 0.9, - statesGrowthRate: 0.9 - }, - - religions: { - religionsNumber: 6, - growthRate: 0.8 - }, - - provinces: { - provincesRatio: 55 - }, - - military: { - year: 100, - eraShort: "AD", - era: "Anno Domini" - }, - - markers: { - culturesSet: "antique" - } -}; - -/** - * Dark Fantasy configuration - * Grim, dangerous world with hostile territories - */ -export const darkFantasyConfig = { - ...defaultConfig, - - graph: { - width: 1920, - height: 1080, - cellsDesired: 15000, - boundary: "box" - }, - - heightmap: { - templateId: "highland", - template: "highland" - }, - - temperature: { - heightExponent: 2.2, - temperatureScale: "C", - temperatureBase: 12 - }, - - precipitation: { - moisture: 0.7 - }, - - lakes: { - lakeElevationLimit: 40, - heightExponent: 2.8 - }, - - rivers: { - resolveDepressionsSteps: 1800 - }, - - oceanLayers: { - outline: "-1,-2,-3,-4,-5" - }, - - cultures: { - culturesInput: 15, - culturesInSetNumber: 20, - culturesSet: "darkFantasy", - sizeVariety: 2, - neutralRate: 1.5, - emblemShape: "gothic", - emblemShapeGroup: "Fantasy" - }, - - burgs: { - statesNumber: 18, - sizeVariety: 1.8, - manorsInput: 1000, - growthRate: 0.7, - statesGrowthRate: 0.6 - }, - - religions: { - religionsNumber: 8, - growthRate: 1.4 - }, - - provinces: { - provincesRatio: 65 - }, - - military: { - year: 666, - eraShort: "DR", - era: "Dark Reckoning" - }, - - markers: { - culturesSet: "darkFantasy" - }, - - zones: { - globalModifier: 2 - } -}; - -/** - * Get a preset by name - * @param {string} name - Name of the preset - * @returns {Object|null} Preset configuration or null if not found - */ -export function getPreset(name) { - const presets = { - default: defaultConfig, - continents: continentsConfig, - archipelago: archipelagoConfig, - fantasy: fantasyConfig, - realistic: realisticConfig, - islandNations: islandNationsConfig, - ancientWorld: ancientWorldConfig, - darkFantasy: darkFantasyConfig - }; - - return presets[name] || null; -} - -/** - * Get all available preset names - * @returns {string[]} Array of preset names - */ -export function getPresetNames() { - return [ - "default", - "continents", - "archipelago", - "fantasy", - "realistic", - "islandNations", - "ancientWorld", - "darkFantasy" - ]; -} - -/** - * Get preset descriptions - * @returns {Object} Object mapping preset names to descriptions - */ -export function getPresetDescriptions() { - return { - default: "Balanced configuration suitable for most use cases", - continents: "Large landmasses with expansive territories", - archipelago: "Island chains and maritime cultures", - fantasy: "High fantasy with many diverse cultures and magical elements", - realistic: "Earth-like terrain with realistic culture distribution", - islandNations: "Small isolated islands with unique cultures", - ancientWorld: "Classical antiquity setting with ancient civilizations", - darkFantasy: "Grim, dangerous world with hostile territories" - }; -} \ No newline at end of file diff --git a/procedural/src/viewer/config-schema.md b/procedural/src/viewer/config-schema.md deleted file mode 100644 index 8b8ed08d..00000000 --- a/procedural/src/viewer/config-schema.md +++ /dev/null @@ -1,421 +0,0 @@ -# Fantasy Map Generator - Configuration Schema - -This document defines the complete TypeScript-style interface for the FMG configuration object. - -## Complete Configuration Interface - -```typescript -interface FMGConfig { - // Core generation parameters - seed?: string; // Random seed for reproducible generation - - // Graph/Grid generation - graph: { - width: number; // Canvas width (default: 1920) - height: number; // Canvas height (default: 1080) - cellsDesired?: number; // Target number of cells (default: 10000) - cellsNumber?: number; // Actual number of cells generated - points?: number; // Number of voronoi points - boundary?: string; // Boundary type: "box", "circle", etc. - }; - - // Map dimensions and coordinates - map: { - coordinatesSize?: number; // Size factor for map coordinates - latitude?: number; // Central latitude for temperature calculations - }; - - // Heightmap generation - heightmap: { - templateId: string; // Template: "continents", "archipelago", "highland", "inland", etc. - template?: string; // Alternative name for templateId - }; - - // Temperature configuration - temperature: { - heightExponent?: number; // Height influence on temperature (default: 1.8) - temperatureScale?: string; // Temperature scale: "C" or "F" - temperatureBase?: number; // Base temperature value - }; - - // Precipitation configuration - precipitation: { - winds?: any[]; // Wind patterns configuration - moisture?: number; // Base moisture level - }; - - // Feature configuration - features: { - // Currently no user-configurable parameters - }; - - // Biomes configuration - biomes: { - // Currently no user-configurable parameters - }; - - // Lakes configuration - lakes: { - lakeElevationLimit: number; // Elevation limit for open lakes (default: 50) - heightExponent: number; // Exponent for lake evaporation (default: 2) - }; - - // River generation - rivers: { - resolveDepressionsSteps: number; // Max iterations for depression resolution (default: 1000) - cellsCount?: number; // Total number of cells (for calculations) - }; - - // Ocean layers - oceanLayers: { - outline: string; // "none", "random", or comma-separated depths ("-1,-2,-3") - }; - - // Cultures configuration - cultures: { - culturesInput: number; // Number of cultures to generate (default: 12) - culturesInSetNumber: number; // Max cultures in selected set - culturesSet: string; // Culture set: "european", "oriental", "english", "antique", "highFantasy", "darkFantasy", "random" - sizeVariety: number; // Culture size variety (default: 1) - neutralRate?: number; // Neutral expansion rate (default: 1) - emblemShape: string; // Shield shape for emblems - emblemShapeGroup?: string; // Shield shape group category - }; - - // Burgs and States configuration - burgs: { - statesNumber: number; // Number of states to generate (default: 15) - sizeVariety: number; // State size variety factor (default: 1) - manorsInput: number; // Number of towns (1000 = auto-calculate) - growthRate: number; // Global growth rate multiplier (default: 1) - statesGrowthRate?: number; // State-specific growth rate (default: 1) - }; - - // Religions configuration - religions: { - religionsNumber: number; // Number of religions to generate (default: 5) - growthRate: number; // Religion expansion rate (default: 1) - }; - - // Provinces configuration - provinces: { - provincesRatio: number; // Ratio of burgs to provinces (0-100, default: 50) - }; - - // Military configuration - military: { - military?: any[]; // Military unit configurations - year?: number; // Current calendar year - eraShort?: string; // Short era designation (e.g., "AD") - era?: string; // Full era designation (e.g., "Anno Domini") - }; - - // Markers configuration - markers: { - culturesSet?: string; // Culture set (affects fantasy markers) - }; - - // Zones configuration - zones: { - globalModifier?: number; // Zone density modifier (default: 1) - }; - - // Debugging and logging - debug?: { - TIME?: boolean; // Enable timing logs - WARN?: boolean; // Enable warning logs - INFO?: boolean; // Enable info logs - ERROR?: boolean; // Enable error logs - }; -} - -// Preset type definitions -type HeightmapTemplate = - | "continents" - | "archipelago" - | "highland" - | "inland" - | "lakes" - | "islands" - | "atoll" - | "volcano" - | "crater"; - -type CultureSet = - | "european" - | "oriental" - | "english" - | "antique" - | "highFantasy" - | "darkFantasy" - | "random" - | "all-world"; - -type EmblemShape = - | "random" - | "heater" - | "spanish" - | "french" - | "horsehead" - | "horsehead2" - | "polish" - | "hessen" - | "swiss" - | "boeotian" - | "roman" - | "kite" - | "oldFrench" - | "renaissance" - | "baroque" - | "targe" - | "targe2" - | "pavise" - | "wedged" - | "round" - | "oval" - | "square" - | "diamond" - | "flag" - | "pennon" - | "guidon" - | "banner" - | "dovetail" - | "gonfalon" - | "pennant" - | "fantasy1" - | "fantasy2" - | "fantasy3" - | "fantasy4" - | "fantasy5" - | "noldor" - | "gondor" - | "easterling" - | "erebor" - | "ironHills" - | "urukHai" - | "moriaOrc"; - -type OceanOutline = - | "none" - | "random" - | string; // Comma-separated depth values - -// Validation interfaces -interface ConfigValidationResult { - valid: boolean; - errors: string[]; - warnings: string[]; -} - -interface ConfigRange { - min?: number; - max?: number; - default: number; -} -``` - -## Default Values - -```typescript -const DEFAULT_CONFIG: Partial = { - seed: undefined, // Will be auto-generated if not provided - - graph: { - width: 1920, - height: 1080, - cellsDesired: 10000, - boundary: "box" - }, - - map: { - coordinatesSize: 1, - latitude: 0 - }, - - heightmap: { - templateId: "continents" - }, - - temperature: { - heightExponent: 1.8, - temperatureScale: "C", - temperatureBase: 25 - }, - - precipitation: { - moisture: 1 - }, - - features: {}, - - biomes: {}, - - lakes: { - lakeElevationLimit: 50, - heightExponent: 2 - }, - - rivers: { - resolveDepressionsSteps: 1000 - }, - - oceanLayers: { - outline: "-1,-2,-3" - }, - - cultures: { - culturesInput: 12, - culturesInSetNumber: 15, - culturesSet: "european", - sizeVariety: 1, - neutralRate: 1, - emblemShape: "random" - }, - - burgs: { - statesNumber: 15, - sizeVariety: 1, - manorsInput: 1000, // Auto-calculate - growthRate: 1, - statesGrowthRate: 1 - }, - - religions: { - religionsNumber: 5, - growthRate: 1 - }, - - provinces: { - provincesRatio: 50 - }, - - military: { - year: 1400, - eraShort: "AD", - era: "Anno Domini" - }, - - markers: {}, - - zones: { - globalModifier: 1 - }, - - debug: { - TIME: false, - WARN: true, - INFO: false, - ERROR: true - } -}; -``` - -## Validation Rules - -### Required Properties -- `graph.width` - Must be positive number -- `graph.height` - Must be positive number -- `heightmap.templateId` - Must be valid template name - -### Range Constraints -- `graph.width`: 100 - 8192 -- `graph.height`: 100 - 8192 -- `graph.cellsDesired`: 1000 - 100000 -- `cultures.culturesInput`: 0 - 99 -- `burgs.statesNumber`: 0 - 999 -- `burgs.manorsInput`: 0 - 10000 -- `religions.religionsNumber`: 0 - 99 -- `provinces.provincesRatio`: 0 - 100 -- `lakes.lakeElevationLimit`: 0 - 100 -- `rivers.resolveDepressionsSteps`: 100 - 10000 -- All growth rates: 0.1 - 10 -- All variety factors: 0 - 5 - -### Type Constraints -- All numeric properties must be valid numbers (not NaN) -- String enums must match predefined values -- Optional properties can be undefined but not null - -## Usage Examples - -### Minimal Configuration -```typescript -const minimalConfig: FMGConfig = { - graph: { - width: 1920, - height: 1080 - }, - heightmap: { - templateId: "continents" - } - // All other properties will use defaults -}; -``` - -### Fantasy World Configuration -```typescript -const fantasyConfig: FMGConfig = { - seed: "fantasy-world-123", - graph: { - width: 2560, - height: 1440, - cellsDesired: 20000 - }, - heightmap: { - templateId: "archipelago" - }, - cultures: { - culturesInput: 20, - culturesInSetNumber: 30, - culturesSet: "highFantasy", - sizeVariety: 2, - emblemShape: "fantasy1" - }, - burgs: { - statesNumber: 25, - sizeVariety: 1.5, - manorsInput: 1000, - growthRate: 1.2 - }, - religions: { - religionsNumber: 12, - growthRate: 1.5 - } -}; -``` - -### Realistic Earth-like Configuration -```typescript -const realisticConfig: FMGConfig = { - seed: "earth-like-456", - graph: { - width: 1920, - height: 1080, - cellsDesired: 15000 - }, - heightmap: { - templateId: "continents" - }, - temperature: { - heightExponent: 2, - temperatureBase: 20 - }, - cultures: { - culturesInput: 8, - culturesInSetNumber: 15, - culturesSet: "european", - sizeVariety: 1, - emblemShape: "heater" - }, - burgs: { - statesNumber: 10, - sizeVariety: 0.8, - manorsInput: 1000, - growthRate: 0.8 - }, - religions: { - religionsNumber: 3, - growthRate: 0.7 - } -}; -``` \ No newline at end of file diff --git a/procedural/src/viewer/config-validator.js b/procedural/src/viewer/config-validator.js deleted file mode 100644 index d4cd5780..00000000 --- a/procedural/src/viewer/config-validator.js +++ /dev/null @@ -1,490 +0,0 @@ -// config-validator.js -// Validation functions for FMG configuration objects - -/** - * Valid heightmap templates - */ -const VALID_TEMPLATES = [ - "continents", "archipelago", "highland", "inland", "lakes", - "islands", "atoll", "volcano", "crater", "shattered" -]; - -/** - * Valid culture sets - */ -const VALID_CULTURE_SETS = [ - "european", "oriental", "english", "antique", - "highFantasy", "darkFantasy", "random", "all-world" -]; - -/** - * Valid emblem shapes - */ -const VALID_EMBLEM_SHAPES = [ - "random", "heater", "spanish", "french", "horsehead", "horsehead2", - "polish", "hessen", "swiss", "boeotian", "roman", "kite", - "oldFrench", "renaissance", "baroque", "targe", "targe2", - "pavise", "wedged", "round", "oval", "square", "diamond", - "flag", "pennon", "guidon", "banner", "dovetail", "gonfalon", - "pennant", "fantasy1", "fantasy2", "fantasy3", "fantasy4", - "fantasy5", "noldor", "gondor", "easterling", "erebor", - "ironHills", "urukHai", "moriaOrc", "gothic" -]; - -/** - * Configuration value ranges - */ -const RANGES = { - "graph.width": { min: 100, max: 8192 }, - "graph.height": { min: 100, max: 8192 }, - "graph.cellsDesired": { min: 1000, max: 100000 }, - "cultures.culturesInput": { min: 0, max: 99 }, - "burgs.statesNumber": { min: 0, max: 999 }, - "burgs.manorsInput": { min: 0, max: 10000 }, - "religions.religionsNumber": { min: 0, max: 99 }, - "provinces.provincesRatio": { min: 0, max: 100 }, - "lakes.lakeElevationLimit": { min: 0, max: 100 }, - "rivers.resolveDepressionsSteps": { min: 100, max: 10000 }, - "growthRate": { min: 0.1, max: 10 }, - "sizeVariety": { min: 0, max: 5 }, - "temperature.heightExponent": { min: 0.5, max: 5 }, - "zones.globalModifier": { min: 0.1, max: 10 } -}; - -/** - * Main validation function - * @param {Object} config - Configuration object to validate - * @returns {Object} Validation result with valid flag, errors, and warnings - */ -export function validateConfig(config) { - const result = { - valid: true, - errors: [], - warnings: [] - }; - - // Check required properties - validateRequiredProperties(config, result); - - // Validate data types - validateDataTypes(config, result); - - // Validate ranges - validateRanges(config, result); - - // Validate enums - validateEnums(config, result); - - // Validate logical constraints - validateLogicalConstraints(config, result); - - // Validate required fields for modules - validateRequiredFields(config, result); - - // Set valid flag based on errors - result.valid = result.errors.length === 0; - - return result; -} - -/** - * Validate required properties exist - */ -function validateRequiredProperties(config, result) { - // Required top-level properties - if (!config.graph) { - result.errors.push("Missing required property: graph"); - } else { - if (typeof config.graph.width === 'undefined') { - result.errors.push("Missing required property: graph.width"); - } - if (typeof config.graph.height === 'undefined') { - result.errors.push("Missing required property: graph.height"); - } - } - - if (!config.heightmap) { - result.errors.push("Missing required property: heightmap"); - } else { - if (!config.heightmap.templateId && !config.heightmap.template) { - result.errors.push("Missing required property: heightmap.templateId or heightmap.template"); - } - } -} - -/** - * Validate data types - */ -function validateDataTypes(config, result) { - // Check numeric types - const numericProperties = [ - "graph.width", "graph.height", "graph.cellsDesired", - "cultures.culturesInput", "cultures.sizeVariety", "cultures.neutralRate", - "burgs.statesNumber", "burgs.sizeVariety", "burgs.manorsInput", - "burgs.growthRate", "burgs.statesGrowthRate", - "religions.religionsNumber", "religions.growthRate", - "provinces.provincesRatio", - "lakes.lakeElevationLimit", "lakes.heightExponent", - "rivers.resolveDepressionsSteps", - "temperature.heightExponent", "temperature.temperatureBase", - "zones.globalModifier" - ]; - - numericProperties.forEach(path => { - const value = getNestedProperty(config, path); - if (value !== undefined && (typeof value !== 'number' || isNaN(value))) { - result.errors.push(`${path} must be a valid number, got: ${typeof value}`); - } - }); - - // Check string types - const stringProperties = [ - "seed", "heightmap.templateId", "heightmap.template", - "cultures.culturesSet", "cultures.emblemShape", - "oceanLayers.outline", "temperature.temperatureScale" - ]; - - stringProperties.forEach(path => { - const value = getNestedProperty(config, path); - if (value !== undefined && typeof value !== 'string') { - result.errors.push(`${path} must be a string, got: ${typeof value}`); - } - }); - - // Check boolean types - const booleanProperties = [ - "debug.TIME", "debug.WARN", "debug.INFO", "debug.ERROR" - ]; - - booleanProperties.forEach(path => { - const value = getNestedProperty(config, path); - if (value !== undefined && typeof value !== 'boolean') { - result.errors.push(`${path} must be a boolean, got: ${typeof value}`); - } - }); -} - -/** - * Validate value ranges - */ -function validateRanges(config, result) { - Object.keys(RANGES).forEach(path => { - const value = getNestedProperty(config, path); - if (value !== undefined && typeof value === 'number') { - const range = RANGES[path]; - if (value < range.min || value > range.max) { - result.errors.push( - `${path} must be between ${range.min} and ${range.max}, got: ${value}` - ); - } - } - }); - - // Special case for growth rates - const growthRates = [ - "burgs.growthRate", "burgs.statesGrowthRate", "religions.growthRate" - ]; - - growthRates.forEach(path => { - const value = getNestedProperty(config, path); - if (value !== undefined && typeof value === 'number') { - if (value < 0.1 || value > 10) { - result.warnings.push( - `${path} is outside recommended range (0.1-10): ${value}` - ); - } - } - }); -} - -/** - * Validate enum values - */ -function validateEnums(config, result) { - // Validate heightmap template - const template = config.heightmap?.templateId || config.heightmap?.template; - if (template && !VALID_TEMPLATES.includes(template)) { - result.warnings.push( - `Unknown heightmap template: ${template}. Valid options: ${VALID_TEMPLATES.join(", ")}` - ); - } - - // Validate culture set - if (config.cultures?.culturesSet && !VALID_CULTURE_SETS.includes(config.cultures.culturesSet)) { - result.warnings.push( - `Unknown culture set: ${config.cultures.culturesSet}. Valid options: ${VALID_CULTURE_SETS.join(", ")}` - ); - } - - // Validate emblem shape - if (config.cultures?.emblemShape && - config.cultures.emblemShape !== "random" && - !VALID_EMBLEM_SHAPES.includes(config.cultures.emblemShape)) { - result.warnings.push( - `Unknown emblem shape: ${config.cultures.emblemShape}` - ); - } - - // Validate temperature scale - if (config.temperature?.temperatureScale && - !["C", "F"].includes(config.temperature.temperatureScale)) { - result.errors.push( - `Temperature scale must be "C" or "F", got: ${config.temperature.temperatureScale}` - ); - } - - // Validate ocean outline - if (config.oceanLayers?.outline) { - const outline = config.oceanLayers.outline; - if (outline !== "none" && outline !== "random") { - // Check if it's a valid comma-separated list of numbers - const parts = outline.split(","); - const valid = parts.every(part => { - const num = parseInt(part.trim()); - return !isNaN(num) && num <= 0; - }); - if (!valid) { - result.errors.push( - `Ocean outline must be "none", "random", or comma-separated negative numbers, got: ${outline}` - ); - } - } - } -} - -/** - * Validate logical constraints between properties - */ -function validateLogicalConstraints(config, result) { - // Check that cultures input doesn't exceed max for set - if (config.cultures?.culturesInput && config.cultures?.culturesInSetNumber) { - if (config.cultures.culturesInput > config.cultures.culturesInSetNumber) { - result.warnings.push( - `culturesInput (${config.cultures.culturesInput}) exceeds max for culture set (${config.cultures.culturesInSetNumber})` - ); - } - } - - // Check that states number is reasonable relative to cells - if (config.burgs?.statesNumber && config.graph?.cellsDesired) { - const ratio = config.burgs.statesNumber / config.graph.cellsDesired; - if (ratio > 0.05) { - result.warnings.push( - `High states to cells ratio (${ratio.toFixed(3)}). Consider reducing states number or increasing cells.` - ); - } - } - - // Check manorsInput auto-calculate value - if (config.burgs?.manorsInput === 1000) { - result.warnings.push( - "manorsInput set to 1000 (auto-calculate mode)" - ); - } - - // Check map dimensions aspect ratio - if (config.graph?.width && config.graph?.height) { - const aspectRatio = config.graph.width / config.graph.height; - if (aspectRatio < 0.5 || aspectRatio > 3) { - result.warnings.push( - `Unusual aspect ratio (${aspectRatio.toFixed(2)}). Standard ratios are between 0.5 and 3.` - ); - } - } - - // Check cells desired vs map size - if (config.graph?.cellsDesired && config.graph?.width && config.graph?.height) { - const area = config.graph.width * config.graph.height; - const cellDensity = config.graph.cellsDesired / area; - if (cellDensity < 0.001) { - result.warnings.push( - "Very low cell density. Consider increasing cellsDesired for better detail." - ); - } else if (cellDensity > 0.1) { - result.warnings.push( - "Very high cell density. This may impact performance." - ); - } - } -} - -/** - * Validate required fields for modules - */ -function validateRequiredFields(config, result) { - const requiredFields = { - 'cultures.culturesInSetNumber': (config) => { - // Ensure this field exists based on culturesSet - const maxCultures = getCultureSetMax(config.cultures.culturesSet); - return maxCultures; - }, - 'rivers.cellsCount': (config) => { - // Ensure this matches the actual cell count - return config.graph.cellsDesired || 10000; - } - }; - - // Check each required field - Object.keys(requiredFields).forEach(fieldPath => { - const value = getNestedProperty(config, fieldPath); - if (value === undefined) { - const defaultValue = requiredFields[fieldPath](config); - result.warnings.push(`Missing field ${fieldPath}, would default to ${defaultValue}`); - } - }); -} - -/** - * Get maximum cultures for a culture set - */ -function getCultureSetMax(culturesSet) { - const sets = { - european: 25, - oriental: 20, - english: 15, - antique: 18, - highFantasy: 30, - darkFantasy: 25, - random: 50, - 'all-world': 100 - }; - return sets[culturesSet] || 25; -} - -/** - * Helper function to get nested property value - */ -function getNestedProperty(obj, path) { - const parts = path.split('.'); - let current = obj; - - for (const part of parts) { - if (current && typeof current === 'object' && part in current) { - current = current[part]; - } else { - return undefined; - } - } - - return current; -} - -/** - * Sanitize configuration object - * Removes invalid values and applies defaults - * @param {Object} config - Configuration object to sanitize - * @returns {Object} Sanitized configuration - */ -export function sanitizeConfig(config) { - const sanitized = JSON.parse(JSON.stringify(config)); // Deep clone - - // Apply defaults for missing required properties - if (!sanitized.graph) sanitized.graph = {}; - if (!sanitized.graph.width) sanitized.graph.width = 1920; - if (!sanitized.graph.height) sanitized.graph.height = 1080; - if (!sanitized.graph.cellsDesired) sanitized.graph.cellsDesired = 10000; - - if (!sanitized.heightmap) sanitized.heightmap = {}; - if (!sanitized.heightmap.templateId && !sanitized.heightmap.template) { - sanitized.heightmap.templateId = "continents"; - } - - // Ensure all number values are valid - function sanitizeNumbers(obj, path = '') { - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - const fullPath = path ? `${path}.${key}` : key; - - if (typeof obj[key] === 'object' && obj[key] !== null) { - sanitizeNumbers(obj[key], fullPath); - } else if (typeof obj[key] === 'number' && isNaN(obj[key])) { - // Replace NaN with default - const range = RANGES[fullPath]; - obj[key] = range ? range.min : 0; - } - } - } - } - - sanitizeNumbers(sanitized); - - // Clamp values to valid ranges - Object.keys(RANGES).forEach(path => { - const value = getNestedProperty(sanitized, path); - if (value !== undefined && typeof value === 'number') { - const range = RANGES[path]; - const parts = path.split('.'); - let current = sanitized; - - for (let i = 0; i < parts.length - 1; i++) { - if (!current[parts[i]]) current[parts[i]] = {}; - current = current[parts[i]]; - } - - current[parts[parts.length - 1]] = Math.max(range.min, Math.min(range.max, value)); - } - }); - - return sanitized; -} - -/** - * Validate and fix configuration - * @param {Object} config - Configuration to validate and fix - * @returns {Object} Object with fixed config and validation result - */ -export function validateAndFix(config) { - const validationResult = validateConfig(config); - - if (!validationResult.valid) { - const fixed = sanitizeConfig(config); - const revalidation = validateConfig(fixed); - - return { - original: config, - fixed: fixed, - originalValidation: validationResult, - fixedValidation: revalidation, - wasFixed: true - }; - } - - return { - original: config, - fixed: config, - originalValidation: validationResult, - fixedValidation: validationResult, - wasFixed: false - }; -} - -/** - * Get a human-readable validation report - * @param {Object} validationResult - Result from validateConfig - * @returns {string} Formatted validation report - */ -export function getValidationReport(validationResult) { - let report = []; - - if (validationResult.valid) { - report.push("✓ Configuration is valid"); - } else { - report.push("✗ Configuration has errors"); - } - - if (validationResult.errors.length > 0) { - report.push("\nErrors:"); - validationResult.errors.forEach(error => { - report.push(` • ${error}`); - }); - } - - if (validationResult.warnings.length > 0) { - report.push("\nWarnings:"); - validationResult.warnings.forEach(warning => { - report.push(` • ${warning}`); - }); - } - - return report.join("\n"); -} \ No newline at end of file diff --git a/procedural/src/viewer/libs/dropbox-sdk.min.js b/procedural/src/viewer/libs/dropbox-sdk.min.js deleted file mode 100644 index c5e32fc2..00000000 --- a/procedural/src/viewer/libs/dropbox-sdk.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Dropbox={})}(this,(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){for(var r=0;t.length>r;r++){var s=t[r];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(e,s.key,s)}}function s(e,t,s){return t&&r(e.prototype,t),s&&r(e,s),e}function i(e){return(i=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function n(e,t){return(n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function u(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}function a(e,t,r){return(a=u()?Reflect.construct:function(e,t,r){var s=[null];s.push.apply(s,t);var i=new(Function.bind.apply(e,s));return r&&n(i,r.prototype),i}).apply(null,arguments)}function o(e){var t="function"==typeof Map?new Map:void 0;return(o=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return a(e,arguments,i(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),n(r,e)})(e)}function c(e,t){return!t||"object"!=typeof t&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}var p="app",l="user",f="team",h="dropboxapi.com",m="dropbox.com",d={api:"api",notify:"bolt",content:"api-content"},_={};function q(e){var t="000".concat(e.charCodeAt(0).toString(16)).slice(-4);return"\\u".concat(t)}_.accountSetProfilePhoto=function(e){return this.request("account/set_profile_photo",e,"user","api","rpc")},_.authTokenFromOauth1=function(e){return this.request("auth/token/from_oauth1",e,"app","api","rpc")},_.authTokenRevoke=function(){return this.request("auth/token/revoke",null,"user","api","rpc")},_.checkApp=function(e){return this.request("check/app",e,"app","api","rpc")},_.checkUser=function(e){return this.request("check/user",e,"user","api","rpc")},_.contactsDeleteManualContacts=function(){return this.request("contacts/delete_manual_contacts",null,"user","api","rpc")},_.contactsDeleteManualContactsBatch=function(e){return this.request("contacts/delete_manual_contacts_batch",e,"user","api","rpc")},_.filePropertiesPropertiesAdd=function(e){return this.request("file_properties/properties/add",e,"user","api","rpc")},_.filePropertiesPropertiesOverwrite=function(e){return this.request("file_properties/properties/overwrite",e,"user","api","rpc")},_.filePropertiesPropertiesRemove=function(e){return this.request("file_properties/properties/remove",e,"user","api","rpc")},_.filePropertiesPropertiesSearch=function(e){return this.request("file_properties/properties/search",e,"user","api","rpc")},_.filePropertiesPropertiesSearchContinue=function(e){return this.request("file_properties/properties/search/continue",e,"user","api","rpc")},_.filePropertiesPropertiesUpdate=function(e){return this.request("file_properties/properties/update",e,"user","api","rpc")},_.filePropertiesTemplatesAddForTeam=function(e){return this.request("file_properties/templates/add_for_team",e,"team","api","rpc")},_.filePropertiesTemplatesAddForUser=function(e){return this.request("file_properties/templates/add_for_user",e,"user","api","rpc")},_.filePropertiesTemplatesGetForTeam=function(e){return this.request("file_properties/templates/get_for_team",e,"team","api","rpc")},_.filePropertiesTemplatesGetForUser=function(e){return this.request("file_properties/templates/get_for_user",e,"user","api","rpc")},_.filePropertiesTemplatesListForTeam=function(){return this.request("file_properties/templates/list_for_team",null,"team","api","rpc")},_.filePropertiesTemplatesListForUser=function(){return this.request("file_properties/templates/list_for_user",null,"user","api","rpc")},_.filePropertiesTemplatesRemoveForTeam=function(e){return this.request("file_properties/templates/remove_for_team",e,"team","api","rpc")},_.filePropertiesTemplatesRemoveForUser=function(e){return this.request("file_properties/templates/remove_for_user",e,"user","api","rpc")},_.filePropertiesTemplatesUpdateForTeam=function(e){return this.request("file_properties/templates/update_for_team",e,"team","api","rpc")},_.filePropertiesTemplatesUpdateForUser=function(e){return this.request("file_properties/templates/update_for_user",e,"user","api","rpc")},_.fileRequestsCount=function(){return this.request("file_requests/count",null,"user","api","rpc")},_.fileRequestsCreate=function(e){return this.request("file_requests/create",e,"user","api","rpc")},_.fileRequestsDelete=function(e){return this.request("file_requests/delete",e,"user","api","rpc")},_.fileRequestsDeleteAllClosed=function(){return this.request("file_requests/delete_all_closed",null,"user","api","rpc")},_.fileRequestsGet=function(e){return this.request("file_requests/get",e,"user","api","rpc")},_.fileRequestsListV2=function(e){return this.request("file_requests/list_v2",e,"user","api","rpc")},_.fileRequestsList=function(){return this.request("file_requests/list",null,"user","api","rpc")},_.fileRequestsListContinue=function(e){return this.request("file_requests/list/continue",e,"user","api","rpc")},_.fileRequestsUpdate=function(e){return this.request("file_requests/update",e,"user","api","rpc")},_.filesAlphaGetMetadata=function(e){return this.request("files/alpha/get_metadata",e,"user","api","rpc")},_.filesAlphaUpload=function(e){return this.request("files/alpha/upload",e,"user","content","upload")},_.filesCopyV2=function(e){return this.request("files/copy_v2",e,"user","api","rpc")},_.filesCopy=function(e){return this.request("files/copy",e,"user","api","rpc")},_.filesCopyBatchV2=function(e){return this.request("files/copy_batch_v2",e,"user","api","rpc")},_.filesCopyBatch=function(e){return this.request("files/copy_batch",e,"user","api","rpc")},_.filesCopyBatchCheckV2=function(e){return this.request("files/copy_batch/check_v2",e,"user","api","rpc")},_.filesCopyBatchCheck=function(e){return this.request("files/copy_batch/check",e,"user","api","rpc")},_.filesCopyReferenceGet=function(e){return this.request("files/copy_reference/get",e,"user","api","rpc")},_.filesCopyReferenceSave=function(e){return this.request("files/copy_reference/save",e,"user","api","rpc")},_.filesCreateFolderV2=function(e){return this.request("files/create_folder_v2",e,"user","api","rpc")},_.filesCreateFolder=function(e){return this.request("files/create_folder",e,"user","api","rpc")},_.filesCreateFolderBatch=function(e){return this.request("files/create_folder_batch",e,"user","api","rpc")},_.filesCreateFolderBatchCheck=function(e){return this.request("files/create_folder_batch/check",e,"user","api","rpc")},_.filesDeleteV2=function(e){return this.request("files/delete_v2",e,"user","api","rpc")},_.filesDelete=function(e){return this.request("files/delete",e,"user","api","rpc")},_.filesDeleteBatch=function(e){return this.request("files/delete_batch",e,"user","api","rpc")},_.filesDeleteBatchCheck=function(e){return this.request("files/delete_batch/check",e,"user","api","rpc")},_.filesDownload=function(e){return this.request("files/download",e,"user","content","download")},_.filesDownloadZip=function(e){return this.request("files/download_zip",e,"user","content","download")},_.filesExport=function(e){return this.request("files/export",e,"user","content","download")},_.filesGetFileLockBatch=function(e){return this.request("files/get_file_lock_batch",e,"user","api","rpc")},_.filesGetMetadata=function(e){return this.request("files/get_metadata",e,"user","api","rpc")},_.filesGetPreview=function(e){return this.request("files/get_preview",e,"user","content","download")},_.filesGetTemporaryLink=function(e){return this.request("files/get_temporary_link",e,"user","api","rpc")},_.filesGetTemporaryUploadLink=function(e){return this.request("files/get_temporary_upload_link",e,"user","api","rpc")},_.filesGetThumbnail=function(e){return this.request("files/get_thumbnail",e,"user","content","download")},_.filesGetThumbnailV2=function(e){return this.request("files/get_thumbnail_v2",e,"app, user","content","download")},_.filesGetThumbnailBatch=function(e){return this.request("files/get_thumbnail_batch",e,"user","content","rpc")},_.filesListFolder=function(e){return this.request("files/list_folder",e,"user","api","rpc")},_.filesListFolderContinue=function(e){return this.request("files/list_folder/continue",e,"user","api","rpc")},_.filesListFolderGetLatestCursor=function(e){return this.request("files/list_folder/get_latest_cursor",e,"user","api","rpc")},_.filesListFolderLongpoll=function(e){return this.request("files/list_folder/longpoll",e,"noauth","notify","rpc")},_.filesListRevisions=function(e){return this.request("files/list_revisions",e,"user","api","rpc")},_.filesLockFileBatch=function(e){return this.request("files/lock_file_batch",e,"user","api","rpc")},_.filesMoveV2=function(e){return this.request("files/move_v2",e,"user","api","rpc")},_.filesMove=function(e){return this.request("files/move",e,"user","api","rpc")},_.filesMoveBatchV2=function(e){return this.request("files/move_batch_v2",e,"user","api","rpc")},_.filesMoveBatch=function(e){return this.request("files/move_batch",e,"user","api","rpc")},_.filesMoveBatchCheckV2=function(e){return this.request("files/move_batch/check_v2",e,"user","api","rpc")},_.filesMoveBatchCheck=function(e){return this.request("files/move_batch/check",e,"user","api","rpc")},_.filesPaperCreate=function(e){return this.request("files/paper/create",e,"user","api","upload")},_.filesPaperUpdate=function(e){return this.request("files/paper/update",e,"user","api","upload")},_.filesPermanentlyDelete=function(e){return this.request("files/permanently_delete",e,"user","api","rpc")},_.filesPropertiesAdd=function(e){return this.request("files/properties/add",e,"user","api","rpc")},_.filesPropertiesOverwrite=function(e){return this.request("files/properties/overwrite",e,"user","api","rpc")},_.filesPropertiesRemove=function(e){return this.request("files/properties/remove",e,"user","api","rpc")},_.filesPropertiesTemplateGet=function(e){return this.request("files/properties/template/get",e,"user","api","rpc")},_.filesPropertiesTemplateList=function(){return this.request("files/properties/template/list",null,"user","api","rpc")},_.filesPropertiesUpdate=function(e){return this.request("files/properties/update",e,"user","api","rpc")},_.filesRestore=function(e){return this.request("files/restore",e,"user","api","rpc")},_.filesSaveUrl=function(e){return this.request("files/save_url",e,"user","api","rpc")},_.filesSaveUrlCheckJobStatus=function(e){return this.request("files/save_url/check_job_status",e,"user","api","rpc")},_.filesSearch=function(e){return this.request("files/search",e,"user","api","rpc")},_.filesSearchV2=function(e){return this.request("files/search_v2",e,"user","api","rpc")},_.filesSearchContinueV2=function(e){return this.request("files/search/continue_v2",e,"user","api","rpc")},_.filesUnlockFileBatch=function(e){return this.request("files/unlock_file_batch",e,"user","api","rpc")},_.filesUpload=function(e){return this.request("files/upload",e,"user","content","upload")},_.filesUploadSessionAppendV2=function(e){return this.request("files/upload_session/append_v2",e,"user","content","upload")},_.filesUploadSessionAppend=function(e){return this.request("files/upload_session/append",e,"user","content","upload")},_.filesUploadSessionFinish=function(e){return this.request("files/upload_session/finish",e,"user","content","upload")},_.filesUploadSessionFinishBatch=function(e){return this.request("files/upload_session/finish_batch",e,"user","api","rpc")},_.filesUploadSessionFinishBatchCheck=function(e){return this.request("files/upload_session/finish_batch/check",e,"user","api","rpc")},_.filesUploadSessionStart=function(e){return this.request("files/upload_session/start",e,"user","content","upload")},_.paperDocsArchive=function(e){return this.request("paper/docs/archive",e,"user","api","rpc")},_.paperDocsCreate=function(e){return this.request("paper/docs/create",e,"user","api","upload")},_.paperDocsDownload=function(e){return this.request("paper/docs/download",e,"user","api","download")},_.paperDocsFolderUsersList=function(e){return this.request("paper/docs/folder_users/list",e,"user","api","rpc")},_.paperDocsFolderUsersListContinue=function(e){return this.request("paper/docs/folder_users/list/continue",e,"user","api","rpc")},_.paperDocsGetFolderInfo=function(e){return this.request("paper/docs/get_folder_info",e,"user","api","rpc")},_.paperDocsList=function(e){return this.request("paper/docs/list",e,"user","api","rpc")},_.paperDocsListContinue=function(e){return this.request("paper/docs/list/continue",e,"user","api","rpc")},_.paperDocsPermanentlyDelete=function(e){return this.request("paper/docs/permanently_delete",e,"user","api","rpc")},_.paperDocsSharingPolicyGet=function(e){return this.request("paper/docs/sharing_policy/get",e,"user","api","rpc")},_.paperDocsSharingPolicySet=function(e){return this.request("paper/docs/sharing_policy/set",e,"user","api","rpc")},_.paperDocsUpdate=function(e){return this.request("paper/docs/update",e,"user","api","upload")},_.paperDocsUsersAdd=function(e){return this.request("paper/docs/users/add",e,"user","api","rpc")},_.paperDocsUsersList=function(e){return this.request("paper/docs/users/list",e,"user","api","rpc")},_.paperDocsUsersListContinue=function(e){return this.request("paper/docs/users/list/continue",e,"user","api","rpc")},_.paperDocsUsersRemove=function(e){return this.request("paper/docs/users/remove",e,"user","api","rpc")},_.paperFoldersCreate=function(e){return this.request("paper/folders/create",e,"user","api","rpc")},_.sharingAddFileMember=function(e){return this.request("sharing/add_file_member",e,"user","api","rpc")},_.sharingAddFolderMember=function(e){return this.request("sharing/add_folder_member",e,"user","api","rpc")},_.sharingCheckJobStatus=function(e){return this.request("sharing/check_job_status",e,"user","api","rpc")},_.sharingCheckRemoveMemberJobStatus=function(e){return this.request("sharing/check_remove_member_job_status",e,"user","api","rpc")},_.sharingCheckShareJobStatus=function(e){return this.request("sharing/check_share_job_status",e,"user","api","rpc")},_.sharingCreateSharedLink=function(e){return this.request("sharing/create_shared_link",e,"user","api","rpc")},_.sharingCreateSharedLinkWithSettings=function(e){return this.request("sharing/create_shared_link_with_settings",e,"user","api","rpc")},_.sharingGetFileMetadata=function(e){return this.request("sharing/get_file_metadata",e,"user","api","rpc")},_.sharingGetFileMetadataBatch=function(e){return this.request("sharing/get_file_metadata/batch",e,"user","api","rpc")},_.sharingGetFolderMetadata=function(e){return this.request("sharing/get_folder_metadata",e,"user","api","rpc")},_.sharingGetSharedLinkFile=function(e){return this.request("sharing/get_shared_link_file",e,"user","content","download")},_.sharingGetSharedLinkMetadata=function(e){return this.request("sharing/get_shared_link_metadata",e,"user","api","rpc")},_.sharingGetSharedLinks=function(e){return this.request("sharing/get_shared_links",e,"user","api","rpc")},_.sharingListFileMembers=function(e){return this.request("sharing/list_file_members",e,"user","api","rpc")},_.sharingListFileMembersBatch=function(e){return this.request("sharing/list_file_members/batch",e,"user","api","rpc")},_.sharingListFileMembersContinue=function(e){return this.request("sharing/list_file_members/continue",e,"user","api","rpc")},_.sharingListFolderMembers=function(e){return this.request("sharing/list_folder_members",e,"user","api","rpc")},_.sharingListFolderMembersContinue=function(e){return this.request("sharing/list_folder_members/continue",e,"user","api","rpc")},_.sharingListFolders=function(e){return this.request("sharing/list_folders",e,"user","api","rpc")},_.sharingListFoldersContinue=function(e){return this.request("sharing/list_folders/continue",e,"user","api","rpc")},_.sharingListMountableFolders=function(e){return this.request("sharing/list_mountable_folders",e,"user","api","rpc")},_.sharingListMountableFoldersContinue=function(e){return this.request("sharing/list_mountable_folders/continue",e,"user","api","rpc")},_.sharingListReceivedFiles=function(e){return this.request("sharing/list_received_files",e,"user","api","rpc")},_.sharingListReceivedFilesContinue=function(e){return this.request("sharing/list_received_files/continue",e,"user","api","rpc")},_.sharingListSharedLinks=function(e){return this.request("sharing/list_shared_links",e,"user","api","rpc")},_.sharingModifySharedLinkSettings=function(e){return this.request("sharing/modify_shared_link_settings",e,"user","api","rpc")},_.sharingMountFolder=function(e){return this.request("sharing/mount_folder",e,"user","api","rpc")},_.sharingRelinquishFileMembership=function(e){return this.request("sharing/relinquish_file_membership",e,"user","api","rpc")},_.sharingRelinquishFolderMembership=function(e){return this.request("sharing/relinquish_folder_membership",e,"user","api","rpc")},_.sharingRemoveFileMember=function(e){return this.request("sharing/remove_file_member",e,"user","api","rpc")},_.sharingRemoveFileMember2=function(e){return this.request("sharing/remove_file_member_2",e,"user","api","rpc")},_.sharingRemoveFolderMember=function(e){return this.request("sharing/remove_folder_member",e,"user","api","rpc")},_.sharingRevokeSharedLink=function(e){return this.request("sharing/revoke_shared_link",e,"user","api","rpc")},_.sharingSetAccessInheritance=function(e){return this.request("sharing/set_access_inheritance",e,"user","api","rpc")},_.sharingShareFolder=function(e){return this.request("sharing/share_folder",e,"user","api","rpc")},_.sharingTransferFolder=function(e){return this.request("sharing/transfer_folder",e,"user","api","rpc")},_.sharingUnmountFolder=function(e){return this.request("sharing/unmount_folder",e,"user","api","rpc")},_.sharingUnshareFile=function(e){return this.request("sharing/unshare_file",e,"user","api","rpc")},_.sharingUnshareFolder=function(e){return this.request("sharing/unshare_folder",e,"user","api","rpc")},_.sharingUpdateFileMember=function(e){return this.request("sharing/update_file_member",e,"user","api","rpc")},_.sharingUpdateFolderMember=function(e){return this.request("sharing/update_folder_member",e,"user","api","rpc")},_.sharingUpdateFolderPolicy=function(e){return this.request("sharing/update_folder_policy",e,"user","api","rpc")},_.teamDevicesListMemberDevices=function(e){return this.request("team/devices/list_member_devices",e,"team","api","rpc")},_.teamDevicesListMembersDevices=function(e){return this.request("team/devices/list_members_devices",e,"team","api","rpc")},_.teamDevicesListTeamDevices=function(e){return this.request("team/devices/list_team_devices",e,"team","api","rpc")},_.teamDevicesRevokeDeviceSession=function(e){return this.request("team/devices/revoke_device_session",e,"team","api","rpc")},_.teamDevicesRevokeDeviceSessionBatch=function(e){return this.request("team/devices/revoke_device_session_batch",e,"team","api","rpc")},_.teamFeaturesGetValues=function(e){return this.request("team/features/get_values",e,"team","api","rpc")},_.teamGetInfo=function(){return this.request("team/get_info",null,"team","api","rpc")},_.teamGroupsCreate=function(e){return this.request("team/groups/create",e,"team","api","rpc")},_.teamGroupsDelete=function(e){return this.request("team/groups/delete",e,"team","api","rpc")},_.teamGroupsGetInfo=function(e){return this.request("team/groups/get_info",e,"team","api","rpc")},_.teamGroupsJobStatusGet=function(e){return this.request("team/groups/job_status/get",e,"team","api","rpc")},_.teamGroupsList=function(e){return this.request("team/groups/list",e,"team","api","rpc")},_.teamGroupsListContinue=function(e){return this.request("team/groups/list/continue",e,"team","api","rpc")},_.teamGroupsMembersAdd=function(e){return this.request("team/groups/members/add",e,"team","api","rpc")},_.teamGroupsMembersList=function(e){return this.request("team/groups/members/list",e,"team","api","rpc")},_.teamGroupsMembersListContinue=function(e){return this.request("team/groups/members/list/continue",e,"team","api","rpc")},_.teamGroupsMembersRemove=function(e){return this.request("team/groups/members/remove",e,"team","api","rpc")},_.teamGroupsMembersSetAccessType=function(e){return this.request("team/groups/members/set_access_type",e,"team","api","rpc")},_.teamGroupsUpdate=function(e){return this.request("team/groups/update",e,"team","api","rpc")},_.teamLegalHoldsCreatePolicy=function(e){return this.request("team/legal_holds/create_policy",e,"team","api","rpc")},_.teamLegalHoldsGetPolicy=function(e){return this.request("team/legal_holds/get_policy",e,"team","api","rpc")},_.teamLegalHoldsListHeldRevisions=function(e){return this.request("team/legal_holds/list_held_revisions",e,"team","api","rpc")},_.teamLegalHoldsListHeldRevisionsContinue=function(e){return this.request("team/legal_holds/list_held_revisions_continue",e,"team","api","rpc")},_.teamLegalHoldsListPolicies=function(e){return this.request("team/legal_holds/list_policies",e,"team","api","rpc")},_.teamLegalHoldsReleasePolicy=function(e){return this.request("team/legal_holds/release_policy",e,"team","api","rpc")},_.teamLegalHoldsUpdatePolicy=function(e){return this.request("team/legal_holds/update_policy",e,"team","api","rpc")},_.teamLinkedAppsListMemberLinkedApps=function(e){return this.request("team/linked_apps/list_member_linked_apps",e,"team","api","rpc")},_.teamLinkedAppsListMembersLinkedApps=function(e){return this.request("team/linked_apps/list_members_linked_apps",e,"team","api","rpc")},_.teamLinkedAppsListTeamLinkedApps=function(e){return this.request("team/linked_apps/list_team_linked_apps",e,"team","api","rpc")},_.teamLinkedAppsRevokeLinkedApp=function(e){return this.request("team/linked_apps/revoke_linked_app",e,"team","api","rpc")},_.teamLinkedAppsRevokeLinkedAppBatch=function(e){return this.request("team/linked_apps/revoke_linked_app_batch",e,"team","api","rpc")},_.teamMemberSpaceLimitsExcludedUsersAdd=function(e){return this.request("team/member_space_limits/excluded_users/add",e,"team","api","rpc")},_.teamMemberSpaceLimitsExcludedUsersList=function(e){return this.request("team/member_space_limits/excluded_users/list",e,"team","api","rpc")},_.teamMemberSpaceLimitsExcludedUsersListContinue=function(e){return this.request("team/member_space_limits/excluded_users/list/continue",e,"team","api","rpc")},_.teamMemberSpaceLimitsExcludedUsersRemove=function(e){return this.request("team/member_space_limits/excluded_users/remove",e,"team","api","rpc")},_.teamMemberSpaceLimitsGetCustomQuota=function(e){return this.request("team/member_space_limits/get_custom_quota",e,"team","api","rpc")},_.teamMemberSpaceLimitsRemoveCustomQuota=function(e){return this.request("team/member_space_limits/remove_custom_quota",e,"team","api","rpc")},_.teamMemberSpaceLimitsSetCustomQuota=function(e){return this.request("team/member_space_limits/set_custom_quota",e,"team","api","rpc")},_.teamMembersAddV2=function(e){return this.request("team/members/add_v2",e,"team","api","rpc")},_.teamMembersAdd=function(e){return this.request("team/members/add",e,"team","api","rpc")},_.teamMembersAddJobStatusGetV2=function(e){return this.request("team/members/add/job_status/get_v2",e,"team","api","rpc")},_.teamMembersAddJobStatusGet=function(e){return this.request("team/members/add/job_status/get",e,"team","api","rpc")},_.teamMembersDeleteProfilePhotoV2=function(e){return this.request("team/members/delete_profile_photo_v2",e,"team","api","rpc")},_.teamMembersDeleteProfilePhoto=function(e){return this.request("team/members/delete_profile_photo",e,"team","api","rpc")},_.teamMembersGetAvailableTeamMemberRoles=function(){return this.request("team/members/get_available_team_member_roles",null,"team","api","rpc")},_.teamMembersGetInfoV2=function(e){return this.request("team/members/get_info_v2",e,"team","api","rpc")},_.teamMembersGetInfo=function(e){return this.request("team/members/get_info",e,"team","api","rpc")},_.teamMembersListV2=function(e){return this.request("team/members/list_v2",e,"team","api","rpc")},_.teamMembersList=function(e){return this.request("team/members/list",e,"team","api","rpc")},_.teamMembersListContinueV2=function(e){return this.request("team/members/list/continue_v2",e,"team","api","rpc")},_.teamMembersListContinue=function(e){return this.request("team/members/list/continue",e,"team","api","rpc")},_.teamMembersMoveFormerMemberFiles=function(e){return this.request("team/members/move_former_member_files",e,"team","api","rpc")},_.teamMembersMoveFormerMemberFilesJobStatusCheck=function(e){return this.request("team/members/move_former_member_files/job_status/check",e,"team","api","rpc")},_.teamMembersRecover=function(e){return this.request("team/members/recover",e,"team","api","rpc")},_.teamMembersRemove=function(e){return this.request("team/members/remove",e,"team","api","rpc")},_.teamMembersRemoveJobStatusGet=function(e){return this.request("team/members/remove/job_status/get",e,"team","api","rpc")},_.teamMembersSecondaryEmailsAdd=function(e){return this.request("team/members/secondary_emails/add",e,"team","api","rpc")},_.teamMembersSecondaryEmailsDelete=function(e){return this.request("team/members/secondary_emails/delete",e,"team","api","rpc")},_.teamMembersSecondaryEmailsResendVerificationEmails=function(e){return this.request("team/members/secondary_emails/resend_verification_emails",e,"team","api","rpc")},_.teamMembersSendWelcomeEmail=function(e){return this.request("team/members/send_welcome_email",e,"team","api","rpc")},_.teamMembersSetAdminPermissionsV2=function(e){return this.request("team/members/set_admin_permissions_v2",e,"team","api","rpc")},_.teamMembersSetAdminPermissions=function(e){return this.request("team/members/set_admin_permissions",e,"team","api","rpc")},_.teamMembersSetProfileV2=function(e){return this.request("team/members/set_profile_v2",e,"team","api","rpc")},_.teamMembersSetProfile=function(e){return this.request("team/members/set_profile",e,"team","api","rpc")},_.teamMembersSetProfilePhotoV2=function(e){return this.request("team/members/set_profile_photo_v2",e,"team","api","rpc")},_.teamMembersSetProfilePhoto=function(e){return this.request("team/members/set_profile_photo",e,"team","api","rpc")},_.teamMembersSuspend=function(e){return this.request("team/members/suspend",e,"team","api","rpc")},_.teamMembersUnsuspend=function(e){return this.request("team/members/unsuspend",e,"team","api","rpc")},_.teamNamespacesList=function(e){return this.request("team/namespaces/list",e,"team","api","rpc")},_.teamNamespacesListContinue=function(e){return this.request("team/namespaces/list/continue",e,"team","api","rpc")},_.teamPropertiesTemplateAdd=function(e){return this.request("team/properties/template/add",e,"team","api","rpc")},_.teamPropertiesTemplateGet=function(e){return this.request("team/properties/template/get",e,"team","api","rpc")},_.teamPropertiesTemplateList=function(){return this.request("team/properties/template/list",null,"team","api","rpc")},_.teamPropertiesTemplateUpdate=function(e){return this.request("team/properties/template/update",e,"team","api","rpc")},_.teamReportsGetActivity=function(e){return this.request("team/reports/get_activity",e,"team","api","rpc")},_.teamReportsGetDevices=function(e){return this.request("team/reports/get_devices",e,"team","api","rpc")},_.teamReportsGetMembership=function(e){return this.request("team/reports/get_membership",e,"team","api","rpc")},_.teamReportsGetStorage=function(e){return this.request("team/reports/get_storage",e,"team","api","rpc")},_.teamTeamFolderActivate=function(e){return this.request("team/team_folder/activate",e,"team","api","rpc")},_.teamTeamFolderArchive=function(e){return this.request("team/team_folder/archive",e,"team","api","rpc")},_.teamTeamFolderArchiveCheck=function(e){return this.request("team/team_folder/archive/check",e,"team","api","rpc")},_.teamTeamFolderCreate=function(e){return this.request("team/team_folder/create",e,"team","api","rpc")},_.teamTeamFolderGetInfo=function(e){return this.request("team/team_folder/get_info",e,"team","api","rpc")},_.teamTeamFolderList=function(e){return this.request("team/team_folder/list",e,"team","api","rpc")},_.teamTeamFolderListContinue=function(e){return this.request("team/team_folder/list/continue",e,"team","api","rpc")},_.teamTeamFolderPermanentlyDelete=function(e){return this.request("team/team_folder/permanently_delete",e,"team","api","rpc")},_.teamTeamFolderRename=function(e){return this.request("team/team_folder/rename",e,"team","api","rpc")},_.teamTeamFolderUpdateSyncSettings=function(e){return this.request("team/team_folder/update_sync_settings",e,"team","api","rpc")},_.teamTokenGetAuthenticatedAdmin=function(){return this.request("team/token/get_authenticated_admin",null,"team","api","rpc")},_.teamLogGetEvents=function(e){return this.request("team_log/get_events",e,"team","api","rpc")},_.teamLogGetEventsContinue=function(e){return this.request("team_log/get_events/continue",e,"team","api","rpc")},_.usersFeaturesGetValues=function(e){return this.request("users/features/get_values",e,"user","api","rpc")},_.usersGetAccount=function(e){return this.request("users/get_account",e,"user","api","rpc")},_.usersGetAccountBatch=function(e){return this.request("users/get_account_batch",e,"user","api","rpc")},_.usersGetCurrentAccount=function(){return this.request("users/get_current_account",null,"user","api","rpc")},_.usersGetSpaceUsage=function(){return this.request("users/get_space_usage",null,"user","api","rpc")};var g=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:h,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:".";return t!==h&&void 0!==d[e]&&(e=d[e],r="-"),"https://".concat(e).concat(r).concat(t,"/2/")},b=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:m;return e!==m&&(e="meta-".concat(e)),"https://".concat(e,"/oauth2/authorize")},v=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:".",r="api";return e!==h&&(r=d[r],t="-"),"https://".concat(r).concat(t).concat(e,"/oauth2/token")};function k(e){return JSON.stringify(e).replace(/[\u007f-\uffff]/g,q)}function y(e){return new Date(Date.now()+1e3*e)}function A(){return"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope||"undefined"==typeof module||"undefined"!=typeof window}function C(){return"undefined"!=typeof window}function S(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var T,L,w,M=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&n(e,t)}(o,e);var r,s,a=(r=o,s=u(),function(){var e,t=i(r);if(s){var n=i(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return c(this,e)});function o(e,r,s){var i;return t(this,o),(i=a.call(this,"Response failed with a ".concat(e," code"))).name="DropboxResponseError",i.status=e,i.headers=r,i.error=s,i}return o}(o(Error)),P=function e(r,s,i){t(this,e),this.status=r,this.headers=s,this.result=i};function R(e){return e.text().then((function(t){var r;try{r=JSON.parse(t)}catch(e){r=t}throw new M(e.status,e.headers,r)}))}function F(e){return e.ok?e.text().then((function(t){var r;try{r=JSON.parse(t)}catch(e){r=t}return new P(e.status,e.headers,r)})):R(e)}T=C()?window.fetch.bind(window):require("node-fetch"),L=C()?window.crypto||window.msCrypto:require("crypto"),w="undefined"==typeof TextEncoder?require("util").TextEncoder:TextEncoder;var D,G=["legacy","offline","online"],U=["code","token"],x=["none","user","team"],E=function(){function e(r){t(this,e),this.fetch=(r=r||{}).fetch||T,this.accessToken=r.accessToken,this.accessTokenExpiresAt=r.accessTokenExpiresAt,this.refreshToken=r.refreshToken,this.clientId=r.clientId,this.clientSecret=r.clientSecret,this.domain=r.domain,this.domainDelimiter=r.domainDelimiter}return s(e,[{key:"setAccessToken",value:function(e){this.accessToken=e}},{key:"getAccessToken",value:function(){return this.accessToken}},{key:"setClientId",value:function(e){this.clientId=e}},{key:"getClientId",value:function(){return this.clientId}},{key:"setClientSecret",value:function(e){this.clientSecret=e}},{key:"getClientSecret",value:function(){return this.clientSecret}},{key:"getRefreshToken",value:function(){return this.refreshToken}},{key:"setRefreshToken",value:function(e){this.refreshToken=e}},{key:"getAccessTokenExpiresAt",value:function(){return this.accessTokenExpiresAt}},{key:"setAccessTokenExpiresAt",value:function(e){this.accessTokenExpiresAt=e}},{key:"setCodeVerifier",value:function(e){this.codeVerifier=e}},{key:"getCodeVerifier",value:function(){return this.codeVerifier}},{key:"generateCodeChallenge",value:function(){var e,t=this,r=(new w).encode(this.codeVerifier);if(C())return L.subtle.digest("SHA-256",r).then((function(r){var s=btoa(String.fromCharCode.apply(null,new Uint8Array(r)));e=S(s).substr(0,128),t.codeChallenge=e}));var s=L.createHash("sha256").update(r).digest();return e=S(s),this.codeChallenge=e,Promise.resolve()}},{key:"generatePKCECodes",value:function(){var e;if(C()){var t=new Uint8Array(128),r=L.getRandomValues(t);e=S(btoa(r)).substr(0,128)}else{e=S(L.randomBytes(128)).substr(0,128)}return this.codeVerifier=e,this.generateCodeChallenge()}},{key:"getAuthenticationUrl",value:function(e,t){var r,s=this,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"token",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,u=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"none",o=arguments.length>6&&void 0!==arguments[6]&&arguments[6],c=this.getClientId(),p=b(this.domain);if(!c)throw Error("A client id is required. You can set the client id using .setClientId().");if("code"!==i&&!e)throw Error("A redirect uri is required.");if(!U.includes(i))throw Error("Authorization type must be code or token");if(n&&!G.includes(n))throw Error("Token Access Type must be legacy, offline, or online");if(u&&!(u instanceof Array))throw Error("Scope must be an array of strings");if(!x.includes(a))throw Error("includeGrantedScopes must be none, user, or team");return r="code"===i?"".concat(p,"?response_type=code&client_id=").concat(c):"".concat(p,"?response_type=token&client_id=").concat(c),e&&(r+="&redirect_uri=".concat(e)),t&&(r+="&state=".concat(t)),n&&(r+="&token_access_type=".concat(n)),u&&(r+="&scope=".concat(u.join(" "))),"none"!==a&&(r+="&include_granted_scopes=".concat(a)),o?this.generatePKCECodes().then((function(){return r+="&code_challenge_method=S256",r+="&code_challenge=".concat(s.codeChallenge)})):Promise.resolve(r)}},{key:"getAccessTokenFromCode",value:function(e,t){var r=this.getClientId(),s=this.getClientSecret();if(!r)throw Error("A client id is required. You can set the client id using .setClientId().");var i=v(this.domain,this.domainDelimiter);if(i+="?grant_type=authorization_code",i+="&code=".concat(t),i+="&client_id=".concat(r),s)i+="&client_secret=".concat(s);else{if(!this.codeVerifier)throw Error("You must use PKCE when generating the authorization URL to not include a client secret");i+="&code_verifier=".concat(this.codeVerifier)}e&&(i+="&redirect_uri=".concat(e));return this.fetch(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"}}).then((function(e){return F(e)}))}},{key:"checkAndRefreshAccessToken",value:function(){var e=this.getRefreshToken()&&this.getClientId(),t=!this.getAccessTokenExpiresAt()||new Date(Date.now()+3e5)>=this.getAccessTokenExpiresAt(),r=!this.getAccessToken();return(t||r)&&e?this.refreshAccessToken():Promise.resolve()}},{key:"refreshAccessToken",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=v(this.domain,this.domainDelimiter),s=this.getClientId(),i=this.getClientSecret();if(!s)throw Error("A client id is required. You can set the client id using .setClientId().");if(t&&!(t instanceof Array))throw Error("Scope must be an array of strings");var n={"Content-Type":"application/json"};r+="?grant_type=refresh_token&refresh_token=".concat(this.getRefreshToken()),r+="&client_id=".concat(s),i&&(r+="&client_secret=".concat(i)),t&&(r+="&scope=".concat(t.join(" ")));var u={method:"POST"};return u.headers=n,this.fetch(r,u).then((function(e){return F(e)})).then((function(t){e.setAccessToken(t.result.access_token),e.setAccessTokenExpiresAt(y(t.result.expires_in))}))}}]),e}();D="undefined"!=typeof window?window.fetch.bind(window):require("node-fetch");var V="undefined"==typeof btoa?function(e){return Buffer.from(e).toString("base64")}:btoa;e.Dropbox=function(){function e(r){t(this,e),this.auth=(r=r||{}).auth?r.auth:new E(r),this.fetch=r.fetch||D,this.selectUser=r.selectUser,this.selectAdmin=r.selectAdmin,this.pathRoot=r.pathRoot,this.domain=r.domain,this.domainDelimiter=r.domainDelimiter,Object.assign(this,_)}return s(e,[{key:"request",value:function(e,t,r,s,i){if(r.split(",").length>1){var n=r.replace(" ","").split(",");n.includes(l)&&this.auth.getAccessToken()?r=l:n.includes(f)&&this.auth.getAccessToken()?r=f:n.includes(p)&&(r=p)}switch(i){case"rpc":return this.rpcRequest(e,t,r,s);case"download":return this.downloadRequest(e,t,r,s);case"upload":return this.uploadRequest(e,t,r,s);default:throw Error("Invalid request style: ".concat(i))}}},{key:"rpcRequest",value:function(e,t,r,s){var i=this;return this.auth.checkAndRefreshAccessToken().then((function(){var e,s={method:"POST",body:t?JSON.stringify(t):null,headers:{}};switch(t&&(s.headers["Content-Type"]="application/json"),r){case p:if(!i.auth.clientId||!i.auth.clientSecret)throw Error("A client id and secret is required for this function");e=V("".concat(i.auth.clientId,":").concat(i.auth.clientSecret)),s.headers.Authorization="Basic ".concat(e);break;case f:case l:s.headers.Authorization="Bearer ".concat(i.auth.getAccessToken());break;case"noauth":break;default:throw Error("Unhandled auth type: ".concat(r))}return i.setCommonHeaders(s),s})).then((function(t){return i.fetch(g(s,i.domain,i.domainDelimiter)+e,t)})).then((function(e){return F(e)}))}},{key:"downloadRequest",value:function(e,t,r,s){var i=this;return this.auth.checkAndRefreshAccessToken().then((function(){if(r!==l)throw Error("Unexpected auth type: ".concat(r));var e={method:"POST",headers:{Authorization:"Bearer ".concat(i.auth.getAccessToken()),"Dropbox-API-Arg":k(t)}};return i.setCommonHeaders(e),e})).then((function(t){return i.fetch(g(s,i.domain,i.domainDelimiter)+e,t)})).then((function(e){return function(e){return e.ok?new Promise((function(t){A()?e.blob().then((function(e){return t(e)})):e.buffer().then((function(e){return t(e)}))})).then((function(t){var r=JSON.parse(e.headers.get("dropbox-api-result"));return A()?r.fileBlob=t:r.fileBinary=t,new P(e.status,e.headers,r)})):R(e)}(e)}))}},{key:"uploadRequest",value:function(e,t,r,s){var i=this;return this.auth.checkAndRefreshAccessToken().then((function(){if(r!==l)throw Error("Unexpected auth type: ".concat(r));var e=t.contents;delete t.contents;var s={body:e,method:"POST",headers:{Authorization:"Bearer ".concat(i.auth.getAccessToken()),"Content-Type":"application/octet-stream","Dropbox-API-Arg":k(t)}};return i.setCommonHeaders(s),s})).then((function(t){return i.fetch(g(s,i.domain,i.domainDelimiter)+e,t)})).then((function(e){return F(e)}))}},{key:"setCommonHeaders",value:function(e){this.selectUser&&(e.headers["Dropbox-API-Select-User"]=this.selectUser),this.selectAdmin&&(e.headers["Dropbox-API-Select-Admin"]=this.selectAdmin),this.pathRoot&&(e.headers["Dropbox-API-Path-Root"]=this.pathRoot)}}]),e}(),e.DropboxAuth=E,e.DropboxResponse=P,e.DropboxResponseError=M,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/procedural/src/viewer/libs/indexedDB.js b/procedural/src/viewer/libs/indexedDB.js deleted file mode 100644 index 37e89c13..00000000 --- a/procedural/src/viewer/libs/indexedDB.js +++ /dev/null @@ -1,65 +0,0 @@ -let db; - -const DATABASE_NAME = "d2"; -const STORE_NAME = "s"; - -const openDatabase = () => { - return new Promise((resolve, reject) => { - if (db) resolve(); - - if (!window.indexedDB) return reject("IndexedDB is not supported"); - const request = window.indexedDB.open(DATABASE_NAME); - - request.onsuccess = event => { - db = event.target.result; - resolve(); - }; - - request.onerror = event => { - console.error("IndexedDB request error"); - reject(); - }; - - request.onupgradeneeded = event => { - db = event.target.result; - const objectStore = db.createObjectStore(STORE_NAME, {keyPath: "key"}); - objectStore.transaction.oncomplete = () => { - db = event.target.result; - }; - }; - }); -}; - -const ldb = { - get: key => { - return new Promise((resolve, reject) => { - openDatabase().then(() => { - const hasStore = Array.from(db.objectStoreNames).includes(STORE_NAME); - if (!hasStore) return reject("IndexedDB: no store found"); - - const transaction = db.transaction(STORE_NAME, "readonly"); - const objectStore = transaction.objectStore(STORE_NAME); - const getRequest = objectStore.get(key); - - getRequest.onsuccess = event => { - const result = event.target.result?.value || null; - resolve(result); - }; - }); - }); - }, - - set: (keyName, value) => { - return new Promise(resolve => { - openDatabase().then(() => { - const transaction = db.transaction(STORE_NAME, "readwrite"); - const objectStore = transaction.objectStore([STORE_NAME]); - const putRequest = objectStore.put({key: keyName, value}); - - putRequest.onsuccess = () => { - resolve(); - }; - }); - }); - } -}; diff --git a/procedural/src/viewer/libs/jquery-3.1.1.min.js b/procedural/src/viewer/libs/jquery-3.1.1.min.js deleted file mode 100644 index 4c5be4c0..00000000 --- a/procedural/src/viewer/libs/jquery-3.1.1.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), -a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), -void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("