mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 23:21:58 +00:00
ae884cb902
## Description: Adds New York City Map. ## 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 Screenshots <img width="1700" height="1081" alt="location-select" src="https://github.com/user-attachments/assets/06d229c1-f804-4766-bd58-35923c6696bc" /> <img width="1705" height="1079" alt="map-bots" src="https://github.com/user-attachments/assets/16a677d1-da6b-4aca-9ecf-8b0fbb161019" /> <img width="1706" height="1082" alt="map-select" src="https://github.com/user-attachments/assets/8adfd26b-f506-4c72-be05-a1fa638311ab" /> <img width="745" height="929" alt="nation-placement" src="https://github.com/user-attachments/assets/6b5ffd0e-0b2e-4189-b118-bd04c8ac4240" /> Misc Dimensions: 1500 x 1900 px Total Area: 2,850,000 px² New Flag Assets: None [Inspired by this Discord Thread](https://discord.com/channels/1284581928254701718/1440439003160641667/1440439003160641667) Some of the water features are adjusted for playability. NYC doesn't have a ton of elevation differences, so marshland is replicated w/ highland noise. This is roughly placed to match [Pre-WWI](https://www.geographicus.com/P/AntiqueMap/newyorkcity-usgs-1915), but allows maintaining the modern silhouette of the area. This 1901 map is also the main inspiration for composition and projection. For the Nations, British and Dutch Colonies along with Native Peoples make this a bit of an amalgamation of the 17th - 20th centuries Other elevation differences come from [Topographic Map](https://en-gb.topographic-map.com/map-6sm14/New-York/?center=40.63067%2C-73.89158&zoom=11) [Tribal Nation Names and Info](https://en.wikipedia.org/wiki/History_of_Long_Island) [Additional Tribal Names/Info](https://libguides.pratt.edu/c.php?g=1088684&p=9380209) --------- Co-authored-by: Evan <evanpelle@gmail.com>
237 lines
6.1 KiB
Go
237 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var mapsFlag string
|
|
|
|
var maps = []struct {
|
|
Name string
|
|
IsTest bool
|
|
}{
|
|
{Name: "africa"},
|
|
{Name: "asia"},
|
|
{Name: "australia"},
|
|
{Name: "achiran"},
|
|
{Name: "baikal"},
|
|
{Name: "baikalnukewars"},
|
|
{Name: "betweentwoseas"},
|
|
{Name: "blacksea"},
|
|
{Name: "britannia"},
|
|
{Name: "deglaciatedantarctica"},
|
|
{Name: "eastasia"},
|
|
{Name: "europe"},
|
|
{Name: "europeclassic"},
|
|
{Name: "falklandislands"},
|
|
{Name: "faroeislands"},
|
|
{Name: "fourislands"},
|
|
{Name: "gatewaytotheatlantic"},
|
|
{Name: "giantworldmap"},
|
|
{Name: "gulfofstlawrence"},
|
|
{Name: "halkidiki"},
|
|
{Name: "iceland"},
|
|
{Name: "italia"},
|
|
{Name: "japan"},
|
|
{Name: "lisbon"},
|
|
{Name: "mars"},
|
|
{Name: "mena"},
|
|
{Name: "montreal"},
|
|
{Name: "newyorkcity"},
|
|
{Name: "northamerica"},
|
|
{Name: "oceania"},
|
|
{Name: "pangaea"},
|
|
{Name: "pluto"},
|
|
{Name: "southamerica"},
|
|
{Name: "straitofgibraltar"},
|
|
{Name: "world"},
|
|
{Name: "big_plains", IsTest: true},
|
|
{Name: "half_land_half_ocean", IsTest: true},
|
|
{Name: "ocean_and_land", IsTest: true},
|
|
{Name: "plains", IsTest: true},
|
|
{Name: "giantworldmap", IsTest: true},
|
|
}
|
|
|
|
func outputMapDir(isTest bool) (string, error) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get working directory: %w", err)
|
|
}
|
|
if isTest {
|
|
return filepath.Join(cwd, "..", "tests", "testdata", "maps"), nil
|
|
}
|
|
return filepath.Join(cwd, "..", "resources", "maps"), nil
|
|
}
|
|
|
|
func inputMapDir(isTest bool) (string, error) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get working directory: %w", err)
|
|
}
|
|
if isTest {
|
|
return filepath.Join(cwd, "assets", "test_maps"), nil
|
|
} else {
|
|
return filepath.Join(cwd, "assets", "maps"), nil
|
|
}
|
|
}
|
|
|
|
func processMap(name string, isTest bool) error {
|
|
outputMapBaseDir, err := outputMapDir(isTest)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get map directory: %w", err)
|
|
}
|
|
|
|
inputMapDir, err := inputMapDir(isTest)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get input map directory: %w", err)
|
|
}
|
|
|
|
inputPath := filepath.Join(inputMapDir, name, "image.png")
|
|
imageBuffer, err := os.ReadFile(inputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read map file %s: %w", inputPath, err)
|
|
}
|
|
|
|
// Read the info.json file
|
|
manifestPath := filepath.Join(inputMapDir, name, "info.json")
|
|
manifestBuffer, err := os.ReadFile(manifestPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read info file %s: %w", manifestPath, err)
|
|
}
|
|
|
|
// Parse the info buffer as dynamic JSON
|
|
var manifest map[string]interface{}
|
|
if err := json.Unmarshal(manifestBuffer, &manifest); err != nil {
|
|
return fmt.Errorf("failed to parse info.json for %s: %w", name, err)
|
|
}
|
|
|
|
// Generate maps
|
|
result, err := GenerateMap(GeneratorArgs{
|
|
ImageBuffer: imageBuffer,
|
|
RemoveSmall: !isTest, // Don't remove small islands for test maps
|
|
Name: name,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate map for %s: %w", name, err)
|
|
}
|
|
|
|
manifest["map"] = map[string]interface{}{
|
|
"width": result.Map.Width,
|
|
"height": result.Map.Height,
|
|
"num_land_tiles": result.Map.NumLandTiles,
|
|
}
|
|
manifest["map4x"] = map[string]interface{}{
|
|
"width": result.Map4x.Width,
|
|
"height": result.Map4x.Height,
|
|
"num_land_tiles": result.Map4x.NumLandTiles,
|
|
}
|
|
manifest["map16x"] = map[string]interface{}{
|
|
"width": result.Map16x.Width,
|
|
"height": result.Map16x.Height,
|
|
"num_land_tiles": result.Map16x.NumLandTiles,
|
|
}
|
|
|
|
mapDir := filepath.Join(outputMapBaseDir, name)
|
|
if err := os.MkdirAll(mapDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create output directory for %s: %w", name, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(mapDir, "map.bin"), result.Map.Data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write combined binary for %s: %w", name, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(mapDir, "map4x.bin"), result.Map4x.Data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write combined binary for %s: %w", name, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(mapDir, "map16x.bin"), result.Map16x.Data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write combined binary for %s: %w", name, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(mapDir, "thumbnail.webp"), result.Thumbnail, 0644); err != nil {
|
|
return fmt.Errorf("failed to write thumbnail for %s: %w", name, err)
|
|
}
|
|
|
|
// Serialize the updated manifest to JSON
|
|
updatedManifest, err := json.MarshalIndent(manifest, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize manifest for %s: %w", name, err)
|
|
}
|
|
|
|
if err := os.WriteFile(filepath.Join(mapDir, "manifest.json"), updatedManifest, 0644); err != nil {
|
|
return fmt.Errorf("failed to write manifest for %s: %w", name, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseMapsFlag() (map[string]bool, error) {
|
|
if mapsFlag == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
validNames := make(map[string]bool, len(maps))
|
|
for _, m := range maps {
|
|
validNames[m.Name] = true
|
|
}
|
|
|
|
selected := make(map[string]bool)
|
|
for _, name := range strings.Split(mapsFlag, ",") {
|
|
if !validNames[name] {
|
|
return nil, fmt.Errorf("map %q is not defined", name)
|
|
}
|
|
selected[name] = true
|
|
}
|
|
return selected, nil
|
|
}
|
|
|
|
func loadTerrainMaps() error {
|
|
selectedMaps, err := parseMapsFlag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var wg sync.WaitGroup
|
|
errChan := make(chan error, len(maps))
|
|
|
|
// Process maps concurrently
|
|
for _, mapItem := range maps {
|
|
if selectedMaps != nil && !selectedMaps[mapItem.Name] {
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
mapItem := mapItem
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := processMap(mapItem.Name, mapItem.IsTest); err != nil {
|
|
errChan <- err
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Wait for all goroutines to complete
|
|
wg.Wait()
|
|
close(errChan)
|
|
|
|
// Check for errors
|
|
for err := range errChan {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
flag.StringVar(&mapsFlag, "maps", "", "optional comma-separated list of maps to process. ex: --maps=world,eastasia,big_plains")
|
|
flag.Parse()
|
|
|
|
if err := loadTerrainMaps(); err != nil {
|
|
log.Fatalf("Error generating terrain maps: %v", err)
|
|
}
|
|
|
|
fmt.Println("Terrain maps generated successfully")
|
|
}
|