Index: ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js (revision 25680) +++ ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js (revision 25681) @@ -1,269 +1,345 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen-common"); +Engine.LoadLibrary("rmbiome"); + +setSelectedBiome(); TILE_CENTERED_HEIGHT_MAP = true; -const tCity = "medit_city_pavement"; -const tCityPlaza = "medit_city_pavement"; -const tHill = ["medit_grass_shrubs", "medit_rocks_grass_shrubs", "medit_rocks_shrubs", "medit_rocks_grass", "medit_shrubs"]; -const tMainDirt = "medit_dirt"; -const tCliff = "medit_cliff_aegean"; -const tForestFloor = "medit_grass_shrubs"; -const tGrass = ["medit_grass_field", "medit_grass_field_a"]; -const tGrassSand50 = "medit_grass_field_a"; -const tGrassSand25 = "medit_grass_field_b"; -const tDirt = "medit_dirt_b"; -const tDirt2 = "medit_rocks_grass"; -const tDirt3 = "medit_rocks_shrubs"; -const tDirtCracks = "medit_dirt_c"; -const tShore = "medit_sand"; -const tWater = "medit_sand_wet"; - -const oBerryBush = "gaia/fruit/berry_01"; -const oDeer = "gaia/fauna_deer"; -const oFish = "gaia/fish/generic"; -const oSheep = "gaia/fauna_sheep"; +const tCity = g_Terrains.road; +const tCityPlaza = g_Terrains.roadWild; +const tHill = g_Terrains.hill; +const tMainDirt = g_Terrains.dirt; +const tCliff = g_Terrains.cliff; +const tForestFloor = g_Terrains.forestFloor1; +const tGrass = g_Terrains.mainTerrain; +const tGrassSand50 = g_Terrains.tier1Terrain; +const tGrassSand25 = g_Terrains.tier3Terrain; +const tDirt = g_Terrains.dirt; +const tDirt2 = g_Terrains.dirt; +const tDirt3 = g_Terrains.tier2Terrain; +const tDirtCracks = g_Terrains.dirt; +const tShore = g_Terrains.shore; +const tWater = g_Terrains.water; + +const oBerryBush = g_Gaia.fruitBush; +const oDeer = g_Gaia.mainHuntableAnimal; +const oFish = g_Gaia.fish; +const oSheep = g_Gaia.secondaryHuntableAnimal; const oGoat = "gaia/fauna_goat"; -const oStoneLarge = "gaia/rock/mediterranean_large"; -const oStoneSmall = "gaia/rock/mediterranean_small"; -const oMetalLarge = "gaia/ore/mediterranean_large"; -const oDatePalm = "gaia/tree/cretan_date_palm_short"; -const oSDatePalm = "gaia/tree/cretan_date_palm_tall"; -const oCarob = "gaia/tree/carob"; -const oFanPalm = "gaia/tree/medit_fan_palm"; -const oPoplar = "gaia/tree/poplar_lombardy"; -const oCypress = "gaia/tree/cypress"; - -const aBush1 = "actor|props/flora/bush_medit_sm.xml"; -const aBush2 = "actor|props/flora/bush_medit_me.xml"; -const aBush3 = "actor|props/flora/bush_medit_la.xml"; -const aBush4 = "actor|props/flora/bush_medit_me.xml"; -const aDecorativeRock = "actor|geology/stone_granite_med.xml"; - -const pForest = [tForestFloor, tForestFloor + TERRAIN_SEPARATOR + oCarob, tForestFloor + TERRAIN_SEPARATOR + oDatePalm, tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, tForestFloor]; +const oStoneLarge = g_Gaia.stoneLarge; +const oStoneSmall = g_Gaia.stoneSmall; +const oMetalLarge = g_Gaia.metalLarge; +const oDatePalm = g_Gaia.tree1; +const oSDatePalm = g_Gaia.tree2; +const oCarob = g_Gaia.tree3; +const oFanPalm = g_Gaia.tree4; +const oPoplar = g_Gaia.tree5; +const oCypress = pickRandom([g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4, g_Gaia.tree5]); + +const aBush1 = g_Decoratives.bushSmall; +const aBush2 = g_Decoratives.bushMedium; +const aBush3 = g_Decoratives.grassShort; +const aBush4 = g_Decoratives.tree; +const aDecorativeRock = g_Decoratives.rockMedium; + +const aLillies = g_Decoratives.lillies; +const aReeds = g_Decoratives.reeds; + +const pForest = [ + tForestFloor, tForestFloor + TERRAIN_SEPARATOR + oCarob, + tForestFloor + TERRAIN_SEPARATOR + oDatePalm, + tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, + tForestFloor +]; -var heightSeaGround = -4; +var heightSeaGround = -7; +var heightShallow = -0.8; var heightLand = 3; var g_Map = new RandomMap(heightLand, tHill); const numPlayers = getNumPlayers(); const mapSize = g_Map.getSize(); const mapCenter = g_Map.getCenter(); var clPlayer = 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 clBaseResource = g_Map.createTileClass(); var clGrass = g_Map.createTileClass(); var clHill = g_Map.createTileClass(); +var clPassageway = g_Map.createTileClass(); +var clShallow = g_Map.createTileClass(); g_Map.log("Creating the main river"); var riverAngle = randomAngle(); -var riverWidth = scaleByMapSize(15, 70); +var riverWidth = scaleByMapSize(20, 90); var riverStart = new Vector2D(mapCenter.x, 0).rotateAround(riverAngle, mapCenter); var riverEnd = new Vector2D(mapCenter.x, mapSize).rotateAround(riverAngle, mapCenter); createArea( new PathPlacer(riverStart, riverEnd, riverWidth, 0.2, 15 * scaleByMapSize(1, 3), 0.04, 0.01), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4)); g_Map.log("Creating small puddles at the map border to ensure players being separated"); -for (let point of [riverStart, riverEnd]) +for (const point of [riverStart, riverEnd]) createArea( new ClumpPlacer(diskArea(riverWidth / 2), 0.95, 0.6, Infinity, point), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4)); +// Paint water so we now where to lay passageway. +paintTileClassBasedOnHeight(heightSeaGround - 1, 0.5, 1, clWater); + g_Map.log("Creating passage connecting the two riversides"); -var passageStart = riverStart.rotateAround(Math.PI / 2, mapCenter); -var passageEnd = riverEnd.rotateAround(Math.PI / 2, mapCenter); +// Add some leeway to make sure things are connected. +const fraction = randFloat(0.38, 0.62); +const middlePoint = riverStart.clone().mult(fraction).add(riverEnd.clone().mult(1 - fraction)); +let passageStart = new Vector2D(middlePoint.x, middlePoint.y - riverWidth * 1.1).rotateAround(riverAngle, middlePoint); +let passageEnd = new Vector2D(middlePoint.x, middlePoint.y + riverWidth * 1.1).rotateAround(riverAngle, middlePoint); +passageStart = passageStart.rotateAround(Math.PI / 2, middlePoint); +passageEnd = passageEnd.rotateAround(Math.PI / 2, middlePoint); +// First create a shallow (walkable) area. +const passageWidth = scaleByMapSize(15, 40); createArea( new PathPlacer( passageStart, passageEnd, - scaleByMapSize(10, 30), + passageWidth * 2, + 0.2, + 3 * scaleByMapSize(1, 4), + 0.1, + 0.01, + 100.0), + new MultiPainter([ + new SmoothElevationPainter(ELEVATION_SET, heightShallow, 3), + new TileClassPainter(clShallow)]), + new OrConstraint([ + stayClasses(clWater, 0), + borderClasses(clWater, 0, 3), + ]) +); + +// Then create the proper passageway. +createArea( + new PathPlacer( + passageStart, + passageEnd, + passageWidth, 0.5, 3 * scaleByMapSize(1, 4), 0.1, 0.01), - new SmoothElevationPainter(ELEVATION_SET, heightLand, 4)); + new MultiPainter([ + new SmoothElevationPainter(ELEVATION_SET, heightLand, 3), + new TileClassPainter(clPassageway)]) +); -paintTerrainBasedOnHeight(-6, 1, 1, tWater); +paintTerrainBasedOnHeight(heightSeaGround - 1, 1, 1, tWater); paintTerrainBasedOnHeight(1, 2, 1, tShore); paintTerrainBasedOnHeight(2, 5, 1, tGrass); -paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); +// Reset water class. +clWater = g_Map.createTileClass(); +paintTileClassBasedOnHeight(heightSeaGround - 1, 0.5, 1, clWater); placePlayerBases({ "PlayerPlacement": playerPlacementRiver(riverAngle, fractionToTiles(0.6)), "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "outerTerrain": tCityPlaza, "innerTerrain": tCity }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, - { "template": oStoneLarge } + { "template": oStoneSmall }, + { "template": oStoneSmall }, ] }, "Trees": { "template": oCarob, "count": 2 }, "Decoratives": { "template": aBush1 } }); Engine.SetProgress(40); createBumps(avoidClasses(clWater, 2, clPlayer, 20)); -var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); -createForests( - [tForestFloor, tForestFloor, tForestFloor, pForest, pForest], - avoidClasses(clPlayer, 20, clForest, 17, clWater, 2, clBaseResource, 3), - clForest, - forestTrees); +const [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); +createDefaultForests( + [tForestFloor, tForestFloor, pForest, pForest, pForest], + avoidClasses(clPlayer, 20, clPassageway, 5, clForest, 17, clWater, 2, clBaseResource, 3), + clForest, + forestTrees); Engine.SetProgress(50); if (randBool()) - createHills([tGrass, tCliff, tHill], avoidClasses(clPlayer, 20, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); + createHills([tGrass, tCliff, tHill], avoidClasses(clPlayer, 20, clPassageway, 10, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); else - createMountains(tCliff, avoidClasses(clPlayer, 20, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); + createMountains(tCliff, avoidClasses(clPlayer, 20, clPassageway, 10, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); g_Map.log("Creating grass patches"); createLayeredPatches( - [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], - [[tGrass,tGrassSand50],[tGrassSand50,tGrassSand25], [tGrassSand25,tGrass]], - [1,1], - avoidClasses(clForest, 0, clGrass, 2, clPlayer, 10, clWater, 2, clDirt, 2, clHill, 1), - scaleByMapSize(15, 45), - clDirt); + [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], + [[tGrass, tGrassSand50], [tGrassSand50, tGrassSand25], [tGrassSand25, tGrass]], + [1, 1], + avoidClasses(clForest, 0, clGrass, 2, clPlayer, 10, clWater, 2, clDirt, 2, clHill, 1), + scaleByMapSize(15, 45), + clDirt); Engine.SetProgress(55); g_Map.log("Creating dirt patches"); createLayeredPatches( - [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], - [tDirt3, tDirt2,[tDirt,tMainDirt], [tDirtCracks,tMainDirt]], - [1,1,1], - avoidClasses(clForest, 0, clDirt, 2, clPlayer, 10, clWater, 2, clGrass, 2, clHill, 1), - scaleByMapSize(15, 45), - clDirt); + [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], + [tDirt3, tDirt2, [tDirt, tMainDirt], [tDirtCracks, tMainDirt]], + [1, 1, 1], + avoidClasses(clForest, 0, clDirt, 2, clPlayer, 10, clWater, 2, clGrass, 2, clHill, 1), + scaleByMapSize(15, 45), + clDirt); Engine.SetProgress(60); -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)] - ], - avoidClasses(clForest, 4, clPlayer, 15, clRock, 10, clWater, 4, clHill, 4), - clRock); +g_Map.log("Creating stone mines in the middle"); +createObjectGroups( + new SimpleGroup([new SimpleObject(oStoneSmall, 1, 4, 0, 4)], true, clRock), + 0, + [ + avoidClasses(clForest, 4, clPlayer, 15, clRock, 6, clWater, 0, clHill, 4), + stayClasses(clPassageway, 2) + ], + scaleByMapSize(4, 15), + 80 +); g_Map.log("Creating metal mines"); createMines( - [ - [new SimpleObject(oMetalLarge, 1,1, 0,4)] - ], - avoidClasses(clForest, 4, clPlayer, 15, clMetal, 10, clRock, 5, clWater, 4, clHill, 4), - clMetal + [ + [new SimpleObject(oMetalLarge, 1, 1, 0, 4)] + ], + avoidClasses(clForest, 4, clPassageway, 10, clPlayer, 15, clMetal, 10, clRock, 5, clWater, 4, clHill, 4), + clMetal, + scaleByMapSize(5, 20) ); Engine.SetProgress(65); createDecoration( [ [ new SimpleObject(aDecorativeRock, 1, 3, 0, 1) ], [ new SimpleObject(aBush2, 1, 2, 0, 1), new SimpleObject(aBush1, 1, 3, 0, 2), new SimpleObject(aBush4, 1, 2, 0, 1), new SimpleObject(aBush3, 1, 3, 0, 2) ] ], [ scaleByMapSize(16, 262), scaleByMapSize(40, 360) ], avoidClasses(clWater, 2, clForest, 0, clPlayer, 5, clBaseResource, 6, clHill, 1, clRock, 6, clMetal, 6)); Engine.SetProgress(70); createFood( [ [new SimpleObject(oFish, 2, 3, 0, 2)] ], [ 3 * scaleByMapSize(5, 20) ], - [avoidClasses(clFood, 10), stayClasses(clWater, 5)], + [avoidClasses(clFood, 8, clShallow, 2), stayClasses(clWater, 4)], clFood); createFood( [ [new SimpleObject(oSheep, 5, 7, 0, 4)], [new SimpleObject(oGoat, 2, 4, 0, 3)], [new SimpleObject(oDeer, 2, 4, 0, 2)] ], [ - scaleByMapSize(5,20), - scaleByMapSize(5,20), - scaleByMapSize(5,20) + scaleByMapSize(5, 20), + scaleByMapSize(5, 20), + scaleByMapSize(5, 20) ], - avoidClasses(clForest, 0, clPlayer, 10, clBaseResource, 6, clWater, 1, clFood, 10, clHill, 1, clRock, 6, clMetal, 6), + avoidClasses(clForest, 0, clPlayer, 10, clPassageway, 0, clBaseResource, 6, clWater, 1, clFood, 10, clHill, 1, clRock, 6, clMetal, 6), clFood); createFood( [ [new SimpleObject(oBerryBush, 5, 7, 0, 4)] ], [ 3 * numPlayers ], - avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10, clRock, 6, clMetal, 6), + avoidClasses(clWater, 3, clForest, 0, clPassageway, 0, clPlayer, 20, clHill, 1, clFood, 10, clRock, 6, clMetal, 6), clFood); Engine.SetProgress(90); createStragglerTrees( [oDatePalm, oSDatePalm, oCarob, oFanPalm, oPoplar, oCypress], avoidClasses(clForest, 1, clWater, 2, clPlayer, 8, clBaseResource, 6, clMetal, 6, clRock, 6, clHill, 1), clForest, stragglerTrees); + +g_Map.log("Placing decorations on shallows"); + +createObjectGroups( + new SimpleGroup([ + new SimpleObject(aDecorativeRock, 1, 2, 0, 1), + new SimpleObject(aReeds, 0, 4, 0, 1), + ], true, clRock), + 0, + [stayClasses(clShallow, 2), avoidClasses(clPassageway, 0)], + scaleByMapSize(30, 100), + 50 +); + +createObjectGroups( + new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 1)], true, clRock), + 0, + [stayClasses(clShallow, 2), avoidClasses(clPassageway, 0)], + scaleByMapSize(6, 36), + 50 +); + placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("sunny"); setSunColor(0.917, 0.828, 0.734); setWaterColor(0, 0.501961, 1); setWaterTint(0.501961, 1, 1); setWaterWaviness(2.5); setWaterType("ocean"); setWaterMurkiness(0.49); setFogFactor(0.3); setFogThickness(0.25); setPPEffect("hdr"); setPPContrast(0.62); setPPSaturation(0.51); setPPBloom(0.12); g_Map.ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.json =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.json (revision 25680) +++ ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.json (revision 25681) @@ -1,10 +1,11 @@ { "settings" : { "Name" : "Corinthian Isthmus", "Script" : "corinthian_isthmus.js", - "Description" : "Two Mediterranean land masses connected by a narrow spit of land, called an 'Isthmus'.", + "Description" : "Two land masses connected by a narrow spit of land, called an 'Isthmus', inspired by the eponymous area in Greece. Most of the stone on the map is in the isthmus itself, making it vital to fight for its control.", + "SupportedBiomes": "generic/", "Keywords": ["multiplayer"], "CircularMap" : true, "Preview" : "corinthian_isthmus.png" } } Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/Constraint.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/Constraint.js (revision 25680) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/Constraint.js (revision 25681) @@ -1,222 +1,241 @@ /** * @file A Constraint decides if a tile satisfies a condition defined by the class. */ /** * The NullConstraint is always satisfied. */ function NullConstraint() {} NullConstraint.prototype.allows = function(position) { return true; }; /** * The AndConstraint is met if every given Constraint is satisfied by the tile. */ function AndConstraint(constraints) { if (constraints instanceof Array) this.constraints = constraints; else if (!constraints) this.constraints = []; else this.constraints = [constraints]; } AndConstraint.prototype.allows = function(position) { return this.constraints.every(constraint => constraint.allows(position)); }; /** + * The OrConstraint is met if any given Constraint is satisfied by the tile. + */ +function OrConstraint(constraints) +{ + if (constraints instanceof Array) + this.constraints = constraints; + else if (!constraints) + this.constraints = []; + else + this.constraints = [constraints]; +} + +OrConstraint.prototype.allows = function(position) +{ + return this.constraints.some(constraint => constraint.allows(position)); +}; + + +/** * The StayAreasConstraint is met if some of the given Areas contains the point. */ function StayAreasConstraint(areas) { this.areas = areas; } StayAreasConstraint.prototype.allows = function(position) { return this.areas.some(area => area.contains(position)); }; /** * The StayAreasConstraint is met if the point is adjacent to one of the given Areas and not contained by that Area. */ function AdjacentToAreaConstraint(areas) { this.areas = areas; } AdjacentToAreaConstraint.prototype.allows = function(position) { return this.areas.some(area => !area.contains(position) && g_Map.getAdjacentPoints(position).some(adjacentPosition => area.contains(adjacentPosition))); }; /** * The AvoidAreasConstraint is met if none of the given Areas contain the point. */ function AvoidAreasConstraint(areas) { this.areas = areas; } AvoidAreasConstraint.prototype.allows = function(position) { return this.areas.every(area => !area.contains(position)); }; /** * The StayTextureConstraint is met if the tile has the given texture. */ function StayTextureConstraint(texture) { this.texture = texture; } StayTextureConstraint.prototype.allows = function(position) { return g_Map.getTexture(position) == this.texture; }; /** * The AvoidTextureConstraint is met if the terrain texture of the tile is different from the given texture. */ function AvoidTextureConstraint(texture) { this.texture = texture; } AvoidTextureConstraint.prototype.allows = function(position) { return g_Map.getTexture(position) != this.texture; }; /** * The AvoidTileClassConstraint is met if there are no tiles marked with the given TileClass within the given radius of the tile. */ function AvoidTileClassConstraint(tileClass, distance) { this.tileClass = tileClass; this.distance = distance; } AvoidTileClassConstraint.prototype.allows = function(position) { return this.tileClass.countMembersInRadius(position, this.distance) == 0; }; /** * The StayInTileClassConstraint is met if every tile within the given radius of the tile is marked with the given TileClass. */ function StayInTileClassConstraint(tileClass, distance) { this.tileClass = tileClass; this.distance = distance; } StayInTileClassConstraint.prototype.allows = function(position) { return this.tileClass.countNonMembersInRadius(position, this.distance) == 0; }; /** * The NearTileClassConstraint is met if at least one tile within the given radius of the tile is marked with the given TileClass. */ function NearTileClassConstraint(tileClass, distance) { this.tileClass = tileClass; this.distance = distance; } NearTileClassConstraint.prototype.allows = function(position) { return this.tileClass.countMembersInRadius(position, this.distance) > 0; }; /** * The BorderTileClassConstraint is met if there are * tiles not marked with the given TileClass within distanceInside of the tile and * tiles marked with the given TileClass within distanceOutside of the tile. */ function BorderTileClassConstraint(tileClass, distanceInside, distanceOutside) { this.tileClass = tileClass; this.distanceInside = distanceInside; this.distanceOutside = distanceOutside; } BorderTileClassConstraint.prototype.allows = function(position) { return this.tileClass.countMembersInRadius(position, this.distanceOutside) > 0 && this.tileClass.countNonMembersInRadius(position, this.distanceInside) > 0; }; /** * The HeightConstraint is met if the elevation of the tile is within the given range. * One can pass Infinity to only test for one side. */ function HeightConstraint(minHeight, maxHeight) { this.minHeight = minHeight; this.maxHeight = maxHeight; } HeightConstraint.prototype.allows = function(position) { return this.minHeight <= g_Map.getHeight(position) && g_Map.getHeight(position) <= this.maxHeight; }; /** * The SlopeConstraint is met if the steepness of the terrain is within the given range. */ function SlopeConstraint(minSlope, maxSlope) { this.minSlope = minSlope; this.maxSlope = maxSlope; } SlopeConstraint.prototype.allows = function(position) { return this.minSlope <= g_Map.getSlope(position) && g_Map.getSlope(position) <= this.maxSlope; }; /** * The StaticConstraint is used for performance improvements of existing Constraints. * It is evaluated for the entire map when the Constraint is created. * So when a createAreas or createObjectGroups call uses this, it can rely on the cache, * rather than reevaluating it for every randomized coordinate. * Account for the fact that the cache is never updated! */ function StaticConstraint(constraints) { let mapSize = g_Map.getSize(); this.constraint = new AndConstraint(constraints); this.cache = new Array(mapSize).fill(0).map(() => new Uint8Array(mapSize)); } StaticConstraint.prototype.allows = function(position) { if (!this.cache[position.x][position.y]) this.cache[position.x][position.y] = this.constraint.allows(position) ? 2 : 1; return this.cache[position.x][position.y] == 2; }; /** * Constrains the area to any tile on the map that is passable. */ function PassableMapAreaConstraint() { } PassableMapAreaConstraint.prototype.allows = function(position) { return g_Map.validTilePassable(position); };