1
0
forked from mirrors/0ad

Elephantine random map script, refs #5040.

Uses OpenStreetMap polygons for the shape of the water, refs #5018.

Proposed by: Sundiata, wowgetoffyourcellphone
This was SVN commit r21330.
This commit is contained in:
elexis
2018-02-23 05:41:35 +00:00
parent dca8a68559
commit 4c979d8e38
6 changed files with 611 additions and 0 deletions
@@ -8,3 +8,9 @@ https://visibleearth.nasa.gov/view.php?id=73934
> Most images published in Visible Earth are freely available for re-publication or re-use, including commercial purposes, except for where copyright is indicated. In those cases you must obtain the copyright holders permission; we usually provide links to the organization that holds the copyright.
> We ask that you use the credit statement attached with each image or else credit Visible Earth; the only mandatory credit is NASA.
> For more information about using NASA imagery visit http://www.nasa.gov/audience/formedia/features/MP_Photo_Guidelines.html.
This directory also contains heightmaps based on OpenStreetMap tiledata, which is available under Open Database Licence and CC-BY-SA, see
https://www.openstreetmap.org/copyright
https://opendatacommons.org/licenses/odbl/1.0/
See the according JS files to find the image sources.
@@ -0,0 +1,516 @@
/**
* Heightmap image source:
* OpenStreetMap, available under Open Database Licence, www.openstreetmap.org/copyright
* http://download.geofabrik.de/africa.html
*
* To reproduce the river image:
* You need a gdal version that supports osm, see http://www.gdal.org/drv_osm.html
* wget http://download.geofabrik.de/africa/egypt-latest.osm.pbf
* lon=32.89; lat=24.09175; width=0.025;
* lat1=$(bc <<< ";scale=5;$lat-$width/2"); lon1=$(bc <<< ";scale=5;$lon+$width/2"); lat2=$(bc <<< ";scale=5;$lat+$width/2"); lon2=$(bc <<< ";scale=5;$lon-$width/2")
* rm elephantine.geojson; ogr2ogr -f GeoJSON elephantine.geojson -clipdst $lon1 $lat1 $lon2 $lat2 egypt-latest.osm.pbf -sql 'select * from multipolygons where natural="water"'
* gdal_rasterize -burn 10 -ts 512 512 elephantine.geojson elephantine.tif
* convert elephantine.tif -threshold 50% -negate elephantine.png
*
* No further changes should be applied to the image to keep it easily interchangeable.
*/
Engine.LoadLibrary("rmgen");
Engine.LoadLibrary("rmgen-common");
TILE_CENTERED_HEIGHT_MAP = true;
const tPrimary = ["savanna_dirt_rocks_a_red", "savanna_dirt_a_red", "savanna_dirt_b_red"];
const tDirt = ["new_savanna_dirt_c", "new_savanna_dirt_d", "savanna_dirt_b_red", "savanna_dirt_plants_cracked"];
const tWater = "desert_sand_wet";
const tRoad = "savanna_tile_a_red";
const tRoadIsland = "savanna_tile_a";
const tGrass = ["savanna_shrubs_a_wetseason", "alpine_grass_b_wild", "medit_shrubs_a", "steppe_grass_green_a"];
const tForestFloorLand = "savanna_forestfloor_b_red";
const tForestFloorIsland = pickRandom(tGrass);
const oAcacia = "gaia/flora_tree_acacia";
const oPalms = [
"gaia/flora_tree_cretan_date_palm_tall",
"gaia/flora_tree_cretan_date_palm_short",
"gaia/flora_tree_palm_tropic",
"gaia/flora_tree_date_palm",
"gaia/flora_tree_senegal_date_palm",
"gaia/flora_tree_medit_fan_palm"
];
const oStoneLarge = "gaia/geology_stonemine_savanna_quarry";
const oStoneSmall = "gaia/geology_stone_desert_small";
const oMetalLarge = "gaia/geology_metal_savanna_slabs";
const oMetalSmall = "gaia/geology_metal_desert_small";
const oBerryBush = "gaia/flora_bush_berry_desert";
const oGazelle = "gaia/fauna_gazelle";
const oRhino = "gaia/fauna_rhino";
const oWarthog = "gaia/fauna_boar";
const oGiraffe = "gaia/fauna_giraffe";
const oGiraffeInfant = "gaia/fauna_giraffe_infant";
const oElephant = "gaia/fauna_elephant_african_bush";
const oElephantInfant = "gaia/fauna_elephant_african_infant";
const oLion = "gaia/fauna_lion";
const oLioness = "gaia/fauna_lioness";
const oCrocodile = "gaia/fauna_crocodile";
const oFish = "gaia/fauna_fish";
const oHawk = "gaia/fauna_hawk";
const oTemples = ["structures/kush_temple_amun", "structures/kush_temple_apedemak"];
const oPyramid = "structures/kush_pyramid_large";
const oTowers = new Array(2).fill("uncapturable|structures/kush_sentry_tower").concat(["uncapturable|structures/kush_defense_tower"]);
const oHeroes = Engine.FindTemplates("units/", true).filter(templateName => templateName.startsWith("units/kush_hero_"));
const oUnits = Engine.FindTemplates("units/", false).filter(templateName =>
templateName.startsWith("units/kush_") &&
oHeroes.every(heroTemplateName => heroTemplateName != templateName) &&
Engine.GetTemplate(templateName).Identity.VisibleClasses._string.split(" ").some(type => ["Soldier", "Healer", "Female"].indexOf(type) != -1));
const aRock = actorTemplate("geology/stone_savanna_med");
const aStatues = [
"props/structures/kushites/statue_bird",
"props/structures/kushites/statue_lion",
"props/structures/kushites/statue_ram"
].map(actorTemplate);
const aBushesIslands = [
...new Array(4).fill("props/flora/ferns"),
...new Array(3).fill("props/flora/foliagebush"),
"props/flora/bush",
"props/flora/bush_medit_la",
"props/flora/bush_medit_la_lush",
"props/flora/bush_medit_me_lush",
"props/flora/bush_medit_sm",
"props/flora/bush_medit_sm_lush",
"props/flora/bush_tempe_la_lush"
].map(actorTemplate);
const aBushesDesert = [
"props/flora/bush_dry_a",
"props/flora/bush_medit_la_dry",
"props/flora/bush_medit_me_dry",
"props/flora/bush_medit_sm",
"props/flora/bush_medit_sm_dry",
"props/flora/bush_tempe_me_dry",
"props/flora/grass_soft_dry_large_tall",
"props/flora/grass_soft_dry_small_tall"
].map(actorTemplate);
const pForestPalmsLand = [
tForestFloorLand,
...oPalms.map(tree => tForestFloorLand + TERRAIN_SEPARATOR + tree),
tForestFloorLand];
const pForest2Land = [
tForestFloorLand,
tForestFloorLand + TERRAIN_SEPARATOR + oAcacia,
tForestFloorLand
];
const pForestPalmsIsland = [
tForestFloorIsland,
...oPalms.map(tree => tForestFloorIsland + TERRAIN_SEPARATOR + tree),
tForestFloorIsland];
const pForest2Island = [
tForestFloorIsland,
tForestFloorIsland + TERRAIN_SEPARATOR + oAcacia,
tForestFloorIsland
];
const heightScale = num => num * g_MapSettings.Size / 320;
const heightSeaGround = heightScale(-15);
const heightWaterLevel = heightScale(0);
const heightShore = heightScale(1);
const minHeight = -1;
const maxHeight = 2;
const g_Map = new RandomMap(0, tPrimary);
const mapBounds = g_Map.getBounds();
const mapCenter = g_Map.getCenter();
const clDune = g_Map.createTileClass();
const clWater = g_Map.createTileClass();
const clIsland = g_Map.createTileClass();
const clCliff = g_Map.createTileClass();
const clPlayer = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const clForest = g_Map.createTileClass();
const clDirt = g_Map.createTileClass();
const clRock = g_Map.createTileClass();
const clMetal = g_Map.createTileClass();
const clFood = g_Map.createTileClass();
const clTemple = g_Map.createTileClass();
const clTower = g_Map.createTileClass();
const clStatue = g_Map.createTileClass();
const clSoldier = g_Map.createTileClass();
const riverAngle = 0.22 * Math.PI;
const riverWidthBorder = fractionToTiles(0.27);
const riverWidthCenter = fractionToTiles(0.35);
const avoidCollisions = avoidClasses(
clPlayer, 15, clWater, 1, clForest, 1, clRock, 4, clMetal, 4, clFood, 6,
clTemple, 10, clCliff, 0, clStatue, 2, clSoldier, 1, clTower, 1, clDune, 0);
g_Map.LoadHeightmapImage("elephantine.png", minHeight, maxHeight);
Engine.SetProgress(3);
g_Map.log("Lowering sea ground");
createArea(
new MapBoundsPlacer(),
[
new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4),
new TileClassPainter(clWater)
],
new HeightConstraint(-Infinity, heightWaterLevel));
Engine.SetProgress(6);
g_Map.log("Smoothing heightmap");
createArea(
new MapBoundsPlacer(),
new SmoothingPainter(1, scaleByMapSize(0.1, 0.5), 1));
Engine.SetProgress(10);
g_Map.log("Marking islands");
createArea(
new ConvexPolygonPlacer(
[
new Vector2D(mapCenter.x - riverWidthBorder / 2, mapBounds.top),
new Vector2D(mapCenter.x - riverWidthBorder / 2, mapBounds.bottom),
new Vector2D(mapCenter.x - riverWidthCenter / 2, mapCenter.y),
new Vector2D(mapCenter.x + riverWidthCenter / 2, mapCenter.y),
new Vector2D(mapCenter.x + riverWidthBorder / 2, mapBounds.top),
new Vector2D(mapCenter.x + riverWidthBorder / 2, mapBounds.bottom)
].map(v => v.rotateAround(riverAngle, mapCenter)),
Infinity),
new TileClassPainter(clIsland),
avoidClasses(clPlayer, 0, clWater, 0));
Engine.SetProgress(13);
g_Map.log("Painting islands");
createArea(
new MapBoundsPlacer(),
new TerrainPainter(tGrass),
stayClasses(clIsland, 0));
Engine.SetProgress(16);
g_Map.log("Painting shoreline");
createArea(
new MapBoundsPlacer(),
new TerrainPainter(tWater),
new HeightConstraint(-Infinity, heightShore));
Engine.SetProgress(19);
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(riverAngle, fractionToTiles(0.62)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"baseResourceConstraint": avoidClasses(clWater, 4),
"Walls": "towers",
"CityPatch": {
"outerTerrain": tRoad,
"innerTerrain": tRoad
},
"Chicken": {
},
"Berries": {
"template": oBerryBush
},
"Mines": {
"types": [
{ "template": oMetalLarge },
{ "template": oStoneLarge }
]
},
"Trees": {
"template": oAcacia,
"count": 2
},
"Decoratives": {
"template": pickRandom(aBushesDesert)
}
});
Engine.SetProgress(22);
createBumps(avoidClasses(clPlayer, 10, clWater, 2), scaleByMapSize(10, 500), 1, 8, 4, 0.2, 3);
Engine.SetProgress(25);
g_Map.log("Marking cliffs");
createArea(
new MapBoundsPlacer(),
new TileClassPainter(clCliff),
[
avoidClasses(clWater, 2),
new SlopeConstraint(2, Infinity)
]);
Engine.SetProgress(30);
g_Map.log("Creating temple");
createObjectGroups(
new SimpleGroup([new RandomObject(oTemples, 1, 1, 0, 1)], true, clTemple),
0,
stayClasses(clIsland, scaleByMapSize(8, 24)),
1,
200);
Engine.SetProgress(34);
g_Map.log("Creating pyramid");
createObjectGroups(
new SimpleGroup([new SimpleObject(oPyramid, 1, 1, 0, 1)], true, clTemple),
0,
[stayClasses(clIsland, scaleByMapSize(8, 24)), avoidCollisions],
1,
200);
Engine.SetProgress(37);
g_Map.log("Painting city patches");
createArea(
new MapBoundsPlacer(),
new TerrainPainter(tRoadIsland),
new NearTileClassConstraint(clTemple, 8));
Engine.SetProgress(40);
g_Map.log("Creating stone mines");
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, 0, 2 * Math.PI, 1)]
],
avoidClasses(clWater, 4, clPlayer, 20, clRock, 10),
clRock,
scaleByMapSize(6, 24));
Engine.SetProgress(43);
g_Map.log("Creating metal mines");
createMines(
[
[new SimpleObject(oMetalSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oMetalLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)],
[new SimpleObject(oMetalSmall, 2, 5, 1, 3, 0, 2 * Math.PI, 1)]
],
avoidClasses(clWater, 4, clPlayer, 20, clMetal, 10, clRock, 5),
clMetal,
scaleByMapSize(6, 24));
Engine.SetProgress(46);
g_Map.log("Creating kushite towers");
createObjectGroups(
new SimpleGroup([new RandomObject(oTowers, 1, 1, 0, 1)], true, clTower),
0,
[
stayClasses(clIsland, scaleByMapSize(4, 24)),
new NearTileClassConstraint(clTemple, 25),
avoidClasses(clTower, 12, clTemple, 12, clPlayer, 30),
avoidCollisions
],
scaleByMapSize(4, 12),
200);
Engine.SetProgress(49);
var [forestTrees, stragglerTrees] = getTreeCounts(400, 3000, 0.7);
createForests(
[tForestFloorLand, tForestFloorLand, tForestFloorLand, pForestPalmsLand, pForest2Land],
[avoidCollisions, avoidClasses(clIsland, 0, clPlayer, 20, clForest, 18, clWater, 2)],
clForest,
forestTrees / 2);
Engine.SetProgress(52);
createForests(
[tForestFloorIsland, tForestFloorIsland, tForestFloorIsland, pForestPalmsIsland, pForest2Island],
[stayClasses(clIsland, 0), avoidClasses(clForest, 15, clWater, 2), avoidCollisions],
clForest,
forestTrees / 2);
Engine.SetProgress(55);
g_Map.log("Creating dirt patches");
createPatches(
[scaleByMapSize(5, 15)],
tDirt,
avoidClasses(clWater, 0, clIsland, 0, clForest, 0, clDirt, 5, clPlayer, 12, clDune, 1),
scaleByMapSize(5, 30),
clDirt);
Engine.SetProgress(58);
g_Map.log("Creating statues");
createObjectGroups(
new SimpleGroup([new RandomObject(aStatues, 1, 1, 0, 1)], true, clStatue),
0,
[
stayClasses(clIsland, scaleByMapSize(8, 24)),
new NearTileClassConstraint(clTemple, 8),
avoidCollisions
],
scaleByMapSize(2, 10),
400);
Engine.SetProgress(61);
g_Map.log("Creating hero");
createObjectGroups(
new SimpleGroup([new RandomObject(oHeroes, 1, 1, 0, 1)], true, clSoldier),
0,
[
stayClasses(clIsland, scaleByMapSize(2, 24)),
new NearTileClassConstraint(clTemple, 14),
avoidCollisions
],
1,
500);
Engine.SetProgress(64);
g_Map.log("Creating soldiers");
createObjectGroups(
new SimpleGroup([new RandomObject(oUnits, 1, 1, 0, 1)], true, clSoldier),
0,
[
stayClasses(clIsland, scaleByMapSize(2, 24)),
new NearTileClassConstraint(clTemple, 20),
avoidCollisions
],
scaleByMapSize(12, 60),
200);
Engine.SetProgress(67);
g_Map.log("Creating berries");
createObjectGroups(
new SimpleGroup([new SimpleObject(oBerryBush, 3, 5, 1, 4)], true, clFood),
0,
avoidCollisions,
clFood,
scaleByMapSize(2, 12));
Engine.SetProgress(70);
g_Map.log("Creating rhinos");
createObjectGroups(
new SimpleGroup([new SimpleObject(oRhino, 1, 1, 0, 1)], true, clFood),
0,
avoidCollisions,
scaleByMapSize(2, 10),
50);
Engine.SetProgress(73);
g_Map.log("Creating warthog");
createObjectGroups(
new SimpleGroup([new SimpleObject(oWarthog, 1, 1, 0, 1)], true, clFood),
0,
avoidCollisions,
scaleByMapSize(2, 10),
50);
Engine.SetProgress(77);
g_Map.log("Creating gazelles");
createObjectGroups(
new SimpleGroup([new SimpleObject(oGazelle, 5, 7, 2, 4)], true, clFood),
0,
[avoidClasses(clIsland, 1), avoidCollisions],
scaleByMapSize(2, 10),
50);
Engine.SetProgress(80);
g_Map.log("Creating giraffes");
createObjectGroups(
new SimpleGroup([new SimpleObject(oGiraffe, 2, 3, 2, 4), new SimpleObject(oGiraffeInfant, 2, 3, 2, 4)], true, clFood),
0,
avoidCollisions,
scaleByMapSize(2, 10),
50);
Engine.SetProgress(83);
if (!isNomad())
{
g_Map.log("Creating lions");
createObjectGroups(
new SimpleGroup([new SimpleObject(oLion, 1, 2, 2, 4), new SimpleObject(oLioness, 2, 3, 2, 4)], true, clFood),
0,
avoidCollisions,
scaleByMapSize(2, 10),
50);
Engine.SetProgress(87);
}
g_Map.log("Creating elephants");
createObjectGroups(
new SimpleGroup([new SimpleObject(oElephant, 2, 3, 2, 4), new SimpleObject(oElephantInfant, 2, 3, 2, 4)], true, clFood),
0,
avoidCollisions,
scaleByMapSize(2, 10),
50);
Engine.SetProgress(88);
g_Map.log("Creating crocodiles");
createObjectGroups(
new SimpleGroup([new SimpleObject(oCrocodile, 2, 3, 2, 4)], true, clFood),
0,
[
new NearTileClassConstraint(clWater, 3),
avoidCollisions
],
scaleByMapSize(2, 10),
50);
Engine.SetProgress(89);
g_Map.log("Creating hawk");
for (let i = 0; i < scaleByMapSize(0, 2); ++i)
g_Map.placeEntityAnywhere(oHawk, 0, mapCenter, randomAngle());
Engine.SetProgress(90);
g_Map.log("Creating fish");
createObjectGroups(
new SimpleGroup([new SimpleObject(oFish, 1, 2, 0, 1)], true, clFood),
0,
[stayClasses(clWater, 4), avoidClasses(clFood, 16)],
scaleByMapSize(15, 80),
50);
Engine.SetProgress(91);
createStragglerTrees(
[oAcacia],
avoidCollisions,
clForest,
stragglerTrees);
Engine.SetProgress(93);
placePlayersNomad(clPlayer, [avoidCollisions, avoidClasses(clIsland, 0)]);
Engine.SetProgress(95);
createDecoration(
aBushesDesert.map(bush => [new SimpleObject(bush, 0, 3, 2, 4)]),
aBushesDesert.map(bush => scaleByMapSize(20, 150) * randIntInclusive(1, 3)),
[avoidClasses(clIsland, 0), avoidCollisions]);
Engine.SetProgress(96);
createDecoration(
aBushesIslands.map(bush => [new SimpleObject(bush, 0, 4, 2, 4)]),
aBushesIslands.map(bush => scaleByMapSize(20, 150) * randIntInclusive(1, 3)),
[stayClasses(clIsland, 0), avoidCollisions]);
Engine.SetProgress(99);
createDecoration(
[[new SimpleObject(aRock, 0, 4, 2, 4)]],
[[scaleByMapSize(80, 500)]],
[avoidClasses(clIsland, 0), avoidCollisions]);
Engine.SetProgress(98);
setSunRotation(randomAngle());
setSunColor(0.85, 0.63, 0.4);
setSunElevation(Math.PI * randFloat(1/4, 1/2));
setWaterColor(0.2, 0.2, 0.4);
setWaterTint(0.75, 0.75, 0.75);
setWaterMurkiness(0.92);
setWaterWaviness(0.5);
setWaterType("clap");
setFogThickness(0.76);
setFogFactor(0);
setPPEffect("hdr");
setPPContrast(0.6);
setPPSaturation(0.45);
setPPBloom(0.4);
setTerrainAmbientColor(0.7, 0.6, 0.7);
setUnitsAmbientColor(0.6, 0.5, 0.6);
g_Map.ExportMap();
@@ -0,0 +1,14 @@
{
"settings" : {
"Name" : "Elephantine",
"Script" : "elephantine.js",
"Description" : "The unknown.",
"Preview" : "elephantine.png",
"Keywords": ["new", "naval"],
"CircularMap": true,
"TriggerScripts" : [
"scripts/TriggerHelper.js",
"random/elephantine_triggers.js"
]
}
}
Binary file not shown.
@@ -0,0 +1,69 @@
Trigger.prototype.InitElephantine = function()
{
let gaiaEnts = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(0);
for (let ent of gaiaEnts)
{
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
if (cmpIdentity && cmpIdentity.HasClass("Soldier"))
Engine.QueryInterface(ent, IID_UnitAI).SwitchToStance("defensive");
}
this.SpawnAndGarrisonBuilding(gaiaEnts, "Tower", ["kush_infantry_archer_e", "kush_infantry_spearman_e"]);
this.SpawnAndGarrisonBuilding(gaiaEnts, "Temple", ["kush_support_healer_e", "kush_support_female_citizen"]);
this.SpawnAndGarrisonBuilding(gaiaEnts, "Pyramid", ["kush_support_healer_e", "kush_support_female_citizen"]);
};
// Shameless copy of Danubius
Trigger.prototype.SpawnAndGarrisonBuilding = function(gaiaEnts, targetClass, templates)
{
for (let gaiaEnt of gaiaEnts)
{
let cmpIdentity = Engine.QueryInterface(gaiaEnt, IID_Identity);
if (!cmpIdentity || !cmpIdentity.HasClass(targetClass))
continue;
let cmpGarrisonHolder = Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder);
if (!cmpGarrisonHolder)
continue;
let unitCounts = this.RandomAttackerTemplates(templates, cmpGarrisonHolder.GetCapacity());
for (let template in unitCounts)
for (let newEnt of TriggerHelper.SpawnUnits(gaiaEnt, "units/" + template, unitCounts[template], 0))
Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder).Garrison(newEnt);
}
};
/**
* Return a random amount of these templates whose sum is count.
*/
Trigger.prototype.RandomAttackerTemplates = function(templates, count)
{
let ratios = new Array(templates.length).fill(1).map(i => randFloat(0, 1));
let ratioSum = ratios.reduce((current, sum) => current + sum, 0);
let remainder = count;
let templateCounts = {};
for (let i in templates)
{
let currentCount = +i == templates.length - 1 ? remainder : Math.round(ratios[i] / ratioSum * count);
if (!currentCount)
continue;
templateCounts[templates[i]] = currentCount;
remainder -= currentCount;
}
if (remainder != 0)
warn("Not as many templates as expected: " + count + " vs " + uneval(templateCounts));
return templateCounts;
};
{
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
cmpTrigger.RegisterTrigger("OnInitGame", "InitElephantine", { "enabled": true });
}