forked from mirrors/0ad
Petra: rewrite of the dock placement. It should be more robust and compatible with the new pathfinder now
This was SVN commit r16457.
This commit is contained in:
@@ -388,7 +388,7 @@ m.Map.prototype.add = function(map)
|
||||
}
|
||||
};
|
||||
|
||||
m.Map.prototype.findBestTile = function(radius, obstructionTiles)
|
||||
m.Map.prototype.findBestTile = function(radius, obstruction)
|
||||
{
|
||||
// Find the best non-obstructed tile
|
||||
let bestIdx = 0;
|
||||
@@ -398,8 +398,8 @@ m.Map.prototype.findBestTile = function(radius, obstructionTiles)
|
||||
let v = this.map[i];
|
||||
if (v > bestVal)
|
||||
{
|
||||
var j = API3.getMaxMapIndex(i, this, obstructionTiles);
|
||||
if (obstructionTiles.map[j] <= radius)
|
||||
let j = this.getNonObstructedTile(i, radius, obstruction);
|
||||
if (j < 0)
|
||||
continue;
|
||||
bestVal = v;
|
||||
bestIdx = j;
|
||||
@@ -409,6 +409,94 @@ m.Map.prototype.findBestTile = function(radius, obstructionTiles)
|
||||
return [bestIdx, bestVal];
|
||||
};
|
||||
|
||||
// return any non obstructed (small) tile inside the (big) tile i from obstruction map
|
||||
m.Map.prototype.getNonObstructedTile = function(i, radius, obstruction)
|
||||
{
|
||||
let ratio = this.cellSize / obstruction.cellSize;
|
||||
let ix = (i % this.width) * ratio;
|
||||
let iy = Math.floor(i / this.width) * ratio;
|
||||
let w = obstruction.width;
|
||||
for (let kx = ix; kx < ix + ratio; ++kx)
|
||||
{
|
||||
if (kx < radius || kx >= w - radius)
|
||||
continue;
|
||||
for (let ky = iy; ky < iy + ratio; ++ky)
|
||||
{
|
||||
if (ky < radius || ky >= w - radius)
|
||||
continue;
|
||||
if (obstruction.isObstructedTile(kx, ky, radius))
|
||||
continue;
|
||||
return (kx + ky*w);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// return true is the area centered on tile kx-ky and with radius is obstructed
|
||||
m.Map.prototype.isObstructedTile = function(kx, ky, radius)
|
||||
{
|
||||
let w = this.width;
|
||||
if (kx < radius || kx >= w - radius || ky < radius || ky >= w - radius || this.map[kx+ky*w] == 0)
|
||||
return true;
|
||||
for (let dy = 0; dy <= radius; ++dy)
|
||||
{
|
||||
let dxmax = radius - dy;
|
||||
let xp = kx + (ky + dy)*w;
|
||||
let xm = kx + (ky - dy)*w;
|
||||
for (let dx = -dxmax; dx <= dxmax; ++dx)
|
||||
if (this.map[xp + dx] == 0 || this.map[xm + dx] == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// returns the nearest obstructed point
|
||||
// TODO check that the landpassmap index is the same
|
||||
m.Map.prototype.findNearestObstructed = function(i, radius)
|
||||
{
|
||||
var w = this.width;
|
||||
var ix = i % w;
|
||||
var iy = Math.floor(i / w);
|
||||
var n = (this.cellSize > 8) ? 1 : Math.floor(8 / this.cellSize);
|
||||
for (let i = 1; i <= n; ++i)
|
||||
{
|
||||
let kx = ix - i;
|
||||
let ky = iy + i;
|
||||
for (let j = 1; j <= 8*i; ++j)
|
||||
{
|
||||
if (this.isObstructedTile(kx, ky, radius))
|
||||
{
|
||||
let akx = Math.abs(kx-ix);
|
||||
let aky = Math.abs(ky-iy);
|
||||
if (akx >= aky)
|
||||
{
|
||||
if (kx > ix)
|
||||
--kx;
|
||||
else
|
||||
++kx;
|
||||
}
|
||||
if (aky >= akx)
|
||||
{
|
||||
if (ky > iy)
|
||||
--ky;
|
||||
else
|
||||
++ky;
|
||||
}
|
||||
return (kx + w*ky);
|
||||
}
|
||||
if (j <= 2*i+1)
|
||||
++kx;
|
||||
else if (j <= 4*i+1)
|
||||
--ky;
|
||||
else if (j < 6*i+1)
|
||||
--kx;
|
||||
else
|
||||
++ky;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// returns the point with the lowest (but still > radius) point in the immediate vicinity
|
||||
m.Map.prototype.findLowestNeighbor = function(x,y,radius)
|
||||
{
|
||||
|
||||
@@ -31,9 +31,9 @@ m.SharedScript = function(settings)
|
||||
this.resourceMaps = {}; // Contains maps showing the density of wood, stone and metal
|
||||
this.CCResourceMaps = {}; // Contains maps showing the density of wood, stone and metal, optimized for CC placement.
|
||||
// Resource maps data.
|
||||
// By how much to divide the resource amount for plotting (ie a tree having 200 wood is "4").
|
||||
// By how much to divide the resource amount when filling the map (ie a tree having 200 wood is "4").
|
||||
this.decreaseFactor = {'wood': 50.0, 'stone': 90.0, 'metal': 90.0, 'food': 40.0};
|
||||
}
|
||||
};
|
||||
|
||||
//Return a simple object (using no classes etc) that will be serialized
|
||||
//into saved games
|
||||
|
||||
@@ -294,7 +294,6 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
|
||||
// The AI will currently not build a CC if it wouldn't connect with an existing CC.
|
||||
|
||||
var obstructions = m.createObstructionMap(gameState, this.accessIndex, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
var DPFoundations = gameState.getOwnFoundations().filter(API3.Filters.byType(gameState.applyCiv("foundation|structures/{civ}_storehouse"))).toEntityArray();
|
||||
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
|
||||
@@ -303,15 +302,15 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
|
||||
var bestVal = undefined;
|
||||
var radius = Math.ceil(template.obstructionRadius() / obstructions.cellSize);
|
||||
|
||||
var territoryMap = gameState.sharedScript.territoryMap;
|
||||
var territoryMap = gameState.ai.HQ.territoryMap;
|
||||
var width = territoryMap.width;
|
||||
var cellSize = territoryMap.cellSize;
|
||||
|
||||
for (var p = 0; p < this.territoryIndices.length; ++p)
|
||||
{
|
||||
var j = this.territoryIndices[p];
|
||||
var i = API3.getMaxMapIndex(j, territoryMap, obstructions);
|
||||
if (obstructions.map[i] <= radius) // check room around
|
||||
var i = territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0) // no room around
|
||||
continue;
|
||||
|
||||
// we add 3 times the needed resource and once the other two (not food)
|
||||
@@ -384,7 +383,7 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
|
||||
if (bestVal !== undefined && total < bestVal)
|
||||
continue;
|
||||
bestVal = total;
|
||||
bestIdx = j;
|
||||
bestIdx = i;
|
||||
}
|
||||
|
||||
if (this.Config.debug > 2)
|
||||
@@ -392,9 +391,9 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
|
||||
|
||||
if (bestVal <= 0)
|
||||
return {"quality": bestVal, "pos": [0, 0]};
|
||||
var i = API3.getMaxMapIndex(bestIdx, territoryMap, obstructions);
|
||||
var x = ((i % obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
|
||||
var x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
return {"quality": bestVal, "pos": [x, z]};
|
||||
};
|
||||
|
||||
|
||||
@@ -552,7 +552,6 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
|
||||
|
||||
// obstruction map
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
var ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre"));
|
||||
var dpEnts = gameState.getOwnDropsites().filter(API3.Filters.not(API3.Filters.byClassesOr(["CivCentre", "Elephant"])));
|
||||
@@ -588,16 +587,16 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
|
||||
{
|
||||
if (this.territoryMap.getOwnerIndex(j) != 0)
|
||||
continue;
|
||||
// We require that it is accessible
|
||||
var index = gameState.ai.accessibility.landPassMap[j];
|
||||
// with enough room around to build the cc
|
||||
var i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0)
|
||||
continue;
|
||||
// we require that it is accessible
|
||||
var index = gameState.ai.accessibility.landPassMap[i];
|
||||
if (!this.landRegions[index])
|
||||
continue;
|
||||
if (proxyAccess && nbShips === 0 && proxyAccess !== index)
|
||||
continue;
|
||||
// and with enough room around to build the cc
|
||||
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
|
||||
if (obstructions.map[i] <= radius)
|
||||
continue;
|
||||
|
||||
var norm = 0.5; // TODO adjust it, knowing that we will sum 5 maps
|
||||
// checking distance to other cc
|
||||
@@ -680,7 +679,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
|
||||
if (bestVal !== undefined && val < bestVal)
|
||||
continue;
|
||||
bestVal = val;
|
||||
bestIdx = j;
|
||||
bestIdx = i;
|
||||
}
|
||||
|
||||
Engine.ProfileStop();
|
||||
@@ -693,13 +692,12 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
|
||||
// not good enough.
|
||||
if (bestVal < cut)
|
||||
return false;
|
||||
|
||||
var i = API3.getMaxMapIndex(bestIdx, this.territoryMap, obstructions);
|
||||
var x = (i % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
|
||||
var x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
|
||||
// Define a minimal number of wanted ships in the seas reaching this new base
|
||||
var index = gameState.ai.accessibility.landPassMap[i];
|
||||
var index = gameState.ai.accessibility.landPassMap[bestIdx];
|
||||
for (var base of this.baseManagers)
|
||||
{
|
||||
if (!base.anchor || base.accessIndex === index)
|
||||
@@ -709,7 +707,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
|
||||
this.navalManager.setMinimalTransportShips(gameState, sea, 1);
|
||||
}
|
||||
|
||||
return [x,z];
|
||||
return [x, z];
|
||||
};
|
||||
|
||||
// Returns the best position to build a new Civil Centre
|
||||
@@ -738,7 +736,6 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
|
||||
|
||||
// obstruction map
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
var bestIdx = undefined;
|
||||
var bestVal = undefined;
|
||||
@@ -753,13 +750,13 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
|
||||
{
|
||||
if (this.territoryMap.getOwnerIndex(j) != 0)
|
||||
continue;
|
||||
// We require that it is accessible
|
||||
var index = gameState.ai.accessibility.landPassMap[j];
|
||||
if (!this.landRegions[index])
|
||||
// with enough room around to build the cc
|
||||
var i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0)
|
||||
continue;
|
||||
// and with enough room around to build the cc
|
||||
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
|
||||
if (obstructions.map[i] <= radius)
|
||||
// we require that it is accessible
|
||||
var index = gameState.ai.accessibility.landPassMap[i];
|
||||
if (!this.landRegions[index])
|
||||
continue;
|
||||
|
||||
// checking distances to other cc
|
||||
@@ -818,7 +815,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
|
||||
if (bestVal !== undefined && currentVal > bestVal)
|
||||
continue;
|
||||
bestVal = currentVal;
|
||||
bestIdx = j;
|
||||
bestIdx = i;
|
||||
}
|
||||
|
||||
if (this.Config.debug > 1)
|
||||
@@ -829,12 +826,11 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
|
||||
if (bestVal === undefined)
|
||||
return undefined;
|
||||
|
||||
var i = API3.getMaxMapIndex(bestIdx, this.territoryMap, obstructions);
|
||||
var x = (i % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
|
||||
// Define a minimal number of wanted ships in the seas reaching this new base
|
||||
var index = gameState.ai.accessibility.landPassMap[i];
|
||||
var index = gameState.ai.accessibility.landPassMap[bestIdx];
|
||||
for (var base of this.baseManagers)
|
||||
{
|
||||
if (!base.anchor || base.accessIndex === index)
|
||||
@@ -844,7 +840,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
|
||||
this.navalManager.setMinimalTransportShips(gameState, sea, 1);
|
||||
}
|
||||
|
||||
return [x,z];
|
||||
return [x, z];
|
||||
};
|
||||
|
||||
// Returns the best position to build a new market: if the allies already have a market, build it as far as possible
|
||||
@@ -862,7 +858,6 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
|
||||
|
||||
// obstruction map
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
var bestIdx = undefined;
|
||||
var bestVal = undefined;
|
||||
@@ -880,8 +875,8 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
|
||||
if (this.basesMap.map[j] == 0) // only in our territory
|
||||
continue;
|
||||
// with enough room around to build the cc
|
||||
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
|
||||
if (obstructions.map[i] <= radius)
|
||||
var i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0)
|
||||
continue;
|
||||
var index = gameState.ai.accessibility.landPassMap[i];
|
||||
if (!this.landRegions[index])
|
||||
@@ -907,7 +902,7 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
|
||||
if (bestVal !== undefined && maxDist < bestVal)
|
||||
continue;
|
||||
bestVal = maxDist;
|
||||
bestIdx = j;
|
||||
bestIdx = i;
|
||||
}
|
||||
|
||||
if (this.Config.debug > 1)
|
||||
@@ -924,9 +919,8 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
|
||||
(expectedGain < 8 && (!template.hasClass("BarterMarket") || gameState.getOwnStructures().filter(API3.Filters.byClass("BarterMarket")).length > 0)))
|
||||
return false;
|
||||
|
||||
var i = API3.getMaxMapIndex(bestIdx, this.territoryMap, obstructions);
|
||||
var x = (i % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
return [x, z, this.basesMap.map[bestIdx], expectedGain];
|
||||
};
|
||||
|
||||
@@ -953,9 +947,9 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
|
||||
|
||||
// obstruction map
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
var bestIdx = undefined;
|
||||
var bestJdx = undefined;
|
||||
var bestVal = undefined;
|
||||
var width = this.territoryMap.width;
|
||||
var cellSize = this.territoryMap.cellSize;
|
||||
@@ -979,9 +973,9 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
|
||||
}
|
||||
if (this.basesMap.map[j] == 0) // inaccessible cell
|
||||
continue;
|
||||
// and with enough room around to build the cc
|
||||
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
|
||||
if (obstructions.map[i] <= radius)
|
||||
// with enough room around to build the cc
|
||||
var i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
var pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
|
||||
@@ -1037,16 +1031,16 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
|
||||
if (bestVal !== undefined && minDist > bestVal)
|
||||
continue;
|
||||
bestVal = minDist;
|
||||
bestIdx = j;
|
||||
bestIdx = i;
|
||||
bestJdx = j;
|
||||
}
|
||||
|
||||
if (bestVal === undefined)
|
||||
return undefined;
|
||||
|
||||
var i = API3.getMaxMapIndex(bestIdx, this.territoryMap, obstructions);
|
||||
var x = (i % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
return [x, z, this.basesMap.map[bestIdx]];
|
||||
var x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
return [x, z, this.basesMap.map[bestJdx]];
|
||||
};
|
||||
|
||||
m.HQ.prototype.buildTemple = function(gameState, queues)
|
||||
|
||||
@@ -8,6 +8,7 @@ m.createObstructionMap = function(gameState, accessIndex, template)
|
||||
{
|
||||
var passabilityMap = gameState.getMap();
|
||||
var territoryMap = gameState.ai.territoryMap;
|
||||
var ratio = territoryMap.cellSize / passabilityMap.cellSize;
|
||||
|
||||
// default values
|
||||
var placementType = "land";
|
||||
@@ -28,110 +29,43 @@ m.createObstructionMap = function(gameState, accessIndex, template)
|
||||
|
||||
if (placementType == "shore")
|
||||
{
|
||||
if (passabilityMap.cellSize == 4)
|
||||
{
|
||||
var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction")
|
||||
| gameState.getPassabilityClassMask("building-shore")
|
||||
| gameState.getPassabilityClassMask("default");
|
||||
var obstructionDefault = gameState.getPassabilityClassMask("default");
|
||||
}
|
||||
else // new pathFinder branch
|
||||
{
|
||||
var obstructionMask = gameState.getPassabilityClassMask("default-no-clearance")
|
||||
| gameState.getPassabilityClassMask("building-shore")
|
||||
| gameState.getPassabilityClassMask("default-terrain-only");
|
||||
var obstructionDefault = gameState.getPassabilityClassMask("default-terrain-only");
|
||||
}
|
||||
var okay = false;
|
||||
for (var x = 0; x < passabilityMap.width; ++x)
|
||||
{
|
||||
for (var y = 0; y < passabilityMap.height; ++y)
|
||||
{
|
||||
var i = x + y*passabilityMap.width;
|
||||
var xter = Math.floor((x+0.5)*passabilityMap.cellSize / territoryMap.cellSize);
|
||||
var yter = Math.floor((y+0.5)*passabilityMap.cellSize / territoryMap.cellSize);
|
||||
var iter = xter + yter*territoryMap.width;
|
||||
var tilePlayer = (territoryMap.data[iter] & m.TERRITORY_PLAYER_MASK);
|
||||
|
||||
if (gameState.isPlayerEnemy(tilePlayer) && tilePlayer !== 0)
|
||||
{
|
||||
obstructionTiles[i] = 0;
|
||||
continue;
|
||||
}
|
||||
if ((passabilityMap.data[i] & (gameState.getPassabilityClassMask("building-shore") | obstructionDefault)))
|
||||
{
|
||||
obstructionTiles[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
okay = false;
|
||||
var positions = [[0,1], [1,1], [1,0], [1,-1], [0,-1], [-1,-1], [-1,0], [-1,1]];
|
||||
var available = 0;
|
||||
for (var stuff of positions)
|
||||
{
|
||||
var index = x + stuff[0] + (y+stuff[1])*passabilityMap.width;
|
||||
var index2 = x + stuff[0]*2 + (y+stuff[1]*2)*passabilityMap.width;
|
||||
var index3 = x + stuff[0]*3 + (y+stuff[1]*3)*passabilityMap.width;
|
||||
var index4 = x + stuff[0]*4 + (y+stuff[1]*4)*passabilityMap.width;
|
||||
|
||||
if ((passabilityMap.data[index] & obstructionDefault) && gameState.ai.accessibility.getRegionSizei(index,true) > 500)
|
||||
if ((passabilityMap.data[index2] & obstructionDefault) && gameState.ai.accessibility.getRegionSizei(index2,true) > 500)
|
||||
if ((passabilityMap.data[index3] & obstructionDefault) && gameState.ai.accessibility.getRegionSizei(index3,true) > 500)
|
||||
if ((passabilityMap.data[index4] & obstructionDefault) && gameState.ai.accessibility.getRegionSizei(index4,true) > 500)
|
||||
{
|
||||
if (available < 2)
|
||||
available++;
|
||||
else
|
||||
okay = true;
|
||||
}
|
||||
}
|
||||
// checking for accessibility: if a neighbor is inaccessible, this is too. If it's not on the same "accessible map" as us, we crash-i~u.
|
||||
var radius = 3;
|
||||
for (var xx = -radius;xx <= radius; xx++)
|
||||
for (var yy = -radius;yy <= radius; yy++)
|
||||
{
|
||||
var id = x + xx + (y+yy)*passabilityMap.width;
|
||||
if (id > 0 && id < passabilityMap.data.length)
|
||||
if (gameState.ai.terrainAnalyzer.map[id] === 0 || gameState.ai.terrainAnalyzer.map[id] == 30 || gameState.ai.terrainAnalyzer.map[id] == 40)
|
||||
okay = false;
|
||||
}
|
||||
obstructionTiles[i] = okay ? 255 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
var passMap = gameState.ai.accessibility.navalPassMap;
|
||||
var obstructionMask = gameState.getPassabilityClassMask("building-shore");
|
||||
}
|
||||
else
|
||||
{
|
||||
var playerID = PlayerID;
|
||||
if (passabilityMap.cellSize == 4)
|
||||
var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction")
|
||||
| gameState.getPassabilityClassMask("building-land");
|
||||
else // new pathFinder branch
|
||||
var obstructionMask = gameState.getPassabilityClassMask("default-no-clearance")
|
||||
| gameState.getPassabilityClassMask("building-land");
|
||||
|
||||
for (var i = 0; i < passabilityMap.data.length; ++i)
|
||||
var passMap = gameState.ai.accessibility.landPassMap;
|
||||
var obstructionMask = gameState.getPassabilityClassMask("building-land");
|
||||
}
|
||||
|
||||
if (passabilityMap.cellSize == 4)
|
||||
var obstructionMask = obstructionMask | gameState.getPassabilityClassMask("foundationObstruction");
|
||||
else // new pathFinder branch
|
||||
var obstructionMask = obstructionMask | gameState.getPassabilityClassMask("default-no-clearance");
|
||||
|
||||
for (var k = 0; k < territoryMap.data.length; ++k)
|
||||
{
|
||||
let tilePlayer = (territoryMap.data[k] & m.TERRITORY_PLAYER_MASK);
|
||||
if ((!buildNeutral && tilePlayer == 0) ||
|
||||
(!buildOwn && tilePlayer == PlayerID) ||
|
||||
(!buildAlly && tilePlayer != PlayerID && gameState.isPlayerAlly(tilePlayer)) ||
|
||||
(!buildEnemy && tilePlayer != 0 && gameState.isPlayerEnemy(tilePlayer)))
|
||||
continue;
|
||||
let x = ratio * (k % territoryMap.width);
|
||||
let y = ratio * (Math.floor(k / territoryMap.width));
|
||||
for (let ix = 0; ix < ratio; ++ix)
|
||||
{
|
||||
var x = i % passabilityMap.width;
|
||||
var y = Math.floor(i / passabilityMap.width);
|
||||
var xter = Math.floor((x+0.5)*passabilityMap.cellSize / territoryMap.cellSize);
|
||||
var yter = Math.floor((y+0.5)*passabilityMap.cellSize / territoryMap.cellSize);
|
||||
var iter = xter + yter*territoryMap.width;
|
||||
var tilePlayer = (territoryMap.data[iter] & m.TERRITORY_PLAYER_MASK);
|
||||
var invalidTerritory = (
|
||||
(!buildOwn && tilePlayer == playerID) ||
|
||||
(!buildAlly && gameState.isPlayerAlly(tilePlayer) && tilePlayer != playerID) ||
|
||||
(!buildNeutral && tilePlayer == 0) ||
|
||||
(!buildEnemy && gameState.isPlayerEnemy(tilePlayer) && tilePlayer != 0)
|
||||
);
|
||||
if (accessIndex)
|
||||
var tileAccessible = (accessIndex === gameState.ai.accessibility.landPassMap[i]);
|
||||
else
|
||||
var tileAccessible = true;
|
||||
obstructionTiles[i] = (!tileAccessible || invalidTerritory || (passabilityMap.data[i] & obstructionMask)) ? 0 : 255;
|
||||
for (let iy = 0; iy < ratio; ++iy)
|
||||
{
|
||||
let i = x + ix + (y + iy)*passabilityMap.width;
|
||||
if (placementType != "shore" && accessIndex && accessIndex !== passMap[i])
|
||||
continue;
|
||||
if (!(passabilityMap.data[i] & obstructionMask))
|
||||
obstructionTiles[i] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var map = new API3.Map(gameState.sharedScript, "passability", obstructionTiles);
|
||||
map.setMaxVal(255);
|
||||
|
||||
@@ -145,9 +79,9 @@ m.createObstructionMap = function(gameState, accessIndex, template)
|
||||
if (ent.buildCategory() === category && ent.position())
|
||||
{
|
||||
var pos = ent.position();
|
||||
var x = Math.round(pos[0] / gameState.cellSize);
|
||||
var z = Math.round(pos[1] / gameState.cellSize);
|
||||
map.addInfluence(x, z, minDist/gameState.cellSize, -255, 'constant');
|
||||
var x = Math.round(pos[0] / passabilityMap.cellSize);
|
||||
var z = Math.round(pos[1] / passabilityMap.cellSize);
|
||||
map.addInfluence(x, z, minDist/passability.cellSize, -255, 'constant');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -407,12 +407,11 @@ m.NavalManager.prototype.splitTransport = function(gameState, plan)
|
||||
* create a transport from a garrisoned ship to a land location
|
||||
* needed at start game when starting with a garrisoned ship
|
||||
*/
|
||||
m.NavalManager.prototype.createTransportIfNeeded = function(gameState, fromPos, toPos)
|
||||
m.NavalManager.prototype.createTransportIfNeeded = function(gameState, fromPos, toPos, toAccess)
|
||||
{
|
||||
let fromAccess = gameState.ai.accessibility.getAccessValue(fromPos);
|
||||
if (fromAccess !== 1)
|
||||
return;
|
||||
let toAccess = gameState.ai.accessibility.getAccessValue(toPos);
|
||||
if (toAccess < 2)
|
||||
return;
|
||||
|
||||
|
||||
@@ -71,22 +71,23 @@ m.ConstructionPlan.prototype.start = function(gameState)
|
||||
this.metadata.base = pos.base;
|
||||
|
||||
if (pos.access)
|
||||
this.metadata.access = pos.access; // needed for Docks for the position is on water
|
||||
this.metadata.access = pos.access; // needed for Docks whose position is on water
|
||||
else
|
||||
this.metadata.access = gameState.ai.accessibility.getAccessValue([pos.x, pos.z]);
|
||||
|
||||
if (this.template.buildCategory() === "Dock")
|
||||
{
|
||||
// try to place it a bit inside the land if possible
|
||||
let cosang = Math.cos(pos.angle);
|
||||
let sinang = Math.sin(pos.angle);
|
||||
if (this.template.get("Obstruction") && this.template.get("Obstruction/Static"))
|
||||
var radius = (+this.template.get("Obstruction/Static/@depth"))/2;
|
||||
else
|
||||
var radius = 0;
|
||||
for (let step = 0; step < radius; step += 4)
|
||||
builders[0].construct(this.type, pos.x+step*sinang, pos.z+step*cosang,
|
||||
pos.angle, this.metadata);
|
||||
// adjust a bit the position if needed
|
||||
// TODO we would need groundLevel and waterLevel to do it properly
|
||||
let cosa = Math.cos(pos.angle);
|
||||
let sina = Math.sin(pos.angle);
|
||||
let shiftMax = gameState.ai.HQ.territoryMap.cellSize;
|
||||
for (let shift = 0; shift <= shiftMax; shift += 2)
|
||||
{
|
||||
builders[0].construct(this.type, pos.x-shift*sina, pos.z-shift*cosa, pos.angle, this.metadata);
|
||||
if (shift > 0)
|
||||
builders[0].construct(this.type, pos.x+shift*sina, pos.z+shift*cosa, pos.angle, this.metadata);
|
||||
}
|
||||
}
|
||||
else if (pos.x == pos.xx && pos.z == pos.zz)
|
||||
builders[0].construct(this.type, pos.x, pos.z, pos.angle, this.metadata);
|
||||
@@ -99,9 +100,9 @@ m.ConstructionPlan.prototype.start = function(gameState)
|
||||
this.onStart(gameState);
|
||||
Engine.ProfileStop();
|
||||
|
||||
// TODO should have a ConstructionStarted event in case the construct order fails
|
||||
// TODO should have a ConstructionStarted even in case the construct order fails
|
||||
if (this.metadata && this.metadata.proximity)
|
||||
gameState.ai.HQ.navalManager.createTransportIfNeeded(gameState, this.metadata.proximity, [pos.x, pos.z]);
|
||||
gameState.ai.HQ.navalManager.createTransportIfNeeded(gameState, this.metadata.proximity, [pos.x, pos.z], this.metadata.access);
|
||||
};
|
||||
|
||||
// TODO for dock, we should allow building them outside territory, and we should check that we are along the right sea
|
||||
@@ -157,13 +158,6 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
}
|
||||
}
|
||||
|
||||
// First, find all tiles that are far enough away from obstructions:
|
||||
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
obstructions.expandInfluences();
|
||||
|
||||
//obstructions.dumpIm(template.buildCategory() + "_obstructions.png");
|
||||
|
||||
// Compute each tile's closeness to friendly structures:
|
||||
|
||||
var placement = new API3.Map(gameState.sharedScript, "territory");
|
||||
@@ -291,12 +285,16 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, this
|
||||
// allows room for units to walk between buildings.
|
||||
|
||||
// Find the best non-obstructed:
|
||||
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close,
|
||||
// this allows room for units to walk between buildings.
|
||||
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
|
||||
// also not for fields who can be stacked quite a bit
|
||||
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
//obstructions.dumpIm(template.buildCategory() + "_obstructions.png");
|
||||
|
||||
var radius = 0;
|
||||
if (template.hasClass("Fortress") || this.type === gameState.applyCiv("structures/{civ}_siege_workshop")
|
||||
|| this.type === gameState.applyCiv("structures/{civ}_elephant_stables"))
|
||||
@@ -306,7 +304,6 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
else
|
||||
radius = Math.ceil(template.obstructionRadius() / obstructions.cellSize);
|
||||
|
||||
// Find the best non-obstructed
|
||||
if (template.hasClass("House") && !alreadyHasHouses)
|
||||
{
|
||||
// try to get some space to place several houses first
|
||||
@@ -327,17 +324,35 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
|
||||
|
||||
var x = ((bestIdx % obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var xx = x;
|
||||
var zz = z;
|
||||
|
||||
if (template.hasClass("House") || template.hasClass("Field") || template.resourceDropsiteTypes() !== undefined)
|
||||
var secondBest = obstructions.findLowestNeighbor(x,z);
|
||||
else
|
||||
var secondBest = [x,z];
|
||||
{
|
||||
if (obstructions.cellSize != 4) // new pathFinder branch
|
||||
{
|
||||
let secondBest = obstructions.findNearestObstructed(bestIdx, radius);
|
||||
if (secondBest >= 0)
|
||||
{
|
||||
x = ((secondBest % obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
z = (Math.floor(secondBest / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
xx = x;
|
||||
zz = z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
obstructions.expandInfluences();
|
||||
let secondBest = obstructions.findLowestNeighbor(x,z);
|
||||
xx = secondBest[0];
|
||||
zz = secondBest[1];
|
||||
}
|
||||
}
|
||||
|
||||
var territorypos = placement.gamePosToMapPos([x,z]);
|
||||
var territoryIndex = territorypos[0] + territorypos[1]*placement.width;
|
||||
let territorypos = placement.gamePosToMapPos([x,z]);
|
||||
let territoryIndex = territorypos[0] + territorypos[1]*placement.width;
|
||||
// default angle = 3*Math.PI/4;
|
||||
return { "x": x, "z": z, "angle": 3*Math.PI/4, "xx": secondBest[0], "zz": secondBest[1],
|
||||
"base": gameState.ai.HQ.basesMap.map[territoryIndex] };
|
||||
return { "x": x, "z": z, "angle": 3*Math.PI/4, "xx": xx, "zz": zz, "base": gameState.ai.HQ.basesMap.map[territoryIndex] };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -348,14 +363,16 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
{
|
||||
var template = this.template;
|
||||
|
||||
var cellSize = gameState.cellSize; // size of each tile
|
||||
var territoryMap = gameState.ai.HQ.territoryMap;
|
||||
|
||||
var obstructions = m.createObstructionMap(gameState, 0, template);
|
||||
//obstructions.dumpIm(template.buildCategory() + "_obstructions.png");
|
||||
|
||||
var bestIdx = undefined;
|
||||
var bestVal = 0;
|
||||
var bestJdx = undefined;
|
||||
var bestAngle = undefined;
|
||||
var bestLand = undefined;
|
||||
var bestVal = -1;
|
||||
var landPassMap = gameState.ai.accessibility.landPassMap;
|
||||
var navalPassMap = gameState.ai.accessibility.navalPassMap;
|
||||
|
||||
@@ -366,69 +383,98 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
if (this.metadata.proximity)
|
||||
proxyAccess = gameState.ai.accessibility.getAccessValue(this.metadata.proximity);
|
||||
|
||||
var radius = Math.ceil(template.obstructionRadius() / obstructions.cellSize);
|
||||
|
||||
var halfSize = 0; // used for dock angle
|
||||
var halfDepth = 0; // used by checkPlacement
|
||||
var halfWidth = 0; // used by checkPlacement
|
||||
if (template.get("Footprint/Square"))
|
||||
{
|
||||
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
|
||||
halfDepth = +template.get("Footprint/Square/@depth") / 2;
|
||||
halfWidth = +template.get("Footprint/Square/@width") / 2;
|
||||
}
|
||||
else if (template.get("Footprint/Circle"))
|
||||
{
|
||||
halfSize = +template.get("Footprint/Circle/@radius");
|
||||
halfDepth = halfSize;
|
||||
halfWidth = halfSize;
|
||||
}
|
||||
|
||||
var maxres = 10;
|
||||
for (let j = 0; j < territoryMap.length; ++j)
|
||||
{
|
||||
if (obstructions.map[j] <= 0)
|
||||
var i = territoryMap.getNonObstructedTile(j, radius, obstructions);
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
var landAccess = this.getLandAccess(gameState, i, radius+1, obstructions.width);
|
||||
if (landAccess.size == 0)
|
||||
continue;
|
||||
if (this.metadata)
|
||||
{
|
||||
if (this.metadata.land && this.metadata.land.indexOf(landPassMap[j]) === -1)
|
||||
if (this.metadata.land && !landAccess.has(+this.metadata.land))
|
||||
continue;
|
||||
if (this.metadata.sea && navalPassMap[j] !== this.metadata.sea)
|
||||
if (this.metadata.sea && navalPassMap[i] != +this.metadata.sea)
|
||||
continue;
|
||||
if (nbShips === 0 && proxyAccess && proxyAccess > 1 && landPassMap[j] !== proxyAccess)
|
||||
if (nbShips === 0 && proxyAccess && proxyAccess > 1 && !landAccess.has(proxyAccess))
|
||||
continue;
|
||||
}
|
||||
let tileOwner = territoryMap.getOwnerIndex(j);
|
||||
if (tileOwner !== 0 && gameState.isPlayerEnemy(tileOwner))
|
||||
continue;
|
||||
|
||||
var res = Math.min(maxres, this.getResourcesAround(gameState, j, 80));
|
||||
|
||||
var dist;
|
||||
if (this.metadata.proximity)
|
||||
{
|
||||
// if proximity is given, we look for the nearest point
|
||||
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
|
||||
let dist = API3.SquareVectorDistance(this.metadata.proximity, pos);
|
||||
if (bestIdx !== undefined && dist > bestVal)
|
||||
continue;
|
||||
bestVal = dist;
|
||||
bestIdx = j;
|
||||
var pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
|
||||
dist = API3.SquareVectorDistance(this.metadata.proximity, pos);
|
||||
dist = Math.sqrt(dist) + 15 * (maxres - res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not in our (or allied) territory, we do not want it too far to be able to defend it
|
||||
let nearby = m.getFrontierProximity(gameState, j);
|
||||
if (nearby > 4)
|
||||
dist = m.getFrontierProximity(gameState, j);
|
||||
if (dist > 4)
|
||||
continue;
|
||||
bestVal = 1;
|
||||
bestIdx = j;
|
||||
dist = dist + 0.4 * (maxres - res)
|
||||
}
|
||||
if (bestIdx !== undefined && dist > bestVal)
|
||||
continue;
|
||||
|
||||
var x = ((i % obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var angle = this.getDockAngle(gameState, x, z, halfSize);
|
||||
if (angle === false)
|
||||
continue;
|
||||
var land = this.checkDockPlacement(gameState, x, z, halfDepth, halfWidth, angle);
|
||||
if (land < 2 || !gameState.ai.HQ.landRegions[land])
|
||||
continue;
|
||||
if (this.metadata.proximity && gameState.ai.accessibility.regionSize[land] < 4000)
|
||||
continue;
|
||||
|
||||
bestVal = dist;
|
||||
bestIdx = i;
|
||||
bestJdx = j;
|
||||
bestAngle = angle;
|
||||
bestLand = land;
|
||||
}
|
||||
|
||||
if (bestVal <= 0)
|
||||
if (bestVal < 0)
|
||||
return false;
|
||||
|
||||
var x = ((bestIdx % territoryMap.width) + 0.5) * cellSize;
|
||||
var z = (Math.floor(bestIdx / territoryMap.width) + 0.5) * cellSize;
|
||||
|
||||
// Needed for dock placement whose position will be changed
|
||||
var access = gameState.ai.accessibility.getAccessValue([x, z]);
|
||||
|
||||
// for Dock placement, we need to improve the position of the building as the position given here
|
||||
// is only the position on the shore, while the need the position of the center of the building
|
||||
// We also need to find the angle of the building
|
||||
var angle = this.getDockAngle(gameState, x, z);
|
||||
if (angle === false)
|
||||
return false;
|
||||
var x = ((bestIdx % obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
var z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
|
||||
|
||||
// Assign this dock to a base
|
||||
var baseIndex = gameState.ai.HQ.basesMap.map[bestIdx];
|
||||
var baseIndex = gameState.ai.HQ.basesMap.map[bestJdx];
|
||||
if (!baseIndex)
|
||||
{
|
||||
for (let base of gameState.ai.HQ.baseManagers)
|
||||
{
|
||||
if (!base.anchor || !base.anchor.position())
|
||||
continue;
|
||||
if (base.accessIndex !== access)
|
||||
if (base.accessIndex !== bestLand)
|
||||
continue;
|
||||
baseIndex = base.ID;
|
||||
break;
|
||||
@@ -442,32 +488,28 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState)
|
||||
}
|
||||
}
|
||||
|
||||
return { "x": x, "z": z, "angle": angle, "xx": x, "zz": z, "base": baseIndex, "access": access };
|
||||
return { "x": x, "z": z, "angle": bestAngle, "xx": x, "zz": z, "base": baseIndex, "access": bestLand };
|
||||
};
|
||||
|
||||
// Algorithm taken from the function GetDockAngle in helpers/Commands.js
|
||||
m.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z)
|
||||
// Algorithm taken from the function GetDockAngle in simulation/helpers/Commands.js
|
||||
m.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z, size)
|
||||
{
|
||||
var radius = this.template.obstructionRadius();
|
||||
if (!radius)
|
||||
return false;
|
||||
|
||||
var pos = gameState.ai.accessibility.gamePosToMapPos([x, z]);
|
||||
var j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
var seaRef = gameState.ai.accessibility.navalPassMap[j];
|
||||
if (seaRef < 2)
|
||||
return false;
|
||||
const numPoints = 16;
|
||||
for (var dist = 0; dist < 2; ++dist)
|
||||
for (var dist = 0; dist < 4; ++dist)
|
||||
{
|
||||
var waterPoints = [];
|
||||
for (var i = 0; i < numPoints; ++i)
|
||||
{
|
||||
var angle = (i/numPoints)*2*Math.PI;
|
||||
var pos = [ x - (2+dist)*radius*Math.sin(angle), z + (2+dist)*radius*Math.cos(angle)];
|
||||
var pos = gameState.ai.accessibility.gamePosToMapPos(pos);
|
||||
var j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
var seaAccess = gameState.ai.accessibility.navalPassMap[j];
|
||||
var landAccess = gameState.ai.accessibility.landPassMap[j];
|
||||
if (seaAccess == seaRef && landAccess < 2)
|
||||
let angle = (i/numPoints)*2*Math.PI;
|
||||
pos = [x - (1+dist)*size*Math.sin(angle), z + (1+dist)*size*Math.cos(angle)];
|
||||
pos = gameState.ai.accessibility.gamePosToMapPos(pos);
|
||||
let j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
if (gameState.ai.accessibility.navalPassMap[j] == seaRef)
|
||||
waterPoints.push(i);
|
||||
}
|
||||
var length = waterPoints.length;
|
||||
@@ -504,6 +546,126 @@ m.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z)
|
||||
return false;
|
||||
};
|
||||
|
||||
// Algorithm taken from checkPlacement in simulation/components/BuildRestriction.js
|
||||
// to determine the special dock requirements
|
||||
m.ConstructionPlan.prototype.checkDockPlacement = function(gameState, x, z, halfDepth, halfWidth, angle)
|
||||
{
|
||||
let sz = halfDepth * Math.sin(angle);
|
||||
let cz = halfDepth * Math.cos(angle);
|
||||
// center back position
|
||||
let pos = gameState.ai.accessibility.gamePosToMapPos([x - sz, z - cz]);
|
||||
let j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
let ret = gameState.ai.accessibility.landPassMap[j];
|
||||
if (ret < 2)
|
||||
return 0;
|
||||
// center front position
|
||||
pos = gameState.ai.accessibility.gamePosToMapPos([x + sz, z + cz]);
|
||||
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
if (gameState.ai.accessibility.landPassMap[j] > 1 || gameState.ai.accessibility.navalPassMap[j] < 2)
|
||||
return 0;
|
||||
// additional constraints compared to BuildRestriction.js to assure we have enough place to build
|
||||
let sw = halfWidth * Math.cos(angle) * 3 / 4;
|
||||
let cw = halfWidth * Math.sin(angle) * 3 / 4;
|
||||
pos = gameState.ai.accessibility.gamePosToMapPos([x - sz + sw, z - cz - cw]);
|
||||
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
if (gameState.ai.accessibility.landPassMap[j] != ret)
|
||||
return 0;
|
||||
pos = gameState.ai.accessibility.gamePosToMapPos([x - sz - sw, z - cz + cw]);
|
||||
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
|
||||
if (gameState.ai.accessibility.landPassMap[j] != ret)
|
||||
return 0;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// get the list of all the land access from this position
|
||||
m.ConstructionPlan.prototype.getLandAccess = function(gameState, i, radius, w)
|
||||
{
|
||||
var access = new Set();
|
||||
var landPassMap = gameState.ai.accessibility.landPassMap;
|
||||
var kx = i % w;
|
||||
var ky = Math.floor(i / w);
|
||||
var land;
|
||||
for (let dy = 0; dy <= radius; ++dy)
|
||||
{
|
||||
let dxmax = radius - dy;
|
||||
let xp = kx + (ky + dy)*w;
|
||||
let xm = kx + (ky - dy)*w;
|
||||
for (let dx = -dxmax; dx <= dxmax; ++dx)
|
||||
{
|
||||
if (kx + dx < 0 || kx + dx >= w)
|
||||
continue;
|
||||
if (ky + dy >= 0 && ky + dy < w)
|
||||
{
|
||||
land = landPassMap[xp + dx];
|
||||
if (land > 1 && !access.has(land))
|
||||
access.add(land);
|
||||
}
|
||||
if (ky - dy >= 0 && ky - dy < w)
|
||||
{
|
||||
land = landPassMap[xm + dx];
|
||||
if (land > 1 && !access.has(land))
|
||||
access.add(land);
|
||||
}
|
||||
}
|
||||
}
|
||||
return access;
|
||||
};
|
||||
|
||||
// get the sum of the resources (except food) around, inside a given radius
|
||||
// resources have a weight (1 if dist=0 and 0 if dist=size) doubled for wood
|
||||
m.ConstructionPlan.prototype.getResourcesAround = function(gameState, i, radius)
|
||||
{
|
||||
let resourceMaps = gameState.sharedScript.resourceMaps;
|
||||
let w = resourceMaps["wood"].width;
|
||||
let cellSize = resourceMaps["wood"].cellSize;
|
||||
let size = Math.floor(radius / cellSize);
|
||||
let ix = i % w;
|
||||
let iy = Math.floor(i / w);
|
||||
let total = 0;
|
||||
let nbcell = 0;
|
||||
for (let k in resourceMaps)
|
||||
{
|
||||
if (k === "food")
|
||||
continue;
|
||||
let weigh0 = (k === "wood") ? 2 : 1;
|
||||
for (let dy = 0; dy <= size; ++dy)
|
||||
{
|
||||
let dxmax = size - dy;
|
||||
let ky = iy + dy;
|
||||
if (ky >= 0 && ky < w)
|
||||
{
|
||||
for (let dx = -dxmax; dx <= dxmax; ++dx)
|
||||
{
|
||||
let kx = ix + dx;
|
||||
if (kx < 0 || kx >= w)
|
||||
continue;
|
||||
let ddx = (dx > 0) ? dx : -dx;
|
||||
let weight = weigh0 * (dxmax - ddx) / size;
|
||||
total += weight * resourceMaps[k].map[kx + w * ky];
|
||||
nbcell += weight;
|
||||
}
|
||||
}
|
||||
if (dy == 0)
|
||||
continue;
|
||||
ky = iy - dy;
|
||||
if (ky >= 0 && ky < w)
|
||||
{
|
||||
for (let dx = -dxmax; dx <= dxmax; ++dx)
|
||||
{
|
||||
let kx = ix + dx;
|
||||
if (kx < 0 || kx >= w)
|
||||
continue;
|
||||
let ddx = (dx > 0) ? dx : -dx;
|
||||
let weight = weigh0 * (dxmax - ddx) / size;
|
||||
total += weight * resourceMaps[k].map[kx + w * ky];
|
||||
nbcell += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (nbcell ? (total / nbcell) : 0);
|
||||
};
|
||||
|
||||
m.ConstructionPlan.prototype.Serialize = function()
|
||||
{
|
||||
let prop = {
|
||||
|
||||
@@ -88,6 +88,7 @@ m.HQ.prototype.assignStartingEntities = function(gameState)
|
||||
this.navalRegions[sea] = true;
|
||||
|
||||
// if garrisoned units inside, ungarrison them except if a ship in which case we will make a transport
|
||||
// when a construction will start (see createTransportIfNeeded)
|
||||
if (ent.isGarrisonHolder() && ent.garrisoned().length && !ent.hasClass("Ship"))
|
||||
for (let id of ent.garrisoned())
|
||||
ent.unload(id);
|
||||
|
||||
Reference in New Issue
Block a user