diff --git a/.gitignore b/.gitignore index 8a3fdf385..3223dce9f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ tests/perf/output/ # Locally built Go binary (gen-maps uses `go run .`) map-generator/map-generator +map-generator/map-generator.exe diff --git a/map-generator/assets/maps/germany/info.json b/map-generator/assets/maps/germany/info.json index 5a035d4e2..4ba2fb4ce 100644 --- a/map-generator/assets/maps/germany/info.json +++ b/map-generator/assets/maps/germany/info.json @@ -4,6 +4,416 @@ "translation_key": "map.germany", "categories": ["europe", "new"], "multiplayer_frequency": 5, + "custom_tribes": [ + "Aachen, Städteregion", + "Ahrweiler", + "Aichach-Friedberg", + "Alb-Donau-Kreis", + "Altenburger Land", + "Altenkirchen (Westerwald)", + "Altmarkkreis Salzwedel", + "Altötting", + "Alzey-Worms", + "Amberg-Sulzbach", + "Ammerland", + "Anhalt-Bitterfeld", + "Ansbach", + "Aschaffenburg", + "Augsburg", + "Aurich", + "Bad Dürkheim", + "Bad Kissingen", + "Bad Kreuznach", + "Bad Tölz-Wolfratshausen", + "Bamberg", + "Barnim", + "Bautzen", + "Bayreuth", + "Berchtesgadener Land", + "Bergstraße", + "Bernkastel-Wittlich", + "Biberach", + "Birkenfeld", + "Böblingen", + "Bodenseekreis", + "Börde", + "Borken", + "Breisgau-Hochschwarzwald", + "Burgenlandkreis", + "Calw", + "Celle", + "Cham", + "Cloppenburg", + "Coburg", + "Cochem-Zell", + "Coesfeld", + "Cuxhaven", + "Dachau", + "Dahme-Spreewald", + "Darmstadt-Dieburg", + "Deggendorf", + "Diepholz", + "Dillingen an der Donau", + "Dingolfing-Landau", + "Dithmarschen", + "Donau-Ries", + "Donnersbergkreis", + "Düren", + "Ebersberg", + "Eichsfeld", + "Eichstätt", + "Eifelkreis Bitburg-Prüm", + "Elbe-Elster", + "Emmendingen", + "Emsland", + "Ennepe-Ruhr-Kreis", + "Enzkreis", + "Erding", + "Erlangen-Höchstadt", + "Erzgebirgskreis", + "Esslingen", + "Euskirchen", + "Forchheim", + "Freising", + "Freudenstadt", + "Freyung-Grafenau", + "Friesland", + "Fulda", + "Fürstenfeldbruck", + "Fürth", + "Garmisch-Partenkirchen", + "Germersheim", + "Gießen", + "Gifhorn", + "Göppingen", + "Görlitz", + "Goslar", + "Gotha", + "Göttingen", + "Grafschaft Bentheim", + "Greiz", + "Groß-Gerau", + "Günzburg", + "Gütersloh", + "Hameln-Pyrmont", + "Hannover, Region", + "Harburg", + "Harz", + "Haßberge", + "Havelland", + "Heidekreis", + "Heidenheim", + "Heilbronn", + "Heinsberg", + "Helmstedt", + "Herford", + "Hersfeld-Rotenburg", + "Herzogtum Lauenburg", + "Hildburghausen", + "Hildesheim", + "Hochsauerlandkreis", + "Hochtaunuskreis", + "Hof", + "Hohenlohekreis", + "Holzminden", + "Höxter", + "Ilm-Kreis", + "Jerichower Land", + "Kaiserslautern", + "Karlsruhe", + "Kassel", + "Kelheim", + "Kitzingen", + "Kleve", + "Konstanz", + "Kronach", + "Kulmbach", + "Kusel", + "Kyffhäuserkreis", + "Lahn-Dill-Kreis", + "Landsberg am Lech", + "Landshut", + "Leer", + "Leipzig", + "Lichtenfels", + "Limburg-Weilburg", + "Lindau (Bodensee)", + "Lippe", + "Lörrach", + "Lüchow-Dannenberg", + "Ludwigsburg", + "Ludwigslust-Parchim", + "Lüneburg", + "Main-Kinzig-Kreis", + "Main-Spessart", + "Main-Tauber-Kreis", + "Main-Taunus-Kreis", + "Mainz-Bingen", + "Mansfeld-Südharz", + "Marburg-Biedenkopf", + "Märkischer Kreis", + "Märkisch-Oderland", + "Mayen-Koblenz", + "Mecklenburgische Seenplatte", + "Meißen", + "Merzig-Wadern", + "Mettmann", + "Miesbach", + "Miltenberg", + "Minden-Lübbecke", + "Mittelsachsen", + "Mühldorf am Inn", + "München", + "Neckar-Odenwald-Kreis", + "Neu-Ulm", + "Neuburg-Schrobenhausen", + "Neumarkt in der Oberpfalz", + "Neunkirchen", + "Neustadt an der Aisch-Bad Windsheim", + "Neustadt an der Waldnaab", + "Neuwied", + "Nienburg/Weser", + "Nordfriesland", + "Nordhausen", + "Nordsachsen", + "Nordwestmecklenburg", + "Northeim", + "Nürnberger Land", + "Oberallgäu", + "Oberbergischer Kreis", + "Oberhavel", + "Oberspreewald-Lausitz", + "Odenwaldkreis", + "Oder-Spree", + "Offenbach", + "Oldenburg", + "Olpe", + "Ortenaukreis", + "Osnabrück", + "Ostalbkreis", + "Ostallgäu", + "Osterholz", + "Ostholstein", + "Ostprignitz-Ruppin", + "Paderborn", + "Passau", + "Peine", + "Pfaffenhofen an der Ilm", + "Pinneberg", + "Plön", + "Potsdam-Mittelmark", + "Prignitz", + "Rastatt", + "Ravensburg", + "Recklinghausen", + "Regen", + "Regensburg", + "Rems-Murr-Kreis", + "Rendsburg-Eckernförde", + "Reutlingen", + "Rhein-Erft-Kreis", + "Rheingau-Taunus-Kreis", + "Rhein-Hunsrück-Kreis", + "Rheinisch-Bergischer Kreis", + "Rhein-Kreis Neuss", + "Rhein-Lahn-Kreis", + "Rhein-Neckar-Kreis", + "Rhein-Pfalz-Kreis", + "Rhein-Sieg-Kreis", + "Rhön-Grabfeld", + "Rosenheim", + "Rostock", + "Rotenburg (Wümme)", + "Roth", + "Rottal-Inn", + "Rottweil", + "Saale-Holzland-Kreis", + "Saalekreis", + "Saale-Orla-Kreis", + "Saalfeld-Rudolstadt", + "Saarbrücken, Regionalverband", + "Saarlouis", + "Saarpfalz-Kreis", + "Sächsische Schweiz-Osterzgebirge", + "Salzlandkreis", + "Schaumburg", + "Schleswig-Flensburg", + "Schmalkalden-Meiningen", + "Schwäbisch Hall", + "Schwalm-Eder-Kreis", + "Schwandorf", + "Schwarzwald-Baar-Kreis", + "Schweinfurt", + "Segeberg", + "Siegen-Wittgenstein", + "Sigmaringen", + "Soest", + "Sömmerda", + "Sonneberg", + "Spree-Neiße", + "St. Wendel", + "Stade", + "Starnberg", + "Steinburg", + "Steinfurt", + "Stendal", + "Stormarn", + "Straubing-Bogen", + "Südliche Weinstraße", + "Südwestpfalz", + "Teltow-Fläming", + "Tirschenreuth", + "Traunstein", + "Trier-Saarburg", + "Tübingen", + "Tuttlingen", + "Uckermark", + "Uelzen", + "Unna", + "Unstrut-Hainich-Kreis", + "Unterallgäu", + "Vechta", + "Verden", + "Viersen", + "Vogelsbergkreis", + "Vogtlandkreis", + "Vorpommern-Greifswald", + "Vorpommern-Rügen", + "Vulkaneifel", + "Waldeck-Frankenberg", + "Waldshut", + "Warendorf", + "Wartburgkreis", + "Weilheim-Schongau", + "Weimarer Land", + "Weißenburg-Gunzenhausen", + "Werra-Meißner-Kreis", + "Wesel", + "Wesermarsch", + "Westerwaldkreis", + "Wetteraukreis", + "Wittenberg", + "Wittmund", + "Wolfenbüttel", + "Wunsiedel im Fichtelgebirge", + "Würzburg", + "Zollernalbkreis", + "Zwickau", + "Aachen", + "Amberg", + "Baden-Baden", + "Bielefeld", + "Bochum", + "Bonn", + "Bottrop", + "Brandenburg an der Havel", + "Braunschweig", + "Bremerhaven", + "Chemnitz", + "Cottbus", + "Darmstadt", + "Delmenhorst", + "Dessau-Roßlau", + "Dortmund", + "Dresden", + "Duisburg", + "Düsseldorf", + "Emden", + "Erfurt", + "Erlangen", + "Essen", + "Flensburg", + "Frankenthal (Pfalz)", + "Frankfurt (Oder)", + "Frankfurt am Main", + "Freiburg im Breisgau", + "Gelsenkirchen", + "Gera", + "Hagen", + "Halle (Saale)", + "Hamm", + "Hanau", + "Hannover", + "Heidelberg", + "Herne", + "Ingolstadt", + "Jena", + "Kaufbeuren", + "Kempten (Allgäu)", + "Kiel", + "Koblenz", + "Köln", + "Krefeld", + "Landau in der Pfalz", + "Leverkusen", + "Lübeck", + "Ludwigshafen am Rhein", + "Magdeburg", + "Mainz", + "Mannheim", + "Memmingen", + "Mönchengladbach", + "Mülheim an der Ruhr", + "Münster", + "Neumünster", + "Neustadt an der Weinstraße", + "Nürnberg", + "Oberhausen", + "Offenbach am Main", + "Oldenburg (Oldb)", + "Pforzheim", + "Pirmasens", + "Potsdam", + "Remscheid", + "Salzgitter", + "Schwabach", + "Schwerin", + "Solingen", + "Speyer", + "Straubing", + "Stuttgart", + "Suhl", + "Trier", + "Ulm", + "Weiden in der Oberpfalz", + "Weimar", + "Wiesbaden", + "Wilhelmshaven", + "Wolfsburg", + "Worms", + "Wuppertal", + "Zweibrücken", + "Preußen", + "Schwaben", + "Franken", + "Rheinland", + "Westfalen", + "Pommern", + "Schleswig", + "Holstein", + "Anhalt", + "Lausitz", + "Elsass", + "Deutscher Orden", + "Nassau", + "Kurpfalz", + "Berg", + "Jülich", + "Lothringen", + "Harzgau", + "Wetterau", + "Ruhrgebiet", + "Allgäu", + "Schwarzwald", + "Spreewald", + "Altmühltal", + "Spessart", + "Odenwald", + "Eifel", + "Sauerland", + "Westerwald", + "Pfalz" + ], "nations": [ { "coordinates": [688, 396], diff --git a/map-generator/codegen.go b/map-generator/codegen.go index b30535081..bbaecb0cd 100644 --- a/map-generator/codegen.go +++ b/map-generator/codegen.go @@ -48,6 +48,16 @@ type mapInfo struct { // Preferred team count in team/special games (see MapPlaylist). // 0 (or omitted) means no preference. SpecialTeamCount int `json:"special_team_count"` + // Theme name(s) for bot tribe names (references tribeNameThemes.json). + // Empty or omitted uses the "default" theme. + Themes []string `json:"themes"` + // Custom tribe names that take priority over theme-generated names. + // Each entry is used as-is (no prefix/suffix composition). + CustomTribes []string `json:"custom_tribes"` + // Nations defined on this map (used for validation only). + Nations []struct { + Name string `json:"name"` + } `json:"nations"` } // hasCategory reports whether the map lists the given category. @@ -113,6 +123,31 @@ func loadMapInfos() ([]mapInfo, error) { if len(info.Categories) == 0 { return nil, fmt.Errorf("map %s: info.json \"categories\" must list at least one category", m.Name) } + for _, ct := range info.CustomTribes { + if ct == "" { + return nil, fmt.Errorf("map %s: info.json \"custom_tribes\" contains an empty string", m.Name) + } + } + { + ctSeen := make(map[string]bool) + for _, ct := range info.CustomTribes { + if ctSeen[ct] { + return nil, fmt.Errorf("map %s: info.json \"custom_tribes\" contains duplicate %q", m.Name, ct) + } + ctSeen[ct] = true + } + } + { + nationNames := make(map[string]bool) + for _, n := range info.Nations { + nationNames[n.Name] = true + } + for _, ct := range info.CustomTribes { + if nationNames[ct] { + return nil, fmt.Errorf("map %s: info.json \"custom_tribes\" contains %q which is already a nation name", m.Name, ct) + } + } + } seen := make(map[string]bool) for _, category := range info.Categories { valid := false @@ -190,6 +225,10 @@ func generateMapsTS(infos []mapInfo) error { b.WriteString(" featuredRank?: number;\n") b.WriteString(" /** Preferred team count in team/special games (see MapPlaylist). */\n") b.WriteString(" specialTeamCount?: number;\n") + b.WriteString(" /** Tribe name theme(s) (keys in tribeNameThemes.json). */\n") + b.WriteString(" themes?: string[];\n") + b.WriteString(" /** Custom tribe names with priority over theme-generated names. */\n") + b.WriteString(" customTribes?: string[];\n") b.WriteString("}\n\n") b.WriteString("export const maps: readonly MapInfo[] = [\n") @@ -213,6 +252,26 @@ func generateMapsTS(infos []mapInfo) error { if info.SpecialTeamCount > 0 { b.WriteString(fmt.Sprintf(" specialTeamCount: %d,\n", info.SpecialTeamCount)) } + if len(info.Themes) > 0 { + b.WriteString(" themes: [") + for i, th := range info.Themes { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(fmt.Sprintf("%q", th)) + } + b.WriteString("],\n") + } + if len(info.CustomTribes) > 0 { + b.WriteString(" customTribes: [") + for i, ct := range info.CustomTribes { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(fmt.Sprintf("%q", ct)) + } + b.WriteString("],\n") + } b.WriteString(" },\n") } b.WriteString("];\n") diff --git a/resources/maps/germany/manifest.json b/resources/maps/germany/manifest.json index daf9ce4ad..bb0309da1 100644 --- a/resources/maps/germany/manifest.json +++ b/resources/maps/germany/manifest.json @@ -1,5 +1,415 @@ { "categories": ["europe", "new"], + "custom_tribes": [ + "Aachen, Städteregion", + "Ahrweiler", + "Aichach-Friedberg", + "Alb-Donau-Kreis", + "Altenburger Land", + "Altenkirchen (Westerwald)", + "Altmarkkreis Salzwedel", + "Altötting", + "Alzey-Worms", + "Amberg-Sulzbach", + "Ammerland", + "Anhalt-Bitterfeld", + "Ansbach", + "Aschaffenburg", + "Augsburg", + "Aurich", + "Bad Dürkheim", + "Bad Kissingen", + "Bad Kreuznach", + "Bad Tölz-Wolfratshausen", + "Bamberg", + "Barnim", + "Bautzen", + "Bayreuth", + "Berchtesgadener Land", + "Bergstraße", + "Bernkastel-Wittlich", + "Biberach", + "Birkenfeld", + "Böblingen", + "Bodenseekreis", + "Börde", + "Borken", + "Breisgau-Hochschwarzwald", + "Burgenlandkreis", + "Calw", + "Celle", + "Cham", + "Cloppenburg", + "Coburg", + "Cochem-Zell", + "Coesfeld", + "Cuxhaven", + "Dachau", + "Dahme-Spreewald", + "Darmstadt-Dieburg", + "Deggendorf", + "Diepholz", + "Dillingen an der Donau", + "Dingolfing-Landau", + "Dithmarschen", + "Donau-Ries", + "Donnersbergkreis", + "Düren", + "Ebersberg", + "Eichsfeld", + "Eichstätt", + "Eifelkreis Bitburg-Prüm", + "Elbe-Elster", + "Emmendingen", + "Emsland", + "Ennepe-Ruhr-Kreis", + "Enzkreis", + "Erding", + "Erlangen-Höchstadt", + "Erzgebirgskreis", + "Esslingen", + "Euskirchen", + "Forchheim", + "Freising", + "Freudenstadt", + "Freyung-Grafenau", + "Friesland", + "Fulda", + "Fürstenfeldbruck", + "Fürth", + "Garmisch-Partenkirchen", + "Germersheim", + "Gießen", + "Gifhorn", + "Göppingen", + "Görlitz", + "Goslar", + "Gotha", + "Göttingen", + "Grafschaft Bentheim", + "Greiz", + "Groß-Gerau", + "Günzburg", + "Gütersloh", + "Hameln-Pyrmont", + "Hannover, Region", + "Harburg", + "Harz", + "Haßberge", + "Havelland", + "Heidekreis", + "Heidenheim", + "Heilbronn", + "Heinsberg", + "Helmstedt", + "Herford", + "Hersfeld-Rotenburg", + "Herzogtum Lauenburg", + "Hildburghausen", + "Hildesheim", + "Hochsauerlandkreis", + "Hochtaunuskreis", + "Hof", + "Hohenlohekreis", + "Holzminden", + "Höxter", + "Ilm-Kreis", + "Jerichower Land", + "Kaiserslautern", + "Karlsruhe", + "Kassel", + "Kelheim", + "Kitzingen", + "Kleve", + "Konstanz", + "Kronach", + "Kulmbach", + "Kusel", + "Kyffhäuserkreis", + "Lahn-Dill-Kreis", + "Landsberg am Lech", + "Landshut", + "Leer", + "Leipzig", + "Lichtenfels", + "Limburg-Weilburg", + "Lindau (Bodensee)", + "Lippe", + "Lörrach", + "Lüchow-Dannenberg", + "Ludwigsburg", + "Ludwigslust-Parchim", + "Lüneburg", + "Main-Kinzig-Kreis", + "Main-Spessart", + "Main-Tauber-Kreis", + "Main-Taunus-Kreis", + "Mainz-Bingen", + "Mansfeld-Südharz", + "Marburg-Biedenkopf", + "Märkischer Kreis", + "Märkisch-Oderland", + "Mayen-Koblenz", + "Mecklenburgische Seenplatte", + "Meißen", + "Merzig-Wadern", + "Mettmann", + "Miesbach", + "Miltenberg", + "Minden-Lübbecke", + "Mittelsachsen", + "Mühldorf am Inn", + "München", + "Neckar-Odenwald-Kreis", + "Neu-Ulm", + "Neuburg-Schrobenhausen", + "Neumarkt in der Oberpfalz", + "Neunkirchen", + "Neustadt an der Aisch-Bad Windsheim", + "Neustadt an der Waldnaab", + "Neuwied", + "Nienburg/Weser", + "Nordfriesland", + "Nordhausen", + "Nordsachsen", + "Nordwestmecklenburg", + "Northeim", + "Nürnberger Land", + "Oberallgäu", + "Oberbergischer Kreis", + "Oberhavel", + "Oberspreewald-Lausitz", + "Odenwaldkreis", + "Oder-Spree", + "Offenbach", + "Oldenburg", + "Olpe", + "Ortenaukreis", + "Osnabrück", + "Ostalbkreis", + "Ostallgäu", + "Osterholz", + "Ostholstein", + "Ostprignitz-Ruppin", + "Paderborn", + "Passau", + "Peine", + "Pfaffenhofen an der Ilm", + "Pinneberg", + "Plön", + "Potsdam-Mittelmark", + "Prignitz", + "Rastatt", + "Ravensburg", + "Recklinghausen", + "Regen", + "Regensburg", + "Rems-Murr-Kreis", + "Rendsburg-Eckernförde", + "Reutlingen", + "Rhein-Erft-Kreis", + "Rheingau-Taunus-Kreis", + "Rhein-Hunsrück-Kreis", + "Rheinisch-Bergischer Kreis", + "Rhein-Kreis Neuss", + "Rhein-Lahn-Kreis", + "Rhein-Neckar-Kreis", + "Rhein-Pfalz-Kreis", + "Rhein-Sieg-Kreis", + "Rhön-Grabfeld", + "Rosenheim", + "Rostock", + "Rotenburg (Wümme)", + "Roth", + "Rottal-Inn", + "Rottweil", + "Saale-Holzland-Kreis", + "Saalekreis", + "Saale-Orla-Kreis", + "Saalfeld-Rudolstadt", + "Saarbrücken, Regionalverband", + "Saarlouis", + "Saarpfalz-Kreis", + "Sächsische Schweiz-Osterzgebirge", + "Salzlandkreis", + "Schaumburg", + "Schleswig-Flensburg", + "Schmalkalden-Meiningen", + "Schwäbisch Hall", + "Schwalm-Eder-Kreis", + "Schwandorf", + "Schwarzwald-Baar-Kreis", + "Schweinfurt", + "Segeberg", + "Siegen-Wittgenstein", + "Sigmaringen", + "Soest", + "Sömmerda", + "Sonneberg", + "Spree-Neiße", + "St. Wendel", + "Stade", + "Starnberg", + "Steinburg", + "Steinfurt", + "Stendal", + "Stormarn", + "Straubing-Bogen", + "Südliche Weinstraße", + "Südwestpfalz", + "Teltow-Fläming", + "Tirschenreuth", + "Traunstein", + "Trier-Saarburg", + "Tübingen", + "Tuttlingen", + "Uckermark", + "Uelzen", + "Unna", + "Unstrut-Hainich-Kreis", + "Unterallgäu", + "Vechta", + "Verden", + "Viersen", + "Vogelsbergkreis", + "Vogtlandkreis", + "Vorpommern-Greifswald", + "Vorpommern-Rügen", + "Vulkaneifel", + "Waldeck-Frankenberg", + "Waldshut", + "Warendorf", + "Wartburgkreis", + "Weilheim-Schongau", + "Weimarer Land", + "Weißenburg-Gunzenhausen", + "Werra-Meißner-Kreis", + "Wesel", + "Wesermarsch", + "Westerwaldkreis", + "Wetteraukreis", + "Wittenberg", + "Wittmund", + "Wolfenbüttel", + "Wunsiedel im Fichtelgebirge", + "Würzburg", + "Zollernalbkreis", + "Zwickau", + "Aachen", + "Amberg", + "Baden-Baden", + "Bielefeld", + "Bochum", + "Bonn", + "Bottrop", + "Brandenburg an der Havel", + "Braunschweig", + "Bremerhaven", + "Chemnitz", + "Cottbus", + "Darmstadt", + "Delmenhorst", + "Dessau-Roßlau", + "Dortmund", + "Dresden", + "Duisburg", + "Düsseldorf", + "Emden", + "Erfurt", + "Erlangen", + "Essen", + "Flensburg", + "Frankenthal (Pfalz)", + "Frankfurt (Oder)", + "Frankfurt am Main", + "Freiburg im Breisgau", + "Gelsenkirchen", + "Gera", + "Hagen", + "Halle (Saale)", + "Hamm", + "Hanau", + "Hannover", + "Heidelberg", + "Herne", + "Ingolstadt", + "Jena", + "Kaufbeuren", + "Kempten (Allgäu)", + "Kiel", + "Koblenz", + "Köln", + "Krefeld", + "Landau in der Pfalz", + "Leverkusen", + "Lübeck", + "Ludwigshafen am Rhein", + "Magdeburg", + "Mainz", + "Mannheim", + "Memmingen", + "Mönchengladbach", + "Mülheim an der Ruhr", + "Münster", + "Neumünster", + "Neustadt an der Weinstraße", + "Nürnberg", + "Oberhausen", + "Offenbach am Main", + "Oldenburg (Oldb)", + "Pforzheim", + "Pirmasens", + "Potsdam", + "Remscheid", + "Salzgitter", + "Schwabach", + "Schwerin", + "Solingen", + "Speyer", + "Straubing", + "Stuttgart", + "Suhl", + "Trier", + "Ulm", + "Weiden in der Oberpfalz", + "Weimar", + "Wiesbaden", + "Wilhelmshaven", + "Wolfsburg", + "Worms", + "Wuppertal", + "Zweibrücken", + "Preußen", + "Schwaben", + "Franken", + "Rheinland", + "Westfalen", + "Pommern", + "Schleswig", + "Holstein", + "Anhalt", + "Lausitz", + "Elsass", + "Deutscher Orden", + "Nassau", + "Kurpfalz", + "Berg", + "Jülich", + "Lothringen", + "Harzgau", + "Wetterau", + "Ruhrgebiet", + "Allgäu", + "Schwarzwald", + "Spreewald", + "Altmühltal", + "Spessart", + "Odenwald", + "Eifel", + "Sauerland", + "Westerwald", + "Pfalz" + ], "id": "Germany", "map": { "height": 1516, diff --git a/resources/tribeNameThemes.json b/resources/tribeNameThemes.json new file mode 100644 index 000000000..234e1bd71 --- /dev/null +++ b/resources/tribeNameThemes.json @@ -0,0 +1,1580 @@ +{ + "default": { + "prefixes": [ + "Akkadian", + "Babylonian", + "Sumerian", + "Hittite", + "Phoenician", + "Canaanite", + "Minoan", + "Mycenaean", + "Etruscan", + "Scythian", + "Thracian", + "Dacian", + "Illyrian", + "Median", + "Chaldean", + "Roman", + "Greek", + "Byzantine", + "Persian", + "Parthian", + "Seleucid", + "Ptolemaic", + "Palmyrene", + "Macedonian", + "Carthaginian", + "Ming", + "Tang", + "Song", + "Yuan", + "Mauryan", + "Kushan", + "Rajput", + "Mughal", + "Satavahana", + "Vijayanagara", + "Egyptian", + "Nubian", + "Kushite", + "Aksumite", + "Ethiopian", + "Songhai", + "Malian", + "Ghanaian", + "Benin", + "Ashanti", + "Zulu", + "Tuareg", + "Berber", + "Kanem-Bornu", + "Buganda", + "Mossi", + "Swahili", + "Somali", + "Wolof", + "Umayyad", + "Abbasid", + "Ayyubid", + "Fatimid", + "Mamluk", + "Seljuk", + "Safavid", + "Ottoman", + "Almoravid", + "Almohad", + "Rashidun", + "Ziyarid", + "Frankish", + "Visigothic", + "Ostrogothic", + "Viking", + "Norman", + "Saxon", + "Anglo-Saxon", + "Celtic", + "Gaulish", + "Carolingian", + "Merovingian", + "Capetian", + "Plantagenet", + "Tudor", + "Stuart", + "Habsburg", + "Romanov", + "Lancaster", + "York", + "Bourbon", + "Napoleonic", + "British", + "French", + "Spanish", + "Portuguese", + "Dutch", + "Russian", + "German", + "Italian", + "Swedish", + "Norwegian", + "Danish", + "Polish", + "Hungarian", + "Austrian", + "Swiss", + "Czech", + "Slovak", + "Serbian", + "Croatian", + "Bosnian", + "Montenegrin", + "Bulgarian", + "Romanian", + "Apache", + "Sioux", + "Cherokee", + "Navajo", + "Iroquois", + "Inuit", + "Arawak", + "Carib", + "Taino", + "Aztec", + "Mayan", + "Incan", + "Mapuche", + "Guarani", + "Tupi", + "Yanomami", + "Zuni", + "Hopi", + "Kiowa", + "Comanche", + "Shoshone", + "Japanese", + "Ryukyu", + "Ainu", + "Cham", + "Khmer", + "Thai", + "Vietnamese", + "Burmese", + "Balinese", + "Malay", + "Filipino", + "Mongolian", + "Korean", + "Tibetan", + "Manchu", + "Uyghur", + "Hmong", + "Karen", + "Pyu", + "Hawaiian", + "Fijian", + "Tongan", + "Samoan", + "Maori", + "Micronesian", + "Hebrew", + "Armenian", + "Circassian", + "Georgian", + "Kurdish", + "Turkic", + "Kazakh", + "Uzbek", + "Kyrgyz", + "Tajik", + "Pashtun", + "Baloch", + "Afghan", + "Kenyan", + "Ugandan", + "Bhutanese", + "Latin", + "Moldovan", + "Militant", + "Spartan" + ], + "suffixes": [ + "Empire", + "Dynasty", + "Kingdom", + "Queendom", + "Sultanate", + "Confederation", + "Union", + "Republic", + "Caliphate", + "Dominion", + "Realm", + "State", + "Federation", + "Territory", + "Commonwealth", + "League", + "Duchy", + "Province", + "Protectorate", + "Colony", + "Mandate", + "Free State", + "Canton", + "Region", + "Nation", + "Assembly", + "Hierarchy", + "Archduchy", + "Grand Duchy", + "Metropolis", + "Cluster", + "Alliance", + "Tribunal", + "Council", + "Confederacy", + "Order", + "Regime", + "Syndicate", + "Guild", + "Corporation", + "Patriarchy", + "Matriarchy", + "Legion", + "Horde", + "Clan", + "Brotherhood", + "Sisterhood", + "Ascendancy", + "Supremacy", + "Tribe", + "Army", + "Dictatorship", + "Country", + "Oligarchy", + "Monkdom", + "Throng", + "Host", + "Area", + "District", + "Fief", + "Wilderness", + "Settlement", + "Parliament", + "Anarchy", + "Democracy", + "Autocracy" + ] + }, + "north_america": { + "prefixes": [ + "Apache", + "Sioux", + "Cherokee", + "Navajo", + "Iroquois", + "Inuit", + "Arawak", + "Carib", + "Taino", + "Aztec", + "Mayan", + "Zuni", + "Hopi", + "Kiowa", + "Comanche", + "Shoshone", + "Cree", + "Ojibwe", + "Seminole", + "Cheyenne", + "Pawnee", + "Mohawk", + "Huron", + "Tlingit", + "Haida", + "Nez Perce", + "Crow", + "Blackfoot", + "Pueblo", + "Mi'kmaq", + "Algonquin", + "Wampanoag", + "Potawatomi", + "Shawnee", + "Chickasaw", + "Choctaw", + "Creek", + "Canadian", + "American", + "Mexican", + "Texan", + "Californian", + "Quebecois", + "Acadian", + "Louisianan", + "Appalachian", + "Yankee", + "Frontiersman" + ], + "suffixes": [ + "Nation", + "Confederation", + "Territory", + "Federation", + "Republic", + "State", + "Province", + "Colony", + "Commonwealth", + "Dominion", + "Council", + "Assembly", + "Tribe", + "Clan", + "League", + "Alliance", + "Union", + "Band", + "Settlement", + "Frontier", + "Range", + "Heartland", + "Homeland", + "Territories" + ] + }, + "south_america": { + "prefixes": [ + "Incan", + "Mapuche", + "Guarani", + "Tupi", + "Yanomami", + "Quechua", + "Aymara", + "Chibcha", + "Muisca", + "Taíno", + "Carib", + "Wari", + "Chimu", + "Nazca", + "Moche", + "Olmec", + "Zapotec", + "Mixtec", + "Toltec", + "Brazilian", + "Argentine", + "Chilean", + "Colombian", + "Peruvian", + "Venezuelan", + "Ecuadorian", + "Bolivian", + "Paraguayan", + "Uruguayan", + "Guyanese", + "Surinamese", + "Patagonian", + "Amazonian", + "Andean", + "Gaucho", + "Carioca", + "Porteño", + "Cordobés", + "Bogotano", + "Limeño" + ], + "suffixes": [ + "Empire", + "Kingdom", + "Republic", + "Confederation", + "Federation", + "State", + "Nation", + "Territory", + "Province", + "Chiefdom", + "Dynasty", + "Alliance", + "League", + "Council", + "Tribe", + "Clan", + "Collective", + "Domain", + "Realm", + "Heartland", + "Amazonia", + "Patagonia", + "Andes", + "Pampas" + ] + }, + "europe": { + "prefixes": [ + "Roman", + "Greek", + "Byzantine", + "Frankish", + "Visigothic", + "Ostrogothic", + "Viking", + "Norman", + "Saxon", + "Anglo-Saxon", + "Celtic", + "Gaulish", + "Carolingian", + "Merovingian", + "Capetian", + "Plantagenet", + "Tudor", + "Stuart", + "Habsburg", + "Romanov", + "Lancaster", + "York", + "Bourbon", + "Napoleonic", + "British", + "French", + "Spanish", + "Portuguese", + "Dutch", + "Russian", + "German", + "Italian", + "Swedish", + "Norwegian", + "Danish", + "Polish", + "Hungarian", + "Austrian", + "Swiss", + "Czech", + "Slovak", + "Serbian", + "Croatian", + "Bosnian", + "Montenegrin", + "Bulgarian", + "Romanian", + "Finnish", + "Estonian", + "Latvian", + "Lithuanian", + "Irish", + "Scottish", + "Welsh", + "Basque", + "Galician", + "Sardinian", + "Sicilian", + "Venetian", + "Florentine", + "Genoese", + "Milanese", + "Prussian", + "Bavarian", + "Teutonic", + "Knights Templar", + "Crusader", + "Papal", + "Holy Roman" + ], + "suffixes": [ + "Kingdom", + "Empire", + "Republic", + "Duchy", + "Grand Duchy", + "Archduchy", + "County", + "Principality", + "Barony", + "Lordship", + "March", + "Shire", + "Fief", + "Realm", + "Dominion", + "Crown", + "Dynasty", + "House", + "Order", + "League", + "Confederation", + "Alliance", + "Pact", + "Union", + "Federation", + "Commonwealth", + "State", + "Nation", + "Province", + "Territory" + ] + }, + "africa": { + "prefixes": [ + "Egyptian", + "Nubian", + "Kushite", + "Aksumite", + "Ethiopian", + "Songhai", + "Malian", + "Ghanaian", + "Benin", + "Ashanti", + "Zulu", + "Tuareg", + "Berber", + "Kanem-Bornu", + "Buganda", + "Mossi", + "Swahili", + "Somali", + "Wolof", + "Yoruba", + "Igbo", + "Hausa", + "Fulani", + "Maasai", + "Kikuyu", + "Shona", + "Luba", + "Lunda", + "Kongo", + "Lozi", + "Tswana", + "Sotho", + "Xhosa", + "Rwandan", + "Burundian", + "Baganda", + "Bamileke", + "Fang", + "San", + "Khoikhoi", + "Merina", + "Sakalava", + "Betsileo", + "Matabele", + "Rozvi", + "Mutapa" + ], + "suffixes": [ + "Empire", + "Kingdom", + "Sultanate", + "Caliphate", + "Dynasty", + "Chiefdom", + "Confederation", + "Republic", + "State", + "Territory", + "Province", + "Region", + "Nation", + "Tribe", + "Clan", + "Alliance", + "League", + "Council", + "Assembly", + "Federation", + "Union", + "Dominion", + "Realm", + "Colony", + "Protectorate" + ] + }, + "asia": { + "prefixes": [ + "Ming", + "Tang", + "Song", + "Yuan", + "Mauryan", + "Kushan", + "Rajput", + "Mughal", + "Satavahana", + "Vijayanagara", + "Japanese", + "Ryukyu", + "Ainu", + "Cham", + "Khmer", + "Thai", + "Vietnamese", + "Burmese", + "Balinese", + "Malay", + "Filipino", + "Mongolian", + "Korean", + "Tibetan", + "Manchu", + "Uyghur", + "Hmong", + "Karen", + "Pyu", + "Han", + "Qin", + "Jin", + "Sui", + "Zhou", + "Shang", + "Xia", + "Choson", + "Goguryeo", + "Baekje", + "Silla", + "Goryeo", + "Joseon", + "Yamato", + "Heian", + "Tokugawa", + "Ashikaga", + "Kamakura", + "Fujiwara", + "Taira", + "Minamoto", + "Srivijayan", + "Majapahit", + "Mataram", + "Ayutthayan", + "Sukhothai", + "Lanna", + "Champa", + "Dai Viet", + "Khitan", + "Jurchen", + "Xiongnu", + "Göktürk", + "Tangut", + "Timurid", + "Gupta", + "Pala", + "Chola", + "Pandya", + "Rashtrakuta", + "Chalukya", + "Hoysala", + "Kalinga" + ], + "suffixes": [ + "Dynasty", + "Empire", + "Kingdom", + "Shogunate", + "Sultanate", + "Khanate", + "Principality", + "Republic", + "State", + "Confederation", + "Horde", + "Realm", + "Province", + "Commandery", + "Satrapy", + "Viceroyalty", + "Mandate", + "Regency", + "Clan", + "Domain", + "Fief", + "March", + "Colony", + "Protectorate", + "Tributary" + ] + }, + "oceania": { + "prefixes": [ + "Hawaiian", + "Fijian", + "Tongan", + "Samoan", + "Maori", + "Micronesian", + "Polynesian", + "Melanesian", + "Papuan", + "Aboriginal", + "Torres Strait", + "Australian", + "New Zealand", + "Tahitian", + "Marquesan", + "Rapa Nui", + "Chamorro", + "Carolinian", + "Marshallese", + "Palauan", + "Nauruan", + "Tuvaluan", + "Niuean", + "Cook Islander", + "Tokelauan", + "Wallisian", + "Futunan", + "New Caledonian", + "Vanuatuan", + "Solomon Islander", + "Bougainvillean" + ], + "suffixes": [ + "Islands", + "Nation", + "Federation", + "Confederation", + "Kingdom", + "Republic", + "Commonwealth", + "Territory", + "Council", + "Alliance", + "League", + "Union", + "Domain", + "Realm", + "Parliament", + "Assembly", + "Tribal Council", + "Clan", + "Tribe", + "Homelands", + "Archipelago", + "Atoll", + "Lagoon" + ] + }, + "space": { + "prefixes": [ + "Andromedan", + "Centaurian", + "Sirian", + "Arcturian", + "Pleiadian", + "Vegan", + "Lyran", + "Orion", + "Antarian", + "Procyon", + "Aldebaran", + "Rigelian", + "Betelgeusian", + "Denebian", + "Altairian", + "Fomalhautian", + "Capellan", + "Zeta", + "Tau", + "Epsilon", + "Keplerian", + "Gliesean", + "Trappist", + "Proximan", + "Barnardian", + "Wolfian", + "Luytenian", + "Rossian", + "Cygnian", + "Aquilan", + "Draconian", + "Pegasian", + "Hydran", + "Corvan", + "Nebulan", + "Quasar", + "Pulsar", + "Novan", + "Stellar", + "Cosmic", + "Galactic", + "Lunar", + "Solar", + "Astral", + "Voidborn", + "Starforged", + "Nebulite", + "Quantum", + "Dark Matter" + ], + "suffixes": [ + "Dominion", + "Empire", + "Federation", + "Confederacy", + "Commonwealth", + "Collective", + "Hive", + "Alliance", + "Coalition", + "Concordat", + "Protectorate", + "Mandate", + "Territory", + "Sector", + "Cluster", + "Nebula", + "Constellation", + "Armada", + "Fleet", + "Legion", + "Order", + "Syndicate", + "Corporation", + "Council", + "Tribunal", + "Ascendancy", + "Supremacy", + "Swarm", + "Horde", + "Brotherhood" + ] + }, + "fantasy": { + "prefixes": [ + "Dragon", + "Elven", + "Dwarven", + "Orcish", + "Goblin", + "Fairy", + "Shadow", + "Crystal", + "Storm", + "Iron", + "Steel", + "Golden", + "Silver", + "Emerald", + "Ruby", + "Sapphire", + "Obsidian", + "Moonlit", + "Starlit", + "Twilight", + "Dawn", + "Dusk", + "Eternal", + "Ancient", + "Arcane", + "Mystic", + "Enchanted", + "Cursed", + "Blessed", + "Sacred", + "Dark", + "Light", + "Frost", + "Flame", + "Thorn", + "Raven", + "Wolf", + "Bear", + "Eagle", + "Lion", + "Serpent", + "Phoenix", + "Griffin", + "Unicorn", + "Pegasus", + "Troll", + "Giant", + "Fey", + "Sylvan", + "Abyssal", + "Celestial", + "Infernal", + "Primordial", + "Runed", + "Warded" + ], + "suffixes": [ + "Kingdom", + "Realm", + "Dominion", + "Empire", + "Dynasty", + "Order", + "Covenant", + "Pact", + "Alliance", + "Guild", + "Circle", + "Council", + "Conclave", + "Sanctum", + "Citadel", + "Throne", + "Crown", + "Spire", + "Hollow", + "Reach", + "Bastion", + "Haven", + "Enclave", + "Brotherhood", + "Sisterhood", + "Horde", + "Legion", + "Swarm", + "Host", + "House", + "Clan", + "Tribe", + "Nation", + "Domain" + ] + }, + "war": { + "prefixes": [ + "Iron", + "Steel", + "Thunder", + "Lightning", + "Blitz", + "Vanguard", + "Phalanx", + "Spartan", + "Gladiator", + "Centurion", + "Hussar", + "Cossack", + "Samurai", + "Mongol", + "Hoplite", + "Legionary", + "Musketeer", + "Dragoon", + "Lancer", + "Commando", + "Ranger", + "Sentinel", + "Warden", + "Reaver", + "Berserker", + "Juggernaut", + "Valkyrie", + "Warlord", + "Overlord", + "General", + "Marshal", + "Captain", + "Sergeant", + "Colonel", + "Admiral", + "Commodore", + "Tactician", + "Strategist", + "Warmonger", + "Conqueror", + "Liberator", + "Insurgent", + "Partisan", + "Guerrilla", + "Militia", + "Mercenary", + "Veteran", + "Elite" + ], + "suffixes": [ + "Legion", + "Army", + "Corps", + "Division", + "Brigade", + "Battalion", + "Regiment", + "Company", + "Platoon", + "Squad", + "Phalanx", + "Bastion", + "Front", + "Theater", + "Warband", + "Host", + "Horde", + "Force", + "Command", + "Operations", + "Strike", + "Assault", + "Defense", + "Garrison", + "Outpost", + "Fortress", + "Citadel", + "Arsenal", + "Armory", + "War Council", + "High Command", + "Tribunal" + ] + }, + "western": { + "prefixes": [ + "Cowboy", + "Outlaw", + "Sheriff", + "Marshal", + "Ranger", + "Desperado", + "Gunslinger", + "Bounty Hunter", + "Frontiersman", + "Pioneer", + "Prospector", + "Rancher", + "Bandit", + "Renegade", + "Drifter", + "Wrangler", + "Vaquero", + "Buckaroo", + "Lawman", + "Deputy", + "Scout", + "Trapper", + "Miner", + "Preacher", + "Doc", + "Dusty", + "Lonesome", + "Wild", + "Iron", + "Silver", + "Gold", + "Copper", + "Dakota", + "Texas", + "Arizona", + "Montana", + "Nevada", + "Colorado", + "Wyoming", + "Oklahoma", + "Kansas", + "Nebraska", + "New Mexico", + "Deadwood", + "Tombstone" + ], + "suffixes": [ + "Territory", + "Frontier", + "Range", + "County", + "District", + "Settlement", + "Outpost", + "Trading Post", + "Ranch", + "Claim", + "Camp", + "Crossing", + "Pass", + "Gulch", + "Canyon", + "Ridge", + "Valley", + "Springs", + "Flats", + "Hollow", + "Posse", + "Gang", + "Cartel", + "Syndicate", + "Alliance", + "Confederation", + "Sheriff's Office", + "Marshal's Office", + "Wagon Train", + "Cattle Drive" + ] + }, + "under_ocean": { + "prefixes": [ + "Atlantean", + "Abyssal", + "Coral", + "Deep", + "Leviathan", + "Kraken", + "Nautilus", + "Trident", + "Pearl", + "Tidal", + "Submarine", + "Bathyal", + "Hadal", + "Pelagic", + "Neritic", + "Benthic", + "Abyssopelagic", + "Mesopelagic", + "Epipelagic", + "Siren", + "Merfolk", + "Triton", + "Poseidon", + "Neptune", + "Nereid", + "Scylla", + "Charybdis", + "Lusca", + "Jormungandr", + "Tiamat", + "Naga", + "Selkie", + "Kelpie", + "Bunyip", + "Djinn", + "Undine", + "Nixie", + "Rusalka" + ], + "suffixes": [ + "Kingdom", + "Depth", + "Abyss", + "Trench", + "Reef", + "Lagoon", + "Atoll", + "Archipelago", + "Domain", + "Realm", + "Empire", + "Dynasty", + "Confederation", + "Federation", + "Collective", + "Colony", + "Hive", + "Swarm", + "School", + "Council", + "Conclave", + "Order", + "Brotherhood", + "Sisterhood", + "Horde", + "Legion", + "Host", + "Current", + "Tide", + "Depths", + "Waters", + "Seas" + ] + }, + "tournament": { + "prefixes": [ + "Champion", + "Knight", + "Gladiator", + "Warrior", + "Paladin", + "Hero", + "Defender", + "Guardian", + "Protector", + "Avenger", + "Slayer", + "Conqueror", + "Victor", + "Legend", + "Titan", + "Colossus", + "Goliath", + "Hercules", + "Achilles", + "Odysseus", + "Leonidas", + "Alexander", + "Caesar", + "Attila", + "Genghis", + "Napoleon", + "Valor", + "Honor", + "Glory", + "Triumph", + "Dominance", + "Supremacy", + "Invincible", + "Unstoppable", + "Relentless", + "Fearless", + "Ruthless", + "Merciless", + "Undying", + "Immortal", + "Eternal", + "Ascendant", + "Apex" + ], + "suffixes": [ + "Arena", + "Colosseum", + "Ring", + "Tournament", + "Championship", + "League", + "Series", + "Circuit", + "Grand Prix", + "Classic", + "Invitational", + "Open", + "Cup", + "Trophy", + "Prize", + "Crown", + "Laurels", + "Medal", + "Award", + "Honor Guard", + "Elite", + "Champions", + "Masters", + "Grandmasters", + "Titans", + "Legends", + "Hall of Fame", + "Order of Merit", + "Circle" + ] + }, + "funny": { + "prefixes": [ + "Silly", + "Wacky", + "Zany", + "Goofy", + "Bonkers", + "Loony", + "Nutty", + "Kooky", + "Quirky", + "Batty", + "Daffy", + "Dizzy", + "Loopy", + "Wobbly", + "Funky", + "Groovy", + "Spunky", + "Peppy", + "Bouncy", + "Jolly", + "Merry", + "Cheerful", + "Giggly", + "Snicker", + "Chuckle", + "Tickle", + "Pickle", + "Noodle", + "Waffle", + "Pancake", + "Muffin", + "Cupcake", + "Donut", + "Taco", + "Burrito", + "Nacho", + "Pretzel", + "Bagel", + "Cookie", + "Biscuit", + "Crumpet", + "Scone", + "Pudding", + "Custard", + "Trifle", + "Jellybean", + "Gummy", + "Lollipop" + ], + "suffixes": [ + "Squad", + "Team", + "Club", + "Gang", + "Crew", + "Bunch", + "Pack", + "Mob", + "Horde", + "Swarm", + "Flock", + "Herd", + "School", + "Colony", + "Band", + "Troupe", + "Ensemble", + "Posse", + "Clique", + "Circle", + "Society", + "Association", + "Fellowship", + "Brotherhood", + "Sisterhood", + "Alliance", + "Coalition", + "League", + "Union", + "Federation", + "Confederation", + "Collective" + ] + }, + "scary": { + "prefixes": [ + "Dark", + "Shadow", + "Nightmare", + "Ghoul", + "Phantom", + "Specter", + "Wraith", + "Revenant", + "Banshee", + "Dread", + "Terror", + "Horror", + "Malice", + "Venom", + "Plague", + "Pestilence", + "Famine", + "Death", + "Doom", + "Ruin", + "Blight", + "Curse", + "Hex", + "Jinx", + "Spell", + "Charm", + "Hexen", + "Witch", + "Warlock", + "Necromancer", + "Lich", + "Vampire", + "Werewolf", + "Zombie", + "Skeleton", + "Corpse", + "Carrion", + "Rot", + "Decay", + "Void", + "Abyss", + "Oblivion", + "Entropy", + "Chaos", + "Madness", + "Insanity", + "Dementia", + "Psychosis" + ], + "suffixes": [ + "Horde", + "Legion", + "Swarm", + "Plague", + "Scourge", + "Blight", + "Curse", + "Nightmare", + "Terror", + "Dread", + "Fear", + "Panic", + "Ruin", + "Doom", + "Death", + "Reaping", + "Harvest", + "Culling", + "Purge", + "Extermination", + "Brood", + "Nest", + "Lair", + "Den", + "Crypt", + "Tomb", + "Catacombs", + "Coven", + "Circle", + "Order", + "Cult", + "Sect", + "Brotherhood", + "Pact" + ] + }, + "weird": { + "prefixes": [ + "Bizarre", + "Strange", + "Odd", + "Peculiar", + "Weird", + "Eerie", + "Uncanny", + "Surreal", + "Absurd", + "Grotesque", + "Macabre", + "Eldritch", + "Ethereal", + "Spectral", + "Gossamer", + "Translucent", + "Iridescent", + "Opalescent", + "Prismatic", + "Kaleidoscopic", + "Phantasmagoric", + "Ineffable", + "Inscrutable", + "Paradoxical", + "Impossible", + "Inconceivable", + "Unfathomable", + "Inexplicable", + "Anomalous", + "Aberrant", + "Deviant", + "Atypical", + "Unorthodox", + "Heterodox", + "Nonconformist", + "Maverick", + "Rogue", + "Rebel", + "Dissident", + "Heretic" + ], + "suffixes": [ + "Collective", + "Hive", + "Swarm", + "Network", + "Matrix", + "Web", + "Lattice", + "Grid", + "Array", + "Spectrum", + "Prism", + "Kaleidoscope", + "Paradox", + "Anomaly", + "Aberration", + "Enigma", + "Mystery", + "Riddle", + "Puzzle", + "Conundrum", + "Dilemma", + "Quandary", + "Oxymoron", + "Contradiction", + "Dichotomy", + "Juxtaposition", + "Irony", + "Satire", + "Absurdity", + "Nonsense" + ] + }, + "vs": { + "prefixes": [ + "Red", + "Blue", + "Green", + "Yellow", + "Orange", + "Purple", + "Pink", + "Black", + "White", + "Grey", + "Gold", + "Silver", + "Bronze", + "Copper", + "Iron", + "Steel", + "Alpha", + "Beta", + "Gamma", + "Delta", + "Epsilon", + "Zeta", + "Eta", + "Theta", + "North", + "South", + "East", + "West", + "Dawn", + "Dusk", + "Day", + "Night", + "Fire", + "Water", + "Earth", + "Air", + "Lightning", + "Ice", + "Nature", + "Shadow", + "Light", + "Dark", + "Order", + "Chaos", + "Life", + "Death", + "War", + "Peace" + ], + "suffixes": [ + "Team", + "Squad", + "Crew", + "Division", + "Brigade", + "Regiment", + "Battalion", + "Company", + "Platoon", + "Unit", + "Force", + "Command", + "Operations", + "Strike", + "Assault", + "Defense", + "Vanguard", + "Rearguard", + "Flank", + "Center", + "Wing", + "Corps", + "Legion", + "Army", + "Horde", + "Host", + "Alliance", + "Coalition", + "League", + "Union", + "Federation", + "Confederation" + ] + } +} diff --git a/src/client/view/GameView.ts b/src/client/view/GameView.ts index 71044ea82..23dd41f03 100644 --- a/src/client/view/GameView.ts +++ b/src/client/view/GameView.ts @@ -3,6 +3,7 @@ import { Cell, GameUpdates, PlayerID, + PlayerType, TerrainType, TerraNullius, Tick, @@ -376,8 +377,12 @@ export class GameView implements GameMap { pu, gu.playerNameViewData?.[pu.id], // First check human by clientID, then check nation by name. + // Only match by name for actual Nations — not Bots (tribes) whose + // random names may coincidentally match a nation name. this._cosmetics.get(pu.clientID ?? "") ?? - this._cosmetics.get(pu.name!) ?? + (pu.playerType === PlayerType.Nation + ? this._cosmetics.get(pu.name!) + : undefined) ?? {}, ); this._players.set(pu.id, player); diff --git a/src/core/Util.ts b/src/core/Util.ts index 1e69f452e..3153bfbeb 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -13,10 +13,7 @@ import { Winner, } from "./Schemas"; -import { - TRIBE_NAME_PREFIXES, - TRIBE_NAME_SUFFIXES, -} from "./execution/utils/TribeNames"; +import { resolveTribeNameData } from "./execution/utils/TribeNames"; export function manhattanDistWrapped( c1: Cell, @@ -366,13 +363,12 @@ export function createRandomName( ): string | null { let randomName: string | null = null; if (playerType === PlayerType.Human) { + const { prefixes, suffixes } = resolveTribeNameData(); const hash = simpleHash(name); - const prefixIndex = hash % TRIBE_NAME_PREFIXES.length; - const suffixIndex = - Math.floor(hash / TRIBE_NAME_PREFIXES.length) % - TRIBE_NAME_SUFFIXES.length; + const prefixIndex = hash % prefixes.length; + const suffixIndex = Math.floor(hash / prefixes.length) % suffixes.length; - randomName = `👤 ${TRIBE_NAME_PREFIXES[prefixIndex]} ${TRIBE_NAME_SUFFIXES[suffixIndex]}`; + randomName = `👤 ${prefixes[prefixIndex]} ${suffixes[suffixIndex]}`; } return randomName; } diff --git a/src/core/execution/TribeSpawner.ts b/src/core/execution/TribeSpawner.ts index 18529bccd..ec1f775b7 100644 --- a/src/core/execution/TribeSpawner.ts +++ b/src/core/execution/TribeSpawner.ts @@ -3,10 +3,12 @@ import { PseudoRandom } from "../PseudoRandom"; import { GameID } from "../Schemas"; import { simpleHash } from "../Util"; import { SpawnExecution } from "./SpawnExecution"; -import { TRIBE_NAME_PREFIXES, TRIBE_NAME_SUFFIXES } from "./utils/TribeNames"; +import { type TribeNameData, resolveTribeNameData } from "./utils/TribeNames"; export class TribeSpawner { private random: PseudoRandom; + private tribeNameData: TribeNameData; + private usedCustomTribes: Set = new Set(); constructor( private gs: Game, @@ -15,6 +17,7 @@ export class TribeSpawner { // Use a different seed than createGameRunner (which uses simpleHash(gameID)) // to avoid tribe IDs colliding with nation/human IDs from the same PRNG sequence. this.random = new PseudoRandom(simpleHash(gameID) + 2); + this.tribeNameData = resolveTribeNameData(gs.config().gameConfig().gameMap); } spawnTribes(numTribes: number): SpawnExecution[] { @@ -33,8 +36,24 @@ export class TribeSpawner { } private randomTribeName(): string { - const prefixIndex = this.random.nextInt(0, TRIBE_NAME_PREFIXES.length); - const suffixIndex = this.random.nextInt(0, TRIBE_NAME_SUFFIXES.length); - return `${TRIBE_NAME_PREFIXES[prefixIndex]} ${TRIBE_NAME_SUFFIXES[suffixIndex]}`; + const { customTribes, prefixes, suffixes } = this.tribeNameData; + + // Use custom tribes first (random selection, no duplicates until exhausted). + if (customTribes !== undefined) { + const available = customTribes.filter( + (name) => !this.usedCustomTribes.has(name), + ); + if (available.length > 0) { + const index = this.random.nextInt(0, available.length); + const chosen = available[index]; + this.usedCustomTribes.add(chosen); + return chosen; + } + } + + // Fall back to theme-based prefix + suffix names. + const prefixIndex = this.random.nextInt(0, prefixes.length); + const suffixIndex = this.random.nextInt(0, suffixes.length); + return `${prefixes[prefixIndex]} ${suffixes[suffixIndex]}`; } } diff --git a/src/core/execution/utils/TribeNames.ts b/src/core/execution/utils/TribeNames.ts index 8f0786dbf..198506db2 100644 --- a/src/core/execution/utils/TribeNames.ts +++ b/src/core/execution/utils/TribeNames.ts @@ -1,248 +1,88 @@ -export const TRIBE_NAME_PREFIXES = [ - "Akkadian", - "Babylonian", - "Sumerian", - "Hittite", - "Phoenician", - "Canaanite", - "Minoan", - "Mycenaean", - "Etruscan", - "Scythian", - "Thracian", - "Dacian", - "Illyrian", - "Median", - "Chaldean", - "Roman", - "Greek", - "Byzantine", - "Persian", - "Parthian", - "Seleucid", - "Ptolemaic", - "Palmyrene", - "Macedonian", - "Carthaginian", - "Ming", - "Tang", - "Song", - "Yuan", - "Mauryan", - "Kushan", - "Rajput", - "Mughal", - "Satavahana", - "Vijayanagara", - "Egyptian", - "Nubian", - "Kushite", - "Aksumite", - "Ethiopian", - "Songhai", - "Malian", - "Ghanaian", - "Benin", - "Ashanti", - "Zulu", - "Tuareg", - "Berber", - "Kanem-Bornu", - "Buganda", - "Mossi", - "Swahili", - "Somali", - "Wolof", - "Umayyad", - "Abbasid", - "Ayyubid", - "Fatimid", - "Mamluk", - "Seljuk", - "Safavid", - "Ottoman", - "Almoravid", - "Almohad", - "Rashidun", - "Ziyarid", - "Frankish", - "Visigothic", - "Ostrogothic", - "Viking", - "Norman", - "Saxon", - "Anglo-Saxon", - "Celtic", - "Gaulish", - "Carolingian", - "Merovingian", - "Capetian", - "Plantagenet", - "Tudor", - "Stuart", - "Habsburg", - "Romanov", - "Lancaster", - "York", - "Bourbon", - "Napoleonic", - "British", - "French", - "Spanish", - "Portuguese", - "Dutch", - "Russian", - "German", - "Italian", - "Swedish", - "Norwegian", - "Danish", - "Polish", - "Hungarian", - "Austrian", - "Swiss", - "Czech", - "Slovak", - "Serbian", - "Croatian", - "Bosnian", - "Montenegrin", - "Bulgarian", - "Romanian", - "Apache", - "Sioux", - "Cherokee", - "Navajo", - "Iroquois", - "Inuit", - "Arawak", - "Carib", - "Taino", - "Aztec", - "Mayan", - "Incan", - "Mapuche", - "Guarani", - "Tupi", - "Yanomami", - "Zuni", - "Hopi", - "Kiowa", - "Comanche", - "Shoshone", - "Japanese", - "Ryukyu", - "Ainu", - "Cham", - "Khmer", - "Thai", - "Vietnamese", - "Burmese", - "Balinese", - "Malay", - "Filipino", - "Mongolian", - "Korean", - "Tibetan", - "Manchu", - "Uyghur", - "Hmong", - "Karen", - "Pyu", - "Hawaiian", - "Fijian", - "Tongan", - "Samoan", - "Maori", - "Micronesian", - "Hebrew", - "Armenian", - "Circassian", - "Georgian", - "Kurdish", - "Turkic", - "Kazakh", - "Uzbek", - "Kyrgyz", - "Tajik", - "Uighur", - "Pashtun", - "Baloch", - "Afghan", - "Kenyan", - "Ugandan", - "Bhutanese", - "Latin", - "Moldovan", - "Militant", - "Spartan", -]; -export const TRIBE_NAME_SUFFIXES = [ - "Empire", - "Dynasty", - "Kingdom", - "Queendom", - "Sultanate", - "Confederation", - "Union", - "Republic", - "Caliphate", - "Dominion", - "Realm", - "State", - "Federation", - "Territory", - "Commonwealth", - "League", - "Duchy", - "Province", - "Protectorate", - "Colony", - "Mandate", - "Free State", - "Canton", - "Region", - "Nation", - "Assembly", - "Hierarchy", - "Archduchy", - "Grand Duchy", - "Metropolis", - "Cluster", - "Alliance", - "Tribunal", - "Council", - "Confederacy", - "Order", - "Regime", - "Syndicate", - "Guild", - "Corporation", - "Patriarchy", - "Matriarchy", - "Legion", - "Horde", - "Clan", - "Brotherhood", - "Sisterhood", - "Ascendancy", - "Supremacy", - "Tribe", - "Republics", - "Army", - "Dictatorship", - "Country", - "Oligarchy", - "Monkdom", - "Throng", - "Host", - "Area", - "District", - "Fief", - "Wilderness", - "Settlement", - "Parliament", - "Anarchy", - "Democracy", - "Autocracy", -]; +import tribeNameThemesData from "resources/tribeNameThemes.json"; +import { GameMapType, type MapInfo, maps } from "../../game/Maps.gen"; + +export interface TribeNameData { + prefixes: string[]; + suffixes: string[]; + customTribes?: string[]; +} + +interface TribeNameTheme { + prefixes: string[]; + suffixes: string[]; +} + +const tribeNameThemes: Record = tribeNameThemesData; + +/** Look up MapInfo by GameMapType. */ +function getMapInfo(mapType: GameMapType): MapInfo | undefined { + return maps.find((m) => m.type === mapType); +} + +/** + * Resolve tribe name data for a given map type. + * + * Priority: + * 1. Custom tribes from the map's info.json (used as-is, no prefix/suffix) + * 2. Theme-based names (prefix + suffix) from the referenced theme + * 3. Default theme if no theme is specified or the referenced theme is missing + */ +export function resolveTribeNameData(mapType?: GameMapType): TribeNameData { + if (mapType !== undefined) { + const mapInfo = getMapInfo(mapType); + if (mapInfo !== undefined) { + const themeNames = + mapInfo.themes !== undefined && mapInfo.themes.length > 0 + ? mapInfo.themes + : ["default"]; + + const mergedPrefixes: string[] = []; + const mergedSuffixes: string[] = []; + + for (const themeName of themeNames) { + const theme = tribeNameThemes[themeName]; + if (theme === undefined) { + console.warn( + `[TribeNames] Map "${mapType}" references unknown tribe name theme "${themeName}". Skipping.`, + ); + continue; + } + mergedPrefixes.push(...theme.prefixes); + mergedSuffixes.push(...theme.suffixes); + } + + // If all themes were unknown, fall back to default. + if (mergedPrefixes.length === 0 || mergedSuffixes.length === 0) { + const defaultTheme = tribeNameThemes["default"]; + if (defaultTheme === undefined) { + throw new Error( + "[TribeNames] Default theme is missing from tribeNameThemes.json", + ); + } + mergedPrefixes.push(...defaultTheme.prefixes); + mergedSuffixes.push(...defaultTheme.suffixes); + } + + return { + prefixes: mergedPrefixes, + suffixes: mergedSuffixes, + customTribes: + mapInfo.customTribes !== undefined && mapInfo.customTribes.length > 0 + ? mapInfo.customTribes + : undefined, + }; + } + } + + // No map type or map not found — use default theme. + const defaultTheme = tribeNameThemes["default"]; + if (defaultTheme === undefined) { + throw new Error( + "[TribeNames] Default theme is missing from tribeNameThemes.json", + ); + } + return { + prefixes: defaultTheme.prefixes, + suffixes: defaultTheme.suffixes, + }; +} diff --git a/src/core/game/Maps.gen.ts b/src/core/game/Maps.gen.ts index fe4c760c3..8ef4509be 100644 --- a/src/core/game/Maps.gen.ts +++ b/src/core/game/Maps.gen.ts @@ -167,6 +167,10 @@ export interface MapInfo { featuredRank?: number; /** Preferred team count in team/special games (see MapPlaylist). */ specialTeamCount?: number; + /** Tribe name theme(s) (keys in tribeNameThemes.json). */ + themes?: string[]; + /** Custom tribe names with priority over theme-generated names. */ + customTribes?: string[]; } export const maps: readonly MapInfo[] = [ @@ -469,6 +473,416 @@ export const maps: readonly MapInfo[] = [ translationKey: "map.germany", categories: ["europe", "new"], multiplayerFrequency: 5, + customTribes: [ + "Aachen, Städteregion", + "Ahrweiler", + "Aichach-Friedberg", + "Alb-Donau-Kreis", + "Altenburger Land", + "Altenkirchen (Westerwald)", + "Altmarkkreis Salzwedel", + "Altötting", + "Alzey-Worms", + "Amberg-Sulzbach", + "Ammerland", + "Anhalt-Bitterfeld", + "Ansbach", + "Aschaffenburg", + "Augsburg", + "Aurich", + "Bad Dürkheim", + "Bad Kissingen", + "Bad Kreuznach", + "Bad Tölz-Wolfratshausen", + "Bamberg", + "Barnim", + "Bautzen", + "Bayreuth", + "Berchtesgadener Land", + "Bergstraße", + "Bernkastel-Wittlich", + "Biberach", + "Birkenfeld", + "Böblingen", + "Bodenseekreis", + "Börde", + "Borken", + "Breisgau-Hochschwarzwald", + "Burgenlandkreis", + "Calw", + "Celle", + "Cham", + "Cloppenburg", + "Coburg", + "Cochem-Zell", + "Coesfeld", + "Cuxhaven", + "Dachau", + "Dahme-Spreewald", + "Darmstadt-Dieburg", + "Deggendorf", + "Diepholz", + "Dillingen an der Donau", + "Dingolfing-Landau", + "Dithmarschen", + "Donau-Ries", + "Donnersbergkreis", + "Düren", + "Ebersberg", + "Eichsfeld", + "Eichstätt", + "Eifelkreis Bitburg-Prüm", + "Elbe-Elster", + "Emmendingen", + "Emsland", + "Ennepe-Ruhr-Kreis", + "Enzkreis", + "Erding", + "Erlangen-Höchstadt", + "Erzgebirgskreis", + "Esslingen", + "Euskirchen", + "Forchheim", + "Freising", + "Freudenstadt", + "Freyung-Grafenau", + "Friesland", + "Fulda", + "Fürstenfeldbruck", + "Fürth", + "Garmisch-Partenkirchen", + "Germersheim", + "Gießen", + "Gifhorn", + "Göppingen", + "Görlitz", + "Goslar", + "Gotha", + "Göttingen", + "Grafschaft Bentheim", + "Greiz", + "Groß-Gerau", + "Günzburg", + "Gütersloh", + "Hameln-Pyrmont", + "Hannover, Region", + "Harburg", + "Harz", + "Haßberge", + "Havelland", + "Heidekreis", + "Heidenheim", + "Heilbronn", + "Heinsberg", + "Helmstedt", + "Herford", + "Hersfeld-Rotenburg", + "Herzogtum Lauenburg", + "Hildburghausen", + "Hildesheim", + "Hochsauerlandkreis", + "Hochtaunuskreis", + "Hof", + "Hohenlohekreis", + "Holzminden", + "Höxter", + "Ilm-Kreis", + "Jerichower Land", + "Kaiserslautern", + "Karlsruhe", + "Kassel", + "Kelheim", + "Kitzingen", + "Kleve", + "Konstanz", + "Kronach", + "Kulmbach", + "Kusel", + "Kyffhäuserkreis", + "Lahn-Dill-Kreis", + "Landsberg am Lech", + "Landshut", + "Leer", + "Leipzig", + "Lichtenfels", + "Limburg-Weilburg", + "Lindau (Bodensee)", + "Lippe", + "Lörrach", + "Lüchow-Dannenberg", + "Ludwigsburg", + "Ludwigslust-Parchim", + "Lüneburg", + "Main-Kinzig-Kreis", + "Main-Spessart", + "Main-Tauber-Kreis", + "Main-Taunus-Kreis", + "Mainz-Bingen", + "Mansfeld-Südharz", + "Marburg-Biedenkopf", + "Märkischer Kreis", + "Märkisch-Oderland", + "Mayen-Koblenz", + "Mecklenburgische Seenplatte", + "Meißen", + "Merzig-Wadern", + "Mettmann", + "Miesbach", + "Miltenberg", + "Minden-Lübbecke", + "Mittelsachsen", + "Mühldorf am Inn", + "München", + "Neckar-Odenwald-Kreis", + "Neu-Ulm", + "Neuburg-Schrobenhausen", + "Neumarkt in der Oberpfalz", + "Neunkirchen", + "Neustadt an der Aisch-Bad Windsheim", + "Neustadt an der Waldnaab", + "Neuwied", + "Nienburg/Weser", + "Nordfriesland", + "Nordhausen", + "Nordsachsen", + "Nordwestmecklenburg", + "Northeim", + "Nürnberger Land", + "Oberallgäu", + "Oberbergischer Kreis", + "Oberhavel", + "Oberspreewald-Lausitz", + "Odenwaldkreis", + "Oder-Spree", + "Offenbach", + "Oldenburg", + "Olpe", + "Ortenaukreis", + "Osnabrück", + "Ostalbkreis", + "Ostallgäu", + "Osterholz", + "Ostholstein", + "Ostprignitz-Ruppin", + "Paderborn", + "Passau", + "Peine", + "Pfaffenhofen an der Ilm", + "Pinneberg", + "Plön", + "Potsdam-Mittelmark", + "Prignitz", + "Rastatt", + "Ravensburg", + "Recklinghausen", + "Regen", + "Regensburg", + "Rems-Murr-Kreis", + "Rendsburg-Eckernförde", + "Reutlingen", + "Rhein-Erft-Kreis", + "Rheingau-Taunus-Kreis", + "Rhein-Hunsrück-Kreis", + "Rheinisch-Bergischer Kreis", + "Rhein-Kreis Neuss", + "Rhein-Lahn-Kreis", + "Rhein-Neckar-Kreis", + "Rhein-Pfalz-Kreis", + "Rhein-Sieg-Kreis", + "Rhön-Grabfeld", + "Rosenheim", + "Rostock", + "Rotenburg (Wümme)", + "Roth", + "Rottal-Inn", + "Rottweil", + "Saale-Holzland-Kreis", + "Saalekreis", + "Saale-Orla-Kreis", + "Saalfeld-Rudolstadt", + "Saarbrücken, Regionalverband", + "Saarlouis", + "Saarpfalz-Kreis", + "Sächsische Schweiz-Osterzgebirge", + "Salzlandkreis", + "Schaumburg", + "Schleswig-Flensburg", + "Schmalkalden-Meiningen", + "Schwäbisch Hall", + "Schwalm-Eder-Kreis", + "Schwandorf", + "Schwarzwald-Baar-Kreis", + "Schweinfurt", + "Segeberg", + "Siegen-Wittgenstein", + "Sigmaringen", + "Soest", + "Sömmerda", + "Sonneberg", + "Spree-Neiße", + "St. Wendel", + "Stade", + "Starnberg", + "Steinburg", + "Steinfurt", + "Stendal", + "Stormarn", + "Straubing-Bogen", + "Südliche Weinstraße", + "Südwestpfalz", + "Teltow-Fläming", + "Tirschenreuth", + "Traunstein", + "Trier-Saarburg", + "Tübingen", + "Tuttlingen", + "Uckermark", + "Uelzen", + "Unna", + "Unstrut-Hainich-Kreis", + "Unterallgäu", + "Vechta", + "Verden", + "Viersen", + "Vogelsbergkreis", + "Vogtlandkreis", + "Vorpommern-Greifswald", + "Vorpommern-Rügen", + "Vulkaneifel", + "Waldeck-Frankenberg", + "Waldshut", + "Warendorf", + "Wartburgkreis", + "Weilheim-Schongau", + "Weimarer Land", + "Weißenburg-Gunzenhausen", + "Werra-Meißner-Kreis", + "Wesel", + "Wesermarsch", + "Westerwaldkreis", + "Wetteraukreis", + "Wittenberg", + "Wittmund", + "Wolfenbüttel", + "Wunsiedel im Fichtelgebirge", + "Würzburg", + "Zollernalbkreis", + "Zwickau", + "Aachen", + "Amberg", + "Baden-Baden", + "Bielefeld", + "Bochum", + "Bonn", + "Bottrop", + "Brandenburg an der Havel", + "Braunschweig", + "Bremerhaven", + "Chemnitz", + "Cottbus", + "Darmstadt", + "Delmenhorst", + "Dessau-Roßlau", + "Dortmund", + "Dresden", + "Duisburg", + "Düsseldorf", + "Emden", + "Erfurt", + "Erlangen", + "Essen", + "Flensburg", + "Frankenthal (Pfalz)", + "Frankfurt (Oder)", + "Frankfurt am Main", + "Freiburg im Breisgau", + "Gelsenkirchen", + "Gera", + "Hagen", + "Halle (Saale)", + "Hamm", + "Hanau", + "Hannover", + "Heidelberg", + "Herne", + "Ingolstadt", + "Jena", + "Kaufbeuren", + "Kempten (Allgäu)", + "Kiel", + "Koblenz", + "Köln", + "Krefeld", + "Landau in der Pfalz", + "Leverkusen", + "Lübeck", + "Ludwigshafen am Rhein", + "Magdeburg", + "Mainz", + "Mannheim", + "Memmingen", + "Mönchengladbach", + "Mülheim an der Ruhr", + "Münster", + "Neumünster", + "Neustadt an der Weinstraße", + "Nürnberg", + "Oberhausen", + "Offenbach am Main", + "Oldenburg (Oldb)", + "Pforzheim", + "Pirmasens", + "Potsdam", + "Remscheid", + "Salzgitter", + "Schwabach", + "Schwerin", + "Solingen", + "Speyer", + "Straubing", + "Stuttgart", + "Suhl", + "Trier", + "Ulm", + "Weiden in der Oberpfalz", + "Weimar", + "Wiesbaden", + "Wilhelmshaven", + "Wolfsburg", + "Worms", + "Wuppertal", + "Zweibrücken", + "Preußen", + "Schwaben", + "Franken", + "Rheinland", + "Westfalen", + "Pommern", + "Schleswig", + "Holstein", + "Anhalt", + "Lausitz", + "Elsass", + "Deutscher Orden", + "Nassau", + "Kurpfalz", + "Berg", + "Jülich", + "Lothringen", + "Harzgau", + "Wetterau", + "Ruhrgebiet", + "Allgäu", + "Schwarzwald", + "Spreewald", + "Altmühltal", + "Spessart", + "Odenwald", + "Eifel", + "Sauerland", + "Westerwald", + "Pfalz", + ], }, { id: "GiantWorldMap", diff --git a/tests/MapConsistency.test.ts b/tests/MapConsistency.test.ts index 0806e8530..b0e22cd6d 100644 --- a/tests/MapConsistency.test.ts +++ b/tests/MapConsistency.test.ts @@ -144,6 +144,8 @@ describe("Map consistency", () => { orOmitted(info.special_team_count), map.specialTeamCount, ], + ["themes", orOmitted(info.themes), map.themes], + ["custom_tribes", orOmitted(info.custom_tribes), map.customTribes], ]; for (const [field, infoValue, mapValue] of fields) { if (JSON.stringify(infoValue) !== JSON.stringify(mapValue)) { diff --git a/tests/TribeNameThemes.test.ts b/tests/TribeNameThemes.test.ts new file mode 100644 index 000000000..3c7aecd08 --- /dev/null +++ b/tests/TribeNameThemes.test.ts @@ -0,0 +1,134 @@ +import { resolveTribeNameData } from "../src/core/execution/utils/TribeNames"; +import { GameMapType, PlayerType } from "../src/core/game/Game"; +import { PseudoRandom } from "../src/core/PseudoRandom"; +import { createRandomName } from "../src/core/Util"; + +describe("resolveTribeNameData", () => { + test("returns default theme when called with no arguments", () => { + const data = resolveTribeNameData(); + expect(data.prefixes.length).toBeGreaterThan(0); + expect(data.suffixes.length).toBeGreaterThan(0); + expect(data.customTribes).toBeUndefined(); + // Spot-check that the default theme contains known prefixes. + expect(data.prefixes).toContain("Roman"); + expect(data.prefixes).toContain("Viking"); + expect(data.suffixes).toContain("Empire"); + expect(data.suffixes).toContain("Kingdom"); + }); + + test("returns default theme when called with undefined", () => { + const data = resolveTribeNameData(undefined); + expect(data.prefixes.length).toBeGreaterThan(0); + expect(data.suffixes.length).toBeGreaterThan(0); + }); + + test("returns default theme for a map with no theme or customTribes", () => { + // Most maps in Maps.gen.ts have no theme/customTribes defined. + const data = resolveTribeNameData(GameMapType.World); + expect(data.prefixes.length).toBeGreaterThan(0); + expect(data.suffixes.length).toBeGreaterThan(0); + expect(data.customTribes).toBeUndefined(); + }); + + test("returns data for every valid GameMapType without throwing", () => { + for (const mapType of Object.values(GameMapType)) { + const data = resolveTribeNameData(mapType); + expect(data.prefixes.length).toBeGreaterThan(0); + expect(data.suffixes.length).toBeGreaterThan(0); + } + }); + + test("all default theme prefixes and suffixes are non-empty strings", () => { + const data = resolveTribeNameData(); + for (const prefix of data.prefixes) { + expect(prefix.length).toBeGreaterThan(0); + } + for (const suffix of data.suffixes) { + expect(suffix.length).toBeGreaterThan(0); + } + }); + + test("prefix + suffix combinations produce valid tribe names", () => { + const data = resolveTribeNameData(); + const random = new PseudoRandom(42); + for (let i = 0; i < 50; i++) { + const prefixIndex = random.nextInt(0, data.prefixes.length); + const suffixIndex = random.nextInt(0, data.suffixes.length); + const name = `${data.prefixes[prefixIndex]} ${data.suffixes[suffixIndex]}`; + expect(name.length).toBeGreaterThan(0); + expect(name).toContain(" "); + } + }); +}); + +describe("TribeNameData consistency", () => { + test("default theme has no duplicate prefixes", () => { + const data = resolveTribeNameData(); + const unique = new Set(data.prefixes); + expect(unique.size).toBe(data.prefixes.length); + }); + + test("default theme has no duplicate suffixes", () => { + const data = resolveTribeNameData(); + const unique = new Set(data.suffixes); + expect(unique.size).toBe(data.suffixes.length); + }); +}); + +describe("tribeNameThemes.json", () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const themes = require("../resources/tribeNameThemes.json"); + + test("default theme exists", () => { + expect(themes.default).toBeDefined(); + expect(themes.default.prefixes).toBeInstanceOf(Array); + expect(themes.default.suffixes).toBeInstanceOf(Array); + }); + + test("all themes have non-empty prefixes and suffixes", () => { + for (const [, theme] of Object.entries(themes)) { + const t = theme as { prefixes: string[]; suffixes: string[] }; + expect(t.prefixes.length).toBeGreaterThan(0); + expect(t.suffixes.length).toBeGreaterThan(0); + } + }); + + test("all theme entries are non-empty strings", () => { + for (const [, theme] of Object.entries(themes)) { + const t = theme as { prefixes: string[]; suffixes: string[] }; + for (const prefix of t.prefixes) { + expect(prefix.length).toBeGreaterThan(0); + } + for (const suffix of t.suffixes) { + expect(suffix.length).toBeGreaterThan(0); + } + } + }); + + test("at least 3 themes are defined", () => { + expect(Object.keys(themes).length).toBeGreaterThanOrEqual(3); + }); +}); + +describe("createRandomName (deterministic)", () => { + test("returns null for non-human players", () => { + expect(createRandomName("test", PlayerType.Bot)).toBeNull(); + }); + + test("returns a deterministic name for human players", () => { + const name1 = createRandomName("Alice", PlayerType.Human); + const name2 = createRandomName("Alice", PlayerType.Human); + expect(name1).toBe(name2); + expect(name1).toBeTruthy(); + expect(name1).toContain("👤"); + }); + + test("different names produce different random names (usually)", () => { + const name1 = createRandomName("Alice", PlayerType.Human); + const name2 = createRandomName("Bob", PlayerType.Human); + // They could theoretically collide, but with 178*66 combinations it's unlikely. + // We just verify both are valid. + expect(name1).toBeTruthy(); + expect(name2).toBeTruthy(); + }); +});