Ticket #4275: optimize_caledonian_meadows2016_10_9d.2.patch

File optimize_caledonian_meadows2016_10_9d.2.patch, 14.7 KB (added by FeXoR, 8 years ago)

Fuxed an error that can occurr when argument avoidClass in getPointsByHeight is undefined

  • binaries/data/mods/public/maps/random/caledonian_meadows.js

     
    142142
    143143/**
    144144 * Drags a path to a target height smoothing it at the edges and return some points along the path.
    145  *
    146  * TODO:
    147  * Would be nice to tell the function what to do and how often in the arguments
    148  * Adding painted tiles to a tile class
    149145 */
    150 function placeRandomPathToHeight(start, pathTexture, target, targetHeight, width = 10, occurrence = 2, strength = 0.1, heightmap = g_Map.height)
     146function placeRandomPathToHeight(
     147    start, target, targetHeight, tileClass = undefined, texture = "road_rome_a",
     148    width = 10, distance = 4, strength = 0.08, heightmap = g_Map.height)
    151149{
    152     if (pathTexture === true)
    153         pathTexture = ['temp_road', "temp_road_overgrown", 'temp_grass_b'];
    154    
    155     let clTempPath = createTileClass();
    156     let targetReached = false;
     150    let pathPoints = [];
    157151    let position = deepcopy(start);
    158     while (!targetReached)
     152    while (true)
    159153    {
    160154        rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap);
    161         if (pathTexture)
    162             createArea(new ClumpPlacer(0.2 * width * width, 1, 1, 1, floor(position.x), floor(position.y)), [new TerrainPainter(pathTexture), paintClass(clTempPath)]);
    163        
    164         // Set lets for next loop
     155        if (texture)
     156        {
     157            if (tileClass !== undefined)
     158                createArea(new ClumpPlacer(0.3 * width * width, 1, 1, 1, floor(position.x), floor(position.y)),
     159                    [new TerrainPainter(texture), paintClass(tileClass)]);
     160            else
     161                createArea(new ClumpPlacer(0.3 * width * width, 1, 1, 1, floor(position.x), floor(position.y)),
     162                    new TerrainPainter(texture));
     163        }
     164        pathPoints.push({ "x": position.x, "y": position.y, "dist": distance });
     165        // Check for distance to target and setup for next loop if needed
     166        if (getDistance(position.x, position.y, target.x, target.y) < distance / 2)
     167            break;
    165168        let angleToTarget = getAngle(position.x, position.y, target.x, target.y);
    166169        let angleOff = PI * (randFloat() - 0.5);
    167         position.x += occurrence * cos(angleToTarget + angleOff);
    168         position.y += occurrence * sin(angleToTarget + angleOff);
    169         if (getDistance(position.x, position.y, target.x, target.y) < occurrence / 2)
    170             targetReached = true;
     170        position.x += distance * cos(angleToTarget + angleOff);
     171        position.y += distance * sin(angleToTarget + angleOff);
    171172    }
    172     return clTempPath;
     173    return pathPoints;
    173174}
    174175
    175176function getGrad(wrapped = true, scalarField = g_Map.height)
     
    240241/**
    241242 * Meant to place e.g. resource spots within a height range
    242243 * @param {array} [heightRange] - The height range in which to place the entities (An associative array with keys "min" and "max" each containing a float)
    243  * @param {array} [avoidPoints] - An array of objects of the form {"x": int, "y": int, "dist": int}, points that will be avoided in the given dist e.g. start locations
    244  * @param {array} [avoidArea] - List of tiles to avoid
     244 * @param {array} [avoidPoints=[]] - An array of objects of the form {"x": int, "y": int, "dist": int}, points that will be avoided in the given dist e.g. start locations
     245 * @param {object} [avoidClass=undefined] - TileClass to be avoided
    245246 * @param {integer} [minDistance=30] - How many tile widths the entities to place have to be away from each other, start locations and the map border
    246247 * @param {array} [heightmap=g_Map.height] - The reliefmap the entities should be distributed on
    247  * @param {integer} [maxTries=1000] - How often random player distributions are rolled to be compared
     248 * @param {integer} [maxTries=2 * g_Map.size] - How often random player distributions are rolled to be compared (256 to 1024)
    248249 * @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular
    249250 */
    250 function getPointsByHeight(heightRange, avoidPoints, avoidArea, minDistance = 20, maxTries = 1000, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)
     251function getPointsByHeight(heightRange, avoidPoints = [], avoidClass = undefined, minDistance = 20, maxTries = 2 * g_Map.size, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)
    251252{
    252253    let points = [];
    253254    let placements = deepcopy(avoidPoints);
    254255    let validVertices = [];
    255256    let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius
     257    let avoidMap = g_Map.tileClasses[avoidClass].inclusionCount;
    256258    for (let x = minDistance; x < heightmap.length - minDistance; ++x)
    257259    {
    258260        for (let y = minDistance; y < heightmap[0].length - minDistance; ++y)
    259261        {
    260             let isValid = true;
    261             for (let i = 0; i < pathArea.length; ++i)
    262                 if (pathArea[i].x == x && pathArea[i].y == y)
    263                     isValid = false;
    264             if (!isValid)
     262            if (avoidClass !== undefined && // Avoid adjecting tiles in avoidClass
     263                (avoidMap[max(x - 1, 0)][y] > 0 ||
     264                avoidMap[x][max(y - 1, 0)] > 0 ||
     265                avoidMap[min(x + 1, avoidMap.length - 1)][y] > 0 ||
     266                avoidMap[x][min(y + 1, avoidMap[0].length - 1)] > 0))
    265267                continue;
    266            
    267             if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Has correct height and enough distance to map border
     268
     269            if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && // Has correct height
     270                (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Enough distance to map border
    268271                validVertices.push({ "x": x, "y": y , "dist": minDistance});
    269272        }
    270273    }
    271    
     274
    272275    for (let tries = 0; tries < maxTries; ++tries)
    273276    {
    274277        let point = validVertices[randInt(validVertices.length)];
     
    277280            points.push(point);
    278281            placements.push(point);
    279282        }
     283        if ((tries != 0) && (tries % 100 == 0)) // Time Check
     284            log(points.length + " points found after " + tries + " tries after " + ((new Date().getTime() - genStartTime) / 1000) + "s");
    280285    }
    281    
     286
    282287    return points;
    283288}
    284289
     
    294299    "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml",
    295300    "actor|props/flora/bush_tempe_a.xml", "actor|props/flora/bush_tempe_b.xml", "actor|props/flora/ferns.xml"
    296301];
    297 
    298302function placeMine(point, centerEntity)
    299303{
    300304    placeObject(point.x, point.y, centerEntity, 0, randFloat(0, TWO_PI));
     
    334338    fences.push(new Fortress("fence", deepcopy(fences[i].wall).reverse()));
    335339
    336340// Groves, only Wood
    337 let groveEntities = [
    338     "gaia/flora_bush_temperate", "gaia/flora_tree_euro_beech"
    339 ];
     341let groveEntities = ["gaia/flora_bush_temperate", "gaia/flora_tree_euro_beech"];
    340342let groveActors = [
    341343    "actor|geology/highland1_moss.xml", "actor|geology/highland2_moss.xml",
    342344    "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml",
     
    383385    }
    384386}
    385387
    386 let foodEntities = ["gaia/flora_bush_berry", "gaia/fauna_chicken", "gaia/fauna_chicken"];
    387 // Start loaction resources
    388 function placeStartLocationResources(point)
     388
     389function placeStartLocationResources(point, foodEntities = ["gaia/flora_bush_berry", "gaia/fauna_chicken", "gaia/fauna_chicken"])
    389390{
    390391    let currentAngle = randFloat(0, TWO_PI);
    391392    // Stone and chicken
     
    438439    }
    439440}
    440441
     442log("Functions loaded after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
     443
    441444/**
    442  * Set height limits and water level by map size
     445 * Environment settings
    443446 */
     447setBiome(g_BiomeAlpine);
     448g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0.01 };
     449g_Environment.Water.WaterBody.Colour = { "r" : 0.3, "g" : 0.05, "b" : 0.1, "a" : 0.1 };
     450g_Environment.Water.WaterBody.Murkiness = 0.4;
     451
     452/**
     453 * Base terrain shape generation and settings
     454 */
     455 // Height range by map size
    444456let heightScale = (g_Map.size + 256) / 768 / 4;
    445457let heightRange = { "min": MIN_HEIGHT * heightScale, "max": MAX_HEIGHT * heightScale };
    446 
     458// Water coverage
    447459let averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value
    448460let waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine
    449461let waterHeightAdjusted = waterHeight + MIN_HEIGHT; // Water height in RMGEN
    450462setWaterHeight(waterHeight);
    451 
    452 
    453 /**
    454  * Generate base terrain
    455  */
     463// Generate base terrain shape
    456464let medH = (heightRange.min + heightRange.max) / 2;
    457465let initialHeightmap = [[medH, medH], [medH, medH]];
    458466setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8);
    459 
    460 /**
    461  * Apply simple erosion
    462  */
    463 // globalSmoothHeightmap(0.5);
     467// Apply simple erosion
    464468for (let i = 0; i < 5; ++i)
    465469    splashErodeMap(0.1);
    466 
     470// Final rescale
    467471rescaleHeightmap(heightRange.min, heightRange.max);
    468472
     473RMS.SetProgress(25);
     474
    469475/**
    470  * Height presets
     476 * Prepare terrain texture placement
    471477 */
    472478let heighLimits = [
    473479    heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
     
    481487    waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest
    482488    waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border
    483489    waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop
    484 
    485 /**
    486  * Set environment
    487  */
    488 setBiome(g_BiomeAlpine);
    489 g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0.01 };
    490 g_Environment.Water.WaterBody.Colour = { "r" : 0.3, "g" : 0.05, "b" : 0.1, "a" : 0.1 };
    491 g_Environment.Water.WaterBody.Murkiness = 0.4;
    492 
    493 /**
    494  * Add tile painting presets
    495  */
    496 let dummyActor = "actor|props/special/common/waypoint_flag.xml";
     490let playerHeight = (heighLimits[4] + heighLimits[5]) / 2; // Average player height
     491// Texture and actor presets
    497492let myBiome = [];
    498493myBiome.push({ // 0 Deep water
    499494    "texture": ["shoreline_stoney_a"],
     
    551546    "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0]
    552547});
    553548
     549log("Terrain shape generation and texture presets after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
    554550
    555551/**
    556552 * Get start locations
     
    573569        teams.push(t);
    574570}
    575571playerIDs = sortPlayers(playerIDs);
    576 
    577572// Minimize maximum distance between players within a team
    578573if (teams.length)
    579574{
     
    616611    }
    617612}
    618613
    619 let playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
     614log("Start location chosen after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
     615RMS.SetProgress(30);
    620616
    621617/**
    622  * Place start locations (resources later)
     618 * Add paths
    623619 */
    624 for (let p = 0; p < playerIDs.length; ++p)
    625 {
    626     let point = startLocations[p];
    627     rectangularSmoothToHeight(point, 35, 35, playerHeight, 0.7);
    628     placeCivDefaultEntities(point.x, point.y, playerIDs[p], { "iberWall": true });
    629 }
    630 
    631 /**
    632  * Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false (default) for this map to work properly)
    633  */
    634 let tchm = getTileCenteredHeightmap();
    635 
    636 /**
    637  * Add paths class and area but don't paint before resource spots are chosen!
    638  */
    639 let pathTerrainClassIDs = [];
     620let tchm = getTileCenteredHeightmap(); // Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false)
     621let pathPoints = [];
     622let clPath = createTileClass();
    640623for (let i = 0; i < startLocations.length; ++i)
    641624{
    642625    let start = startLocations[i];
    643626    let target = startLocations[(i + 1) % startLocations.length];
    644     pathTerrainClassIDs.push(placeRandomPathToHeight(start, ["road_rome_a"], target, playerHeight, 8, 3, 0.1));
     627    pathPoints = pathPoints.concat(placeRandomPathToHeight(start, target, playerHeight, clPath));
    645628}
    646 let pathArea = [];
    647 for (let x = 0; x < tchm.length; ++x)
    648     for (let y = 0; y < tchm[0].length; ++y)
    649         for (let i = 0; i < pathTerrainClassIDs.length; ++i)
    650             if (getTileClass(pathTerrainClassIDs[i]).countMembersInRadius(x, y, 0.5))
    651                 pathArea.push({ "x": x, "y": y });
    652629
     630log("Paths placed after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
     631RMS.SetProgress(45);
     632
    653633/**
    654  * Get resource spots after players start locations after path are calculated but before they are placed!
     634 * Get resource spots after players start locations calculation
    655635 */
    656636let avoidPoints = deepcopy(startLocations);
    657637for (let i = 0; i < avoidPoints.length; ++i)
    658638    avoidPoints[i].dist = 30;
    659 let resourceSpots = getPointsByHeight({ "min": (heighLimits[3] + heighLimits[4]) / 2, "max": (heighLimits[5] + heighLimits[6]) / 2 }, avoidPoints, pathArea);
     639let resourceSpots = getPointsByHeight({ "min": (heighLimits[3] + heighLimits[4]) / 2, "max": (heighLimits[5] + heighLimits[6]) / 2 }, avoidPoints, clPath);
    660640
    661 /**
    662  * Calculate slope map
    663  */
    664 let slopeMap = getSlopeMap();
     641log("Resource spots chosen after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
     642RMS.SetProgress(55);
    665643
    666644/**
    667645 * Divide tiles in areas by height and avoid paths
     
    673651{
    674652    for (let y = 0; y < tchm[0].length; ++y)
    675653    {
    676         let isPath = false;
    677         for (let i = 0; i < pathArea.length; ++i)
    678             if (pathArea[i].x == x && pathArea[i].y == y)
    679                 isPath = true;
    680         if (isPath)
     654        if (g_Map.tileClasses[clPath].inclusionCount[x][y] > 0) // Avoid paths
    681655            continue;
    682656       
    683657        let minHeight = heightRange.min;
     
    697671/**
    698672 * Get max slope of each area
    699673 */
     674let slopeMap = getSlopeMap(); // Calculate slope map
    700675let minSlope = [];
    701676let maxSlope = [];
    702677for (let h = 0; h < heighLimits.length; ++h)
     
    744719    }
    745720}
    746721
     722log("Terrain texture placement finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check
     723RMS.SetProgress(80);
     724
    747725/**
    748  * Add starting resources after terrain texture painting
     726 * Add start locations and resource spots after terrain texture and path painting
    749727 */
    750 for (let p = 0; p < g_MapSettings.PlayerData.length - 1; ++p)
     728for (let p = 0; p < playerIDs.length; ++p)
     729{
     730    let point = startLocations[p];
     731    rectangularSmoothToHeight(point, 40, 40, playerHeight, 0.7);
     732    placeCivDefaultEntities(point.x, point.y, playerIDs[p], { "iberWall": true });
    751733    placeStartLocationResources(startLocations[p]);
    752 
    753 /**
    754  * Add resource spots after terrain texture painting
    755  */
     734}
    756735for (let i = 0; i < resourceSpots.length; ++i)
    757736{
    758737    let choice = i % 5;
     
    771750/**
    772751 * Stop Timer
    773752 */
    774 log("Map generation finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s")
     753log("Map generation finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s");
    775754
    776755/**
    777756 * Export map data