From 9ab1319126ffd6f77b6bed478d6cc1ada01a89c2 Mon Sep 17 00:00:00 2001 From: Aaron Tidwell Date: Sun, 21 Dec 2025 17:00:54 -0500 Subject: [PATCH] Map Generator Go Code Documentation (#2656) Resolves #2602 ## Description: tldr: `npm run docs:map-generator` Adds documentation to the `map-generator` go code. This has no functional changes, other than the renaming of the package. I used the github url, though this can be set to anything as long as it contains a `.` so that the docs parse it correctly. Go doc best practices seem a little verbose and terse, but attempted to comply Future Facing (to get these docs viewable without running locally): - Wait until the -http issue is sorted, then these are easy to statically host alongside builds - Could use the legacy `godoc` - Could do formatting after outputting the `txt` output ## Change List: - Add documentation to all types/fns in map-generator go code - Ensure this outputs correctly with `go doc` - Add `docs:map-generator` command to package.json. This runs `go doc` in `map-generator` w/ appropriate flags to generate full documentation. (see notes in readme) - rename `map-generator` module to work around pkgsite assuming all packages without a . are stdlib (this makes `-http` work at all) - Add new sections to README and update existing sections - Add additional references to locations in the primary code base where things can be found - Update documentation in the ts theme files to add output color mappings - this ensures that everything needed to trace the input file -> in game rendered asset is fully documented. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tidwell --- map-generator/README.md | 82 ++++++++++++--- map-generator/go.mod | 3 +- map-generator/main.go | 17 +++ map-generator/map_generator.go | 121 ++++++++++++++++++++-- package.json | 1 + src/core/configuration/PastelTheme.ts | 8 ++ src/core/configuration/PastelThemeDark.ts | 9 ++ src/core/game/GameMap.ts | 2 + 8 files changed, 217 insertions(+), 26 deletions(-) diff --git a/map-generator/README.md b/map-generator/README.md index 2dea2fa07..6afeb55f5 100644 --- a/map-generator/README.md +++ b/map-generator/README.md @@ -1,19 +1,25 @@ # MapGenerator -This is a tool to generate map files for OpenFront. +This is a go-based tool to generate map files for OpenFront. + +The map generator reads PNG files and converts pixels into terrain based primarily on the **Blue** channel. +Because only blue values are used, grayscale and other formats are fully supported. Many maps in `assets/maps/` are grayscale. + +Additional Guides, Tutorials, Scripts, Resources, and Third Party Unofficial Applications can be found on +the [Official Openfront Wiki](https://openfront.wiki/Map_Making) ## Installation -1. Install go https://go.dev/doc/install +1. Install go 2. Install dependencies: `go mod download` 3. Run the generator: `go run .` ## Creating a new map 1. Create a new folder in `assets/maps/` -2. Create image.png -3. Create info.json with name and countries -4. Add the map name in main.go +2. Create `assets/maps//image.png` +3. Create `assets/maps//info.json` with name and countries +4. Add the map name in `main.go` The `` in `{Name: ""},` should match the `` folder at `assets/maps/` 5. Run the generator: `go run .` 6. Find the output folder at `../resources/maps/` @@ -29,16 +35,27 @@ To process a subset of maps, pass a comma-separated list: ## Create image.png +The map-generator will process your input file at `assets/maps//image.png` to generate the map +thumbnail and binary files. To create this `png` input file, you can crop the world map: + 1. [Download world map (warning very large file)](https://drive.google.com/file/d/1W2oMPj1L5zWRyPhh8LfmnY3_kve-FBR2/view?usp=sharing) 2. Crop the file (recommend Gimp) -- We recommend roughly 2 million pixels for performance reasons -- Do not go over 4 million pixels. +If you are doing work in image editing software or using automated tools, `./map_generator.go` contains documentation for: + +- `Pixel` -> `Terrain Type & Magnitude` mapping in `GenerateMap` +- `Terrain Type` -> `Thumbnail Color` mapping in `getThumbnailColor` + +In-Game, terrain is rendered using themes. The color of a tile is determined dynamically based on +its **Terrain Type** and **Magnitude**. Theme Files: + +- `../src/core/configuration/PastelTheme.ts` (Light) +- `../src/core/configuration/PastelThemeDark.ts` (Dark). ## Create info.json -- Look at existing info.json for structure -- [Use country codes found here](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) +The map-generator will process your input file at `assets/maps//info.json` to determine the +position of Nations, their starting coordinates, and any flags. Example: @@ -55,18 +72,43 @@ Example: } ``` +`coordinates` is x/y position of the nation spawn on the map. Origin is at top left, with x extending right and y extending down + +`name` is a `CamelCaseName` of your map. It is used to enable the map in-game. + +`flag` is the code for a country + +- The full list of supported codes can be seen in `../src/client/data/countries.json` - all ISO_3166 codes are supported, with several additions. + +- For quick reference, [Use country codes found here](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) + +## Update CREDITS.md + +Add License & Attribution information to `../CREDITS.md`. If you are unsure if +a map's license can be used, open an issue or ask in Discord before beginning work. + +## Adding Flags + +Flags can be added to `../resources/flags/.svg` + +The country will need to be added to `../src/client/data/countries.json` + ## To Enable In-Game -- Add a translation for the map name to `resources/lang/en.json` -- Add the MapDescription `src/client/components/Maps.ts` -- Add the numPlayersConfig `src/core/configuration/DefaultConfig.ts` -- Add the GameMapType `src/core/game/Game.ts` -- To add to the map playlist, modify `src/server/MapPlaylist.ts` +Using the `name` from your json: + +- Add to the MapDescription `../src/client/components/Maps.ts` +- Add to the numPlayersConfig `../src/core/configuration/DefaultConfig.ts` +- Add to the mapCategories `../src/core/game/Game.ts` +- Add to the map playlist `../src/server/MapPlaylist.ts` +- Add to the `map` translation object in `../resources/lang/en.json` ## Notes +- Maps should be between 2 - 3 million pixels square (area) - Islands smaller than 30 tiles (pixels) are automatically removed by the script. - Bodies of water smaller than 200 tiles (pixels) are also removed. +- The map generator normalizes dimensions to multiples of 4. Any pixels beyond `Width - (Width % 4)` or `Height - (Height % 4)` are cropped. ## 🛠️ Development Tools @@ -75,3 +117,15 @@ Example: ```bash go fmt . ``` + +- **Output Map Generator Documentation**: + + ```bash + go doc -cmd -u -all + ``` + + The map-generator is a cli tool, to get any visibility, we pass `-cmd`. It also + does not expose any API, so we use `-u` and `-all` to show all documentation for + unexposed values. + + _Known Bug_ Using `-http` does not respect the other flags and only renders the README diff --git a/map-generator/go.mod b/map-generator/go.mod index 2311e4032..87a124a97 100644 --- a/map-generator/go.mod +++ b/map-generator/go.mod @@ -1,5 +1,4 @@ -module map-generator - +module github.com/openfrontio/OpenFrontIO/map-generator go 1.24.4 require github.com/chai2010/webp v1.4.0 diff --git a/map-generator/main.go b/map-generator/main.go index 611c0226e..7fdfa4e54 100644 --- a/map-generator/main.go +++ b/map-generator/main.go @@ -11,8 +11,13 @@ import ( "sync" ) +// mapsFlag holds the comma-separated list of map names passed via the --maps command-line argument. var mapsFlag string +// maps defines the registry of available maps to be processed. +// Each entry contains the folder name and a flag indicating if it's a test map. +// +// New maps need to be added here in order to allow the map-generator to process them. var maps = []struct { Name string IsTest bool @@ -61,6 +66,8 @@ var maps = []struct { {Name: "giantworldmap", IsTest: true}, } +// outputMapDir returns the absolute path to the directory where generated map files should be written. +// It distinguishes between test and production output locations. func outputMapDir(isTest bool) (string, error) { cwd, err := os.Getwd() if err != nil { @@ -72,6 +79,8 @@ func outputMapDir(isTest bool) (string, error) { return filepath.Join(cwd, "..", "resources", "maps"), nil } +// inputMapDir returns the absolute path to the directory containing source map assets. +// It distinguishes between test and production asset locations. func inputMapDir(isTest bool) (string, error) { cwd, err := os.Getwd() if err != nil { @@ -84,6 +93,8 @@ func inputMapDir(isTest bool) (string, error) { } } +// processMap handles the end-to-end generation for a single map. +// It reads the source image and JSON, generates the terrain data, and writes the binary outputs and updated manifest. func processMap(name string, isTest bool) error { outputMapBaseDir, err := outputMapDir(isTest) if err != nil { @@ -169,6 +180,8 @@ func processMap(name string, isTest bool) error { return nil } +// parseMapsFlag validates and parses the --maps command-line argument. +// It returns a set of selected map names or nil if no flag was provided (implying all maps). func parseMapsFlag() (map[string]bool, error) { if mapsFlag == "" { return nil, nil @@ -189,6 +202,8 @@ func parseMapsFlag() (map[string]bool, error) { return selected, nil } +// loadTerrainMaps manages the concurrent generation of all selected maps. +// It spins up goroutines for each map and aggregates any errors. func loadTerrainMaps() error { selectedMaps, err := parseMapsFlag() if err != nil { @@ -226,6 +241,8 @@ func loadTerrainMaps() error { return nil } +// main is the entry point for the map generator tool. +// It parses flags and triggers the map generation process. func main() { flag.StringVar(&mapsFlag, "maps", "", "optional comma-separated list of maps to process. ex: --maps=world,eastasia,big_plains") flag.Parse() diff --git a/map-generator/map_generator.go b/map-generator/map_generator.go index 0e0ad1f48..f27ea0fda 100644 --- a/map-generator/map_generator.go +++ b/map-generator/map_generator.go @@ -12,28 +12,35 @@ import ( "github.com/chai2010/webp" ) +// The smallest a body of land or lake can be, all smaller are removed const ( minIslandSize = 30 minLakeSize = 200 ) +// Holds raw RGBA image data for the thumbnail type ThumbData struct { Data []byte Width int Height int } +// XY coord, origin top left, x extending right, y extends down type Coord struct { X, Y int } +// TerrainType represents the classification of a map tile (e.g., Land or Water). type TerrainType int +// Enumeration of possible TerrainType values. const ( Land TerrainType = iota Water ) +// Terrain represents the properties of a single map tile. +// Magnitude represents elevation for Land (0-30) or distance to land for Water. type Terrain struct { Type TerrainType Shoreline bool @@ -41,6 +48,7 @@ type Terrain struct { Ocean bool } +// MapResult is the output format from the GenerateMap workflow type MapResult struct { Thumbnail []byte Map MapInfo @@ -48,19 +56,44 @@ type MapResult struct { Map16x MapInfo } +// MapInfo contains the serialized map data and metadata for a specific scale. type MapInfo struct { - Data []byte + Data []byte // packed map data Width int Height int NumLandTiles int } +// GeneratorArgs defines the input parameters for the map generation process. type GeneratorArgs struct { Name string ImageBuffer []byte RemoveSmall bool } +// GenerateMap is the main map-generator workflow. +// - Maps each pixel to a Terrain type based on its blue value +// - Removes small islands and lakes +// - Creates a WebP thumbnail +// - Packs the map data into binary format for full scale, 1/4 tile count (half dimensions), and 1/16 tile count (quarter dimensions) +// +// Red/green pixel values have no impact, only blue values are used +// For Land tiles, "Magnitude" is determined by `(Blue - 140) / 2“. +// For Water tiles, "Magnitude" is calculated during generation as the distance to the nearest land. +// +// Pixel -> Terrain & Magnitude mapping +// | Input Condition | Terrain Type | Magnitude | Notes | +// | :----------------- | :-------------- | :----------------- | :------------------------------- | +// | **Alpha < 20** | Water | Distance to Land\* | Transparent pixels become water. | +// | **Blue = 106** | Water | Distance to Land\* | Specific key color for water. | +// | **Blue < 140** | Land (Plains) | 0 | Clamped to minimum magnitude. | +// | **Blue 140 - 158** | Land (Plains) | 0 - 9 | | +// | **Blue 159 - 178** | Land (Highland) | 10 - 19 | | +// | **Blue 179 - 200** | Land (Mountain) | 20 - 30 | | +// | **Blue > 200** | Land (Mountain) | 30 | Clamped to maximum magnitude. | +// +// Misc Notes +// - It normalizes map width/height to multiples of 4 for the mini map downscaling. func GenerateMap(args GeneratorArgs) (MapResult, error) { img, err := png.Decode(bytes.NewReader(args.ImageBuffer)) if err != nil { @@ -150,6 +183,7 @@ func GenerateMap(args GeneratorArgs) (MapResult, error) { }, nil } +// convertToWebP encodes raw RGBA thumbnail data into WebP format. func convertToWebP(thumb ThumbData) ([]byte, error) { // Create RGBA image from raw data img := image.NewRGBA(image.Rect(0, 0, thumb.Width, thumb.Height)) @@ -171,6 +205,10 @@ func convertToWebP(thumb ThumbData) ([]byte, error) { return webpData, nil } +// createMiniMap downscales the terrain grid by half. +// It maps 2x2 blocks of input tiles to a single output tile. +// The logic prioritizes Water: if any of the 4 source tiles is Water, +// the resulting mini-map tile becomes Water. func createMiniMap(tm [][]Terrain) [][]Terrain { width := len(tm) height := len(tm[0]) @@ -200,6 +238,10 @@ func createMiniMap(tm [][]Terrain) [][]Terrain { return miniMap } +// processShore identifies shoreline tiles by checking adjacency. +// It marks Land tiles as shoreline if they neighbor Water, and Water tiles as +// shoreline if they neighbor Land. +// Returns a list of coordinates for all shoreline Water tiles found. func processShore(terrain [][]Terrain) []Coord { log.Println("Identifying shorelines") var shorelineWaters []Coord @@ -235,6 +277,9 @@ func processShore(terrain [][]Terrain) []Coord { return shorelineWaters } +// processDistToLand calculates the distance of water tiles from the nearest land. +// It uses a Breadth-First Search (BFS) starting from the shoreline water tiles. +// The distance is stored in the Magnitude field of the Water tiles. func processDistToLand(shorelineWaters []Coord, terrain [][]Terrain) { log.Println("Setting Water tiles magnitude = Manhattan distance from nearest land") @@ -280,6 +325,7 @@ func processDistToLand(shorelineWaters []Coord, terrain [][]Terrain) { } } +// getNeighbors returns a list of Terrain tiles adjacent to the specified coordinates. func getNeighbors(x, y int, terrain [][]Terrain) []Terrain { coords := getNeighborCoords(x, y, terrain) neighbors := make([]Terrain, len(coords)) @@ -289,6 +335,8 @@ func getNeighbors(x, y int, terrain [][]Terrain) []Terrain { return neighbors } +// getNeighborCoords returns a list of valid adjacent coordinates (up, down, left, right). +// It ensures that the returned coordinates are within the bounds of the terrain grid. func getNeighborCoords(x, y int, terrain [][]Terrain) []Coord { width := len(terrain) height := len(terrain[0]) @@ -310,6 +358,10 @@ func getNeighborCoords(x, y int, terrain [][]Terrain) []Coord { return coords } +// processWater identifies and processes bodies of water in the terrain. +// It finds all connected water bodies and marks the largest one as Ocean. +// If removeSmall is true, lakes smaller than minLakeSize are converted to Land. +// Finally, it triggers shoreline identification and distance-to-land calculations. func processWater(terrain [][]Terrain, removeSmall bool) { log.Println("Processing water bodies") visited := make(map[string]bool) @@ -382,6 +434,9 @@ func processWater(terrain [][]Terrain, removeSmall bool) { } } +// getArea performs a Breadth-First Search (BFS) to find a contiguous area of tiles +// sharing the same TerrainType as the passed x,y coordinates. +// The visited map is updated to prevent reprocessing tiles. func getArea(x, y int, terrain [][]Terrain, visited map[string]bool) []Coord { targetType := terrain[x][y].Type var area []Coord @@ -408,6 +463,8 @@ func getArea(x, y int, terrain [][]Terrain, visited map[string]bool) []Coord { return area } +// removeSmallIslands identifies and removes small land masses from the terrain. +// If removeSmall is true, any removed bodies are converted to Water. func removeSmallIslands(terrain [][]Terrain, removeSmall bool) { if !removeSmall { return @@ -456,6 +513,14 @@ func removeSmallIslands(terrain [][]Terrain, removeSmall bool) { smallIslands, minIslandSize) } +// packTerrain serializes the terrain grid into a byte slice. +// Each byte represents a single tile with bit flags: +// - Bit 7: Land (1) / Water (0) +// - Bit 6: Shoreline +// - Bit 5: Ocean +// - Bits 0-4: Magnitude (0-31). For Water, this is (Distance / 2). +// +// Returns the packed data and the count of land tiles. func packTerrain(terrain [][]Terrain) (data []byte, numLandTiles int) { width := len(terrain) height := len(terrain[0]) @@ -492,6 +557,9 @@ func packTerrain(terrain [][]Terrain) (data []byte, numLandTiles int) { return packedData, numLandTiles } +// createMapThumbnail generates an RGBA image representation of the terrain. +// It scales the map dimensions based on the provided quality factor. +// Each pixel's color is determined by the terrain type and magnitude via getThumbnailColor. func createMapThumbnail(terrain [][]Terrain, quality float64) *image.RGBA { log.Println("Creating thumbnail") @@ -520,10 +588,28 @@ func createMapThumbnail(terrain [][]Terrain, quality float64) *image.RGBA { return img } +// RGBA represents a color with Red, Green, Blue, and Alpha channels. +// It is used locally for thumbnail generation. type RGBA struct { R, G, B, A uint8 } +// getThumbnailColor determines the RGBA color for a specific terrain tile for +// the map preview thumbnail. +// +// It handles color generation for Water (shoreline vs deep water) and Land +// (shoreline, plains, highlands, mountains) based on the tile's magnitude. +// +// The thumbnail renders its own set of colors separate from the in-game light/dark +// color schemes. +// +// For thumbnail purposes, the terrain type -> color mapping: +// - Water Shoreline: (Transparent) +// - Deep Water: (Transparent) +// - Land Shoreline: `rgb(204, 203, 158)` +// - Plains (Mag < 10): `rgb(190, 220, 138)` - `rgb(190, 202, 138)` +// - Highlands (Mag 10-19): `rgb(220, 203, 158)` - `rgb(238, 221, 176)` +// - Mountains (Mag >= 20): `rgb(240, 240, 240)` - `rgb(245, 245, 245)` func getThumbnailColor(t Terrain) RGBA { if t.Type == Water { // Shoreline water @@ -576,6 +662,8 @@ func getThumbnailColor(t Terrain) RGBA { } } +// logBinaryAsBits logs the binary representation of the first 'length' bytes of data. +// It is a helper function for debugging packed terrain data. func logBinaryAsBits(data []byte, length int) { if length > len(data) { length = len(data) @@ -588,21 +676,23 @@ func logBinaryAsBits(data []byte, length int) { log.Printf("Binary data (bits): %s", bits) } +// createCombinedBinary combines the info JSON, map data, and mini-map data into a single binary buffer. +// +// Note: This function is currently unused by the main workflow, which writes separate files instead. +// It creates a header with the following structure: +// - Bytes 0-3: Version (1) +// - Bytes 4-7: Info section offset +// - Bytes 8-11: Info section size +// - Bytes 12-15: Map section offset +// - Bytes 16-19: Map section size +// - Bytes 20-23: MiniMap section offset +// - Bytes 24-27: MiniMap section size func createCombinedBinary(infoBuffer []byte, mapData []byte, miniMapData []byte) []byte { // Calculate section sizes infoSize := len(infoBuffer) mapSize := len(mapData) miniMapSize := len(miniMapData) - // Header structure: - // Bytes 0-3: Version (1) - // Bytes 4-7: Info section offset (always 28) - // Bytes 8-11: Info section size - // Bytes 12-15: Map section offset - // Bytes 16-19: Map section size - // Bytes 20-23: MiniMap section offset - // Bytes 24-27: MiniMap section size - headerSize := 28 infoOffset := headerSize mapOffset := infoOffset + infoSize @@ -634,6 +724,9 @@ func createCombinedBinary(infoBuffer []byte, mapData []byte, miniMapData []byte) return combined } +// writeUint32 writes a 32-bit unsigned integer to the byte slice at the specified offset. +// It uses Little Endian byte order. +// Note: This function is currently unused. func writeUint32(data []byte, offset int, value uint32) { data[offset] = byte(value & 0xff) data[offset+1] = byte((value >> 8) & 0xff) @@ -641,10 +734,16 @@ func writeUint32(data []byte, offset int, value uint32) { data[offset+3] = byte((value >> 24) & 0xff) } +// readUint32 reads a 32-bit unsigned integer from the byte slice at the specified offset. +// It assumes Little Endian byte order. +// Note: This function is currently unused. func readUint32(data []byte, offset int) uint32 { return uint32(data[offset]) | uint32(data[offset+1])<<8 | uint32(data[offset+2])<<16 | uint32(data[offset+3])<<24 } +// decodeCombinedBinary parses a combined binary buffer into its constituent parts. +// It validates the header and extracts the Info JSON, Map data, and MiniMap data sections. +// Note: This function is currently unused. func decodeCombinedBinary(data []byte) (*CombinedBinaryHeader, []byte, []byte, []byte, error) { if len(data) < 28 { return nil, nil, nil, nil, fmt.Errorf("data too short for header") @@ -675,6 +774,8 @@ func decodeCombinedBinary(data []byte) (*CombinedBinaryHeader, []byte, []byte, [ return header, infoData, mapData, miniMapData, nil } +// CombinedBinaryHeader represents the metadata header of the combined map file format. +// Note: This struct is currently unused. type CombinedBinaryHeader struct { Version uint32 InfoOffset uint32 diff --git a/package.json b/package.json index a77ec1133..0a49301d2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dev": "cross-env GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", "dev:staging": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", "dev:prod": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.io concurrently \"npm run start:client\" \"npm run start:server-dev\"", + "docs:map-generator": "cd map-generator && go doc -cmd -u -all", "tunnel": "npm run build-prod && npm run start:server", "test": "jest", "perf": "npx tsx tests/perf/*.ts", diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index e024afc32..23ae4e653 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -138,6 +138,14 @@ export class PastelTheme implements Theme { return player.type() === PlayerType.Human ? "#000000" : "#4D4D4D"; } + // | Terrain Type | Magnitude | Base Color Logic | Visual Description | + // | :---------------- | :-------- | :---------------------------------------------- | :------------------------------------------------------------------- | + // | **Shore (Land)** | N/A | Fixed: `rgb(204, 203, 158)` | Sandy beige. Overrides other land types if adjacent to water. | + // | **Plains** | 0 - 9 | `rgb(190, 220, 138)` - `rgb(190, 202, 138)` | Light green. Gets slightly darker/less green as magnitude increases. | + // | **Highland** | 10 - 19 | `rgb(220, 203, 158)` - `rgb(238, 221, 176)` | Tan/Beige. Gets lighter as magnitude increases. | + // | **Mountain** | 20 - 30 | `rgb(240, 240, 240)` - `rgb(245, 245, 245)` | Grayscale (White/Grey). Represents snow caps or rocky peaks. | + // | **Water (Shore)** | 0 | Fixed: `rgb(100, 143, 255)` | Light blue near land. | + // | **Water (Deep)** | 1 - 10+ | `rgb(70, 132, 180)` - `rgb(61, 123, 171)` | Darker blue, adjusted slightly by distance to land. | terrainColor(gm: GameMap, tile: TileRef): Colord { const mag = gm.magnitude(tile); if (gm.isShore(tile)) { diff --git a/src/core/configuration/PastelThemeDark.ts b/src/core/configuration/PastelThemeDark.ts index 1ece7a63c..2cff80685 100644 --- a/src/core/configuration/PastelThemeDark.ts +++ b/src/core/configuration/PastelThemeDark.ts @@ -9,6 +9,15 @@ export class PastelThemeDark extends PastelTheme { private darkWater = colord("rgb(14,11,30)"); private darkShorelineWater = colord("rgb(50,50,50)"); + // | Terrain Type | Magnitude | Base Color Logic | Visual Description | + // | :---------------- | :-------- | :---------------------------------------------- | :-------------------- | + // | **Shore (Land)** | N/A | Fixed: `rgb(134, 133, 88)` | Dark olive. | + // | **Plains** | 0 - 9 | `rgb(140, 170, 88)` - `rgb(140, 152, 88)` | Muted green. | + // | **Highland** | 10 - 19 | `rgb(170, 153, 108)` - `rgb(188, 171, 126)` | Dark earth tone. | + // | **Mountain** | 20 - 30 | `rgb(190, 190, 190)` - `rgb(195, 195, 195)` | Dark gray. | + // | **Water (Shore)** | 0 | Fixed: `rgb(50, 50, 50)` | Dark gray/black. | + // | **Water (Deep)** | 1 - 10+ | `rgb(22, 19, 38)` - `rgb(14, 11, 30)` | Very dark blue/black. | + terrainColor(gm: GameMap, tile: TileRef): Colord { const mag = gm.magnitude(tile); if (gm.isShore(tile)) { diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 7a3bd8e6d..634321e77 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -249,6 +249,8 @@ export class GameMapImpl implements GameMap { return this.magnitude(ref) < 10 ? 2 : 1; } + // if updating these magnitude values, also update + // `../../../map-generator/map_generator.go` `getThumbnailColor` terrainType(ref: TileRef): TerrainType { if (this.isLand(ref)) { const magnitude = this.magnitude(ref);