From af15d5972d938e716ced55e71f6186843b4bd9f0 Mon Sep 17 00:00:00 2001 From: elexis Date: Tue, 14 Jan 2020 07:02:40 +0000 Subject: [PATCH] Landscape and Daytime gamesetup options for the Unknown, Danubius and Polar Sea, refs #4838. Allow gamesetup options other than biome to determine the map peview image, refs #4962, 8adc6d8e93. Add previews for the 12 Unknown, 2 Danubius and 2 Polar Sea variations. Concludes deduplication of the Unknown (0d0bc32736), Unknown Land (25682da568) and Unknown Nomad (7f8adcf8cb), refs #4317. Differential Revision: https://code.wildfiregames.com/D2564 This was SVN commit r23392. --- .../session/icons/mappreview/danubius_day.png | 3 + .../{danubius.png => danubius_night.png} | 0 .../{polar_sea.png => polar_sea_dawn.png} | 0 .../icons/mappreview/polar_sea_daylight.png | 3 + .../icons/mappreview/unknown_archipelago.png | 3 + .../mappreview/unknown_central_river.png | 3 + .../icons/mappreview/unknown_central_sea.png | 3 + .../icons/mappreview/unknown_continent.png | 3 + .../icons/mappreview/unknown_edge_seas.png | 3 + .../session/icons/mappreview/unknown_gulf.png | 3 + .../icons/mappreview/unknown_isthmus.png | 3 + .../icons/mappreview/unknown_lakes.png | 3 + .../icons/mappreview/unknown_lowlands.png | 3 + .../icons/mappreview/unknown_mainland.png | 3 + .../icons/mappreview/unknown_passes.png | 3 + .../mappreview/unknown_rivers_and_lake.png | 3 + .../data/mods/public/gui/common/MapCache.js | 20 +- .../GameSettings/GameSettingsLayout.js | 2 + .../GameSettings/Single/Dropdowns/Biome.js | 18 +- .../GameSettings/Single/Dropdowns/Daytime.js | 107 ++ .../Single/Dropdowns/Landscape.js | 145 +++ .../public/gui/gamesetup/Panels/MapPreview.js | 12 + .../data/mods/public/maps/random/danubius.js | 16 +- .../mods/public/maps/random/danubius.json | 17 +- .../data/mods/public/maps/random/polar_sea.js | 10 +- .../mods/public/maps/random/polar_sea.json | 17 +- .../maps/random/the_unknown/unknown_common.js | 1060 ---------------- .../data/mods/public/maps/random/unknown.js | 1070 ++++++++++++++++- .../data/mods/public/maps/random/unknown.json | 82 +- .../mods/public/maps/random/unknown_land.js | 10 - .../mods/public/maps/random/unknown_land.json | 10 - 31 files changed, 1525 insertions(+), 1113 deletions(-) create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_day.png rename binaries/data/mods/public/art/textures/ui/session/icons/mappreview/{danubius.png => danubius_night.png} (100%) rename binaries/data/mods/public/art/textures/ui/session/icons/mappreview/{polar_sea.png => polar_sea_dawn.png} (100%) create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_daylight.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_archipelago.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_river.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_sea.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_continent.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_edge_seas.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_gulf.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_isthmus.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lakes.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lowlands.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_mainland.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_passes.png create mode 100644 binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_rivers_and_lake.png create mode 100644 binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Daytime.js create mode 100644 binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Landscape.js delete mode 100644 binaries/data/mods/public/maps/random/the_unknown/unknown_common.js delete mode 100644 binaries/data/mods/public/maps/random/unknown_land.js delete mode 100644 binaries/data/mods/public/maps/random/unknown_land.json diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_day.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_day.png new file mode 100644 index 0000000000..d8fb9489c9 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_day.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e3c591f6fcb0f6757fad440ea58a5c3e4cbfbe6eebd7f323ce56f359eeeeee2 +size 126023 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_night.png similarity index 100% rename from binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius.png rename to binaries/data/mods/public/art/textures/ui/session/icons/mappreview/danubius_night.png diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_dawn.png similarity index 100% rename from binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea.png rename to binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_dawn.png diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_daylight.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_daylight.png new file mode 100644 index 0000000000..054b7f91fc --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/polar_sea_daylight.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d313d730d0f96c4f7f0c635afdd3cafed37e4905d07547f716c0046e3ef22e33 +size 124104 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_archipelago.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_archipelago.png new file mode 100644 index 0000000000..b038a9fdf1 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_archipelago.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4f6d9f6e16d2f5c62569d4c4fb90f70c5e5507d086f5f514d2709e8fd52f528 +size 133302 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_river.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_river.png new file mode 100644 index 0000000000..6b88324256 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_river.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23668ff3fba1dc613ade63e23e93aac56646a1e23d577c5d6ae32e60d2365b6e +size 136070 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_sea.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_sea.png new file mode 100644 index 0000000000..0832216ecf --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_central_sea.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1a461ab292e9df5676d4bbe7c7ce917b1fab7d42b4a7d6b3cfef12115996ef4 +size 146836 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_continent.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_continent.png new file mode 100644 index 0000000000..1c1664a7e2 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_continent.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7afccfa004ab768f0a49fe985ee1f877d24af0435eb81854451f2c7adc40d1e1 +size 132012 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_edge_seas.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_edge_seas.png new file mode 100644 index 0000000000..a4c86a4883 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_edge_seas.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd0008fa5f8640db63e503c8891301d2509d7959aa04b04e5af060d12a411f8e +size 151083 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_gulf.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_gulf.png new file mode 100644 index 0000000000..480be3551e --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_gulf.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:173cceb679452adc357a60fa0c53bbf27521f9a0e20deec38efd578dda753944 +size 130579 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_isthmus.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_isthmus.png new file mode 100644 index 0000000000..baa7c1f18e --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_isthmus.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3df32aa8bbf54a37a85bcc1697ccf2491e1a37c824c1daceacb7f10f58d51f98 +size 146278 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lakes.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lakes.png new file mode 100644 index 0000000000..365bd68ca0 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lakes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a84011a3cbef8d6492b8adbaff7a18d93acbbf8c39307b9a8f3fcb8986740e39 +size 141883 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lowlands.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lowlands.png new file mode 100644 index 0000000000..f13f29bca3 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_lowlands.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:611febb458b0d93e4b854e821452215a3a80690bc02e5fb3ea8fdc27a71cec18 +size 147220 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_mainland.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_mainland.png new file mode 100644 index 0000000000..ac2341f774 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_mainland.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:731a10d0baca2ffac4c10f0d6fcfbba504a82ad76e5aa4119a94bd7122330540 +size 146814 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_passes.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_passes.png new file mode 100644 index 0000000000..5bf45bcf98 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_passes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8379b0a8da0fd00a4f69d4e17b61cc04f5c03c58dec61a020c4c6618b45ac53 +size 145124 diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_rivers_and_lake.png b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_rivers_and_lake.png new file mode 100644 index 0000000000..3364de7d24 --- /dev/null +++ b/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/unknown_rivers_and_lake.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a4e04de14c1deef77b6d2b1b007ca6fef66f48aa805b5847efbdf71cfb9d726 +size 144083 diff --git a/binaries/data/mods/public/gui/common/MapCache.js b/binaries/data/mods/public/gui/common/MapCache.js index 280e882f39..da19ba4259 100644 --- a/binaries/data/mods/public/gui/common/MapCache.js +++ b/binaries/data/mods/public/gui/common/MapCache.js @@ -64,21 +64,13 @@ class MapCache getMapPreview(mapType, mapPath, gameAttributes = undefined) { - let mapData = this.getMapData(mapType, mapPath); + let filename = gameAttributes && gameAttributes.settings && gameAttributes.settings.Preview || undefined; - let biomePreviewFile = - basename(mapPath) + "_" + - basename(gameAttributes && gameAttributes.settings.Biome || "") + ".png"; - - let biomePreview = Engine.TextureExists( - this.TexturesPath + this.PreviewsPath + biomePreviewFile) && biomePreviewFile; - - let filename = - biomePreview ? - biomePreview : - mapData && mapData.settings && mapData.settings.Preview ? - mapData.settings.Preview : - this.DefaultPreview; + if (!filename) + { + let mapData = this.getMapData(mapType, mapPath); + filename = mapData && mapData.settings && mapData.settings.Preview || this.DefaultPreview; + } return "cropped:" + this.PreviewWidth + "," + this.PreviewHeight + ":" + this.PreviewsPath + filename; } diff --git a/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js b/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js index 6eca1252f6..1f55e0dca3 100644 --- a/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js +++ b/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js @@ -10,7 +10,9 @@ var g_GameSettingsLayout = [ "MapFilter", "MapSelection", "MapSize", + "Landscape", "Biome", + "Daytime", "TriggerDifficulty", "Nomad", "Treasures", diff --git a/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js index 0e20121606..afe2ef7681 100644 --- a/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js +++ b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js @@ -7,6 +7,8 @@ GameSettingControls.Biome = class extends GameSettingControlDropdown this.values = undefined; this.biomeValues = undefined; + this.lastBiome = undefined; + this.randomItem = { "Id": this.RandomBiomeId, "Title": setStringTags(this.RandomBiome, this.RandomItemTags), @@ -46,11 +48,13 @@ GameSettingControls.Biome = class extends GameSettingControlDropdown } else this.values = undefined; + + this.lastBiome = undefined; } onGameAttributesChange() { - if (!g_GameAttributes.mapType) + if (!g_GameAttributes.mapType || !g_GameAttributes.map) return; if (this.values) @@ -60,6 +64,18 @@ GameSettingControls.Biome = class extends GameSettingControlDropdown g_GameAttributes.settings.Biome = this.RandomBiomeId; this.gameSettingsControl.updateGameAttributes(); } + + if (this.lastBiome != g_GameAttributes.settings.Biome) + { + let biomePreviewFile = + basename(g_GameAttributes.map) + "_" + + basename(g_GameAttributes.settings.Biome || "") + ".png"; + + if (Engine.TextureExists(this.mapCache.TexturesPath + this.mapCache.PreviewsPath + biomePreviewFile)) + g_GameAttributes.settings.Preview = biomePreviewFile; + + this.lastBiome = g_GameAttributes.settings.Biome; + } } else if (g_GameAttributes.settings.Biome) { diff --git a/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Daytime.js b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Daytime.js new file mode 100644 index 0000000000..d852048588 --- /dev/null +++ b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Daytime.js @@ -0,0 +1,107 @@ +GameSettingControls.Daytime = class extends GameSettingControlDropdown +{ + constructor(...args) + { + super(...args); + + this.values = undefined; + } + + onHoverChange() + { + this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip; + } + + onMapChange(mapData) + { + if (mapData && mapData.settings && mapData.settings.Daytime) + { + this.values = prepareForDropdown([ + { + "Id": "random", + "Name": setStringTags(this.RandomTitle, this.RandomItemTags), + "Description": this.RandomDescription, + "Preview": mapData.settings.Preview + }, + ...mapData.settings.Daytime + ]); + + this.dropdown.list = this.values.Name; + this.dropdown.list_data = this.values.Id; + } + else + this.values = undefined; + + this.setHidden(!this.values); + } + + onGameAttributesChange() + { + if (!g_GameAttributes.map) + return; + + if (this.values) + { + if (this.values.Id.indexOf(g_GameAttributes.settings.Daytime || undefined) == -1) + { + g_GameAttributes.settings.Daytime = "random"; + this.gameSettingsControl.updateGameAttributes(); + } + + let preview = this.values.Preview[this.values.Id.indexOf(g_GameAttributes.settings.Daytime)]; + if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != preview) + { + g_GameAttributes.settings.Preview = preview; + this.gameSettingsControl.updateGameAttributes(); + } + } + else if (g_GameAttributes.settings.Daytime !== undefined) + { + delete g_GameAttributes.settings.Daytime; + this.gameSettingsControl.updateGameAttributes(); + } + } + + onGameAttributesBatchChange() + { + if (g_GameAttributes.settings.Daytime) + this.setSelectedValue(g_GameAttributes.settings.Daytime); + } + + getAutocompleteEntries() + { + return this.values.Name; + } + + onSelectionChange(itemIdx) + { + g_GameAttributes.settings.Daytime = this.values.Id[itemIdx]; + + this.gameSettingsControl.updateGameAttributes(); + this.gameSettingsControl.setNetworkGameAttributes(); + } + + onPickRandomItems() + { + if (this.values && g_GameAttributes.settings.Daytime == "random") + { + g_GameAttributes.settings.Daytime = pickRandom(this.values.Id.slice(1)); + this.gameSettingsControl.updateGameAttributes(); + this.gameSettingsControl.pickRandomItems(); + } + } +}; + +GameSettingControls.Daytime.prototype.TitleCaption = + translate("Daytime"); + +GameSettingControls.Daytime.prototype.Tooltip = + translate("Select whether the match takes place at daylight or night."); + +GameSettingControls.Daytime.prototype.RandomTitle = + translateWithContext("daytime selection", "Random"); + +GameSettingControls.Daytime.prototype.RandomDescription = + translateWithContext("daytime selection", "Randomly pick a time of the day."); + +GameSettingControls.Daytime.prototype.AutocompleteOrder = 0; diff --git a/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Landscape.js b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Landscape.js new file mode 100644 index 0000000000..76ab99dc8a --- /dev/null +++ b/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Landscape.js @@ -0,0 +1,145 @@ +GameSettingControls.Landscape = class extends GameSettingControlDropdown +{ + constructor(...args) + { + super(...args); + + this.values = undefined; + this.lastLandscape = undefined; + this.mapData = undefined; + } + + onHoverChange() + { + this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip; + } + + onMapChange(mapData) + { + this.mapData = mapData; + + if (mapData && mapData.settings && mapData.settings.Landscapes) + { + let randomItems = []; + for (let item of this.RandomItems) + if (item.Id == "random" || mapData.settings.Landscapes.land && mapData.settings.Landscapes.naval) + randomItems.push({ + "Id": item.Id, + "Name": setStringTags(item.Name, this.RandomItemTags), + "Description": item.Description, + "Preview": mapData.settings.Preview || this.mapCache.DefaultPreview + }); + + let sort = (item1, item2) => item1.Name > item2.Name; + + this.values = prepareForDropdown([ + ...randomItems, + ...mapData.settings.Landscapes.land.sort(sort), + ...mapData.settings.Landscapes.naval.sort(sort) + ]); + + this.dropdown.list = this.values.Name; + this.dropdown.list_data = this.values.Id; + } + else + this.values = undefined; + + this.lastLandscape = undefined; + + this.setHidden(!this.values); + } + + onGameAttributesChange() + { + if (this.values) + { + if (this.values.Id.indexOf(g_GameAttributes.settings.Landscape || undefined) == -1) + { + g_GameAttributes.settings.Landscape = "random"; + this.gameSettingsControl.updateGameAttributes(); + } + + if (this.lastLandscape != g_GameAttributes.settings.Landscape) + { + g_GameAttributes.settings.Preview = this.values.Preview[this.values.Id.indexOf(g_GameAttributes.settings.Landscape)]; + this.lastLandscape = g_GameAttributes.settings.Biome; + } + } + else if (g_GameAttributes.settings.Landscape !== undefined) + { + delete g_GameAttributes.settings.Landscape; + this.gameSettingsControl.updateGameAttributes(); + } + } + + onGameAttributesBatchChange() + { + if (g_GameAttributes.settings.Landscape) + this.setSelectedValue(g_GameAttributes.settings.Landscape); + } + + getAutocompleteEntries() + { + return this.values.Name; + } + + onSelectionChange(itemIdx) + { + g_GameAttributes.settings.Landscape = this.values.Id[itemIdx]; + + this.gameSettingsControl.updateGameAttributes(); + this.gameSettingsControl.setNetworkGameAttributes(); + } + + onPickRandomItems() + { + let items; + let landscapes = this.mapData.settings.Landscapes; + + switch (g_GameAttributes.settings.Landscape || undefined) + { + case "random": + items = [...landscapes.land, ...landscapes.naval]; + break; + case "random_land": + items = landscapes.land; + break; + case "random_naval": + items = landscapes.naval; + break; + default: + return; + } + + g_GameAttributes.settings.Landscape = pickRandom(items).Id; + this.gameSettingsControl.updateGameAttributes(); + this.gameSettingsControl.pickRandomItems(); + } +}; + +GameSettingControls.Landscape.prototype.TitleCaption = + translate("Landscape"); + +GameSettingControls.Landscape.prototype.Tooltip = + translate("Select one of the landscapes of this map."); + +GameSettingControls.Landscape.prototype.RandomItems = +[ + { + "Id": "random", + "Name": translateWithContext("landscape selection", "Random Land or Naval"), + "Description": translateWithContext("landscape selection", "Select a random land or naval map generation.") + }, + { + "Id": "random_land", + "Name": translateWithContext("landscape selection", "Random Land"), + "Description": translateWithContext("landscape selection", "Select a random land map generation.") + }, + { + "Id": "random_naval", + "Name": translateWithContext("landscape selection", "Random Naval"), + "Description": translateWithContext("landscape selection", "Select a random naval map generation.") + } +]; + +GameSettingControls.Landscape.prototype.AutocompleteOrder = 0; diff --git a/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js b/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js index 8b94a5649a..a215f514a0 100644 --- a/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js +++ b/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js @@ -2,14 +2,26 @@ class MapPreview { constructor(gameSettingsControl, mapCache) { + this.gameSettingsControl = gameSettingsControl; this.mapCache = mapCache; this.mapInfoName = Engine.GetGUIObjectByName("mapInfoName"); this.mapPreview = Engine.GetGUIObjectByName("mapPreview"); + gameSettingsControl.registerMapChangeHandler(this.onMapChange.bind(this)); gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this)); } + onMapChange(mapData) + { + let preview = mapData && mapData.settings && mapData.settings.Preview; + if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != preview) + { + g_GameAttributes.settings.Preview = preview; + this.gameSettingsControl.updateGameAttributes(); + } + } + onGameAttributesBatchChange() { if (!g_GameAttributes.map || !g_GameAttributes.mapType) diff --git a/binaries/data/mods/public/maps/random/danubius.js b/binaries/data/mods/public/maps/random/danubius.js index 6e7bd88fe2..c52e1187f8 100644 --- a/binaries/data/mods/public/maps/random/danubius.js +++ b/binaries/data/mods/public/maps/random/danubius.js @@ -1,6 +1,8 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen-common"); +const day = g_MapSettings.dayTime !== undefined ? g_MapSettings.dayTime == "day" : randBool(2/3); + // Spawn ships away from the shoreline, but patrol close to the shoreline const triggerPointShipSpawn = "trigger/trigger_point_A"; const triggerPointShipPatrol = "trigger/trigger_point_B"; @@ -235,7 +237,7 @@ var fortressDanubiusVillage = new Fortress( "Geto-Dacian Tribal Confederation", new Array(2).fill([ "gate", "pillar", "hut", "long", "long", - "cornerIn", "defense_tower", "long", "temple", "long", + "cornerIn", "defense_tower", "long", "temple", "long", "pillar", "house", "long", "short", "pillar", "gate", "pillar", "longhouse", "long", "long", "cornerIn", "defense_tower", "long", "tavern", "long", "pillar" ]).reduce((result, items) => result.concat(items), [])); @@ -303,8 +305,8 @@ if (gallicCC) for (let participants of ritualParticipants) { let [positions, angles] = distributePointsOnCircle(participants.count, startAngle, participants.radius * mRadius, meetingPlacePosition); - for (let i = 0; i < positions.length; ++i) - g_Map.placeEntityPassable(pickRandom(participants.templates), 0, positions[i], angles[i] + participants.angle); + for (let j = 0; j < positions.length; ++j) + g_Map.placeEntityPassable(pickRandom(participants.templates), 0, positions[j], angles[j] + participants.angle); } } @@ -323,7 +325,7 @@ if (gallicCC) placeCustomFortress(civicCenterPosition, fortressDanubiusSpikes, "danubius_spikes", 0, startAngle + Math.PI); // Place treasure, potentially inside buildings - for (let i = 0; i < gallicCCTreasureCount; ++i) + for (let j = 0; j < gallicCCTreasureCount; ++j) g_Map.placeEntityPassable( pickRandom(oTreasures), 0, @@ -444,7 +446,7 @@ Engine.SetProgress(50); g_Map.log("Creating grass patches"); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], - [[tGrass, tGrass2],[tGrass2, tGrass3], [tGrass3, tGrass]], + [[tGrass, tGrass2], [tGrass2, tGrass3], [tGrass3, tGrass]], [1, 1], avoidClasses(clForest, 0, clPlayer, 10, clWater, 2, clDirt, 2, clHill, 1, clGauls, 5, clPath, 1), scaleByMapSize(15, 45), @@ -566,7 +568,7 @@ for (let i = 0; i < 2; ++i) ], i == 0 ? avoidClasses(clWater, 4, clForest, 1, clPlayer, 16, clRock, 4, clMetal, 4, clHill, 4, clGauls, 5, clPath, 1) : - [stayClasses(clIsland, 4) , avoidClasses(clForest, 1, clRock, 4, clMetal, 4)]); + [stayClasses(clIsland, 4), avoidClasses(clForest, 1, clRock, 4, clMetal, 4)]); Engine.SetProgress(75); g_Map.log("Creating fish"); @@ -797,7 +799,7 @@ createObjectGroupsByAreas( placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clMetal, 4, clRock, 4, clIsland, 4, clGauls, 20, clRitualPlace, 20, clForest, 1, clBaseResource, 4, clHill, 4, clFood, 2)); -if (randBool(2/3)) +if (day) { g_Map.log("Setting day theme"); setSkySet("cumulus"); diff --git a/binaries/data/mods/public/maps/random/danubius.json b/binaries/data/mods/public/maps/random/danubius.json index 03edf9b439..9d328b9afe 100644 --- a/binaries/data/mods/public/maps/random/danubius.json +++ b/binaries/data/mods/public/maps/random/danubius.json @@ -5,10 +5,25 @@ "Description" : "Players start along the banks of the river Danube in the period following the expansion into Pannonia by the Celtic Boii tribe. Seeking to consolidate their hold on this land, celtic reinforcements have been sent to root out the remaining foreign cultures. Players not only have to vie for power amongst themselves, but also beat back these ruthless invaders. Ultimately, the Boii were defeated by the rising power of the Dacians, hence leading to the reemergence of the Geto-Dacian Confederation under King Burebista.", "Keywords": ["trigger", "naval"], "CircularMap" : true, - "Preview" : "danubius.png", + "Preview" : "danubius_night.png", "TriggerScripts" : [ "scripts/TriggerHelper.js", "random/danubius_triggers.js" + ], + "Daytime": + [ + { + "Id": "day", + "Name": "Day", + "Description": "Bright daylight illuminates the scene.", + "Preview" : "danubius_day.png" + }, + { + "Id": "night", + "Name": "Night", + "Description": "Attackers are covered by the darkness of the night.", + "Preview" : "danubius_night.png" + } ] } } diff --git a/binaries/data/mods/public/maps/random/polar_sea.js b/binaries/data/mods/public/maps/random/polar_sea.js index 7156444165..45d815668e 100644 --- a/binaries/data/mods/public/maps/random/polar_sea.js +++ b/binaries/data/mods/public/maps/random/polar_sea.js @@ -134,7 +134,7 @@ Engine.SetProgress(65); g_Map.log("Creating dirt patches"); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], - [[tDirt,tHalfSnow], [tHalfSnow,tSnowLimited]], + [[tDirt, tHalfSnow], [tHalfSnow, tSnowLimited]], [2], avoidClasses(clWater, 3, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45), @@ -150,10 +150,10 @@ createPatches( Engine.SetProgress(70); g_Map.log("Creating stone mines"); - createMines( +createMines( [ [new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)], - [new SimpleObject(oStoneSmall, 2,5, 1,3)] + [new SimpleObject(oStoneSmall, 2, 5, 1, 3)] ], avoidClasses(clWater, 3, clPlayer, 20, clRock, 18, clHill, 2), clRock); @@ -161,7 +161,7 @@ g_Map.log("Creating stone mines"); g_Map.log("Creating metal mines"); createMines( [ - [new SimpleObject(oMetalLarge, 1,1, 0,4)] + [new SimpleObject(oMetalLarge, 1, 1, 0, 4)] ], avoidClasses(clWater, 3, clPlayer, 20, clMetal, 18, clRock, 5, clHill, 2), clMetal); @@ -241,7 +241,7 @@ createObjectGroupsDeprecated( 100); Engine.SetProgress(95); -if (randBool(1/3)) +if (g_MapSettings.Daytime !== undefined ? g_MapSettings.Daytime == "dawn" : randBool(1/3)) { setSkySet("sunset 1"); setSunColor(0.8, 0.7, 0.6); diff --git a/binaries/data/mods/public/maps/random/polar_sea.json b/binaries/data/mods/public/maps/random/polar_sea.json index e4edeaad3b..894cf7be9d 100644 --- a/binaries/data/mods/public/maps/random/polar_sea.json +++ b/binaries/data/mods/public/maps/random/polar_sea.json @@ -4,7 +4,7 @@ "Script" : "polar_sea.js", "Description" : "Players start in a cold polar region barren of vegetation. In the sea fish and whales abound, while the fragile icy land teems with huntable walruses and deadly wolves. These wolves, made ravenous by the harsh and forbidding climate, drawn by the scent of prey, have started appearing in terrifying numbers. A wise and strong ruler will not only achieve victory over his enemies, but also keep the number of these beasts at bay, lest they undermine his economy and cause his downfall. [color=\"red\"]Warning: It is inadvisable to disable treasures, since there is no gatherable wood. Not recommended for inexperienced players.[/color]", "Keywords": ["trigger"], - "Preview" : "polar_sea.png", + "Preview" : "polar_sea_dawn.png", "CircularMap" : true, "TriggerScripts" : [ "scripts/TriggerHelper.js", @@ -14,6 +14,21 @@ "gather_lumbering_ironaxes", "gather_lumbering_sharpaxes", "gather_lumbering_strongeraxes" + ], + "Daytime": + [ + { + "Id": "dawn", + "Name": "Dawn", + "Description": "Scattered sunlight illuminates the scenery in a vivid red tone.", + "Preview" : "polar_sea_dawn.png" + }, + { + "Id": "daylight", + "Name": "Daylight", + "Description": "The scenery is illuminated by direct or indirect sunlight.", + "Preview" : "polar_sea_daylight.png" + } ] } } diff --git a/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js b/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js deleted file mode 100644 index 9482b3426c..0000000000 --- a/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js +++ /dev/null @@ -1,1060 +0,0 @@ -/** - * @file This library is used to generate different map variations on the map Unknown, Unknown Land and Unknown Nomad. - */ - -/** - * True if all players should be connected via land and false if river or islands can split some if not all the players. - */ -var g_AllowNaval; - -TILE_CENTERED_HEIGHT_MAP = true; - -setSelectedBiome(); - -const tMainTerrain = g_Terrains.mainTerrain; -const tForestFloor1 = g_Terrains.forestFloor1; -const tForestFloor2 = g_Terrains.forestFloor2; -const tCliff = g_Terrains.cliff; -const tTier1Terrain = g_Terrains.tier1Terrain; -const tTier2Terrain = g_Terrains.tier2Terrain; -const tTier3Terrain = g_Terrains.tier3Terrain; -const tHill = g_Terrains.hill; -const tRoad = g_Terrains.road; -const tRoadWild = g_Terrains.roadWild; -const tTier4Terrain = g_Terrains.tier4Terrain; -const tShore = g_Terrains.shore; -const tWater = g_Terrains.water; - -const oTree1 = g_Gaia.tree1; -const oTree2 = g_Gaia.tree2; -const oTree4 = g_Gaia.tree4; -const oTree5 = g_Gaia.tree5; -const oFruitBush = g_Gaia.fruitBush; -const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; -const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; -const oFish = g_Gaia.fish; -const oStoneLarge = g_Gaia.stoneLarge; -const oStoneSmall = g_Gaia.stoneSmall; -const oMetalLarge = g_Gaia.metalLarge; -const oWoodTreasure = "gaia/treasure/wood"; - -const aGrass = g_Decoratives.grass; -const aGrassShort = g_Decoratives.grassShort; -const aReeds = g_Decoratives.reeds; -const aLillies = g_Decoratives.lillies; -const aRockLarge = g_Decoratives.rockLarge; -const aRockMedium = g_Decoratives.rockMedium; -const aBushMedium = g_Decoratives.bushMedium; -const aBushSmall = g_Decoratives.bushSmall; - -const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; -const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; - -const heightSeaGround = -5; -const heightLand = 3; -const heightCliff = 3.12; -const heightHill = 18; -const heightOffsetBump = 2; - -var g_Map = new RandomMap(heightSeaGround, tWater); - -const numPlayers = getNumPlayers(); -const mapSize = g_Map.getSize(); -const mapCenter = g_Map.getCenter(); -const mapBounds = g_Map.getBounds(); - -var clPlayer = g_Map.createTileClass(); -var clPlayerTerritory = g_Map.createTileClass(); -var clHill = g_Map.createTileClass(); -var clForest = g_Map.createTileClass(); -var clWater = g_Map.createTileClass(); -var clDirt = g_Map.createTileClass(); -var clRock = g_Map.createTileClass(); -var clMetal = g_Map.createTileClass(); -var clFood = g_Map.createTileClass(); -var clPeninsulaSteam = g_Map.createTileClass(); -var clBaseResource = g_Map.createTileClass(); -var clLand = g_Map.createTileClass(); -var clShallow = g_Map.createTileClass(); - -var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, heightLand, 4); - -var unknownMapFunctions = { - "land": [ - "Continent", - "CentralSea", - "CentralRiver", - "EdgeSeas", - "Gulf", - "Lakes", - "Passes", - "Lowlands", - "Mainland" - ], - "naval": [ - "Archipelago", - "RiversAndLake" - ] -}; - -/** - * The player IDs and locations shall only be determined by the landscape functions if it's not a nomad game, - * because nomad maps randomize the locations after the terrain generation. - * The locations should only determined by the landscape functions to avoid placing bodies of water and resources into civic centers and the starting resources. - */ -var playerIDs = sortAllPlayers(); -var playerPosition = []; - -var g_StartingTreasures = false; -var g_StartingWalls = true; - -function createUnknownMap() -{ - let funcs = unknownMapFunctions.land; - - if (g_AllowNaval) - funcs = funcs.concat(unknownMapFunctions.naval); - - global["unknown" + pickRandom(funcs)](); - - paintUnknownMapBasedOnHeight(); - - createUnknownPlayerBases(); - - createUnknownObjects(); - - placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2, clWater, 10)); -} - -/** - * Chain of islands or many disconnected islands. - */ -function unknownArchipelago() -{ - g_StartingWalls = "towers"; - g_StartingTreasures = true; - - let [pIDs, islandPosition] = playerPlacementCircle(fractionToTiles(0.35)); - if (!isNomad()) - { - [playerIDs, playerPosition] = [pIDs, islandPosition]; - markPlayerArea("large"); - } - - g_Map.log("Creating islands"); - let islandSize = diskArea(scaleByMapSize(17, 29)); - for (let i = 0; i < numPlayers; ++i) - createArea( - new ClumpPlacer(islandSize, 0.8, 0.1, Infinity, islandPosition[i]), - landElevationPainter); - - let type = isNomad() ? randIntInclusive(1, 2) : randIntInclusive(1, 3); - if (type == 1) - { - g_Map.log("Creating archipelago"); - createAreas( - new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity), - [ - landElevationPainter, - new TileClassPainter(clLand) - ], - null, - scaleByMapSize(2, 5) * randIntInclusive(8, 14)); - - g_Map.log("Creating shore jaggedness with small puddles"); - createAreas( - new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, Infinity), - [ - new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), - new TileClassPainter(clLand) - ], - borderClasses(clLand, 6, 3), - scaleByMapSize(12, 130) * 2, - 150); - } - else if (type == 2) - { - g_Map.log("Creating islands"); - createAreas( - new ClumpPlacer(islandSize * randFloat(0.6, 1.4), 0.8, 0.1, randFloat(0.0, 0.2)), - [ - landElevationPainter, - new TileClassPainter(clLand) - ], - avoidClasses(clLand, 3, clPlayerTerritory, 3), - scaleByMapSize(6, 10) * randIntInclusive(8, 14)); - - g_Map.log("Creating small islands"); - createAreas( - new ClumpPlacer(islandSize * randFloat(0.3, 0.7), 0.8, 0.1, 0.07), - [ - new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), - new TileClassPainter(clLand) - ], - avoidClasses(clLand, 3, clPlayerTerritory, 3), - scaleByMapSize(2, 6) * randIntInclusive(6, 15), - 25); - } - else if (type == 3) - { - g_Map.log("Creating tight islands"); - createAreas( - new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity), - [ - landElevationPainter, - new TileClassPainter(clLand) - ], - avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3), - scaleByMapSize(2, 5) * randIntInclusive(8, 14)); - } -} - -/** - * Disk shaped mainland with water on the edge. - */ -function unknownContinent() -{ - let waterHeight = -5; - - if (!isNomad()) - { - g_Map.log("Ensuring player area"); - [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25)); - markPlayerArea("small"); - - for (let i = 0; i < numPlayers; ++i) - createArea( - new ChainPlacer( - 2, - Math.floor(scaleByMapSize(5, 9)), - Math.floor(scaleByMapSize(5, 20)), - Infinity, - playerPosition[i], - 0, - [Math.floor(scaleByMapSize(23, 50))]), - [ - landElevationPainter, - new TileClassPainter(clLand) - ]); - } - - g_Map.log("Creating continent"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, mapCenter), - [ - landElevationPainter, - new TileClassPainter(clLand) - ]); - - if (randBool(1/3)) - { - g_Map.log("Creating peninsula (i.e. half the map not being surrounded by water)"); - let angle = randomAngle(); - let peninsulaPosition1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.25), 0).rotate(-angle)); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, peninsulaPosition1), - [ - landElevationPainter, - new TileClassPainter(clLand) - ]); - - g_Map.log("Remembering to not paint shorelines into the peninsula"); - let peninsulaPosition2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.35), 0).rotate(-angle)); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.33)), 0.9, 0.01, Infinity, peninsulaPosition2), - new TileClassPainter(clPeninsulaSteam)); - } - - createShoreJaggedness(waterHeight, clLand, 7); -} - -/** - * Creates a huge central river, possibly connecting the riversides with a narrow piece of land. - */ -function unknownCentralSea() -{ - let waterHeight = -3; - - let startAngle = randomAngle(); - - let [riverStart, riverEnd] = centralRiverCoordinates(startAngle); - - paintRiver({ - "parallel": false, - "start": riverStart, - "end": riverEnd, - "width": fractionToTiles(scaleByMapSize(0.27, 0.42) + randFloat(0, 0.08)), - "fadeDist": scaleByMapSize(3, 12), - "deviation": 0, - "heightRiverbed": waterHeight, - "heightLand": heightLand, - "meanderShort": 20, - "meanderLong": 0, - "waterFunc": (position, height, riverFraction) => { - if (height < 0) - clWater.add(position); - }, - "landFunc": (position, shoreDist1, shoreDist2) => { - g_Map.setHeight(position, 3.1); - clLand.add(position); - } - }); - - if (!isNomad()) - { - [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.6)); - markPlayerArea("small"); - } - - if (!g_AllowNaval || randBool()) - { - g_Map.log("Creating isthmus (i.e. connecting the two riversides with a big land passage)"); - let [isthmusStart, isthmusEnd] = centralRiverCoordinates(startAngle + Math.PI / 2); - createArea( - new PathPlacer( - isthmusStart, - isthmusEnd, - scaleByMapSize(randIntInclusive(16, 24), randIntInclusive(100, 140)), - 0.5, - 3 * scaleByMapSize(1, 4), - 0.1, - 0.01), - [ - landElevationPainter, - new TileClassPainter(clLand), - new TileClassUnPainter(clWater) - ]); - } - - createExtensionsOrIslands(); - // Don't createShoreJaggedness since it doesn't fit artistically here -} - -/** - * Creates a very small central river. - */ -function unknownCentralRiver() -{ - let waterHeight = -4; - let heightShallow = -2; - - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - let startAngle = randomAngle(); - - if (!isNomad()) - { - [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.5)); - markPlayerArea("large"); - } - - g_Map.log("Creating the main river"); - let [coord1, coord2] = centralRiverCoordinates(startAngle); - createArea( - new PathPlacer(coord1, coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01), - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), - avoidClasses(clPlayerTerritory, 4)); - - g_Map.log("Creating small water spots at the map border to ensure separation of players"); - for (let coord of [coord1, coord2]) - createArea( - new ClumpPlacer(diskArea(scaleByMapSize(5, 10)), 0.95, 0.6, Infinity, coord), - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2), - avoidClasses(clPlayerTerritory, 8)); - - if (!g_AllowNaval || randBool()) - { - g_Map.log("Creating the shallows of the main river"); - for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i) - { - let location = fractionToTiles(randFloat(0.15, 0.85)); - createPassage({ - "start": new Vector2D(location, mapBounds.top).rotateAround(startAngle, mapCenter), - "end": new Vector2D(location, mapBounds.bottom).rotateAround(startAngle, mapCenter), - "startWidth": scaleByMapSize(8, 12), - "endWidth": scaleByMapSize(8, 12), - "smoothWidth": 2, - "startHeight": heightShallow, - "endHeight": heightShallow, - "constraints": new HeightConstraint(-Infinity, heightShallow), - "tileClass": clShallow - }); - } - } - - if (randBool(2/3)) - createTributaryRivers( - startAngle, - randIntInclusive(8, scaleByMapSize(12, 16)), - scaleByMapSize(10, 20), - -4, - [-6, -1.5], - Math.PI / 5, - clWater, - clShallow, - avoidClasses(clPlayerTerritory, 3)); -} - -/** - * Creates a circular lake in the middle and possibly a river between each player ("pizza slices"). - */ -function unknownRiversAndLake() -{ - let waterHeight = -4; - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - let startAngle; - if (!isNomad()) - { - let playerAngle; - [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); - markPlayerArea("small"); - } - - let lake = randBool(3/4); - if (lake) - { - g_Map.log("Creating lake"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.17)), 0.7, 0.1, Infinity, mapCenter), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), - new TileClassPainter(clWater) - ]); - - createShoreJaggedness(waterHeight, clWater, 3); - } - - // Don't do this on nomad because the imbalances on the different islands are too drastic - if (!isNomad() && (!lake || randBool(1/3))) - { - g_Map.log("Creating small rivers separating players"); - for (let river of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) - { - createArea( - new PathPlacer(mapCenter, river, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), - new TileClassPainter(clWater) - ], - avoidClasses(clPlayer, 5)); - - createArea( - new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, river), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0), - new TileClassPainter(clWater) - ], - avoidClasses(clPlayer, 5)); - } - - g_Map.log("Creating lake"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.04)), 0.7, 0.1, Infinity, mapCenter), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), - new TileClassPainter(clWater) - ]); - } - - if (!isNomad && lake && randBool()) - { - g_Map.log("Creating small central island"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter), - [ - landElevationPainter, - new TileClassPainter(clWater) - ]); - } -} - -/** - * Align players on a land strip with seas bordering on one or both sides that can hold islands. - */ -function unknownEdgeSeas() -{ - let waterHeight = -4; - - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - let startAngle = randomAngle(); - if (!isNomad()) - { - playerIDs = sortAllPlayers(); - playerPosition = playerPlacementLine(startAngle + Math.PI / 2, mapCenter, fractionToTiles(0.2)); - // Don't place the shoreline inside the CC, but possibly into the players territory - markPlayerArea("small"); - } - - for (let side of pickRandom([[0], [Math.PI], [0, Math.PI]])) - paintRiver({ - "parallel": true, - "start": new Vector2D(mapBounds.left, mapBounds.top).rotateAround(side + startAngle, mapCenter), - "end": new Vector2D(mapBounds.left, mapBounds.bottom).rotateAround(side + startAngle, mapCenter), - "width": scaleByMapSize(80, randFloat(270, 320)), - "fadeDist": scaleByMapSize(2, 8), - "deviation": 0, - "heightRiverbed": waterHeight, - "heightLand": heightLand, - "meanderShort": 20, - "meanderLong": 0 - }); - - createExtensionsOrIslands(); - paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); - createShoreJaggedness(waterHeight, clLand, 7, false); -} - -/** - * Land shaped like a concrescent moon around a central lake. - */ -function unknownGulf() -{ - let waterHeight = -3; - - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - let startAngle = randomAngle(); - if (!isNomad()) - { - g_Map.log("Determining player locations"); - - playerPosition = playerPlacementCustomAngle( - fractionToTiles(0.35), - mapCenter, - i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))))[0]; - - markPlayerArea("large"); - } - - let gulfParts = [ - { "radius": fractionToTiles(0.16), "distance": fractionToTiles(0) }, - { "radius": fractionToTiles(0.2), "distance": fractionToTiles(0.2) }, - { "radius": fractionToTiles(0.22), "distance": fractionToTiles(0.49) } - ]; - - for (let gulfPart of gulfParts) - { - let position = Vector2D.sub(mapCenter, new Vector2D(gulfPart.distance, 0).rotate(-startAngle)).round(); - createArea( - new ClumpPlacer(diskArea(gulfPart.radius), 0.7, 0.05, Infinity, position), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), - new TileClassPainter(clWater) - ], - avoidClasses(clPlayerTerritory, defaultPlayerBaseRadius())); - } -} - -/** - * Mainland style with some small random lakes. - */ -function unknownLakes() -{ - let waterHeight = -5; - - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - if (!isNomad()) - { - [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); - markPlayerArea("large"); - } - - g_Map.log("Creating lakes"); - createAreas( - new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, Infinity), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5), - new TileClassPainter(clWater) - ], - [avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()], - scaleByMapSize(5, 16)); -} - -/** - * A large hill leaving players only a small passage to each of the the two neighboring players. - */ -function unknownPasses() -{ - let heightMountain = 24; - let waterHeight = -4; - - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightLand)); - - let playerAngle; - let startAngle; - if (!isNomad()) - { - [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); - markPlayerArea("small"); - } - else - startAngle = randomAngle(); - - g_Map.log("Creating a mountain range between neighboring players"); - for (let mountain of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) - { - createArea( - new PathPlacer(mapCenter, mountain, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), - [ - // More smoothing than this often results in the mountainrange becoming passable to one player. - new SmoothElevationPainter(ELEVATION_SET, heightMountain, 1), - new TileClassPainter(clWater) - ], - avoidClasses(clPlayer, 5)); - - // Small mountain at the map border between the players to ensure separation of players - createArea( - new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, mountain), - new SmoothElevationPainter(ELEVATION_SET, heightMountain, 0), - avoidClasses(clPlayer, 5)); - } - - g_Map.log("Creating passages between neighboring players"); - let passes = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mapCenter)[0]; - for (let i = 0; i < numPlayers; ++i) - { - createArea( - new PathPlacer( - passes[2 * i], - passes[2 * ((i + 1) % numPlayers)], - scaleByMapSize(14, 24), - 0.4, - 3 * scaleByMapSize(1, 3), - 0.2, - 0.05), - new SmoothElevationPainter(ELEVATION_SET, heightLand, 2)); - } - - if (randBool(2/5)) - { - g_Map.log("Create central lake"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.1)), 0.7, 0.1, Infinity, mapCenter), - [ - new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3), - new TileClassPainter(clWater) - ]); - } - else - { - g_Map.log("Fill area between the paths"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter), - [ - new SmoothElevationPainter(ELEVATION_SET, heightMountain, 4), - new TileClassPainter(clWater) - ]); - } -} - -/** - * Land enclosed by a hill that leaves small areas for civic centers and large central place. - */ -function unknownLowlands() -{ - let heightMountain = 30; - - g_Map.log("Creating mountain that is going to separate players"); - createArea( - new MapBoundsPlacer(), - new ElevationPainter(heightMountain)); - - let playerAngle; - let startAngle; - if (!isNomad()) - { - [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); - markPlayerArea("small"); - } - else - startAngle = randomAngle(); - - g_Map.log("Creating valleys enclosed by the mountain"); - let valleys = numPlayers; - if (mapSize >= 128 && numPlayers <= 2 || - mapSize >= 192 && numPlayers <= 3 || - mapSize >= 320 && numPlayers <= 4 || - mapSize >= 384 && numPlayers <= 5 || - mapSize >= 448 && numPlayers <= 6) - valleys *= 2; - - g_Map.log("Creating player valley"); - for (let valley of distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mapCenter)[0]) - { - createArea( - new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, Infinity, valley), - [ - new SmoothElevationPainter(ELEVATION_SET, heightLand, 2), - new TileClassPainter(clLand) - ]); - - // Passage from player to center - createArea( - new PathPlacer(mapCenter, valley, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), - [ - landElevationPainter, - new TileClassPainter(clWater) - ]); - } - - g_Map.log("Creating the big central area"); - createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.7, 0.1, Infinity, mapCenter), - [ - landElevationPainter, - new TileClassPainter(clWater) - ]); -} - -/** - * No water, no hills. - */ -function unknownMainland() -{ - createArea( - new MapBoundsPlacer(), - new ElevationPainter(3)); - - if (!isNomad()) - { - [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); - markPlayerArea("small"); - } -} - -function centralRiverCoordinates(angle) -{ - return [ - new Vector2D(mapBounds.left + 1, mapCenter.y), - new Vector2D(mapBounds.right - 1, mapCenter.y) - ].map(v => v.rotateAround(angle, mapCenter)); -} - -function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true) -{ - g_Map.log("Creating shore jaggedness"); - for (let i = 0; i < 2; ++i) - if (i || inwards) - createAreas( - new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, Infinity), - [ - new SmoothElevationPainter(ELEVATION_SET, i ? heightLand : waterHeight, 4), - i ? new TileClassPainter(clLand) : new TileClassUnPainter(clLand) - ], - [ - avoidClasses(clPlayer, 20, clPeninsulaSteam, 20), - borderClasses(borderClass, shoreDist, shoreDist) - ], - scaleByMapSize(7, 130) * 2, - 150); -} - -function createExtensionsOrIslands() -{ - let rnd = randIntInclusive(1, 3); - - if (rnd == 1) - { - g_Map.log("Creating islands"); - createAreas( - new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)), - [ - landElevationPainter, - new TileClassPainter(clLand) - ], - avoidClasses(clLand, 3, clPlayer, 3), - scaleByMapSize(2, 5) * randIntInclusive(8, 14)); - } - else if (rnd == 2) - { - g_Map.log("Creating extentions"); - createAreas( - new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07), - [ - landElevationPainter, - new TileClassPainter(clLand) - ], - null, - scaleByMapSize(2, 5) * randIntInclusive(8, 14)); - } -} - -/** - * Prevent impassable terrain and resource collisions at the the civic center and starting resources. - */ -function markPlayerArea(size) -{ - for (let i = 0; i < numPlayers; ++i) - { - addCivicCenterAreaToClass(playerPosition[i], clPlayer); - - if (size == "large") - createArea( - new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, Infinity, playerPosition[i]), - new TileClassPainter(clPlayerTerritory)); - } -} - -function paintUnknownMapBasedOnHeight() -{ - paintTerrainBasedOnHeight(heightCliff, 40, 1, tCliff); - paintTerrainBasedOnHeight(3, heightCliff, 1, tMainTerrain); - paintTerrainBasedOnHeight(1, 3, 1, tShore); - paintTerrainBasedOnHeight(-8, 1, 2, tWater); - - unPaintTileClassBasedOnHeight(0, heightCliff, 1, clWater); - unPaintTileClassBasedOnHeight(-6, 0, 1, clLand); - - paintTileClassBasedOnHeight(-6, 0, 1, clWater); - paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); - paintTileClassBasedOnHeight(heightCliff, 40, 1, clHill); -} - -/** - * Place resources and decoratives after the player territory was marked. - */ -function createUnknownObjects() -{ - g_Map.log("Creating bumps"); - createAreas( - new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, Infinity), - new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), - [avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)], - randIntInclusive(0, scaleByMapSize(1, 2) * 200)); - - g_Map.log("Creating hills"); - createAreas( - new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, Infinity), - [ - new LayeredPainter([tCliff, tHill], [2]), - new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), - new TileClassPainter(clHill) - ], - [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)], - randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9)) - ); - Engine.SetProgress(30); - - g_Map.log("Creating forests"); - let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1)); - let types = [ - [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], - [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] - ]; - - let size = numForest / (scaleByMapSize(2, 8) * numPlayers); - let num = Math.floor(size / types.length); - for (let type of types) - createAreas( - new ClumpPlacer(numForest / num, 0.1, 0.1, Infinity), - [ - new LayeredPainter(type, [2]), - new TileClassPainter(clForest) - ], - [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 2), stayClasses(clLand, 4)], - num); - Engine.SetProgress(50); - - g_Map.log("Creating dirt patches"); - let patchCount = (currentBiome() == "generic/savanna" ? 3 : 1) * scaleByMapSize(15, 45); - for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) - createAreas( - new ClumpPlacer(size, 0.3, 0.06, 0.5), - [ - new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), - new TileClassPainter(clDirt) - ], - [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], - patchCount); - - g_Map.log("Creating grass patches"); - for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) - createAreas( - new ClumpPlacer(size, 0.3, 0.06, 0.5), - new TerrainPainter(tTier4Terrain), - [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], - patchCount); - - Engine.SetProgress(55); - - g_Map.log("Creating stone mines"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)], true, clRock), - 0, - [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], - randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), - 100); - - g_Map.log("Creating small stone quarries"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), - 0, - [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], - randIntInclusive(scaleByMapSize(2, 9),scaleByMapSize(9, 40)), - 100); - - g_Map.log("Creating metal mines"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), - 0, - [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 2), stayClasses(clLand, 3)], - randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), - 100); - Engine.SetProgress(65); - - g_Map.log("Creating small decorative rocks"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true), - 0, - [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], - scaleByMapSize(16, 262), - 50); - - g_Map.log("Creating large decorative rocks"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true), - 0, - [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], - scaleByMapSize(8, 131), - 50); - Engine.SetProgress(70); - - g_Map.log("Creating deer"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood), - 0, - [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], - randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), - 50); - - g_Map.log("Creating berry bush"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood), - 0, - [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], - randIntInclusive(1, 4) * numPlayers + 2, - 50); - Engine.SetProgress(75); - - g_Map.log("Creating sheep"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood), - 0, - [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], - randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), - 50); - - g_Map.log("Creating fish"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood), - 0, - avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 2, clFood, 20), - randIntInclusive(15, 40) * numPlayers, - 60); - Engine.SetProgress(85); - - g_Map.log("Creating straggler trees"); - types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4]; - - num = Math.floor(numStragglers / types.length); - for (let type of types) - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest), - 0, - [avoidClasses(clWater, 1, clForest, 1, clHill, 2, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)], - num); - - let planetm = currentBiome() == "generic/tropic" ? 8 : 1; - - g_Map.log("Creating small grass tufts"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]), - 0, - [avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)], - planetm * scaleByMapSize(13, 200)); - Engine.SetProgress(90); - - g_Map.log("Creating large grass tufts"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -Math.PI / 8, Math.PI / 8)]), - 0, - [avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)], - planetm * scaleByMapSize(13, 200)); - Engine.SetProgress(95); - - g_Map.log("Creating shallow flora"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]), - 0, - stayClasses(clShallow, 1), - 60 * scaleByMapSize(13, 200), - 80); - - g_Map.log("Creating bushes"); - createObjectGroupsDeprecated( - new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]), - 0, - [avoidClasses(clWater, 1, clHill, 2, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)], - planetm * scaleByMapSize(13, 200), - 50); - - setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"])); - setSunRotation(randomAngle()); - setSunElevation(Math.PI * randFloat(1/5, 1/3)); -} - -function createUnknownPlayerBases() -{ - placePlayerBases({ - "PlayerPlacement": [playerIDs, playerPosition], - "BaseResourceClass": clBaseResource, - "Walls": g_StartingWalls, - "CityPatch": { - "outerTerrain": tRoadWild, - "innerTerrain": tRoad, - "painters": [ - new TileClassPainter(clPlayer) - ] - }, - "Chicken": { - }, - "Berries": { - "template": oFruitBush - }, - "Mines": { - "types": [ - { "template": oMetalLarge }, - { "template": oStoneLarge } - ] - }, - "Treasures": { - "types": [ - { - "template": oWoodTreasure, - "count": g_StartingTreasures ? 14 : 0 - } - ] - }, - "Trees": { - "template": oTree1 - }, - "Decoratives": { - "template": aGrassShort - } - }); -} diff --git a/binaries/data/mods/public/maps/random/unknown.js b/binaries/data/mods/public/maps/random/unknown.js index fd525be3df..c0936bf3e9 100644 --- a/binaries/data/mods/public/maps/random/unknown.js +++ b/binaries/data/mods/public/maps/random/unknown.js @@ -1,9 +1,1075 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen-common"); Engine.LoadLibrary("rmbiome"); -Engine.LoadLibrary("the_unknown"); -g_AllowNaval = true; +TILE_CENTERED_HEIGHT_MAP = true; + +setSelectedBiome(); + +const tMainTerrain = g_Terrains.mainTerrain; +const tForestFloor1 = g_Terrains.forestFloor1; +const tForestFloor2 = g_Terrains.forestFloor2; +const tCliff = g_Terrains.cliff; +const tTier1Terrain = g_Terrains.tier1Terrain; +const tTier2Terrain = g_Terrains.tier2Terrain; +const tTier3Terrain = g_Terrains.tier3Terrain; +const tHill = g_Terrains.hill; +const tRoad = g_Terrains.road; +const tRoadWild = g_Terrains.roadWild; +const tTier4Terrain = g_Terrains.tier4Terrain; +const tShore = g_Terrains.shore; +const tWater = g_Terrains.water; + +const oTree1 = g_Gaia.tree1; +const oTree2 = g_Gaia.tree2; +const oTree4 = g_Gaia.tree4; +const oTree5 = g_Gaia.tree5; +const oFruitBush = g_Gaia.fruitBush; +const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; +const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; +const oFish = g_Gaia.fish; +const oStoneLarge = g_Gaia.stoneLarge; +const oStoneSmall = g_Gaia.stoneSmall; +const oMetalLarge = g_Gaia.metalLarge; +const oWoodTreasure = "gaia/treasure/wood"; + +const aGrass = g_Decoratives.grass; +const aGrassShort = g_Decoratives.grassShort; +const aReeds = g_Decoratives.reeds; +const aLillies = g_Decoratives.lillies; +const aRockLarge = g_Decoratives.rockLarge; +const aRockMedium = g_Decoratives.rockMedium; +const aBushMedium = g_Decoratives.bushMedium; +const aBushSmall = g_Decoratives.bushSmall; + +const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; +const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; + +const heightSeaGround = -5; +const heightLand = 3; +const heightCliff = 3.12; +const heightHill = 18; +const heightOffsetBump = 2; + +var g_Map = new RandomMap(heightSeaGround, tWater); + +const numPlayers = getNumPlayers(); +const mapSize = g_Map.getSize(); +const mapCenter = g_Map.getCenter(); +const mapBounds = g_Map.getBounds(); + +var clPlayer = g_Map.createTileClass(); +var clPlayerTerritory = g_Map.createTileClass(); +var clHill = g_Map.createTileClass(); +var clForest = g_Map.createTileClass(); +var clWater = g_Map.createTileClass(); +var clDirt = g_Map.createTileClass(); +var clRock = g_Map.createTileClass(); +var clMetal = g_Map.createTileClass(); +var clFood = g_Map.createTileClass(); +var clPeninsulaSteam = g_Map.createTileClass(); +var clBaseResource = g_Map.createTileClass(); +var clLand = g_Map.createTileClass(); +var clShallow = g_Map.createTileClass(); + +var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, heightLand, 4); + +var unknownMapFunctions = { + "land": [ + "Continent", + "Isthmus", + "CentralRiverLand", + "EdgeSeas", + "Gulf", + "Lakes", + "Passes", + "Lowlands", + "Mainland" + ], + "naval": [ + "CentralSea", + "CentralRiverNaval", + "Archipelago", + "RiversAndLake" + ] +}; + +/** + * The player IDs and locations shall only be determined by the landscape functions if it's not a nomad game, + * because nomad maps randomize the locations after the terrain generation. + * The locations should only determined by the landscape functions to avoid placing bodies of water and resources into civic centers and the starting resources. + */ +var playerIDs = sortAllPlayers(); +var playerPosition = []; + +var g_StartingTreasures = false; +var g_StartingWalls = true; + +function createUnknownMap() +{ + global["unknown" + g_MapSettings.Landscape || pickRandom([...unknownMapFunctions.land, ...unknownMapFunctions.naval])](); + + paintUnknownMapBasedOnHeight(); + + createUnknownPlayerBases(); + + createUnknownObjects(); + + placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2, clWater, 10)); +} + +/** + * Chain of islands or many disconnected islands. + */ +function unknownArchipelago() +{ + g_StartingWalls = "towers"; + g_StartingTreasures = true; + + let [pIDs, islandPosition] = playerPlacementCircle(fractionToTiles(0.35)); + if (!isNomad()) + { + [playerIDs, playerPosition] = [pIDs, islandPosition]; + markPlayerArea("large"); + } + + g_Map.log("Creating islands"); + let islandSize = diskArea(scaleByMapSize(17, 29)); + for (let i = 0; i < numPlayers; ++i) + createArea( + new ClumpPlacer(islandSize, 0.8, 0.1, Infinity, islandPosition[i]), + landElevationPainter); + + let type = isNomad() ? randIntInclusive(1, 2) : randIntInclusive(1, 3); + if (type == 1) + { + g_Map.log("Creating archipelago"); + createAreas( + new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity), + [ + landElevationPainter, + new TileClassPainter(clLand) + ], + null, + scaleByMapSize(2, 5) * randIntInclusive(8, 14)); + + g_Map.log("Creating shore jaggedness with small puddles"); + createAreas( + new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, Infinity), + [ + new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), + new TileClassPainter(clLand) + ], + borderClasses(clLand, 6, 3), + scaleByMapSize(12, 130) * 2, + 150); + } + else if (type == 2) + { + g_Map.log("Creating islands"); + createAreas( + new ClumpPlacer(islandSize * randFloat(0.6, 1.4), 0.8, 0.1, randFloat(0.0, 0.2)), + [ + landElevationPainter, + new TileClassPainter(clLand) + ], + avoidClasses(clLand, 3, clPlayerTerritory, 3), + scaleByMapSize(6, 10) * randIntInclusive(8, 14)); + + g_Map.log("Creating small islands"); + createAreas( + new ClumpPlacer(islandSize * randFloat(0.3, 0.7), 0.8, 0.1, 0.07), + [ + new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), + new TileClassPainter(clLand) + ], + avoidClasses(clLand, 3, clPlayerTerritory, 3), + scaleByMapSize(2, 6) * randIntInclusive(6, 15), + 25); + } + else if (type == 3) + { + g_Map.log("Creating tight islands"); + createAreas( + new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity), + [ + landElevationPainter, + new TileClassPainter(clLand) + ], + avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3), + scaleByMapSize(2, 5) * randIntInclusive(8, 14)); + } +} + +/** + * Disk shaped mainland with water on the edge. + */ +function unknownContinent() +{ + let waterHeight = -5; + + if (!isNomad()) + { + g_Map.log("Ensuring player area"); + [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25)); + markPlayerArea("small"); + + for (let i = 0; i < numPlayers; ++i) + createArea( + new ChainPlacer( + 2, + Math.floor(scaleByMapSize(5, 9)), + Math.floor(scaleByMapSize(5, 20)), + Infinity, + playerPosition[i], + 0, + [Math.floor(scaleByMapSize(23, 50))]), + [ + landElevationPainter, + new TileClassPainter(clLand) + ]); + } + + g_Map.log("Creating continent"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, mapCenter), + [ + landElevationPainter, + new TileClassPainter(clLand) + ]); + + if (randBool(1/3)) + { + g_Map.log("Creating peninsula (i.e. half the map not being surrounded by water)"); + let angle = randomAngle(); + let peninsulaPosition1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.25), 0).rotate(-angle)); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, peninsulaPosition1), + [ + landElevationPainter, + new TileClassPainter(clLand) + ]); + + g_Map.log("Remembering to not paint shorelines into the peninsula"); + let peninsulaPosition2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.35), 0).rotate(-angle)); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.33)), 0.9, 0.01, Infinity, peninsulaPosition2), + new TileClassPainter(clPeninsulaSteam)); + } + + createShoreJaggedness(waterHeight, clLand, 7); +} + +function unknownCentralSea() +{ + unknownCentralSeaOrIsthmus(false); +} + +function unknownIsthmus() +{ + unknownCentralSeaOrIsthmus(true); +} + +/** + * Creates a huge central river, possibly connecting the riversides with a narrow piece of land. + */ +function unknownCentralSeaOrIsthmus(isthmus) +{ + let waterHeight = -3; + + let startAngle = randomAngle(); + + let [riverStart, riverEnd] = centralRiverCoordinates(startAngle); + + paintRiver({ + "parallel": false, + "start": riverStart, + "end": riverEnd, + "width": fractionToTiles(scaleByMapSize(0.27, 0.42) + randFloat(0, 0.08)), + "fadeDist": scaleByMapSize(3, 12), + "deviation": 0, + "heightRiverbed": waterHeight, + "heightLand": heightLand, + "meanderShort": 20, + "meanderLong": 0, + "waterFunc": (position, height, riverFraction) => { + if (height < 0) + clWater.add(position); + }, + "landFunc": (position, shoreDist1, shoreDist2) => { + g_Map.setHeight(position, 3.1); + clLand.add(position); + } + }); + + if (!isNomad()) + { + [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.6)); + markPlayerArea("small"); + } + + if (isthmus) + { + g_Map.log("Creating isthmus (i.e. connecting the two riversides with a big land passage)"); + let [isthmusStart, isthmusEnd] = centralRiverCoordinates(startAngle + Math.PI / 2); + createArea( + new PathPlacer( + isthmusStart, + isthmusEnd, + scaleByMapSize(randIntInclusive(16, 24), randIntInclusive(100, 140)), + 0.5, + 3 * scaleByMapSize(1, 4), + 0.1, + 0.01), + [ + landElevationPainter, + new TileClassPainter(clLand), + new TileClassUnPainter(clWater) + ]); + } + + createExtensionsOrIslands(); + // Don't createShoreJaggedness since it doesn't fit artistically here +} + +function unknownCentralRiverLand() +{ + unknownCentralRiver(true); +} + +function unknownCentralRiverNaval() +{ + unknownCentralRiver(false); +} + +/** + * Creates a very small central river. + */ +function unknownCentralRiver(shallows) +{ + let waterHeight = -4; + let heightShallow = -2; + + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + let startAngle = randomAngle(); + + if (!isNomad()) + { + [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.5)); + markPlayerArea("large"); + } + + g_Map.log("Creating the main river"); + let [coord1, coord2] = centralRiverCoordinates(startAngle); + createArea( + new PathPlacer(coord1, coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01), + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), + avoidClasses(clPlayerTerritory, 4)); + + g_Map.log("Creating small water spots at the map border to ensure separation of players"); + for (let coord of [coord1, coord2]) + createArea( + new ClumpPlacer(diskArea(scaleByMapSize(5, 10)), 0.95, 0.6, Infinity, coord), + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2), + avoidClasses(clPlayerTerritory, 8)); + + if (shallows) + { + g_Map.log("Creating the shallows of the main river"); + for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i) + { + let location = fractionToTiles(randFloat(0.15, 0.85)); + createPassage({ + "start": new Vector2D(location, mapBounds.top).rotateAround(startAngle, mapCenter), + "end": new Vector2D(location, mapBounds.bottom).rotateAround(startAngle, mapCenter), + "startWidth": scaleByMapSize(8, 12), + "endWidth": scaleByMapSize(8, 12), + "smoothWidth": 2, + "startHeight": heightShallow, + "endHeight": heightShallow, + "constraints": new HeightConstraint(-Infinity, heightShallow), + "tileClass": clShallow + }); + } + } + + if (randBool(2/3)) + createTributaryRivers( + startAngle, + randIntInclusive(8, scaleByMapSize(12, 16)), + scaleByMapSize(10, 20), + -4, + [-6, -1.5], + Math.PI / 5, + clWater, + clShallow, + avoidClasses(clPlayerTerritory, 3)); +} + +/** + * Creates a circular lake in the middle and possibly a river between each player ("pizza slices"). + */ +function unknownRiversAndLake() +{ + let waterHeight = -4; + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + let startAngle; + if (!isNomad()) + { + let playerAngle; + [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); + markPlayerArea("small"); + } + + let lake = randBool(3/4); + if (lake) + { + g_Map.log("Creating lake"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.17)), 0.7, 0.1, Infinity, mapCenter), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), + new TileClassPainter(clWater) + ]); + + createShoreJaggedness(waterHeight, clWater, 3); + } + + // TODO: On nomad because the resource imbalances per island are too drastic + + { + g_Map.log("Creating small rivers separating players"); + for (let river of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) + { + createArea( + new PathPlacer(mapCenter, river, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), + new TileClassPainter(clWater) + ], + avoidClasses(clPlayer, 5)); + + createArea( + new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, river), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0), + new TileClassPainter(clWater) + ], + avoidClasses(clPlayer, 5)); + } + + g_Map.log("Creating small lake"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.04)), 0.7, 0.1, Infinity, mapCenter), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), + new TileClassPainter(clWater) + ]); + } + + if (!isNomad && lake && randBool(2/3)) + { + g_Map.log("Creating small central island"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter), + [ + landElevationPainter, + new TileClassPainter(clWater) + ]); + } +} + +/** + * Align players on a land strip with seas bordering on one or both sides that can hold islands. + */ +function unknownEdgeSeas() +{ + let waterHeight = -4; + + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + let startAngle = randomAngle(); + if (!isNomad()) + { + playerIDs = sortAllPlayers(); + playerPosition = playerPlacementLine(startAngle + Math.PI / 2, mapCenter, fractionToTiles(0.2)); + // Don't place the shoreline inside the CC, but possibly into the players territory + markPlayerArea("small"); + } + + for (let side of pickRandom([[0], [Math.PI], [0, Math.PI]])) + paintRiver({ + "parallel": true, + "start": new Vector2D(mapBounds.left, mapBounds.top).rotateAround(side + startAngle, mapCenter), + "end": new Vector2D(mapBounds.left, mapBounds.bottom).rotateAround(side + startAngle, mapCenter), + "width": scaleByMapSize(80, randFloat(270, 320)), + "fadeDist": scaleByMapSize(2, 8), + "deviation": 0, + "heightRiverbed": waterHeight, + "heightLand": heightLand, + "meanderShort": 20, + "meanderLong": 0 + }); + + createExtensionsOrIslands(); + paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); + createShoreJaggedness(waterHeight, clLand, 7, false); +} + +/** + * Land shaped like a concrescent moon around a central lake. + */ +function unknownGulf() +{ + let waterHeight = -3; + + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + let startAngle = randomAngle(); + if (!isNomad()) + { + g_Map.log("Determining player locations"); + + playerPosition = playerPlacementCustomAngle( + fractionToTiles(0.35), + mapCenter, + i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))))[0]; + + markPlayerArea("large"); + } + + let gulfParts = [ + { "radius": fractionToTiles(0.16), "distance": fractionToTiles(0) }, + { "radius": fractionToTiles(0.2), "distance": fractionToTiles(0.2) }, + { "radius": fractionToTiles(0.22), "distance": fractionToTiles(0.49) } + ]; + + for (let gulfPart of gulfParts) + { + let position = Vector2D.sub(mapCenter, new Vector2D(gulfPart.distance, 0).rotate(-startAngle)).round(); + createArea( + new ClumpPlacer(diskArea(gulfPart.radius), 0.7, 0.05, Infinity, position), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), + new TileClassPainter(clWater) + ], + avoidClasses(clPlayerTerritory, defaultPlayerBaseRadius())); + } +} + +/** + * Mainland style with some small random lakes. + */ +function unknownLakes() +{ + let waterHeight = -5; + + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + if (!isNomad()) + { + [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); + markPlayerArea("large"); + } + + g_Map.log("Creating lakes"); + createAreas( + new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, Infinity), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5), + new TileClassPainter(clWater) + ], + [avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()], + scaleByMapSize(5, 16)); +} + +/** + * A large hill leaving players only a small passage to each of the the two neighboring players. + */ +function unknownPasses() +{ + let heightMountain = 24; + let waterHeight = -4; + + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightLand)); + + let playerAngle; + let startAngle; + if (!isNomad()) + { + [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); + markPlayerArea("small"); + } + else + startAngle = randomAngle(); + + g_Map.log("Creating a mountain range between neighboring players"); + for (let mountain of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) + { + createArea( + new PathPlacer(mapCenter, mountain, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), + [ + // More smoothing than this often results in the mountainrange becoming passable to one player. + new SmoothElevationPainter(ELEVATION_SET, heightMountain, 1), + new TileClassPainter(clWater) + ], + avoidClasses(clPlayer, 5)); + + // Small mountain at the map border between the players to ensure separation of players + createArea( + new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, mountain), + new SmoothElevationPainter(ELEVATION_SET, heightMountain, 0), + avoidClasses(clPlayer, 5)); + } + + g_Map.log("Creating passages between neighboring players"); + let passes = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mapCenter)[0]; + for (let i = 0; i < numPlayers; ++i) + { + createArea( + new PathPlacer( + passes[2 * i], + passes[2 * ((i + 1) % numPlayers)], + scaleByMapSize(14, 24), + 0.4, + 3 * scaleByMapSize(1, 3), + 0.2, + 0.05), + new SmoothElevationPainter(ELEVATION_SET, heightLand, 2)); + } + + if (randBool(2/5)) + { + g_Map.log("Create central lake"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.1)), 0.7, 0.1, Infinity, mapCenter), + [ + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3), + new TileClassPainter(clWater) + ]); + } + else + { + g_Map.log("Fill area between the paths"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter), + [ + new SmoothElevationPainter(ELEVATION_SET, heightMountain, 4), + new TileClassPainter(clWater) + ]); + } +} + +/** + * Land enclosed by a hill that leaves small areas for civic centers and large central place. + */ +function unknownLowlands() +{ + let heightMountain = 30; + + g_Map.log("Creating mountain that is going to separate players"); + createArea( + new MapBoundsPlacer(), + new ElevationPainter(heightMountain)); + + let playerAngle; + let startAngle; + if (!isNomad()) + { + [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); + markPlayerArea("small"); + } + else + startAngle = randomAngle(); + + g_Map.log("Creating valleys enclosed by the mountain"); + let valleys = numPlayers; + if (mapSize >= 128 && numPlayers <= 2 || + mapSize >= 192 && numPlayers <= 3 || + mapSize >= 320 && numPlayers <= 4 || + mapSize >= 384 && numPlayers <= 5 || + mapSize >= 448 && numPlayers <= 6) + valleys *= 2; + + g_Map.log("Creating player valley"); + for (let valley of distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mapCenter)[0]) + { + createArea( + new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, Infinity, valley), + [ + new SmoothElevationPainter(ELEVATION_SET, heightLand, 2), + new TileClassPainter(clLand) + ]); + + // Passage from player to center + createArea( + new PathPlacer(mapCenter, valley, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), + [ + landElevationPainter, + new TileClassPainter(clWater) + ]); + } + + g_Map.log("Creating the big central area"); + createArea( + new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.7, 0.1, Infinity, mapCenter), + [ + landElevationPainter, + new TileClassPainter(clWater) + ]); +} + +/** + * No water, no hills. + */ +function unknownMainland() +{ + createArea( + new MapBoundsPlacer(), + new ElevationPainter(3)); + + if (!isNomad()) + { + [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); + markPlayerArea("small"); + } +} + +function centralRiverCoordinates(angle) +{ + return [ + new Vector2D(mapBounds.left + 1, mapCenter.y), + new Vector2D(mapBounds.right - 1, mapCenter.y) + ].map(v => v.rotateAround(angle, mapCenter)); +} + +function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true) +{ + g_Map.log("Creating shore jaggedness"); + for (let i = 0; i < 2; ++i) + if (i || inwards) + createAreas( + new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, Infinity), + [ + new SmoothElevationPainter(ELEVATION_SET, i ? heightLand : waterHeight, 4), + i ? new TileClassPainter(clLand) : new TileClassUnPainter(clLand) + ], + [ + avoidClasses(clPlayer, 20, clPeninsulaSteam, 20), + borderClasses(borderClass, shoreDist, shoreDist) + ], + scaleByMapSize(7, 130) * 2, + 150); +} + +function createExtensionsOrIslands() +{ + let rnd = randIntInclusive(1, 3); + + if (rnd == 1) + { + g_Map.log("Creating islands"); + createAreas( + new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)), + [ + landElevationPainter, + new TileClassPainter(clLand) + ], + avoidClasses(clLand, 3, clPlayer, 3), + scaleByMapSize(2, 5) * randIntInclusive(8, 14)); + } + else if (rnd == 2) + { + g_Map.log("Creating extentions"); + createAreas( + new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07), + [ + landElevationPainter, + new TileClassPainter(clLand) + ], + null, + scaleByMapSize(2, 5) * randIntInclusive(8, 14)); + } +} + +/** + * Prevent impassable terrain and resource collisions at the the civic center and starting resources. + */ +function markPlayerArea(size) +{ + for (let i = 0; i < numPlayers; ++i) + { + addCivicCenterAreaToClass(playerPosition[i], clPlayer); + + if (size == "large") + createArea( + new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, Infinity, playerPosition[i]), + new TileClassPainter(clPlayerTerritory)); + } +} + +function paintUnknownMapBasedOnHeight() +{ + paintTerrainBasedOnHeight(heightCliff, 40, 1, tCliff); + paintTerrainBasedOnHeight(3, heightCliff, 1, tMainTerrain); + paintTerrainBasedOnHeight(1, 3, 1, tShore); + paintTerrainBasedOnHeight(-8, 1, 2, tWater); + + unPaintTileClassBasedOnHeight(0, heightCliff, 1, clWater); + unPaintTileClassBasedOnHeight(-6, 0, 1, clLand); + + paintTileClassBasedOnHeight(-6, 0, 1, clWater); + paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); + paintTileClassBasedOnHeight(heightCliff, 40, 1, clHill); +} + +/** + * Place resources and decoratives after the player territory was marked. + */ +function createUnknownObjects() +{ + g_Map.log("Creating bumps"); + createAreas( + new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, Infinity), + new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), + [avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)], + randIntInclusive(0, scaleByMapSize(1, 2) * 200)); + + g_Map.log("Creating hills"); + createAreas( + new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, Infinity), + [ + new LayeredPainter([tCliff, tHill], [2]), + new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), + new TileClassPainter(clHill) + ], + [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)], + randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9)) + ); + Engine.SetProgress(30); + + g_Map.log("Creating forests"); + let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1)); + let types = [ + [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], + [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] + ]; + + let size = numForest / (scaleByMapSize(2, 8) * numPlayers); + let num = Math.floor(size / types.length); + for (let type of types) + createAreas( + new ClumpPlacer(numForest / num, 0.1, 0.1, Infinity), + [ + new LayeredPainter(type, [2]), + new TileClassPainter(clForest) + ], + [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 2), stayClasses(clLand, 4)], + num); + Engine.SetProgress(50); + + g_Map.log("Creating dirt patches"); + let patchCount = (currentBiome() == "generic/savanna" ? 3 : 1) * scaleByMapSize(15, 45); + for (let patchSize of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) + createAreas( + new ClumpPlacer(patchSize, 0.3, 0.06, 0.5), + [ + new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), + new TileClassPainter(clDirt) + ], + [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], + patchCount); + + g_Map.log("Creating grass patches"); + for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) + createAreas( + new ClumpPlacer(size, 0.3, 0.06, 0.5), + new TerrainPainter(tTier4Terrain), + [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], + patchCount); + + Engine.SetProgress(55); + + g_Map.log("Creating stone mines"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)], true, clRock), + 0, + [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], + randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), + 100); + + g_Map.log("Creating small stone quarries"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), + 0, + [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], + randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), + 100); + + g_Map.log("Creating metal mines"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), + 0, + [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 2), stayClasses(clLand, 3)], + randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), + 100); + Engine.SetProgress(65); + + g_Map.log("Creating small decorative rocks"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true), + 0, + [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], + scaleByMapSize(16, 262), + 50); + + g_Map.log("Creating large decorative rocks"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true), + 0, + [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], + scaleByMapSize(8, 131), + 50); + Engine.SetProgress(70); + + g_Map.log("Creating deer"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood), + 0, + [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], + randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), + 50); + + g_Map.log("Creating berry bush"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood), + 0, + [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], + randIntInclusive(1, 4) * numPlayers + 2, + 50); + Engine.SetProgress(75); + + g_Map.log("Creating sheep"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood), + 0, + [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], + randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), + 50); + + g_Map.log("Creating fish"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood), + 0, + avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 2, clFood, 20), + randIntInclusive(15, 40) * numPlayers, + 60); + Engine.SetProgress(85); + + g_Map.log("Creating straggler trees"); + types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4]; + + num = Math.floor(numStragglers / types.length); + for (let type of types) + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest), + 0, + [avoidClasses(clWater, 1, clForest, 1, clHill, 2, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)], + num); + + let planetm = currentBiome() == "generic/tropic" ? 8 : 1; + + g_Map.log("Creating small grass tufts"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]), + 0, + [avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)], + planetm * scaleByMapSize(13, 200)); + Engine.SetProgress(90); + + g_Map.log("Creating large grass tufts"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -Math.PI / 8, Math.PI / 8)]), + 0, + [avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)], + planetm * scaleByMapSize(13, 200)); + Engine.SetProgress(95); + + g_Map.log("Creating shallow flora"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]), + 0, + stayClasses(clShallow, 1), + 60 * scaleByMapSize(13, 200), + 80); + + g_Map.log("Creating bushes"); + createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]), + 0, + [avoidClasses(clWater, 1, clHill, 2, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)], + planetm * scaleByMapSize(13, 200), + 50); + + setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"])); + setSunRotation(randomAngle()); + setSunElevation(Math.PI * randFloat(1/5, 1/3)); +} + +function createUnknownPlayerBases() +{ + placePlayerBases({ + "PlayerPlacement": [playerIDs, playerPosition], + "BaseResourceClass": clBaseResource, + "Walls": g_StartingWalls, + "CityPatch": { + "outerTerrain": tRoadWild, + "innerTerrain": tRoad, + "painters": [ + new TileClassPainter(clPlayer) + ] + }, + "Chicken": { + }, + "Berries": { + "template": oFruitBush + }, + "Mines": { + "types": [ + { "template": oMetalLarge }, + { "template": oStoneLarge } + ] + }, + "Treasures": { + "types": [ + { + "template": oWoodTreasure, + "count": g_StartingTreasures ? 14 : 0 + } + ] + }, + "Trees": { + "template": oTree1 + }, + "Decoratives": { + "template": aGrassShort + } + }); +} createUnknownMap(); diff --git a/binaries/data/mods/public/maps/random/unknown.json b/binaries/data/mods/public/maps/random/unknown.json index dcb92495ec..44b8f93046 100644 --- a/binaries/data/mods/public/maps/random/unknown.json +++ b/binaries/data/mods/public/maps/random/unknown.json @@ -2,9 +2,87 @@ "settings" : { "Name" : "Unknown", "Script" : "unknown.js", - "Description" : "The unknown. Warning: May be a naval map.", + "Description" : "The unknown.", "Preview" : "unknown.png", "SupportedBiomes": "generic/", - "CircularMap" : true + "CircularMap" : true, + "Landscapes": { + "land" : [ + { + "Id": "Continent", + "Name": "Continent", + "Description": "All players starts on a continent surrounded by water.", + "Preview": "unknown_continent.png" + }, + { + "Id": "Isthmus", + "Name": "Isthmus", + "Description": "Two Mediterranean land masses connected by a narrow spit of land, called an 'Isthmus'.", + "Preview": "unknown_isthmus.png" + }, + { + "Id": "CentralRiver", + "Name": "Central River", + "Description": "A small central river.", + "Preview": "unknown_central_river.png" + }, + { + "Id": "EdgeSeas", + "Name": "Edge Seas", + "Description": "Players are aligned on a strip of land with seas bordering on one or both sides that may hold islands.", + "Preview": "unknown_edge_seas.png" + }, + { + "Id": "Gulf", + "Name": "Gulf", + "Description": "Land shaped like a concrescent moon around a central lake.", + "Preview": "unknown_gulf.png" + }, + { + "Id": "Lakes", + "Name": "Lakes", + "Description": "Mainland style with some small random lakes.", + "Preview": "unknown_lakes.png" + }, + { + "Id": "Passes", + "Name": "Passes", + "Description": "A large hill encompasses the map and leaves players only a small passage to the two neighboring players.", + "Preview": "unknown_passes.png" + }, + { + "Id": "Lowlands", + "Name": "Lowlands", + "Description": "The land is enclosed by a hill that leaves a small area per player connected to the large central place.", + "Preview": "unknown_lowlands.png" + }, + { + "Id": "Mainland", + "Name": "Mainland", + "Description": "A typical map without any water.", + "Preview": "unknown_mainland.png" + } + ], + "naval": [ + { + "Id": "CentralSea", + "Name": "Naval: Central Sea", + "Description": "A huge sea is dividing the map into two halves.", + "Preview": "unknown_central_sea.png" + }, + { + "Id": "Archipelago", + "Name": "Naval: Archipelago", + "Description": "Players are scattered across island chains and disconnected islands.", + "Preview": "unknown_archipelago.png" + }, + { + "Id": "RiversAndLake", + "Name": "Naval: Lake and rivers", + "Description": "A central lake with rivers possibly separating neighboring players.", + "Preview": "unknown_rivers_and_lake.png" + } + ] + } } } diff --git a/binaries/data/mods/public/maps/random/unknown_land.js b/binaries/data/mods/public/maps/random/unknown_land.js deleted file mode 100644 index 8987a6438a..0000000000 --- a/binaries/data/mods/public/maps/random/unknown_land.js +++ /dev/null @@ -1,10 +0,0 @@ -Engine.LoadLibrary("rmgen"); -Engine.LoadLibrary("rmgen-common"); -Engine.LoadLibrary("rmbiome"); -Engine.LoadLibrary("the_unknown"); - -g_AllowNaval = false; - -createUnknownMap(); - -g_Map.ExportMap(); diff --git a/binaries/data/mods/public/maps/random/unknown_land.json b/binaries/data/mods/public/maps/random/unknown_land.json deleted file mode 100644 index 73a2b14d1c..0000000000 --- a/binaries/data/mods/public/maps/random/unknown_land.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "settings" : { - "Name" : "Unknown Land", - "Script" : "unknown_land.js", - "Description" : "The unknown.", - "Preview" : "unknown.png", - "SupportedBiomes": "generic/", - "CircularMap" : true - } -}