Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/scythian_rivulet.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/scythian_rivulet.png =================================================================== --- ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/scythian_rivulet.png (nonexistent) +++ ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/scythian_rivulet.png (revision 21079) Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/scythian_rivulet.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js (revision 21078) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js (revision 21079) @@ -1,685 +1,692 @@ /** * @file These functions locate and place the starting entities of players. */ var g_NomadTreasureTemplates = { "food": "gaia/special_treasure_food_jars", "wood": "gaia/special_treasure_wood", "stone": "gaia/special_treasure_stone", "metal": "gaia/special_treasure_metal" }; /** * These are identifiers of functions that can generate parts of a player base. * There must be a function starting with placePlayerBase and ending with this name. * This is a global so mods can extend this from external files. */ var g_PlayerBaseFunctions = [ // Possibly mark player class first here and use it afterwards "CityPatch", // Create the largest and most important entities first "Trees", "Mines", "Treasures", "Berries", "Chicken", "Decoratives" ]; function isNomad() { return !!g_MapSettings.Nomad; } function getNumPlayers() { return g_MapSettings.PlayerData.length - 1; } function getCivCode(playerID) { return g_MapSettings.PlayerData[playerID].Civ; } function areAllies(playerID1, playerID2) { return g_MapSettings.PlayerData[playerID1].Team !== undefined && g_MapSettings.PlayerData[playerID2].Team !== undefined && g_MapSettings.PlayerData[playerID1].Team != -1 && g_MapSettings.PlayerData[playerID2].Team != -1 && g_MapSettings.PlayerData[playerID1].Team === g_MapSettings.PlayerData[playerID2].Team; } function getPlayerTeam(playerID) { if (g_MapSettings.PlayerData[playerID].Team === undefined) return -1; return g_MapSettings.PlayerData[playerID].Team; } /** * Gets the default starting entities for the civ of the given player, as defined by the civ file. */ function getStartingEntities(playerID) { return g_CivData[getCivCode(playerID)].StartEntities; } /** * Places the given entities at the given location (typically a civic center and starting units). * @param location - A Vector2D specifying tile coordinates. * @param civEntities - An array of objects with the Template property and optionally a Count property. * The first entity is placed in the center, the other ones surround it. */ function placeStartingEntities(location, playerID, civEntities, dist = 6, orientation = BUILDING_ORIENTATION) { // Place the central structure let i = 0; let firstTemplate = civEntities[i].Template; if (firstTemplate.startsWith("structures/")) { g_Map.placeEntityPassable(firstTemplate, playerID, location, orientation); ++i; } // Place entities surrounding it let space = 2; for (let j = i; j < civEntities.length; ++j) { let angle = orientation - Math.PI * (1 - j / 2); let count = civEntities[j].Count || 1; for (let num = 0; num < count; ++num) { let position = Vector2D.sum([ location, new Vector2D(dist, 0).rotate(-angle), new Vector2D(space * (-num + 0.75 * Math.floor(count / 2)), 0).rotate(angle) ]); g_Map.placeEntityPassable(civEntities[j].Template, playerID, position, angle); } } } /** * Places the default starting entities as defined by the civilization definition, optionally including city walls. */ function placeCivDefaultStartingEntities(position, playerID, wallType, dist = 6, orientation = BUILDING_ORIENTATION) { placeStartingEntities(position, playerID, getStartingEntities(playerID), dist, orientation); placeStartingWalls(position, playerID, wallType, orientation); } /** * If the map is large enough and the civilization defines them, places the initial city walls or towers. * @param {string|boolean} wallType - Either "towers" to only place the wall turrets or a boolean indicating enclosing city walls. */ function placeStartingWalls(position, playerID, wallType, orientation = BUILDING_ORIENTATION) { let civ = getCivCode(playerID); if (civ != "iber" || g_Map.getSize() <= 128) return; if (wallType == "towers") placePolygonalWall(position, 15, ["entry"], "tower", civ, playerID, orientation, 7); else if (wallType) placeGenericFortress(position, 20, playerID); } /** * Places the civic center and starting resources for all given players. */ function placePlayerBases(playerBaseArgs) { g_Map.log("Creating playerbases"); let [playerIDs, playerPosition] = playerBaseArgs.PlayerPlacement; for (let i = 0; i < getNumPlayers(); ++i) { playerBaseArgs.playerID = playerIDs[i]; playerBaseArgs.playerPosition = playerPosition[i]; placePlayerBase(playerBaseArgs); } } /** * Places the civic center and starting resources. */ function placePlayerBase(playerBaseArgs) { if (isNomad()) return; placeCivDefaultStartingEntities(playerBaseArgs.playerPosition, playerBaseArgs.playerID, playerBaseArgs.Walls !== undefined ? playerBaseArgs.Walls : true); if (playerBaseArgs.PlayerTileClass) addCivicCenterAreaToClass(playerBaseArgs.playerPosition, playerBaseArgs.PlayerTileClass); for (let functionID of g_PlayerBaseFunctions) { let funcName = "placePlayerBase" + functionID; let func = global[funcName]; if (!func) throw new Error("Could not find " + funcName); if (!playerBaseArgs[functionID]) continue; let args = playerBaseArgs[functionID]; // Copy some global arguments to the arguments for each function for (let prop of ["playerID", "playerPosition", "BaseResourceClass", "baseResourceConstraint"]) args[prop] = playerBaseArgs[prop]; func(args); } } function defaultPlayerBaseRadius() { return scaleByMapSize(15, 25); } /** * Marks the corner and center tiles of an area that is about the size of a Civic Center with the given TileClass. * Used to prevent resource collisions with the Civic Center. */ function addCivicCenterAreaToClass(position, tileClass) { createArea( new ClumpPlacer(diskArea(5), 1, 1, Infinity, position), new TileClassPainter(tileClass)); } /** * Helper function. */ function getPlayerBaseArgs(playerBaseArgs) { let baseResourceConstraint = playerBaseArgs.BaseResourceClass && avoidClasses(playerBaseArgs.BaseResourceClass, 4); if (playerBaseArgs.baseResourceConstraint) baseResourceConstraint = new AndConstraint([baseResourceConstraint, playerBaseArgs.baseResourceConstraint]); return [ (property, defaultVal) => playerBaseArgs[property] === undefined ? defaultVal : playerBaseArgs[property], playerBaseArgs.playerPosition, baseResourceConstraint ]; } function placePlayerBaseCityPatch(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let painters = []; if (args.outerTerrain && args.innerTerrain) painters.push(new LayeredPainter([args.outerTerrain, args.innerTerrain], [get("width", 1)])); if (args.painters) painters = painters.concat(args.painters); createArea( new ClumpPlacer( Math.floor(diskArea(get("radius", defaultPlayerBaseRadius() / 3))), get("coherence", 0.6), get("smoothness", 0.3), get("failFraction", 10), basePosition), painters); } function placePlayerBaseChicken(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let i = 0; i < get("groupCount", 2); ++i) { let success = false; for (let tries = 0; tries < get("maxTries", 30); ++tries) { let position = new Vector2D(0, get("distance", 9)).rotate(randomAngle()).add(basePosition); if (createObjectGroup( new SimpleGroup( - [new SimpleObject(get("template", "gaia/fauna_chicken"), 5, 5, 0, get("count", 2))], + [ + new SimpleObject( + get("template", "gaia/fauna_chicken"), + get("minGroupCount", 5), + get("maxGroupCount", 5), + get("minGroupDistance", 0), + get("maxGroupDistance", 2)) + ], true, args.BaseResourceClass, position), 0, baseResourceConstraint)) { success = true; break; } } if (!success) { error("Could not place chicken for player " + args.playerID); return; } } } function placePlayerBaseBerries(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let tries = 0; tries < get("maxTries", 30); ++tries) { let position = new Vector2D(0, get("distance", 12)).rotate(randomAngle()).add(basePosition); if (createObjectGroup( new SimpleGroup( [new SimpleObject(args.template, get("minCount", 5), get("maxCount", 5), get("maxDist", 1), get("maxDist", 3))], true, args.BaseResourceClass, position), 0, baseResourceConstraint)) return; } error("Could not place berries for player " + args.playerID); } function placePlayerBaseMines(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let angleBetweenMines = randFloat(get("minAngle", Math.PI / 6), get("maxAngle", Math.PI / 3)); let mineCount = args.types.length; let groupElements = []; if (args.groupElements) groupElements = groupElements.concat(args.groupElements); for (let tries = 0; tries < get("maxTries", 75); ++tries) { // First find a place where all mines can be placed let pos = []; let startAngle = randomAngle(); for (let i = 0; i < mineCount; ++i) { let angle = startAngle + angleBetweenMines * (i + (mineCount - 1) / 2); pos[i] = new Vector2D(0, get("distance", 12)).rotate(angle).add(basePosition).round(); if (!g_Map.validTilePassable(pos[i]) || !baseResourceConstraint.allows(pos[i])) { pos = undefined; break; } } if (!pos) continue; // Place the mines for (let i = 0; i < mineCount; ++i) { if (args.types[i].type && args.types[i].type == "stone_formation") { createStoneMineFormation(pos[i], args.types[i].template, args.types[i].terrain); args.BaseResourceClass.add(pos[i]); continue; } createObjectGroup( new SimpleGroup( [new SimpleObject(args.types[i].template, 1, 1, 0, 0)].concat(groupElements), true, args.BaseResourceClass, pos[i]), 0); } return; } error("Could not place mines for player " + args.playerID); } function placePlayerBaseTrees(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let num = Math.floor(get("count", scaleByMapSize(7, 20))); for (let x = 0; x < get("maxTries", 30); ++x) { let position = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round(); if (createObjectGroup( new SimpleGroup( [new SimpleObject(args.template, num, num, get("minDistGroup", 0), get("maxDistGroup", 5))], false, args.BaseResourceClass, position), 0, baseResourceConstraint)) return; } error("Could not place starting trees for player " + args.playerID); } function placePlayerBaseTreasures(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let resourceTypeArgs of args.types) { get = (property, defaultVal) => resourceTypeArgs[property] === undefined ? defaultVal : resourceTypeArgs[property]; let success = false; for (let tries = 0; tries < get("maxTries", 30); ++tries) { let position = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round(); if (createObjectGroup( new SimpleGroup( [new SimpleObject(resourceTypeArgs.template, get("count", 14), get("count", 14), get("minDistGroup", 1), get("maxDistGroup", 3))], false, args.BaseResourceClass, position), 0, baseResourceConstraint)) { success = true; break; } } if (!success) { error("Could not place treasure " + resourceTypeArgs.template + " for player " + args.playerID); return; } } } /** * Typically used for placing grass tufts around the civic centers. */ function placePlayerBaseDecoratives(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let i = 0; i < get("count", scaleByMapSize(2, 5)); ++i) { let success = false; for (let x = 0; x < get("maxTries", 30); ++x) { let position = new Vector2D(0, randIntInclusive(get("minDist", 8), get("maxDist", 11))).rotate(randomAngle()).add(basePosition).round(); if (createObjectGroup( new SimpleGroup( [new SimpleObject(args.template, get("minCount", 2), get("maxCount", 5), 0, 1)], false, args.BaseResourceClass, position), 0, baseResourceConstraint)) { success = true; break; } } if (!success) // Don't warn since the decoratives are not important return; } } function placePlayersNomad(playerClass, constraints) { if (!isNomad()) return undefined; g_Map.log("Placing nomad starting units"); let distance = scaleByMapSize(60, 240); let constraint = new AndConstraint(constraints); let numPlayers = getNumPlayers(); let playerIDs = shuffleArray(sortAllPlayers()); let playerPosition = []; for (let i = 0; i < numPlayers; ++i) { let objects = getStartingEntities(playerIDs[i]).filter(ents => ents.Template.startsWith("units/")).map( ents => new SimpleObject(ents.Template, ents.Count || 1, ents.Count || 1, 1, 3)); // Add treasure if too few resources for a civic center let ccCost = Engine.GetTemplate("structures/" + getCivCode(playerIDs[i]) + "_civil_centre").Cost.Resources; for (let resourceType in ccCost) { let treasureTemplate = g_NomadTreasureTemplates[resourceType]; let count = Math.max(0, Math.ceil( (ccCost[resourceType] - (g_MapSettings.StartingResources || 0)) / Engine.GetTemplate(treasureTemplate).ResourceSupply.Amount)); objects.push(new SimpleObject(treasureTemplate, count, count, 3, 5)); } // Try place these entities at a random location let group = new SimpleGroup(objects, true, playerClass); let success = false; for (let distanceFactor of [1, 1/2, 1/4, 0]) { if (createObjectGroups(group, playerIDs[i], new AndConstraint([constraint, avoidClasses(playerClass, distance * distanceFactor)]), 1, 200, false)) { success = true; playerPosition[i] = group.centerPosition; break; } } if (!success) throw new Error("Could not place starting units for player " + playerIDs[i] + "!"); } return [playerIDs, playerPosition]; } /** * Sorts an array of player IDs by team index. Players without teams come first. * Randomize order for players of the same team. */ function sortPlayers(playerIDs) { return shuffleArray(playerIDs).sort((playerID1, playerID2) => getPlayerTeam(playerID1) - getPlayerTeam(playerID2)); } /** * Randomize playerIDs but sort by team. * * @returns {Array} - every item is an array of player indices */ function sortAllPlayers() { let playerIDs = []; for (let i = 0; i < getNumPlayers(); ++i) playerIDs.push(i+1); return sortPlayers(playerIDs); } /** * Rearrange order so that teams of neighboring players alternate (if the given IDs are sorted by team). */ function primeSortPlayers(playerIDs) { if (!playerIDs.length) return []; let prime = []; for (let i = 0; i < Math.ceil(playerIDs.length / 2); ++i) { prime.push(playerIDs[i]); prime.push(playerIDs[playerIDs.length - 1 - i]); } return prime; } function primeSortAllPlayers() { return primeSortPlayers(sortAllPlayers()); } /** * Determine player starting positions on a circular pattern. */ function playerPlacementCircle(radius, startingAngle = undefined, center = undefined) { let startAngle = startingAngle !== undefined ? startingAngle : randomAngle(); let [playerPosition, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius, center || g_Map.getCenter()); return [sortAllPlayers(), playerPosition.map(p => p.round()), playerAngle, startAngle]; } /** * Determine player starting positions on a circular pattern, with a custom angle for each player. * Commonly used for gulf terrains. */ function playerPlacementCustomAngle(radius, center, playerAngleFunc) { let playerPosition = []; let playerAngle = []; let numPlayers = getNumPlayers(); for (let i = 0; i < numPlayers; ++i) { playerAngle[i] = playerAngleFunc(i); playerPosition[i] = Vector2D.add(center, new Vector2D(radius, 0).rotate(-playerAngle[i])).round(); } return [playerPosition, playerAngle]; } /** * Returns player starting positions located on two parallel lines, typically used by central river maps. * If there are two teams with an equal number of players, each team will occupy exactly one line. * Angle 0 means the players are placed in north to south direction, i.e. along the Z axis. */ function playerPlacementRiver(angle, width, center = undefined) { let numPlayers = getNumPlayers(); let numPlayersEven = numPlayers % 2 == 0; let mapSize = g_Map.getSize(); let centerPosition = center || g_Map.getCenter(); let playerPosition = []; for (let i = 0; i < numPlayers; ++i) { let currentPlayerEven = i % 2 == 0; let offsetDivident = numPlayersEven || currentPlayerEven ? (i + 1) % 2 : 0; let offsetDivisor = numPlayersEven ? 0 : currentPlayerEven ? +1 : -1; playerPosition[i] = new Vector2D( width * (i % 2) + (mapSize - width) / 2, fractionToTiles(((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1)) ).rotateAround(angle, centerPosition).round(); } return [primeSortAllPlayers(), playerPosition]; } /*** * Returns starting positions located on two parallel lines. * The locations on the first line are shifted in comparison to the other line. */ function playerPlacementLine(angle, center, width) { let playerPosition = []; let numPlayers = getNumPlayers(); for (let i = 0; i < numPlayers; ++i) playerPosition[i] = Vector2D.add( center, new Vector2D( fractionToTiles((i + 1) / (numPlayers + 1) - 0.5), width * (i % 2 - 1/2) ).rotate(angle) ).round(); return playerPosition; } /** * Sorts the playerIDs so that team members are as close as possible. */ function sortPlayersByLocation(startLocations) { // Sort start locations to form a "ring" let startLocationOrder = sortPointsShortestCycle(startLocations); let newStartLocations = []; for (let i = 0; i < startLocations.length; ++i) newStartLocations.push(startLocations[startLocationOrder[i]]); startLocations = newStartLocations; // Sort players by team let playerIDs = []; let teams = []; for (let i = 0; i < g_MapSettings.PlayerData.length - 1; ++i) { playerIDs.push(i+1); let t = g_MapSettings.PlayerData[i + 1].Team; if (teams.indexOf(t) == -1 && t !== undefined) teams.push(t); } playerIDs = sortPlayers(playerIDs); if (!teams.length) return [playerIDs, startLocations]; // Minimize maximum distance between players within a team let minDistance = Infinity; let bestShift; for (let s = 0; s < playerIDs.length; ++s) { let maxTeamDist = 0; for (let pi = 0; pi < playerIDs.length - 1; ++pi) { let t1 = getPlayerTeam(playerIDs[(pi + s) % playerIDs.length]); if (teams.indexOf(t1) === -1) continue; for (let pj = pi + 1; pj < playerIDs.length; ++pj) { if (t1 != getPlayerTeam(playerIDs[(pj + s) % playerIDs.length])) continue; maxTeamDist = Math.max( maxTeamDist, Math.euclidDistance2D( startLocations[pi].x, startLocations[pi].y, startLocations[pj].x, startLocations[pj].y)); } } if (maxTeamDist < minDistance) { minDistance = maxTeamDist; bestShift = s; } } if (bestShift) { let newPlayerIDs = []; for (let i = 0; i < playerIDs.length; ++i) newPlayerIDs.push(playerIDs[(i + bestShift) % playerIDs.length]); playerIDs = newPlayerIDs; } return [playerIDs, startLocations]; } Index: ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.js (nonexistent) +++ ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.js (revision 21079) @@ -0,0 +1,321 @@ +Engine.LoadLibrary("rmgen"); +Engine.LoadLibrary("heightmap"); + +const tMainTerrain = "alpine_snow_a"; +const tTier1Terrain = "snow rough"; +const tTier2Terrain = "snow_01"; +const tTier3Terrain = "snow rocks"; +const tForestFloor1 = "alpine_forrestfloor_snow"; +const tForestFloor2 = "polar_snow_rocks"; +const tCliff = ["alpine_cliff_a", "alpine_cliff_b"]; +const tHill = "alpine_snow_glacial"; +const tRoad = "new_alpine_citytile"; +const tRoadWild = "alpine_snow_rocky"; +const tShore = "alpine_shore_rocks_icy"; +const tWater = "polar_ice_b"; + +const oTreeDead = "gaia/flora_tree_dead"; +const oOak = "gaia/flora_tree_oak_dead"; +const oPine = "gaia/flora_tree_pine"; +const oGrapes = "gaia/flora_bush_grapes"; +const oBush = "gaia/flora_bush_badlands"; +const oDeer = "gaia/fauna_deer"; +const oRabbit = "gaia/fauna_rabbit"; +const oWolf1 = "gaia/fauna_wolf"; +const oWolf2 = "gaia/fauna_arctic_wolf"; +const oFox = "gaia/fauna_fox_arctic"; +const oHawk = "gaia/fauna_hawk"; +const oFish = "gaia/fauna_fish"; +const oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; +const oStoneSmall = "gaia/geology_stone_alpine_a"; +const oMetalLarge = "gaia/geology_metal_alpine_slabs"; + +const aRockLarge = "actor|geology/stone_granite_large.xml"; +const aRockMedium = "actor|geology/stone_granite_med.xml"; +const aBushMedium = "actor|props/flora/plant_desert_a.xml"; +const aBushSmall = "actor|props/flora/bush_desert_a.xml"; +const aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; +const aOutpostPalisade = "actor|props/structures/britons/outpost_palisade.xml"; +const aWorkshopChariot= "actor|props/structures/britons/workshop_chariot_01.xml"; + +const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTreeDead, tForestFloor2 + TERRAIN_SEPARATOR + oOak, tForestFloor2]; +const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTreeDead, tForestFloor1]; + +var heightSeaGround = -2; +var heightShoreLower = 0.7; +var heightShoreUpper = 1; +var heightLand = 2; +var heightSnowline = 12; +var heightOffsetLargeBumps = 4; + +var g_Map = new RandomMap(heightShoreUpper, tMainTerrain); + +const numPlayers = getNumPlayers(); +const mapCenter = g_Map.getCenter(); +const mapBounds = g_Map.getBounds(); + +var clPlayer = g_Map.createTileClass(); +var clForest = g_Map.createTileClass(); +var clRock = g_Map.createTileClass(); +var clMetal = g_Map.createTileClass(); +var clFood = g_Map.createTileClass(); +var clBaseResource = g_Map.createTileClass(); +var clHill = g_Map.createTileClass(); +var clDirt = g_Map.createTileClass(); +var clRiver = g_Map.createTileClass(); +var clWater = g_Map.createTileClass(); +var clShallowsFlora = g_Map.createTileClass(); + +var riverWidth = fractionToTiles(0.1); + +var startAngle = randomAngle(); + +var [playerIDs, playerPosition] = playerPlacementRiver(startAngle, fractionToTiles(0.6)); + +if (!isNomad()) + for (let position of playerPosition) + addCivicCenterAreaToClass(position, clPlayer); + +paintRiver({ + "parallel": false, + "start": new Vector2D(mapCenter.x, mapBounds.top).rotateAround(startAngle, mapCenter), + "end": new Vector2D(mapCenter.x, mapBounds.bottom).rotateAround(startAngle, mapCenter), + "width": riverWidth, + "fadeDist": scaleByMapSize(3, 14), + "deviation": 6, + "heightRiverbed": heightSeaGround, + "heightLand": heightLand, + "meanderShort": 40, + "meanderLong": 20 +}); +Engine.SetProgress(10); + +paintTileClassBasedOnHeight(-Infinity, heightShoreUpper, Elevation_ExcludeMin_ExcludeMax, clRiver); +Engine.SetProgress(15); + +createTributaryRivers( + startAngle + Math.PI / 2, + 4, + 10, + heightSeaGround, + [-Infinity, heightSeaGround], + Math.PI / 5, + clWater, + undefined, + avoidClasses(clPlayer, 4)); + +Engine.SetProgress(25); + +placePlayerBases({ + "PlayerPlacement": [playerIDs, playerPosition], + "PlayerTileClass": clPlayer, + "BaseResourceClass": clBaseResource, + "baseResourceConstraint": avoidClasses(clWater, 4), + "CityPatch": { + "outerTerrain": tRoadWild, + "innerTerrain": tRoad + }, + "Chicken": { + "template": oDeer, + "distance": 18, + "minGroupDistance": 2, + "maxGroupDistance": 4, + "minGroupCount": 2, + "maxGroupCount": 3 + }, + "Berries": { + "template": oGrapes, + "minCount": 3, + "maxCount": 3 + }, + "Mines": { + "types": [ + { "template": oMetalLarge }, + { "template": oStoneLarge } + ] + }, + "Trees": { + "template": oTreeDead, + "count": 10 + }, + "Decoratives": { + "template": aBushSmall, + "minDist": 10, + "maxDist": 12 + } +}); +Engine.SetProgress(30); + +g_Map.log("Creating pools"); +createAreas( + new ChainPlacer(1, Math.floor(scaleByMapSize(2, 5)), Math.floor(scaleByMapSize(15, 60)), 0.8), + new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 3), + avoidClasses(clPlayer, 20), + scaleByMapSize(6, 20)); + +Engine.SetProgress(40); + +createBumps(avoidClasses(clPlayer, 2)); + +if (randBool()) + createHills([tCliff, tCliff, tHill], avoidClasses(clPlayer, 20, clWater, 1, clHill, 15, clRiver, 10), clHill, scaleByMapSize(3, 15)); +else + createMountains(tCliff, avoidClasses(clPlayer, 20, clWater, 1, clHill, 15, clRiver, 10), clHill, scaleByMapSize(3, 15)); + +g_Map.log("Creating large bumps"); +createAreas( + new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), + new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetLargeBumps, 3), + avoidClasses(clPlayer, 2), + scaleByMapSize(100, 800)); + +createBumps(avoidClasses(clPlayer, 20)); + +paintTileClassBasedOnHeight(-Infinity, heightShoreUpper, Elevation_ExcludeMin_ExcludeMax, clWater); +paintTerrainBasedOnHeight(-Infinity, heightShoreUpper, Elevation_ExcludeMin_ExcludeMax, tWater); +paintTerrainBasedOnHeight(heightShoreUpper, heightShoreLower, Elevation_ExcludeMin_ExcludeMax, tShore); +paintTerrainBasedOnHeight(heightSnowline, Infinity, Elevation_ExcludeMin_ExcludeMax, tMainTerrain); + +Engine.SetProgress(50); + +g_Map.log("Creating dirt patches"); +createLayeredPatches( + [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], + [[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], + [1, 1], + avoidClasses(clHill, 2, clDirt, 5, clPlayer, 12, clWater, 5, clForest, 4), + scaleByMapSize(25, 55), + clDirt); + +var [forestTrees, stragglerTrees] = getTreeCounts(200, 1200, 0.7); +createForests( + [tForestFloor1, tForestFloor2, tForestFloor1, pForest1, pForest2], + avoidClasses(clPlayer, 20, clWater, 2, clHill, 2, clForest, 12), + clForest, + forestTrees); + +createStragglerTrees( + [oTreeDead, oOak, oPine, oBush], + avoidClasses(clPlayer, 17, clWater, 2, clHill, 2, clForest, 1, clRiver, 4), + clForest, + stragglerTrees); + +Engine.SetProgress(55); + +g_Map.log("Creating stone mines"); +// Allow mines on the bumps at the river +createMines( + [ + [new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], + [new SimpleObject(oStoneSmall, 2, 5, 1, 3)] + ], + avoidClasses(clForest, 4, clWater, 1, clPlayer, 20, clRock, 15, clHill, 1), + clRock); + +g_Map.log("Creating metal mines"); +createMines( + [ + [new SimpleObject(oMetalLarge, 1, 1, 0, 4)] + ], + avoidClasses(clForest, 4, clWater, 1, clPlayer, 20, clMetal, 15, clRock, 5, clHill, 1), + clMetal); + +Engine.SetProgress(65); + +createDecoration( + [ + [ + new SimpleObject(aRockMedium, 1, 3, 0, 1) + ], + [ + new SimpleObject(aBushSmall, 1, 2, 0, 1), + new SimpleObject(aBushMedium, 1, 3, 0, 2), + new SimpleObject(aRockLarge, 1, 2, 0, 1) + ] + ], + [ + scaleByMapSize(16, 262), + scaleByMapSize(40, 360) + ], + avoidClasses(clWater, 2, clForest, 0, clPlayer, 20, clHill, 1)); + +Engine.SetProgress(70); + +createFood( + [ + [new SimpleObject(oHawk, 1, 1, 0, 3)], + [new SimpleObject(oWolf1, 4, 6, 0, 4)], + [new SimpleObject(oWolf2, 4, 8, 0, 4)], + [new SimpleObject(oFox, 2, 3, 0, 4)], + [new SimpleObject(oDeer, 4, 6, 0, 2)], + [new SimpleObject(oRabbit, 1, 3, 4, 6)] + ], + [ + scaleByMapSize(3, 10), + scaleByMapSize(3, 10), + scaleByMapSize(3, 10), + scaleByMapSize(5, 20), + scaleByMapSize(5, 20), + scaleByMapSize(5, 20) + ], + avoidClasses(clWater, 3, clPlayer, 20, clHill, 1, clFood, 10)); + +Engine.SetProgress(75); + +createFood( + [ + [new SimpleObject(oFish, 1, 2, 0, 2)] + ], + [ + 3 * numPlayers + ], + [avoidClasses(clPlayer, 8, clForest, 1, clHill, 4), stayClasses (clWater, 6)], + clFood); + +g_Map.log("Creating shallow flora"); +createObjectGroupsDeprecated( + new SimpleGroup([new SimpleObject(aReeds, 6, 14, 1, 5)], false, clShallowsFlora), + 0, + [ + new HeightConstraint(-1, 0), + avoidClasses(clShallowsFlora, 25), + ], + 20 * scaleByMapSize(13, 200), + 80); + +g_Map.log("Creating gallic decoratives"); +createDecoration( + [ + [new SimpleObject(aOutpostPalisade, 1, 1, 0, 1)], + [new SimpleObject(aWorkshopChariot, 1, 1, 0, 1)], + ], + [ + scaleByMapSize(2, 7), + scaleByMapSize(2, 7) + ], + avoidClasses(clForest, 1, clPlayer, 20, clBaseResource, 5, clHill, 4, clFood, 4, clWater, 5, clRock, 9, clMetal, 9)); + + +placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 10, clWater, 5)); + +setSkySet(pickRandom(["fog", "stormy", "sunset"])); +setSunElevation(0.27); +setSunRotation(randomAngle()); +setSunColor(0.746, 0.718, 0.539); +setWaterColor(0.292, 0.347, 0.691); +setWaterTint(0.550, 0.543, 0.437); +setWaterMurkiness(0.83); +setWaterType("clap"); + +setWindAngle(startAngle); + +setFogColor(0.8, 0.76, 0.61); +setFogThickness(2); +setFogFactor(1.2); + +setPPEffect("hdr"); +setPPContrast(0.65); +setPPSaturation(0.42); +setPPBloom(0.6); + +g_Map.ExportMap(); Property changes on: ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.json =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.json (nonexistent) +++ ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.json (revision 21079) @@ -0,0 +1,10 @@ +{ + "settings" : { + "Name" : "Scythian Rivulet", + "Script" : "scythian_rivulet.js", + "Description" : "Carpeted in snow, the land slumbers in repose, but this fragile peace is destined to be shattered - and not by spring. Large packs of wolves lurk in the wild searching for prey, a lone arctic fox scurries away to hide. Starting in this seemingly hostile terrain, players must conquer - or be conquered.", + "Preview" : "scythian_rivulet.png", + "Keywords": ["new"], + "CircularMap" : true + } +} Property changes on: ps/trunk/binaries/data/mods/public/maps/random/scythian_rivulet.json ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property